This commit is contained in:
xl
2023-11-13 21:20:10 +08:00
parent 90c65a8c5f
commit 0d1cb506aa
10 changed files with 266 additions and 61 deletions
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace wchat\wx\V3\Notify;
class GoodsDetail
{
public string $goods_remark = "商品备注信息";
public int $quantity = 1;
public int $discount_amount = 1;
public string $goods_id = "M1006";
public int $unit_price = 100;
/**
* @param array $value
*/
public function __construct(readonly public array $value)
{
$this->goods_remark = $this->value['goods_remark'];
$this->quantity = $this->value['quantity'];
$this->discount_amount = $this->value['discount_amount'];
$this->goods_id = $this->value['goods_id'];
$this->unit_price = $this->value['unit_price'];
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
namespace wchat\wx\V3\Notify;
use JetBrains\PhpStorm\ArrayShape;
class NotifyModel
{
//公众号支付
const PAY_TYPE_JSAPI = 'JSAPI';
//扫码支付
const PAY_TYPE_NATIVE = 'NATIVE';
//App支付
const PAY_TYPE_App = 'App';
//付款码支付
const PAY_TYPE_MICROPAY = 'MICROPAY';
//H5支付
const PAY_TYPE_MWEB = 'MWEB';
//刷脸支付
const PAY_TYPE_FACEPAY = 'FACEPAY';
// 支付成功
const PAY_RESULT_SUCCESS = 'SUCCESS';
// 转入退款
const PAY_RESULT_REFUND = 'REFUND';
// 未支付
const PAY_RESULT_NOTPAY = 'NOTPAY';
// 已关闭
const PAY_RESULT_CLOSED = 'CLOSED';
// 已撤销(付款码支付)
const PAY_RESULT_REVOKED = 'REVOKED';
// 用户支付中(付款码支付)
const PAY_RESULT_USERPAYING = 'USERPAYING';
// 支付失败(其他原因,如银行返回失败)
const PAY_RESULT_PAYERROR = 'PAYERROR';
public string $appid;
public string $mchid;
public string $out_trade_no;
public string $transaction_id;
public string $trade_type;
public string $trade_state;
public string $trade_state_desc;
public string $bank_type;
public string $attach;
public string $success_time;
/**
* @var array|string[]
*/
#[ArrayShape(['openid' => 'string'])]
public array $payer = ['openid' => ''];
/**
* @var array
*/
#[ArrayShape(['payer_total' => 'int', 'total' => 'int', 'currency' => 'string', 'payer_currency' => 'string'])]
public array $amount = [
"payer_total" => 100,
"total" => 100,
"currency" => "CNY",
"payer_currency" => "CNY"
];
/**
* @var array|string[]
*/
#[ArrayShape(['device_id' => 'string'])]
public array $scene_info = [
'device_id' => ''
];
/**
* @var array<PromotionDetail>
*/
public array $promotion_detail = [];
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace wchat\wx\V3\Notify;
class PromotionDetail
{
public int $amount = 100;
public int $wechatpay_contribute = 0;
public string $coupon_id = "109519";
public string $scope = "GLOBAL";
public int $merchant_contribute = 0;
public string $name = "单品惠-6";
public int $other_contribute = 0;
public string $currency = "CNY";
public string $stock_id = "931386";
/**
* @var array<GoodsDetail>
*/
public array $goods_detail = [];
}
+1 -10
View File
@@ -33,16 +33,7 @@ class TransferBatches extends SmallProgram
$sign = $this->signature('POST', '/v3/transfer/batches', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
$client = new Client('api.mch.weixin.qq.com', 443, TRUE);
$client->withAddedHeader('Authorization', $sign)->withContentType('application/json')
->withAgent('application/json')->withBody($json)->withAddedHeader("Accept", "*/*");
$proxyHost = $this->getConfig()->getProxyHost();
$proxyPort = $this->getConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
$client = $this->createClient($sign, $json);
$client->post('/v3/transfer/batches');
$client->close();
+1 -10
View File
@@ -29,16 +29,7 @@ class WxV3AppPayment extends SmallProgram
$sign = $this->signature('POST', '/v3/pay/transactions/components', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
$client = new Client('api.mch.weixin.qq.com', 443, TRUE);
$client->withAddedHeader('Authorization', $sign)->withContentType('application/json')
->withAgent('application/json')->withBody($json)->withAddedHeader("Accept", "*/*");
$proxyHost = $this->getConfig()->getProxyHost();
$proxyPort = $this->getConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
$client = $this->createClient($sign, $json);
$client->post('/v3/pay/transactions/components');
$client->close();
+1 -10
View File
@@ -29,16 +29,7 @@ class WxV3NativePayment extends SmallProgram
$sign = $this->signature('POST', '/v3/pay/transactions/native', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
$client = new Client('api.mch.weixin.qq.com', 443, TRUE);
$client->withAddedHeader('Authorization', $sign)->withContentType('application/json')
->withAgent('application/json')->withBody($json)->withAddedHeader("Accept", "*/*");
$proxyHost = $this->getConfig()->getProxyHost();
$proxyPort = $this->getConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
$client = $this->createClient($sign, $json);
$client->post('/v3/pay/transactions/native');
$client->close();
+1 -11
View File
@@ -29,17 +29,7 @@ class WxV3Payment extends SmallProgram
$sign = $this->signature('POST', '/v3/pay/transactions/jsapi', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
$client = new Client('api.mch.weixin.qq.com', 443, TRUE);
$client->withAddedHeader('Authorization', $sign)
->withContentType('application/json')->withAddedHeader('User-Agent', 'application/json')
->withBody($json)->withAddedHeader("Accept", "*/*");
$proxyHost = $this->getConfig()->getProxyHost();
$proxyPort = $this->getConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
$client = $this->createClient($sign, $json);
$client->post('/v3/pay/transactions/jsapi');
$client->close();
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace wchat\wx\V3;
use Exception;
use wchat\wx\SmallProgram;
use wchat\wx\V3\Notify\GoodsDetail;
use wchat\wx\V3\Notify\NotifyModel;
use wchat\wx\V3\Notify\PromotionDetail;
class WxV3PaymentNotify extends SmallProgram
{
use WxV3PaymentTait;
/**
* @param string $id
* @param string $create_time
* @param string $resource_type
* @param string $event_type
* @param string $summary
* @param array $resource
* @throws Exception
*/
public function __construct(
public string $id = "EV-2018022511223320873",
public string $create_time = "2015-05-20T13:29:35+08:00",
public string $resource_type = "encrypt-resource",
public string $event_type = "TRANSACTION.SUCCESS",
public string $summary = "支付成功",
public array $resource = []
)
{
$this->decode();
}
/**
* @var NotifyModel
*/
public NotifyModel $notifyModel;
/**
* @return void
* @throws Exception
*/
public function decode(): void
{
$data = $this->decryptToString($this->resource);
if ($data === false) {
throw new Exception('消息体格式错误, 解码失败.');
}
$data = json_decode($data, true);
$this->notifyModel = new NotifyModel();
$this->notifyModel->amount = $data['amount'];
$this->notifyModel->payer = $data['payer'];
$this->notifyModel->scene_info = $data['payer'];
$this->notifyModel->appid = $data['appid'];
$this->notifyModel->mchid = $data['mchid'];
$this->notifyModel->out_trade_no = $data['out_trade_no'];
$this->notifyModel->transaction_id = $data['transaction_id'];
$this->notifyModel->trade_type = $data['trade_type'];
$this->notifyModel->trade_state = $data['trade_state'];
$this->notifyModel->trade_state_desc = $data['trade_state_desc'];
$this->notifyModel->bank_type = $data['bank_type'];
$this->notifyModel->attach = $data['attach'];
$this->notifyModel->success_time = $data['success_time'];
$this->notifyModel->promotion_detail = [];
foreach ($data['promotion_detail'] as $datum) {
$detail = new PromotionDetail();
$detail->amount = $datum['amount'];
$detail->wechatpay_contribute = $datum['wechatpay_contribute'];
$detail->coupon_id = $datum['coupon_id'];
$detail->scope = $datum['scope'];
$detail->merchant_contribute = $datum['merchant_contribute'];
$detail->name = $datum['name'];
$detail->other_contribute = $datum['other_contribute'];
$detail->currency = $datum['currency'];
$detail->stock_id = $datum['stock_id'];
$detail->goods_detail = [];
foreach ($datum['goods_detail'] as $value) {
$detail->goods_detail[] = new GoodsDetail($value);
}
$this->notifyModel->promotion_detail[] = $detail;
}
}
}
+27 -6
View File
@@ -3,6 +3,7 @@
namespace wchat\wx\V3;
use Exception;
use Kiri\Client;
use wchat\common\Help;
use function Sodium\crypto_aead_aes256gcm_decrypt;
use function Sodium\crypto_aead_aes256gcm_is_available;
@@ -30,6 +31,27 @@ trait WxV3PaymentTait
}
/**
* @param string $sign
* @param string $json
* @return Client
*/
public function createClient(string $sign, string $json): Client
{
$client = new Client('api.mch.weixin.qq.com', 443, TRUE);
$client->withAddedHeader('Authorization', $sign)
->withContentType('application/json')->withAddedHeader('User-Agent', 'application/json')
->withBody($json)->withAddedHeader("Accept", "*/*");
$proxyHost = $this->getConfig()->getProxyHost();
$proxyPort = $this->getConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
return $client;
}
/**
* @param string $http_method
* @param string $canonical_url
@@ -90,13 +112,12 @@ trait WxV3PaymentTait
/**
* @param $associatedData
* @param $nonceStr
* @param $ciphertext
* @param array $resource
* @return bool|string
*/
public function decryptToString($associatedData, $nonceStr, $ciphertext): bool|string
public function decryptToString(array $resource): bool|string
{
[$associatedData, $nonceStr, $cipher_algo, $ciphertext] = [$resource['associated_data'], $resource['nonce'], $resource['nonce'], $resource['ciphertext']];
$ciphertext = \base64_decode($ciphertext);
if (strlen($ciphertext) <= AUTH_TAG_LENGTH_BYTE) {
return FALSE;
@@ -107,10 +128,10 @@ trait WxV3PaymentTait
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && crypto_aead_aes256gcm_is_available()) {
return crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, 'XGwwZbmMXy6sD5w0IrxfaBHLl7b7jCaR');
}
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
if (PHP_VERSION_ID >= 70100 && in_array($cipher_algo, \openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -AUTH_TAG_LENGTH_BYTE);
$authTag = substr($ciphertext, -AUTH_TAG_LENGTH_BYTE);
return \openssl_decrypt($ctext, 'aes-256-gcm', 'XGwwZbmMXy6sD5w0IrxfaBHLl7b7jCaR', \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData);
return \openssl_decrypt($ctext, $cipher_algo, 'XGwwZbmMXy6sD5w0IrxfaBHLl7b7jCaR', \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData);
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}
+1 -12
View File
@@ -5,7 +5,6 @@ namespace wchat\wx\V3;
use Exception;
use JetBrains\PhpStorm\ArrayShape;
use Kiri\Client;
use Kiri\Message\Stream;
use wchat\wx\SmallProgram;
class WxV3Withdrawal extends SmallProgram
@@ -29,17 +28,7 @@ class WxV3Withdrawal extends SmallProgram
$sign = $this->signature('POST', '/v3/pay/transactions/batches', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
$client = new Client('api.mch.weixin.qq.com', 443, TRUE);
$client->withAddedHeader('Authorization', $sign)
->withContentType('application/json')->withAddedHeader('User-Agent', 'application/json')
->withBody($json)->withAddedHeader("Accept", "*/*");
$proxyHost = $this->getConfig()->getProxyHost();
$proxyPort = $this->getConfig()->getProxyPort();
if (!empty($proxyHost) && $proxyPort > 0) {
$client->withProxyHost($proxyHost)->withProxyPort($proxyPort);
}
$client = $this->createClient($sign, $json);
$client->post('/v3/pay/transactions/batches');
$client->close();