Files
kiri-wchat/wx/V3/WxV3PaymentTait.php
T

171 lines
5.5 KiB
PHP
Raw Normal View History

2022-09-09 16:42:55 +08:00
<?php
namespace wchat\wx\V3;
use Exception;
2023-11-13 21:20:10 +08:00
use Kiri\Client;
2023-12-06 16:37:52 +08:00
use wchat\common\AppConfig;
2022-09-09 16:42:55 +08:00
use wchat\common\Help;
2023-11-13 22:13:44 +08:00
/**
* Bytes Length of the AES block
*/
const BLOCK_SIZE = 16;
/**
* The `aes-256-gcm` algorithm string
*/
const ALGO_AES_256_GCM = 'aes-256-gcm';
2022-09-09 16:42:55 +08:00
trait WxV3PaymentTait
{
2023-11-07 15:39:46 +08:00
/**
2023-11-14 14:10:36 +08:00
* @param string $orderNo
* @param int $total
2023-11-07 15:39:46 +08:00
* @return array
*/
2023-11-14 14:10:36 +08:00
public function getInitCore(string $orderNo, int $total): array
2023-11-07 15:39:46 +08:00
{
2023-11-14 00:06:57 +08:00
$payConfig = $this->getPayConfig();
if ($payConfig->typeIsApp()) {
2023-11-14 09:29:44 +08:00
$body['appid'] = $payConfig->pay->wx->appId;
2023-11-14 00:06:57 +08:00
} else {
2023-11-14 09:29:44 +08:00
$body['appid'] = $payConfig->appId;
2023-11-14 00:06:57 +08:00
}
$body['mchid'] = $payConfig->pay->wx->mchId;
$body['description'] = $payConfig->getBody();
2023-11-07 15:39:46 +08:00
$body['out_trade_no'] = $orderNo;
2023-11-14 00:06:57 +08:00
$body['notify_url'] = $payConfig->getNotifyUrl();
$body['amount'] = ['total' => $total, 'currency' => $payConfig->getCurrency()];
2023-11-07 15:39:46 +08:00
return $body;
}
2023-11-13 21:20:10 +08:00
/**
* @param string $sign
* @param string $json
* @return Client
*/
public function createClient(string $sign, string $json): Client
{
2023-12-06 16:44:46 +08:00
$client = new Client('api.mch.weixin.qq.com', 80, TRUE);
2023-11-13 21:20:10 +08:00
$client->withAddedHeader('Authorization', $sign)
->withContentType('application/json')->withAddedHeader('User-Agent', 'application/json')
2023-12-06 16:43:46 +08:00
->withAddedHeader("Accept", "*/*");
2023-12-06 16:50:53 +08:00
if (!empty($json)) {
2023-12-06 16:43:46 +08:00
$client->withBody($json);
}
2023-11-14 09:29:44 +08:00
$proxyHost = $this->getPayConfig()->getProxyHost();
$proxyPort = $this->getPayConfig()->getProxyPort();
2023-11-13 21:20:10 +08:00
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
return $client;
}
2023-12-06 16:37:52 +08:00
/**
* @param string $orderNo
* @return array
* @throws Exception
*/
2023-12-06 16:39:11 +08:00
public function searchByOutTradeNo(string $orderNo): array
2023-12-06 16:37:52 +08:00
{
2023-12-06 16:56:09 +08:00
$config = $this->getPayConfig();
$parseUrl = '/v3/pay/transactions/out-trade-no/' . $orderNo;
2023-12-06 17:03:01 +08:00
$sign = $this->signature('POST', $parseUrl . '?mchid=' . $config->pay->wx->mchId);
$client = $this->createClient($sign, '');
2023-12-06 16:56:55 +08:00
$client->get($parseUrl, ['mchid' => $config->pay->wx->mchId]);
2023-12-06 16:37:52 +08:00
$client->close();
2023-12-06 16:53:39 +08:00
return json_decode($client->getBody(), TRUE);
2023-12-06 16:37:52 +08:00
}
2023-11-07 15:39:46 +08:00
/**
* @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
{
2023-11-14 00:06:57 +08:00
$payConfig = $this->getPayConfig();
2023-11-13 21:20:10 +08:00
$rand = md5(random_bytes(32));
$time = time();
2023-11-07 16:09:40 +08:00
$message = sprintf("%s\n%s\n%d\n%s\n%s\n", $http_method, $canonical_url, $time, $rand, $body);
2023-11-07 15:39:46 +08:00
$sign = $this->openssl_signature($message);
2023-11-14 00:06:57 +08:00
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);
2023-11-07 15:39:46 +08:00
}
/**
2023-11-14 14:10:36 +08:00
* @param string $body
2023-11-07 15:39:46 +08:00
* @return string
*/
2023-11-14 14:10:36 +08:00
public function openssl_signature(string $body): string
2023-11-07 15:39:46 +08:00
{
2023-11-14 00:06:57 +08:00
$payConfig = $this->getPayConfig();
2023-11-07 15:39:46 +08:00
2023-11-14 12:39:28 +08:00
$mch_private_key = openssl_get_privatekey(file_get_contents($payConfig->pay->wx->mchKey));
2023-11-07 15:39:46 +08:00
openssl_sign($body, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
return base64_encode($raw_sign);
}
/**
2023-11-14 14:10:36 +08:00
* @param array $json
* @param array $body
2023-11-07 15:39:46 +08:00
* @return array
*/
2023-11-14 14:10:36 +08:00
private function createResponse(array $json, array $body): array
2023-11-07 15:39:46 +08:00
{
$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;
}
/**
2023-11-13 22:13:44 +08:00
* @param string $ciphertext
2023-11-13 22:59:34 +08:00
* @param string $v3Key
2023-11-13 22:13:44 +08:00
* @param string $iv
* @param string $aad
* @return array
2023-11-07 15:39:46 +08:00
*/
2023-11-13 22:59:34 +08:00
public function decrypt(string $ciphertext, string $v3Key, string $iv = '', string $aad = ''): array
2023-11-07 15:39:46 +08:00
{
2023-11-13 22:13:44 +08:00
$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.');
2023-11-07 15:39:46 +08:00
}
2023-11-13 22:59:34 +08:00
$plaintext = openssl_decrypt(substr($ciphertext, 0, $tailLength), ALGO_AES_256_GCM, $v3Key, OPENSSL_RAW_DATA, $iv, $authTag, $aad);
2023-11-13 22:13:44 +08:00
if (false === $plaintext) {
throw new \UnexpectedValueException('Decrypting the input $ciphertext failed, please checking your $key and $iv whether or nor correct.');
2023-11-07 15:39:46 +08:00
}
2023-11-13 22:13:44 +08:00
return json_decode($plaintext, true);
2023-11-07 15:39:46 +08:00
}
2022-09-09 16:42:55 +08:00
}