mirror of
https://gitee.com/zoujingli/WeChatDeveloper.git
synced 2026-07-08 22:31:09 +08:00
Compare commits
6 Commits
683eb10ebd
...
e72641a5ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e72641a5ae | ||
|
|
cc460dbdba | ||
|
|
a629b2d676 | ||
|
|
b84cc6d54f | ||
|
|
9f236c374c | ||
|
|
b9c4823486 |
2
We.php
2
We.php
@ -93,7 +93,7 @@ class We
|
||||
* 定义当前版本
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.2.37';
|
||||
const VERSION = '1.2.47';
|
||||
|
||||
/**
|
||||
* 静态配置
|
||||
|
||||
@ -27,6 +27,13 @@ use WePayV3\Contracts\DecryptAes;
|
||||
*/
|
||||
class Cert extends BasicWePay
|
||||
{
|
||||
|
||||
/**
|
||||
* 自动配置平台证书
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoCert = false;
|
||||
|
||||
/**
|
||||
* 商户平台下载证书
|
||||
* @return void
|
||||
@ -37,13 +44,18 @@ class Cert extends BasicWePay
|
||||
try {
|
||||
$aes = new DecryptAes($this->config['mch_v3_key']);
|
||||
$result = $this->doRequest('GET', '/v3/certificates');
|
||||
$certs = [];
|
||||
foreach ($result['data'] as $vo) {
|
||||
$this->tmpFile($vo['serial_no'], $aes->decryptToString(
|
||||
$vo['encrypt_certificate']['associated_data'],
|
||||
$vo['encrypt_certificate']['nonce'],
|
||||
$vo['encrypt_certificate']['ciphertext']
|
||||
));
|
||||
$certs[$vo['serial_no']] = [
|
||||
'expire' => strtotime($vo['expire_time']),
|
||||
'content' => $aes->decryptToString(
|
||||
$vo['encrypt_certificate']['associated_data'],
|
||||
$vo['encrypt_certificate']['nonce'],
|
||||
$vo['encrypt_certificate']['ciphertext']
|
||||
)
|
||||
];
|
||||
}
|
||||
$this->tmpFile("{$this->config['mch_id']}_certs", $certs);
|
||||
} catch (\Exception $exception) {
|
||||
throw new InvalidResponseException($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
|
||||
@ -41,22 +41,32 @@ abstract class BasicWePay
|
||||
*/
|
||||
static $cache = [];
|
||||
|
||||
/**
|
||||
* 自动配置平台证书
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoCert = true;
|
||||
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
'appid' => '', // 微信绑定APPID,需配置
|
||||
'mch_id' => '', // 微信商户编号,需要配置
|
||||
'mch_v3_key' => '', // 微信商户密钥,需要配置
|
||||
'cert_serial' => '', // 商户证书序号,无需配置
|
||||
'cert_public' => '', // 商户公钥内容,需要配置
|
||||
'cert_private' => '', // 商户密钥内容,需要配置
|
||||
'appid' => '', // 微信绑定APPID,需配置
|
||||
'mch_id' => '', // 微信商户编号,需要配置
|
||||
'mch_v3_key' => '', // 微信商户密钥,需要配置
|
||||
'cert_serial' => '', // 商户证书序号,无需配置
|
||||
'cert_public' => '', // 商户公钥内容,需要配置
|
||||
'cert_private' => '', // 商户密钥内容,需要配置
|
||||
'mp_cert_serial' => '', // 平台证书序号,无需配置
|
||||
'mp_cert_content' => '', // 平台证书内容,无需配置
|
||||
];
|
||||
|
||||
/**
|
||||
* BasicWePayV3 constructor.
|
||||
* @param array $options [mch_id, mch_v3_key, cert_public, cert_private]
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
@ -94,7 +104,6 @@ abstract class BasicWePay
|
||||
$this->config['mch_v3_key'] = $options['mch_v3_key'];
|
||||
$this->config['cert_public'] = $options['cert_public'];
|
||||
$this->config['cert_private'] = $options['cert_private'];
|
||||
|
||||
if (empty($options['cert_serial'])) {
|
||||
$this->config['cert_serial'] = openssl_x509_parse($this->config['cert_public'], true)['serialNumberHex'];
|
||||
} else {
|
||||
@ -108,6 +117,11 @@ abstract class BasicWePay
|
||||
Tools::$cache_path = $options['cache_path'];
|
||||
}
|
||||
|
||||
// 自动配置平台证书
|
||||
if ($this->autoCert) {
|
||||
$this->_autoCert();
|
||||
}
|
||||
|
||||
// 服务商参数支持
|
||||
// if (!empty($options['sp_appid'])) {
|
||||
// $this->config['sp_appid'] = $options['sp_appid'];
|
||||
@ -127,6 +141,8 @@ abstract class BasicWePay
|
||||
* 静态创建对象
|
||||
* @param array $config
|
||||
* @return static
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
public static function instance($config)
|
||||
{
|
||||
@ -161,7 +177,7 @@ abstract class BasicWePay
|
||||
'Content-Type: application/json',
|
||||
'User-Agent: https://thinkadmin.top',
|
||||
"Authorization: WECHATPAY2-SHA256-RSA2048 {$token}",
|
||||
"Wechatpay-Serial: {$this->config['cert_serial']}"
|
||||
"Wechatpay-Serial: {$this->config['mp_cert_serial']}"
|
||||
],
|
||||
]);
|
||||
|
||||
@ -242,61 +258,95 @@ abstract class BasicWePay
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
protected function signVerify($data, $sign, $serial = '')
|
||||
protected function signVerify($data, $sign, $serial)
|
||||
{
|
||||
$cert = $this->tmpFile($serial);
|
||||
if (empty($cert)) {
|
||||
Cert::instance($this->config)->download();
|
||||
$cert = $this->tmpFile($serial);
|
||||
}
|
||||
$cert = $this->_getCert($serial);
|
||||
return @openssl_verify($data, base64_decode($sign), openssl_x509_read($cert), 'sha256WithRSAEncryption');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台证书
|
||||
* @param string $serial
|
||||
* @return string
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
protected function _getCert($serial = '')
|
||||
{
|
||||
$certs = $this->tmpFile("{$this->config['mch_id']}_certs");
|
||||
if (empty($certs) || empty($certs[$serial]['content'])) {
|
||||
Cert::instance($this->config)->download();
|
||||
$certs = $this->tmpFile("{$this->config['mch_id']}_certs");
|
||||
}
|
||||
if (empty($certs[$serial]['content']) || $certs[$serial]['expire'] < time()) {
|
||||
throw new InvalidResponseException("读取平台证书失败!");
|
||||
} else {
|
||||
return $certs[$serial]['content'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动配置平台证书
|
||||
* @return void
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
protected function _autoCert()
|
||||
{
|
||||
$certs = $this->tmpFile("{$this->config['mch_id']}_certs");
|
||||
if (is_array($certs)) foreach ($certs as $k => $v) {
|
||||
if ($v['expire'] < time()) unset($certs[$k]);
|
||||
}
|
||||
if (empty($certs)) {
|
||||
Cert::instance($this->config)->download();
|
||||
$certs = $this->tmpFile("{$this->config['mch_id']}_certs");
|
||||
}
|
||||
if (empty($certs) || !is_array($certs)) {
|
||||
throw new InvalidResponseException("读取平台证书失败!");
|
||||
}
|
||||
foreach ($certs as $k => $v) if ($v['expire'] > time() + 10) {
|
||||
$this->config['mp_cert_serial'] = $k;
|
||||
$this->config['mp_cert_content'] = $v['content'];
|
||||
break;
|
||||
}
|
||||
if (empty($this->config['mp_cert_serial']) || empty($this->config['mp_cert_content'])) {
|
||||
throw new InvalidResponseException("自动配置平台证书失败!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入或读取临时文件
|
||||
* @param string $name
|
||||
* @param null|string $content
|
||||
* @return string
|
||||
* @param null|array|string $content
|
||||
* @param integer $expire
|
||||
* @return array|string
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
protected function tmpFile($name, $content = null)
|
||||
protected function tmpFile($name, $content = null, $expire = 7200)
|
||||
{
|
||||
if (is_null($content)) {
|
||||
return base64_decode(Tools::getCache($name) ?: '');
|
||||
$text = Tools::getCache($name);
|
||||
if (empty($text)) return '';
|
||||
$json = json_decode(Tools::getCache($name) ?: '', true);
|
||||
return isset($json[0]) ? $json[0] : '';
|
||||
} else {
|
||||
return Tools::setCache($name, base64_encode($content), 7200);
|
||||
return Tools::setCache($name, json_encode([$content], JSON_UNESCAPED_UNICODE), $expire);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA加密处理
|
||||
* RSA加密处理-平台证书
|
||||
* @param string $string
|
||||
* @return string
|
||||
* @throws \WeChat\Exceptions\InvalidDecryptException
|
||||
*/
|
||||
protected function rsaEncode($string)
|
||||
{
|
||||
$publicKey = file_get_contents($this->config['cert_public']);
|
||||
$publicKey = $this->config['mp_cert_content'];
|
||||
if (openssl_public_encrypt($string, $encrypted, $publicKey, OPENSSL_PKCS1_OAEP_PADDING)) {
|
||||
return base64_encode($encrypted);
|
||||
} else {
|
||||
throw new InvalidDecryptException('Rsa Encrypt Error.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA 解密处理
|
||||
* @param string $string
|
||||
* @return string
|
||||
* @throws \WeChat\Exceptions\InvalidDecryptException
|
||||
*/
|
||||
protected function rsaDecode($string)
|
||||
{
|
||||
$private = file_get_contents($this->config['cert_private']);
|
||||
if (openssl_private_decrypt(base64_decode($string), $content, $private, OPENSSL_PKCS1_OAEP_PADDING)) {
|
||||
return $content;
|
||||
} else {
|
||||
throw new InvalidDecryptException('Rsa Decrypt Error.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,14 +31,21 @@ class Transfers extends BasicWePay
|
||||
* @return array
|
||||
* @throws \WeChat\Exceptions\InvalidDecryptException
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml
|
||||
*/
|
||||
public function batchs($body)
|
||||
{
|
||||
if (empty($body['appid'])) {
|
||||
$body['appid'] = $this->config['appid'];
|
||||
}
|
||||
if (isset($body['transfer_detail_list']) && is_array($body['transfer_detail_list'])) {
|
||||
foreach ($body['transfer_detail_list'] as &$item) if (isset($item['user_name'])) {
|
||||
$item['user_name'] = $this->rsaEncode($item['user_name']);
|
||||
}
|
||||
}
|
||||
if (empty($body['total_num'])) {
|
||||
$body['total_num'] = count($body['transfer_detail_list']);
|
||||
}
|
||||
return $this->doRequest('POST', '/v3/transfer/batches', json_encode($body, JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
|
||||
48
_test/pay-v3-transfer.php
Normal file
48
_test/pay-v3-transfer.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2023 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免责声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/WeChatDeveloper
|
||||
// | github 代码仓库:https://github.com/zoujingli/WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
try {
|
||||
// 1. 手动加载入口文件
|
||||
include "../include.php";
|
||||
|
||||
// 2. 准备公众号配置参数
|
||||
$config = include "./pay-v3-config.php";
|
||||
|
||||
$pay = \WePayV3\Transfers::instance($config);
|
||||
|
||||
$result = $pay->batchs([
|
||||
'out_batch_no' => 'plfk2020042013',
|
||||
'batch_name' => '2019年1月深圳分部报销单',
|
||||
'batch_remark' => '2019年1月深圳分部报销单',
|
||||
'total_amount' => 100,
|
||||
'transfer_detail_list' => [
|
||||
[
|
||||
'out_detail_no' => 'x23zy545Bd5436',
|
||||
'transfer_amount' => 100,
|
||||
'transfer_remark' => '2020年4月报销',
|
||||
'openid' => 'o-MYE42l80oelYMDE34nYD456Xoy',
|
||||
'user_name' => '小小邹'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
echo "\n--- 批量打款 ---\n";
|
||||
var_export($result);
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
// 出错啦,处理下吧
|
||||
echo $exception->getMessage() . PHP_EOL;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user