Files
kiri-wchat/wx/V3/WxV3PaymentTait.php
T
2023-12-06 17:03:01 +08:00

171 lines
5.5 KiB
PHP

<?php
namespace wchat\wx\V3;
use Exception;
use Kiri\Client;
use wchat\common\AppConfig;
use wchat\common\Help;
/**
* Bytes Length of the AES block
*/
const BLOCK_SIZE = 16;
/**
* The `aes-256-gcm` algorithm string
*/
const ALGO_AES_256_GCM = 'aes-256-gcm';
trait WxV3PaymentTait
{
/**
* @param string $orderNo
* @param int $total
* @return array
*/
public function getInitCore(string $orderNo, int $total): array
{
$payConfig = $this->getPayConfig();
if ($payConfig->typeIsApp()) {
$body['appid'] = $payConfig->pay->wx->appId;
} else {
$body['appid'] = $payConfig->appId;
}
$body['mchid'] = $payConfig->pay->wx->mchId;
$body['description'] = $payConfig->getBody();
$body['out_trade_no'] = $orderNo;
$body['notify_url'] = $payConfig->getNotifyUrl();
$body['amount'] = ['total' => $total, 'currency' => $payConfig->getCurrency()];
return $body;
}
/**
* @param string $sign
* @param string $json
* @return Client
*/
public function createClient(string $sign, string $json): Client
{
$client = new Client('api.mch.weixin.qq.com', 80, TRUE);
$client->withAddedHeader('Authorization', $sign)
->withContentType('application/json')->withAddedHeader('User-Agent', 'application/json')
->withAddedHeader("Accept", "*/*");
if (!empty($json)) {
$client->withBody($json);
}
$proxyHost = $this->getPayConfig()->getProxyHost();
$proxyPort = $this->getPayConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
return $client;
}
/**
* @param string $orderNo
* @return array
* @throws Exception
*/
public function searchByOutTradeNo(string $orderNo): array
{
$config = $this->getPayConfig();
$parseUrl = '/v3/pay/transactions/out-trade-no/' . $orderNo;
$sign = $this->signature('POST', $parseUrl . '?mchid=' . $config->pay->wx->mchId);
$client = $this->createClient($sign, '');
$client->get($parseUrl, ['mchid' => $config->pay->wx->mchId]);
$client->close();
return json_decode($client->getBody(), TRUE);
}
/**
* @param string $http_method
* @param string $canonical_url
* @param string $body
* @return string
* @throws Exception
*/
public function signature(string $http_method, string $canonical_url, string $body = ''): string
{
$payConfig = $this->getPayConfig();
$rand = md5(random_bytes(32));
$time = time();
$message = sprintf("%s\n%s\n%d\n%s\n%s\n", $http_method, $canonical_url, $time, $rand, $body);
$sign = $this->openssl_signature($message);
return sprintf('%s mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $payConfig->pay->wx->schema,
$payConfig->pay->wx->mchId, $rand, $time, $payConfig->pay->wx->SerialNumber, $sign);
}
/**
* @param string $body
* @return string
*/
public function openssl_signature(string $body): string
{
$payConfig = $this->getPayConfig();
$mch_private_key = openssl_get_privatekey(file_get_contents($payConfig->pay->wx->mchKey));
openssl_sign($body, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
return base64_encode($raw_sign);
}
/**
* @param array $json
* @param array $body
* @return array
*/
private function createResponse(array $json, array $body): array
{
$responseArray['appId'] = $body['appid'];
$responseArray['timeStamp'] = (string)time();
$responseArray['nonceStr'] = Help::random(32);
$responseArray['package'] = "prepay_id=" . $json['prepay_id'];
$responseBody = $responseArray['appId'] . PHP_EOL . $responseArray['timeStamp'] . PHP_EOL . $responseArray['nonceStr'] . PHP_EOL . $responseArray['package'] . PHP_EOL;
$responseArray['signType'] = 'RSA';
$responseArray['signBody'] = $responseBody;
$responseArray['paySign'] = $this->openssl_signature($responseBody);
$responseArray['prepay_id'] = $json['prepay_id'];
return $responseArray;
}
/**
* @param string $ciphertext
* @param string $v3Key
* @param string $iv
* @param string $aad
* @return array
*/
public function decrypt(string $ciphertext, string $v3Key, string $iv = '', string $aad = ''): array
{
$ciphertext = base64_decode($ciphertext);
$authTag = substr($ciphertext, $tailLength = 0 - BLOCK_SIZE);
$tagLength = strlen($authTag);
/* Manually checking the length of the tag, because the `openssl_decrypt` was mentioned there, it's the caller's responsibility. */
if ($tagLength > BLOCK_SIZE || ($tagLength < 12 && $tagLength !== 8 && $tagLength !== 4)) {
throw new \RuntimeException('The inputs `$ciphertext` incomplete, the bytes length must be one of 16, 15, 14, 13, 12, 8 or 4.');
}
$plaintext = openssl_decrypt(substr($ciphertext, 0, $tailLength), ALGO_AES_256_GCM, $v3Key, OPENSSL_RAW_DATA, $iv, $authTag, $aad);
if (false === $plaintext) {
throw new \UnexpectedValueException('Decrypting the input $ciphertext failed, please checking your $key and $iv whether or nor correct.');
}
return json_decode($plaintext, true);
}
}