Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 63e24922ad | |||
| 0a535c3a89 | |||
| 2f469d6b60 | |||
| 895988cf27 | |||
| 87c597c3fe | |||
| f536b12f00 | |||
| d3b0f436fa | |||
| f5b6bd54f4 | |||
| 59a3b45c77 | |||
| dd96d653d1 | |||
| 97d1c32310 | |||
| d7733525bc | |||
| 62e57b99a0 | |||
| 24655a4644 | |||
| 4ca64c8205 | |||
| d9dbfb3ca6 | |||
| 1ff0203511 | |||
| 561b105d31 | |||
| 3d423acec2 | |||
| d76680a7a2 | |||
| aeb4f3d7fb | |||
| a3aa8e8ac3 | |||
| de4f58d3e3 |
+3
-3
@@ -172,10 +172,10 @@ abstract class Subject extends Multiprogramming
|
||||
$client->post($this->getUrl() . '?access_token=' . $access_token, $params);
|
||||
$client->close();
|
||||
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$body = json_decode($client->getBody(), true);
|
||||
$body = json_decode($client->body, true);
|
||||
if (isset($body['errcode']) && $body['errcode'] != 0) {
|
||||
return new Result(code: $body['errcode'], message: $body['errmsg']);
|
||||
} else {
|
||||
|
||||
+3
-3
@@ -143,10 +143,10 @@ abstract class Template extends Multiprogramming
|
||||
$client->post($this->getUrl() . '?access_token=' . $access_token, $params);
|
||||
$client->close();
|
||||
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$body = json_decode($client->getBody(), true);
|
||||
$body = json_decode($client->body, true);
|
||||
if (isset($body['errcode']) && $body['errcode'] != 0) {
|
||||
return new Result(code: $body['errcode'], message: $body['errmsg']);
|
||||
}
|
||||
|
||||
+209
-205
@@ -11,250 +11,254 @@ class AppConfig
|
||||
{
|
||||
|
||||
|
||||
const int TYPE_WCHAT_PROJECT = 0;
|
||||
const int TYPE_ALI_GAME_OR_APPLET = 3;
|
||||
const int TYPE_QQ_GAME_OR_APPLET = 1;
|
||||
const int TYPE_APP_PROJECT = 2;
|
||||
const int TYPE_WCHAT_PROJECT = 0;
|
||||
const int TYPE_ALI_GAME_OR_APPLET = 3;
|
||||
const int TYPE_QQ_GAME_OR_APPLET = 1;
|
||||
const int TYPE_APP_PROJECT = 2;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $appId = '';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $appId = '';
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $host = '';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $host = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $appSecret = '';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $appSecret = '';
|
||||
|
||||
|
||||
/**
|
||||
* @var PayConfig
|
||||
*/
|
||||
public PayConfig $pay;
|
||||
/**
|
||||
* @var PayConfig
|
||||
*/
|
||||
public PayConfig $pay;
|
||||
|
||||
|
||||
private string $proxyHost = '';
|
||||
private int $proxyPort = 0;
|
||||
private string $notifyUrl = '';
|
||||
private string $body = '';
|
||||
private string $currency = 'CNY';
|
||||
private string $remoteIp = '';
|
||||
private string $accessToken = '';
|
||||
private string $tradeType = 'JSAPI';
|
||||
private string $signType = 'MD5';
|
||||
private string $proxyHost = '';
|
||||
private int $proxyPort = 0;
|
||||
private string $notifyUrl = '';
|
||||
private string $body = '';
|
||||
private string $currency = 'CNY';
|
||||
private string $remoteIp = '';
|
||||
private string $accessToken = '';
|
||||
private string $tradeType = 'JSAPI';
|
||||
private string $signType = 'MD5';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public int $type;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public int $type;
|
||||
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
#[ArrayShape(['token' => 'string', 'secret' => 'string', 'unionId' => 'string'])]
|
||||
public array $notice = ['token' => '', 'secret' => '', 'unionId' => ''];
|
||||
/**
|
||||
* @var NoticeConfig|null
|
||||
*/
|
||||
public ?NoticeConfig $notice = null;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->pay = new PayConfig();
|
||||
$this->pay->wx = new Wx();
|
||||
$this->pay->qq = new Qq();
|
||||
$this->pay->ali = new Aliyun();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->pay = new PayConfig;
|
||||
$this->pay->wx = new Wx;
|
||||
$this->pay->qq = new Qq;
|
||||
$this->pay->ali = new Aliyun;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param object $app
|
||||
* @return static
|
||||
*/
|
||||
public static function instance(object $app): static
|
||||
{
|
||||
$model = new static();
|
||||
$model->appId = $app->appId;
|
||||
$model->appSecret = $app->appSecret;
|
||||
$model->type = $app->type;
|
||||
/**
|
||||
* @param object $app
|
||||
* @return static
|
||||
*/
|
||||
public static function instance(object $app): static
|
||||
{
|
||||
$model = new static;
|
||||
$model->appId = $app->appId;
|
||||
$model->appSecret = $app->appSecret;
|
||||
$model->type = $app->type;
|
||||
$model->notice = NoticeConfig::parse($app->notice);
|
||||
|
||||
PayConfig::parse($app->pay, $model->pay);
|
||||
PayConfig::parse($app->pay, $model->pay);
|
||||
|
||||
return $model;
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function typeIsApp(): bool
|
||||
{
|
||||
return $this->type === self::TYPE_APP_PROJECT;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function typeIsApp(): bool
|
||||
{
|
||||
return $this->type === self::TYPE_APP_PROJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getProxyHost(): string
|
||||
{
|
||||
return $this->proxyHost;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getProxyHost(): string
|
||||
{
|
||||
return $this->proxyHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $proxyHost
|
||||
*/
|
||||
public function setProxyHost(string $proxyHost): void
|
||||
{
|
||||
$this->proxyHost = $proxyHost;
|
||||
}
|
||||
/**
|
||||
* @param string $proxyHost
|
||||
*/
|
||||
public function setProxyHost(string $proxyHost): void
|
||||
{
|
||||
$this->proxyHost = $proxyHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProxyPort(): int
|
||||
{
|
||||
return $this->proxyPort;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProxyPort(): int
|
||||
{
|
||||
return $this->proxyPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $proxyPort
|
||||
*/
|
||||
public function setProxyPort(int $proxyPort): void
|
||||
{
|
||||
$this->proxyPort = $proxyPort;
|
||||
}
|
||||
/**
|
||||
* @param int $proxyPort
|
||||
*/
|
||||
public function setProxyPort(int $proxyPort): void
|
||||
{
|
||||
$this->proxyPort = $proxyPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNotifyUrl(): string
|
||||
{
|
||||
return $this->notifyUrl;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNotifyUrl(): string
|
||||
{
|
||||
return $this->notifyUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $notifyUrl
|
||||
*/
|
||||
public function setNotifyUrl(string $notifyUrl): void
|
||||
{
|
||||
$this->notifyUrl = $notifyUrl;
|
||||
}
|
||||
/**
|
||||
* @param string $notifyUrl
|
||||
*/
|
||||
public function setNotifyUrl(string $notifyUrl): void
|
||||
{
|
||||
$this->notifyUrl = $notifyUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody(): string
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody(): string
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
*/
|
||||
public function setBody(string $body): void
|
||||
{
|
||||
$this->body = $body;
|
||||
}
|
||||
/**
|
||||
* @param string $body
|
||||
*/
|
||||
public function setBody(string $body): void
|
||||
{
|
||||
$this->body = $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrency(): string
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrency(): string
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $currency
|
||||
*/
|
||||
public function setCurrency(string $currency): void
|
||||
{
|
||||
$this->currency = $currency;
|
||||
}
|
||||
/**
|
||||
* @param string $currency
|
||||
*/
|
||||
public function setCurrency(string $currency): void
|
||||
{
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteIp(): string
|
||||
{
|
||||
return $this->remoteIp;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteIp(): string
|
||||
{
|
||||
return $this->remoteIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $remoteIp
|
||||
*/
|
||||
public function setRemoteIp(string $remoteIp): void
|
||||
{
|
||||
$this->remoteIp = $remoteIp;
|
||||
}
|
||||
/**
|
||||
* @param string $remoteIp
|
||||
*/
|
||||
public function setRemoteIp(string $remoteIp): void
|
||||
{
|
||||
$this->remoteIp = $remoteIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
return $this->accessToken;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*/
|
||||
public function setAccessToken(string $accessToken): void
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
}
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*/
|
||||
public function setAccessToken(string $accessToken): void
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTradeType(): string
|
||||
{
|
||||
return $this->tradeType;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTradeType(): string
|
||||
{
|
||||
return $this->tradeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tradeType
|
||||
*/
|
||||
public function setTradeType(string $tradeType): void
|
||||
{
|
||||
$this->tradeType = $tradeType;
|
||||
}
|
||||
/**
|
||||
* @param string $tradeType
|
||||
*/
|
||||
public function setTradeType(string $tradeType): void
|
||||
{
|
||||
$this->tradeType = $tradeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSignType(): string
|
||||
{
|
||||
return $this->signType;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSignType(): string
|
||||
{
|
||||
return $this->signType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $signType
|
||||
*/
|
||||
public function setSignType(string $signType): void
|
||||
{
|
||||
$this->signType = $signType;
|
||||
}
|
||||
/**
|
||||
* @param string $signType
|
||||
*/
|
||||
public function setSignType(string $signType): void
|
||||
{
|
||||
$this->signType = $signType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
*/
|
||||
public function setHost(string $host): void
|
||||
{
|
||||
$this->host = $host;
|
||||
}
|
||||
/**
|
||||
* @param string $host
|
||||
*/
|
||||
public function setHost(string $host): void
|
||||
{
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -147,10 +147,10 @@ abstract class Multiprogramming implements Progaram
|
||||
$client->get($requestUrl, $body);
|
||||
}
|
||||
$client->close();
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$body = json_decode($client->getBody(), true);
|
||||
$body = json_decode($client->body, true);
|
||||
if (is_null($body) || (isset($body['errcode']) && $body['errcode'] != 0)) {
|
||||
return new Result(code: $body['errcode'], message: $body['errmsg']);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\common;
|
||||
|
||||
class NoticeConfig
|
||||
{
|
||||
|
||||
public string $token;
|
||||
public string $encodingAESKey;
|
||||
public string $unionId;
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @param string $encodingAESKey
|
||||
* @param string $unionId
|
||||
*/
|
||||
public function __construct(string $token, string $encodingAESKey, string $unionId)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->encodingAESKey = $encodingAESKey;
|
||||
$this->unionId = $unionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param array $map
|
||||
* @return static
|
||||
*/
|
||||
public static function parse(array $map): static
|
||||
{
|
||||
return new static($map['token'], $map['secret'], $map['unionId'] ?? '');
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -56,10 +56,10 @@ class Result
|
||||
*/
|
||||
public static function init(Client $client): static
|
||||
{
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$body = json_decode($client->getBody(), true);
|
||||
$body = json_decode($client->body, true);
|
||||
if (isset($body['errcode']) && $body['errcode'] != 0) {
|
||||
return new Result(code: $body['errcode'], message: $body['errmsg']);
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,12 @@ class Wx
|
||||
{
|
||||
|
||||
|
||||
public string $offerId;
|
||||
|
||||
|
||||
public string $appKey;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
+4
-3
@@ -19,9 +19,10 @@
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 8.0",
|
||||
"game-worker/kiri-client": "~v2.0",
|
||||
"psr/container": "*"
|
||||
"php": ">= 8.5",
|
||||
"game-worker/kiri-client": "^v2.5",
|
||||
"psr/container": "*",
|
||||
"ext-openssl": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "@dev"
|
||||
|
||||
+6
-6
@@ -309,10 +309,10 @@ class Redhat extends SmallProgram
|
||||
}
|
||||
$client->post($this->sendUrl, http_build_query($this->generate()));
|
||||
$client->close();
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$json = json_decode($client->getBody(), true);
|
||||
$json = json_decode($client->body, true);
|
||||
if (isset($json['return_code']) && $json['return_code'] != 'SUCCESS') {
|
||||
return new Result(code: 500, message: $json['return_msg'] ?? $json['retmsg']);
|
||||
}
|
||||
@@ -347,10 +347,10 @@ class Redhat extends SmallProgram
|
||||
$client->withHeader(['Content-Type' => 'application/json']);
|
||||
$client->post($this->searchUrl, $requestParam);
|
||||
$client->close();
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$json = json_decode($client->getBody(), true);
|
||||
$json = json_decode($client->body, true);
|
||||
if (isset($json['result']) && $json['result'] != 'SUCCESS') {
|
||||
return new Result(code: 500, message: $response['res_info'] ?? '订单查询失败!');
|
||||
} else {
|
||||
|
||||
@@ -84,10 +84,10 @@ class Cash_Bonus extends SmallProgram
|
||||
public function mch_send(string $mch_billno, string $openId, float $price): Result
|
||||
{
|
||||
$client = $this->createClient($this->_cash, $this->orderConfig($mch_billno, $openId, $price));
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$json = json_decode($client->getBody(), true);
|
||||
$json = json_decode($client->body, true);
|
||||
|
||||
if (isset($json['return_code']) && $json['return_code'] != 'SUCCESS') {
|
||||
return new Result(code: 500, message: $json['return_msg'] ?? $json['retmsg']);
|
||||
|
||||
@@ -70,10 +70,10 @@ class Enterprise_payment extends SmallProgram
|
||||
public function mch_send(string $mch_billno, string $openId, float $price): Result
|
||||
{
|
||||
$client = $this->createClient($this->_cash, $this->orderConfig($mch_billno, $openId, $price));
|
||||
if (!in_array($client->getStatusCode(), [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->getBody());
|
||||
if (!in_array($client->statusCode, [101, 200, 201])) {
|
||||
return new Result(code: 505, message: $client->body);
|
||||
}
|
||||
$json = json_decode($client->getBody(), true);
|
||||
$json = json_decode($client->body, true);
|
||||
if (isset($json['return_code']) && $json['return_code'] != 'SUCCESS') {
|
||||
return new Result(code: 500, data: $json['return_msg'] ?? $json['retmsg']);
|
||||
} else if ($json['result_code'] != 'SUCCESS') {
|
||||
|
||||
+32
-2
@@ -6,6 +6,29 @@ namespace wchat\wx;
|
||||
|
||||
use wchat\common\Result;
|
||||
|
||||
//if (PHP_VERSION > '8.2') {
|
||||
// enum Scene
|
||||
// {
|
||||
//
|
||||
// case SCENE_INFO;
|
||||
// case SCENE_REPLY;
|
||||
// case SCENE_DISCAZ;
|
||||
// case SCENE_SHEJIAO;
|
||||
//
|
||||
//
|
||||
// public function getValue(): int
|
||||
// {
|
||||
// return match ($this) {
|
||||
// self::SCENE_INFO => 1,
|
||||
// self::SCENE_REPLY => 2,
|
||||
// self::SCENE_DISCAZ => 3,
|
||||
// self::SCENE_SHEJIAO => 4,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
/**
|
||||
* Class SecCheck
|
||||
* @package wchat
|
||||
@@ -65,16 +88,23 @@ class SecCheck extends SmallProgram
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @param int $scene
|
||||
* @param string $openId
|
||||
* @return Result
|
||||
*/
|
||||
public function text(string $content): Result
|
||||
public function text(string $content, int $scene, string $openId): Result
|
||||
{
|
||||
if (empty($content)) {
|
||||
return $this->sendError('文件不存在', 404);
|
||||
}
|
||||
$requestUrl = $this->_msgUrl . $this->payConfig->getAccessToken();
|
||||
|
||||
return $this->post('api.weixin.qq.com', $requestUrl, ['content' => $content]);
|
||||
return $this->post('api.weixin.qq.com', $requestUrl, json_encode([
|
||||
'content' => $content,
|
||||
'version' => 2,
|
||||
'scene' => $scene,
|
||||
'openid' => $openId
|
||||
], JSON_UNESCAPED_UNICODE), 'application/json');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,35 @@
|
||||
namespace wchat\wx;
|
||||
|
||||
|
||||
use Exception;
|
||||
use wchat\common\Multiprogramming;
|
||||
use wchat\wx\V3\Libs\WxMsgCrypt;
|
||||
use wchat\wx\V3\Libs\XPayGoodsDeliverNotify;
|
||||
|
||||
class SmallProgram extends Multiprogramming
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $encrypt
|
||||
* @param string $signature
|
||||
* @param string $timestamp
|
||||
* @param string $nonce
|
||||
* @param string $msg_signature
|
||||
* @return array|bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function decode(string $encrypt, string $signature, string $timestamp, string $nonce, string $msg_signature): XPayGoodsDeliverNotify|bool
|
||||
{
|
||||
$WxMsgCrypt = new WxMsgCrypt($this->payConfig->notice->token, $this->payConfig->notice->encodingAESKey, $this->payConfig->appId);
|
||||
if (!$WxMsgCrypt->verifySignature($timestamp, $nonce, $encrypt, $msg_signature)) {
|
||||
return false;
|
||||
} else {
|
||||
return XPayGoodsDeliverNotify::fromJson($WxMsgCrypt->decryptMsg($encrypt));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
class GoodsInfo
|
||||
{
|
||||
public string $ProductId;
|
||||
public int $Quantity;
|
||||
public int $OrigPrice;
|
||||
public int $ActualPrice;
|
||||
public string $Attach;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->ProductId = $data['ProductId'];
|
||||
$this->Quantity = $data['Quantity'];
|
||||
$this->OrigPrice = $data['OrigPrice'];
|
||||
$this->ActualPrice = $data['ActualPrice'];
|
||||
$this->Attach = $data['Attach'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
use Exception;
|
||||
|
||||
class TransferDetail
|
||||
{
|
||||
|
||||
|
||||
private string $out_bill_no;
|
||||
private string $transfer_scene_id;
|
||||
private string $openid;
|
||||
private string $user_name = '';
|
||||
private int $transfer_amount;
|
||||
private string $transfer_remark;
|
||||
private string $notify_url = '';
|
||||
private string $user_recv_perception = '';
|
||||
private array $transfer_scene_report_infos;
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOutBillNo(): string
|
||||
{
|
||||
return $this->out_bill_no;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $out_bill_no
|
||||
* @return void
|
||||
*/
|
||||
public function setOutBillNo(string $out_bill_no): void
|
||||
{
|
||||
$this->out_bill_no = $out_bill_no;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTransferSceneId(): string
|
||||
{
|
||||
return $this->transfer_scene_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $transfer_scene_id
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferSceneId(string $transfer_scene_id): void
|
||||
{
|
||||
$this->transfer_scene_id = $transfer_scene_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOpenid(): string
|
||||
{
|
||||
return $this->openid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $openid
|
||||
* @return void
|
||||
*/
|
||||
public function setOpenid(string $openid): void
|
||||
{
|
||||
$this->openid = $openid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(): string
|
||||
{
|
||||
return $this->user_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $user_name
|
||||
* @return void
|
||||
*/
|
||||
public function setUserName(string $user_name): void
|
||||
{
|
||||
$this->user_name = $user_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTransferAmount(): int
|
||||
{
|
||||
return $this->transfer_amount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $transfer_amount
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferAmount(int $transfer_amount): void
|
||||
{
|
||||
$this->transfer_amount = $transfer_amount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTransferRemark(): string
|
||||
{
|
||||
return $this->transfer_remark;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $transfer_remark
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferRemark(string $transfer_remark): void
|
||||
{
|
||||
$this->transfer_remark = $transfer_remark;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNotifyUrl(): string
|
||||
{
|
||||
return $this->notify_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $notify_url
|
||||
* @return void
|
||||
*/
|
||||
public function setNotifyUrl(string $notify_url): void
|
||||
{
|
||||
$this->notify_url = $notify_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserRecvPerception(): string
|
||||
{
|
||||
return $this->user_recv_perception;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $user_recv_perception
|
||||
* @return void
|
||||
*/
|
||||
public function setUserRecvPerception(string $user_recv_perception): void
|
||||
{
|
||||
$this->user_recv_perception = $user_recv_perception;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array<TransferSceneReportInfo>
|
||||
*/
|
||||
public function getTransferSceneReportInfos(): array
|
||||
{
|
||||
return $this->transfer_scene_report_infos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $transfer_scene_report_infos
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferSceneReportInfos(TransferSceneReportInfo ...$transfer_scene_report_infos): void
|
||||
{
|
||||
$this->transfer_scene_report_infos = $transfer_scene_report_infos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$transfer_scene_report_infos = [];
|
||||
foreach ($this->transfer_scene_report_infos as $transfer_scene_report_info) {
|
||||
$transfer_scene_report_infos[] = $transfer_scene_report_info->toArray();
|
||||
}
|
||||
$array = [
|
||||
'out_bill_no' => $this->out_bill_no,
|
||||
'transfer_scene_id' => $this->transfer_scene_id,
|
||||
'openid' => $this->openid,
|
||||
'transfer_amount' => $this->transfer_amount,
|
||||
'transfer_remark' => $this->transfer_remark,
|
||||
'transfer_scene_report_infos' => $transfer_scene_report_infos,
|
||||
];
|
||||
foreach ($array as $key => $value) {
|
||||
if (empty($value)) {
|
||||
throw new Exception('必填项' . $key . '不能为空.');
|
||||
}
|
||||
}
|
||||
if (!empty($this->user_name)) {
|
||||
$array['user_name'] = $this->user_name;
|
||||
}
|
||||
if (!empty($this->notify_url)) {
|
||||
$array['notify_url'] = $this->notify_url;
|
||||
}
|
||||
if (!empty($this->user_recv_perception)) {
|
||||
$array['user_recv_perception'] = $this->user_recv_perception;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
use Arrayable;
|
||||
|
||||
readonly class TransferSceneReportInfo implements Arrayable
|
||||
{
|
||||
|
||||
|
||||
public function __construct(
|
||||
public string $info_type,
|
||||
public string $info_content,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
"info_type" => $this->info_type,
|
||||
"info_content" => $this->info_content
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
class WeChatPayInfo
|
||||
{
|
||||
public string $MchOrderNo;
|
||||
public string $TransactionId;
|
||||
public int $PaidTime;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->MchOrderNo = $data['MchOrderNo'];
|
||||
$this->TransactionId = $data['TransactionId'];
|
||||
$this->PaidTime = $data['PaidTime'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 微信公众号消息加解密类
|
||||
* 基于微信官方文档实现:https://developers.weixin.qq.com/doc/oplatform/developers/dev/push/encryption.html
|
||||
* 适用场景:安全模式(推荐)下的消息接收与回复
|
||||
*/
|
||||
class WxMsgCrypt
|
||||
{
|
||||
private string $token; // 开发者设置的Token
|
||||
private string $encodingAesKey; // 消息加解密密钥(43位字符)
|
||||
private string $appid; // 公众号AppId
|
||||
private string|false $aesKey; // 解码后的AES密钥(32字节)
|
||||
private string $iv; // 初始向量(AES密钥的前16字节)
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param string $token 开发者设置的Token
|
||||
* @param string $encodingAesKey 公众号后台的EncodingAESKey(43位)
|
||||
* @param string $appid 公众号AppId
|
||||
* @throws Exception 当EncodingAESKey格式不正确时抛出异常
|
||||
*/
|
||||
public function __construct(string $token, string $encodingAesKey, string $appid)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->encodingAesKey = $encodingAesKey;
|
||||
$this->appid = $appid;
|
||||
|
||||
// 解码EncodingAESKey:官方文档要求尾部补一个"="后做Base64解码
|
||||
$key = $encodingAesKey . '=';
|
||||
$this->aesKey = base64_decode($key);
|
||||
if (strlen($this->aesKey) != 32) {
|
||||
throw new Exception('EncodingAESKey 解码后长度必须为32字节,当前长度为:' . strlen($this->aesKey));
|
||||
}
|
||||
|
||||
// AES CBC模式的IV取密钥的前16字节(官方示例代码标准做法)
|
||||
$this->iv = substr($this->aesKey, 0, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证消息签名(用于接收消息时)
|
||||
* @param string $timestamp URL中的timestamp参数
|
||||
* @param string $nonce URL中的nonce参数
|
||||
* @param string $encrypt 消息体中的Encrypt字段(密文)
|
||||
* @param string $msgSignature URL中的msg_signature参数
|
||||
* @return bool 签名验证结果
|
||||
*/
|
||||
public function verifySignature(string $timestamp, string $nonce, string $encrypt, string $msgSignature): bool
|
||||
{
|
||||
$signature = $this->generateSignature($timestamp, $nonce, $encrypt);
|
||||
return $signature === $msgSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密消息(安全模式接收消息时调用)
|
||||
* @param string $encrypt 消息体中的Encrypt字段(Base64密文)
|
||||
* @return string 解密后的明文消息(XML或JSON格式)
|
||||
* @throws Exception 当解密失败或AppId不匹配时抛出异常
|
||||
*/
|
||||
public function decryptMsg(string $encrypt): string
|
||||
{
|
||||
// 1. Base64解码
|
||||
$encryptData = base64_decode($encrypt);
|
||||
if ($encryptData === false) {
|
||||
throw new Exception('Base64解码失败');
|
||||
}
|
||||
|
||||
// 2. AES解密(CBC模式,PKCS#7填充)
|
||||
$decrypted = openssl_decrypt($encryptData, 'AES-256-CBC', $this->aesKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
|
||||
if ($decrypted === false) {
|
||||
throw new Exception('AES解密失败');
|
||||
}
|
||||
|
||||
// 3. 去除PKCS#7填充
|
||||
$result = $this->pkcs7Unpad($decrypted);
|
||||
|
||||
// 4. 解析明文结构:16字节随机字符串 + 4字节网络字节序长度 + 消息内容 + AppId
|
||||
if (strlen($result) < 20) { // 至少需要16+4=20字节
|
||||
throw new Exception('解密后的数据长度异常');
|
||||
}
|
||||
|
||||
$random = substr($result, 0, 16); // 16字节随机字符串(可忽略)
|
||||
$msgLen = unpack('N', substr($result, 16, 4))[1]; // 4字节网络字节序长度
|
||||
$msg = substr($result, 20, $msgLen); // 消息内容
|
||||
$appid = substr($result, 20 + $msgLen); // 剩余的AppId
|
||||
|
||||
// 5. 验证AppId是否匹配
|
||||
if ($appid !== $this->appid) {
|
||||
throw new Exception('AppId不匹配,可能被篡改');
|
||||
}
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密消息(安全模式回复消息时调用)
|
||||
* @param string $replyMsg 待回复的明文消息(XML或JSON格式)
|
||||
* @param int|null $timestamp 时间戳(可空,为空则自动生成)
|
||||
* @param string|null $nonce 随机字符串(可空,为空则自动生成)
|
||||
* @return array 包含Encrypt, MsgSignature, TimeStamp, Nonce的关联数组
|
||||
* @throws Exception 当加密失败时抛出异常
|
||||
*/
|
||||
public function encryptMsg(string $replyMsg, ?int $timestamp = null, ?string $nonce = null): array
|
||||
{
|
||||
// 1. 生成16字节随机字符串
|
||||
$random = openssl_random_pseudo_bytes(16);
|
||||
|
||||
// 2. 计算消息长度(网络字节序)
|
||||
$msgLen = pack('N', strlen($replyMsg));
|
||||
|
||||
// 3. 构造待加密的字符串:随机字符串(16B) + 消息长度(4B) + 消息内容 + AppId
|
||||
$plainText = $random . $msgLen . $replyMsg . $this->appid;
|
||||
|
||||
// 4. PKCS#7填充(按16字节块)
|
||||
$padded = $this->pkcs7Pad($plainText);
|
||||
|
||||
// 5. AES加密
|
||||
$encrypted = openssl_encrypt($padded, 'AES-256-CBC', $this->aesKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
|
||||
if ($encrypted === false) {
|
||||
throw new Exception('AES加密失败');
|
||||
}
|
||||
|
||||
// 6. Base64编码得到Encrypt字段
|
||||
$encrypt = base64_encode($encrypted);
|
||||
|
||||
// 7. 生成时间戳和随机数
|
||||
$timestamp = $timestamp ?: time();
|
||||
$nonce = $nonce ?: $this->generateNonce();
|
||||
|
||||
// 8. 生成消息签名
|
||||
$msgSignature = $this->generateSignature($timestamp, $nonce, $encrypt);
|
||||
|
||||
// 9. 返回加密后的数据包
|
||||
return [
|
||||
'Encrypt' => $encrypt,
|
||||
'MsgSignature' => $msgSignature,
|
||||
'TimeStamp' => $timestamp,
|
||||
'Nonce' => $nonce,
|
||||
];
|
||||
}
|
||||
|
||||
/* -------------------- 内部辅助方法 -------------------- */
|
||||
|
||||
/**
|
||||
* 生成消息签名(使用token、timestamp、nonce、encrypt)
|
||||
* @param string $timestamp 时间戳
|
||||
* @param string $nonce 随机数
|
||||
* @param string $encrypt 密文
|
||||
* @return string 签名字符串
|
||||
*/
|
||||
private function generateSignature(string $timestamp, string $nonce, string $encrypt): string
|
||||
{
|
||||
$params = [$this->token, $timestamp, $nonce, $encrypt];
|
||||
sort($params, SORT_STRING); // 字典序排序
|
||||
$str = implode('', $params); // 拼接字符串
|
||||
return sha1($str); // SHA1哈希
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串(用于nonce)
|
||||
* @param int $length 长度
|
||||
* @return string
|
||||
*/
|
||||
private function generateNonce(int $length = 8): string
|
||||
{
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
$nonce = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$nonce .= $chars[mt_rand(0, strlen($chars) - 1)];
|
||||
}
|
||||
return $nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* PKCS#7填充(按16字节块)
|
||||
* @param string $text 待填充的数据
|
||||
* @return string 填充后的数据
|
||||
*/
|
||||
private function pkcs7Pad(string $text): string
|
||||
{
|
||||
$blockSize = 16; // AES块大小固定16字节
|
||||
$padLen = $blockSize - (strlen($text) % $blockSize);
|
||||
$pad = str_repeat(chr($padLen), $padLen);
|
||||
return $text . $pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除PKCS#7填充
|
||||
* @param string $text 解密后的数据(已去除OPENSSL_ZERO_PADDING的填充)
|
||||
* @return string 去除填充后的数据
|
||||
* @throws Exception 当填充格式错误时抛出异常
|
||||
*/
|
||||
private function pkcs7Unpad(string $text): string
|
||||
{
|
||||
$len = strlen($text);
|
||||
if ($len == 0) {
|
||||
return $text;
|
||||
}
|
||||
$padLen = ord($text[$len - 1]); // 最后一个字节表示填充的长度
|
||||
if ($padLen < 1 || $padLen > 16) {
|
||||
throw new Exception('无效的PKCS#7填充');
|
||||
}
|
||||
// 验证填充字节是否一致(简化验证,可根据需要加强)
|
||||
for ($i = 0; $i < $padLen; $i++) {
|
||||
if (ord($text[$len - 1 - $i]) != $padLen) {
|
||||
throw new Exception('PKCS#7填充验证失败');
|
||||
}
|
||||
}
|
||||
return substr($text, 0, $len - $padLen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
class XPayGoodsDeliverNotify
|
||||
{
|
||||
public string $ToUserName;
|
||||
public string $FromUserName;
|
||||
public int $CreateTime;
|
||||
public string $MsgType;
|
||||
public string $Event;
|
||||
public string $OpenId;
|
||||
public string $OutTradeNo;
|
||||
public WeChatPayInfo $WeChatPayInfo;
|
||||
public int $Env;
|
||||
public GoodsInfo $GoodsInfo;
|
||||
public int $RetryTimes;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->ToUserName = $data['ToUserName'];
|
||||
$this->FromUserName = $data['FromUserName'];
|
||||
$this->CreateTime = $data['CreateTime'];
|
||||
$this->MsgType = $data['MsgType'];
|
||||
$this->Event = $data['Event'];
|
||||
$this->OpenId = $data['OpenId'];
|
||||
$this->OutTradeNo = $data['OutTradeNo'];
|
||||
$this->WeChatPayInfo = new WeChatPayInfo($data['WeChatPayInfo']);
|
||||
$this->Env = $data['Env'];
|
||||
$this->GoodsInfo = new GoodsInfo($data['GoodsInfo']);
|
||||
$this->RetryTimes = $data['RetryTimes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JSON 字符串创建对象
|
||||
*/
|
||||
public static function fromJson(string $json): self
|
||||
{
|
||||
$data = json_decode($json, true);
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use wchat\wx\SmallProgram;
|
||||
|
||||
class TransferBatches extends SmallProgram
|
||||
{
|
||||
use WxV3PaymentTait;
|
||||
|
||||
|
||||
/**
|
||||
* @param TransferDetail $detail
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
#[ArrayShape([])]
|
||||
public function transfer(TransferDetail $detail): array
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
$body = [];
|
||||
if ($payConfig->typeIsApp()) {
|
||||
$body['appid'] = $payConfig->pay->wx->appId;
|
||||
} else {
|
||||
$body['appid'] = $payConfig->appId;
|
||||
}
|
||||
$body['out_batch_no'] = $detail->out_detail_no;
|
||||
$body["batch_name"] = $payConfig->getBody();
|
||||
$body["body"] = $payConfig->getBody();
|
||||
$body["batch_remark"] = $payConfig->getBody();
|
||||
$body["total_amount"] = $detail->transfer_amount;
|
||||
$body["total_num"] = 1;
|
||||
$body["transfer_detail_list"] = [$detail->toArray()];
|
||||
|
||||
$sign = $this->signature('POST', '/v3/transfer/batches', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$client = $this->createClient($sign, $json);
|
||||
$client->post('/v3/transfer/batches');
|
||||
$client->close();
|
||||
|
||||
$data = json_decode($client->getBody(), TRUE);
|
||||
if (json_last_error() != JSON_ERROR_NONE) {
|
||||
return ['code' => $client->getStatusCode(), 'message' => $client->getBody()];
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
|
||||
class TransferDetail
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param string $out_detail_no
|
||||
* @param int|float $transfer_amount
|
||||
* @param string $transfer_remark
|
||||
* @param string $openid
|
||||
* @param string $user_name
|
||||
*/
|
||||
public function __construct(
|
||||
public string $out_detail_no,
|
||||
public int|float $transfer_amount,
|
||||
public string $transfer_remark,
|
||||
public string $openid,
|
||||
public string $user_name = ''
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape(['out_detail_no' => "string", 'transfer_amount' => "float|int", 'transfer_remark' => "string", 'openid' => "string", 'user_name' => "string"])]
|
||||
public function toArray(): array
|
||||
{
|
||||
if (empty($this->user_name)) {
|
||||
return [
|
||||
'out_detail_no' => $this->out_detail_no,
|
||||
'transfer_amount' => $this->transfer_amount,
|
||||
'transfer_remark' => $this->transfer_remark,
|
||||
'openid' => $this->openid,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'out_detail_no' => $this->out_detail_no,
|
||||
'transfer_amount' => $this->transfer_amount,
|
||||
'transfer_remark' => $this->transfer_remark,
|
||||
'openid' => $this->openid,
|
||||
'user_name' => $this->user_name,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class WxV3AppPayment extends SmallProgram
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
public function payment(string $orderNo, int $total, string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
public function payment(string $orderNo, int $total, ?string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
{
|
||||
$body = $this->getInitCore($orderNo, $total);
|
||||
|
||||
@@ -32,7 +32,7 @@ class WxV3AppPayment extends SmallProgram
|
||||
$client->post('/v3/pay/transactions/components');
|
||||
$client->close();
|
||||
|
||||
$json = json_decode($client->getBody(), TRUE);
|
||||
$json = json_decode($client->body, TRUE);
|
||||
if (!isset($json['prepay_id'])) {
|
||||
throw new Exception('微信支付调用失败');
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class WxV3NativePayment extends SmallProgram
|
||||
* @throws
|
||||
*/
|
||||
#[ArrayShape(['code_url' => "string"])]
|
||||
public function payment(string $orderNo, int $total, string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
public function payment(string $orderNo, int $total, ?string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
{
|
||||
$body = $this->getInitCore($orderNo, $total);
|
||||
|
||||
@@ -32,7 +32,7 @@ class WxV3NativePayment extends SmallProgram
|
||||
$client->post('/v3/pay/transactions/native');
|
||||
$client->close();
|
||||
|
||||
$json = json_decode($client->getBody(), TRUE);
|
||||
$json = json_decode($client->body, TRUE);
|
||||
if (!isset($json['code_url'])) {
|
||||
throw new Exception('微信支付调用失败');
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class WxV3Payment extends SmallProgram
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
public function payment(string $orderNo, int $total, string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
public function payment(string $orderNo, int $total, ?string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
{
|
||||
$body = $this->getInitCore($orderNo, $total);
|
||||
$body['payer'] = ['openid' => $openId];
|
||||
@@ -32,9 +32,9 @@ class WxV3Payment extends SmallProgram
|
||||
$client->post('/v3/pay/transactions/jsapi');
|
||||
$client->close();
|
||||
|
||||
$json = json_decode($client->getBody(), TRUE);
|
||||
$json = json_decode($client->body, TRUE);
|
||||
if (!isset($json['prepay_id'])) {
|
||||
throw new Exception('微信支付调用失败: ' . $client->getBody());
|
||||
throw new Exception('微信支付调用失败: ' . $client->body);
|
||||
}
|
||||
|
||||
return $this->createResponse($json, $body);
|
||||
|
||||
@@ -54,19 +54,23 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
public function verify(RequestInterface $request): bool
|
||||
{
|
||||
$platformPublicKeyInstance = $this->rsaFrom($this->payConfig->pay->wx->mchKey, KEY_TYPE_PUBLIC);
|
||||
$inWechatpaySignature = $request->getHeaderLine('Wechatpay-Signature'); // 请根据实际情况获取
|
||||
$inWechatpayTimestamp = $request->getHeaderLine('Wechatpay-Timestamp'); // 请根据实际情况获取
|
||||
$inWechatpayNonce = $request->getHeaderLine('Wechatpay-Nonce'); // 请根据实际情况获取
|
||||
$inWechatpaySignature = $request->getHeaderLine('wechatpay-signature'); // 请根据实际情况获取
|
||||
$inWechatpayTimestamp = $request->getHeaderLine('wechatpay-timestamp'); // 请根据实际情况获取
|
||||
$inWechatpayNonce = $request->getHeaderLine('wechatpay-nonce'); // 请根据实际情况获取
|
||||
$inBody = $request->getBody()->getContents(); // 请根据实际情况获取,例如: file_get_contents('php://input');
|
||||
$timeOffsetStatus = 300 >= abs(time() - (int)$inWechatpayTimestamp);
|
||||
$verifiedStatus = $this->notifyVerify(
|
||||
$this->lineFeed([$inWechatpayTimestamp, $inWechatpayNonce, $inBody]),
|
||||
$this->lineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
|
||||
$inWechatpaySignature,
|
||||
$platformPublicKeyInstance);
|
||||
|
||||
$this->decode($this->resource['ciphertext'], $this->resource['nonce'], $this->resource['associated_data']);
|
||||
|
||||
if (!$timeOffsetStatus || !$verifiedStatus) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return $this->decode($this->resource['ciphertext'], $this->resource['nonce'], $this->resource['associated_data']);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +115,7 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
*/
|
||||
protected function rsaFrom(string $thing, string $type = KEY_TYPE_PRIVATE): OpenSSLAsymmetricKey
|
||||
{
|
||||
$pkey = (($isPublic = $type === KEY_TYPE_PUBLIC) ? openssl_pkey_get_public(file_get_contents($thing)) : openssl_pkey_get_private(file_get_contents($thing)));
|
||||
$pkey = (($isPublic = $type === KEY_TYPE_PUBLIC) ? openssl_pkey_get_public('file://' . $thing) : openssl_pkey_get_private('file://' . $thing));
|
||||
if (false === $pkey) {
|
||||
throw new \UnexpectedValueException(sprintf('Cannot load %s from(%s), please take care about the $thing input.', $isPublic ? 'publicKey' : 'privateKey', gettype($thing)));
|
||||
}
|
||||
@@ -142,6 +146,9 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
$this->notifyModel->attach = $data['attach'];
|
||||
$this->notifyModel->success_time = $data['success_time'];
|
||||
$this->notifyModel->promotion_detail = [];
|
||||
if (!isset($data['promotion_detail'])) {
|
||||
return true;
|
||||
}
|
||||
foreach ($data['promotion_detail'] as $datum) {
|
||||
$detail = new PromotionDetail();
|
||||
$detail->amount = $datum['amount'];
|
||||
@@ -154,8 +161,10 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
$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);
|
||||
if (isset($data['goods_detail'])) {
|
||||
foreach ($datum['goods_detail'] as $value) {
|
||||
$detail->goods_detail[] = new GoodsDetail($value);
|
||||
}
|
||||
}
|
||||
$this->notifyModel->promotion_detail[] = $detail;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ trait WxV3PaymentTait
|
||||
$client->get($parseUrl, ['mchid' => $config->pay->wx->mchId]);
|
||||
$client->close();
|
||||
|
||||
return json_decode($client->getBody(), TRUE);
|
||||
return json_decode($client->body, TRUE);
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ trait WxV3PaymentTait
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
|
||||
$mch_private_key = openssl_get_privatekey(file_get_contents($payConfig->pay->wx->mchKey));
|
||||
$mch_private_key = openssl_get_privatekey(file_get_contents($payConfig->pay->wx->mchCert));
|
||||
|
||||
openssl_sign($body, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
|
||||
return base64_encode($raw_sign);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Kiri;
|
||||
use wchat\wx\SmallProgram;
|
||||
use wchat\wx\V3\Libs\TransferDetail;
|
||||
|
||||
class WxV3Transfer extends SmallProgram
|
||||
{
|
||||
use WxV3PaymentTait;
|
||||
|
||||
/**
|
||||
* @param TransferDetail $detail
|
||||
* @return array<'out_bill_no', 'transfer_bill_no', 'create_time', 'state'>
|
||||
* @throws Exception
|
||||
*/
|
||||
#[ArrayShape([])]
|
||||
public function transfer(TransferDetail $detail): array
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
$body = $detail->toArray();
|
||||
if ($payConfig->typeIsApp()) {
|
||||
$body['appid'] = $payConfig->pay->wx->appId;
|
||||
} else {
|
||||
$body['appid'] = $payConfig->appId;
|
||||
}
|
||||
$sign = $this->signature('POST', '/v3/fund-app/mch-transfer/transfer-bills', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$client = $this->createClient($sign, $json);
|
||||
$client->post('/v3/fund-app/mch-transfer/transfer-bills');
|
||||
$client->close();
|
||||
|
||||
Kiri::getLogger()->println($client->body);
|
||||
|
||||
if ($client->statusCode == 200) {
|
||||
return json_decode($client->body, TRUE);
|
||||
}
|
||||
throw new Exception('转账申请发起失败');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,8 +4,8 @@ namespace wchat\wx\V3;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Kiri\Client;
|
||||
use wchat\wx\SmallProgram;
|
||||
use wchat\wx\V3\Libs\TransferDetail;
|
||||
|
||||
class WxV3Withdrawal extends SmallProgram
|
||||
{
|
||||
@@ -32,7 +32,7 @@ class WxV3Withdrawal extends SmallProgram
|
||||
$client->post('/v3/pay/transactions/batches');
|
||||
$client->close();
|
||||
|
||||
$json = json_decode($client->getBody(), TRUE);
|
||||
$json = json_decode($client->body, TRUE);
|
||||
if (!isset($json['prepay_id'])) {
|
||||
throw new Exception('微信支付调用失败');
|
||||
}
|
||||
@@ -66,4 +66,34 @@ class WxV3Withdrawal extends SmallProgram
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param TransferDetail $detail
|
||||
* @return array<'out_bill_no', 'transfer_bill_no', 'create_time', 'state'>
|
||||
* @throws Exception
|
||||
*/
|
||||
#[ArrayShape([])]
|
||||
public function transfer(TransferDetail $detail): array
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
$body = $detail->toArray();
|
||||
if ($payConfig->typeIsApp()) {
|
||||
$body['appid'] = $payConfig->pay->wx->appId;
|
||||
} else {
|
||||
$body['appid'] = $payConfig->appId;
|
||||
}
|
||||
$sign = $this->signature('POST', '/v3/fund-app/mch-transfer/transfer-bills', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$client = $this->createClient($sign, $json);
|
||||
$client->post('/v3/fund-app/mch-transfer/transfer-bills');
|
||||
$client->close();
|
||||
|
||||
Kiri::getLogger()->println($client->body);
|
||||
|
||||
if ($client->statusCode == 200) {
|
||||
return json_decode($client->body, TRUE);
|
||||
}
|
||||
throw new Exception('转账申请发起失败');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use Kiri\Client;
|
||||
use wchat\wx\SmallProgram;
|
||||
use wchat\wx\V3\Libs\XPayGoodsDeliverNotify;
|
||||
|
||||
class WxVirtualPayment extends SmallProgram
|
||||
{
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $session_key
|
||||
* @param string $projectId
|
||||
* @param float $price
|
||||
* @param string $orderNo
|
||||
* @return array
|
||||
*/
|
||||
public function payment(string $session_key, string $projectId, float $price, string $orderNo, array $attach = []): array
|
||||
{
|
||||
$signData = [
|
||||
'offerId' => $this->getPayConfig()->pay->wx->offerId,
|
||||
'buyQuantity' => 1,
|
||||
'env' => 0,
|
||||
'currencyType' => 'CNY',
|
||||
'productId' => $projectId,
|
||||
'goodsPrice' => $price,
|
||||
'outTradeNo' => $orderNo,
|
||||
'attach' => json_encode($attach, JSON_UNESCAPED_UNICODE),
|
||||
];
|
||||
|
||||
$signJson = json_encode($signData);
|
||||
return [
|
||||
'signData' => $signData,
|
||||
'mode' => 'short_series_goods',
|
||||
'paySig' => $this->paySign('requestVirtualPayment', $signJson),
|
||||
'signature' => $this->sign($session_key, $signJson),
|
||||
'session_key' => $session_key,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* @param string $prefix
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
private function paySign(string $prefix, string $data): string
|
||||
{
|
||||
return hash_hmac('sha256', $prefix . '&' . $data, $this->getPayConfig()->pay->wx->appKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $session_key
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
private function sign(string $session_key, string $data): string
|
||||
{
|
||||
return hash_hmac('sha256', $data, $session_key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $openId
|
||||
* @param string $orderNo
|
||||
* @return bool|array
|
||||
*/
|
||||
public function queryOrder(string $openId, string $orderNo): bool|array
|
||||
{
|
||||
$data = json_encode(['openid' => $openId, 'order_id' => $orderNo, 'env' => 0], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$client = new Client('api.weixin.qq.com', 443, true);
|
||||
$client->post('/xpay/query_order?access_token=' . $this->payConfig->getAccessToken() . '&pay_sig=' . $this->paySign('/xpay/query_order', $data), $data);
|
||||
$resp = $client->body;
|
||||
|
||||
$client->close();
|
||||
|
||||
return json_decode($resp, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $orderNo
|
||||
* @return array
|
||||
*/
|
||||
public function notify_provide_goods(string $orderNo): array
|
||||
{
|
||||
$data = json_encode(['order_id' => $orderNo, 'env' => 0], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$client = new Client('api.weixin.qq.com', 443, true);
|
||||
$client->post('/xpay/notify_provide_goods?access_token=' . $this->payConfig->getAccessToken() . '&pay_sig=' . $this->paySign('/xpay/notify_provide_goods', $data), $data);
|
||||
$resp = $client->body;
|
||||
$client->close();
|
||||
|
||||
return json_decode($resp, true);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user