eee
This commit is contained in:
@@ -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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = [];
|
||||||
|
}
|
||||||
@@ -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 = [];
|
||||||
|
|
||||||
|
}
|
||||||
@@ -33,16 +33,7 @@ class TransferBatches extends SmallProgram
|
|||||||
|
|
||||||
$sign = $this->signature('POST', '/v3/transfer/batches', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
$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 = $this->createClient($sign, $json);
|
||||||
$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->post('/v3/transfer/batches');
|
$client->post('/v3/transfer/batches');
|
||||||
$client->close();
|
$client->close();
|
||||||
|
|
||||||
|
|||||||
@@ -29,16 +29,7 @@ class WxV3AppPayment extends SmallProgram
|
|||||||
|
|
||||||
$sign = $this->signature('POST', '/v3/pay/transactions/components', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
$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 = $this->createClient($sign, $json);
|
||||||
$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->post('/v3/pay/transactions/components');
|
$client->post('/v3/pay/transactions/components');
|
||||||
$client->close();
|
$client->close();
|
||||||
|
|
||||||
|
|||||||
@@ -29,16 +29,7 @@ class WxV3NativePayment extends SmallProgram
|
|||||||
|
|
||||||
$sign = $this->signature('POST', '/v3/pay/transactions/native', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
$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 = $this->createClient($sign, $json);
|
||||||
$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->post('/v3/pay/transactions/native');
|
$client->post('/v3/pay/transactions/native');
|
||||||
$client->close();
|
$client->close();
|
||||||
|
|
||||||
|
|||||||
@@ -29,17 +29,7 @@ class WxV3Payment extends SmallProgram
|
|||||||
|
|
||||||
$sign = $this->signature('POST', '/v3/pay/transactions/jsapi', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
$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 = $this->createClient($sign, $json);
|
||||||
$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->post('/v3/pay/transactions/jsapi');
|
$client->post('/v3/pay/transactions/jsapi');
|
||||||
$client->close();
|
$client->close();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace wchat\wx\V3;
|
namespace wchat\wx\V3;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Kiri\Client;
|
||||||
use wchat\common\Help;
|
use wchat\common\Help;
|
||||||
use function Sodium\crypto_aead_aes256gcm_decrypt;
|
use function Sodium\crypto_aead_aes256gcm_decrypt;
|
||||||
use function Sodium\crypto_aead_aes256gcm_is_available;
|
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 $http_method
|
||||||
* @param string $canonical_url
|
* @param string $canonical_url
|
||||||
@@ -39,8 +61,8 @@ trait WxV3PaymentTait
|
|||||||
*/
|
*/
|
||||||
public function signature(string $http_method, string $canonical_url, string $body = ''): string
|
public function signature(string $http_method, string $canonical_url, string $body = ''): string
|
||||||
{
|
{
|
||||||
$rand = md5(random_bytes(32));
|
$rand = md5(random_bytes(32));
|
||||||
$time = time();
|
$time = time();
|
||||||
|
|
||||||
$message = sprintf("%s\n%s\n%d\n%s\n%s\n", $http_method, $canonical_url, $time, $rand, $body);
|
$message = sprintf("%s\n%s\n%d\n%s\n%s\n", $http_method, $canonical_url, $time, $rand, $body);
|
||||||
|
|
||||||
@@ -90,13 +112,12 @@ trait WxV3PaymentTait
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $associatedData
|
* @param array $resource
|
||||||
* @param $nonceStr
|
|
||||||
* @param $ciphertext
|
|
||||||
* @return bool|string
|
* @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);
|
$ciphertext = \base64_decode($ciphertext);
|
||||||
if (strlen($ciphertext) <= AUTH_TAG_LENGTH_BYTE) {
|
if (strlen($ciphertext) <= AUTH_TAG_LENGTH_BYTE) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -107,10 +128,10 @@ trait WxV3PaymentTait
|
|||||||
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && crypto_aead_aes256gcm_is_available()) {
|
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && crypto_aead_aes256gcm_is_available()) {
|
||||||
return crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, 'XGwwZbmMXy6sD5w0IrxfaBHLl7b7jCaR');
|
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);
|
$ctext = substr($ciphertext, 0, -AUTH_TAG_LENGTH_BYTE);
|
||||||
$authTag = substr($ciphertext, -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');
|
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ namespace wchat\wx\V3;
|
|||||||
use Exception;
|
use Exception;
|
||||||
use JetBrains\PhpStorm\ArrayShape;
|
use JetBrains\PhpStorm\ArrayShape;
|
||||||
use Kiri\Client;
|
use Kiri\Client;
|
||||||
use Kiri\Message\Stream;
|
|
||||||
use wchat\wx\SmallProgram;
|
use wchat\wx\SmallProgram;
|
||||||
|
|
||||||
class WxV3Withdrawal extends 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));
|
$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 = $this->createClient($sign, $json);
|
||||||
$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->post('/v3/pay/transactions/batches');
|
$client->post('/v3/pay/transactions/batches');
|
||||||
$client->close();
|
$client->close();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user