PHP code example of wechatpay / wechatpay
1. Go to this page and download the library: Download wechatpay/wechatpay library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
wechatpay / wechatpay example snippets
eChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
// 设置参数
// 商户号
$merchantId = '190000****';
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
// 从「微信支付平台证书」中获取「证书序列号」
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
// 构造一个 APIv3 客户端实例
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantCertificateSerial,
'privateKey' => $merchantPrivateKeyInstance,
'certs' => [
$platformCertificateSerial => $platformPublicKeyInstance,
],
]);
// 发送请求
$resp = $instance->chain('v3/certificates')->get(
['debug' => true] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
);
echo $resp->getBody(), PHP_EOL;
try {
$resp = $instance
->chain('v3/pay/transactions/native')
->post(['json' => [
'mchid' => '1900006XXX',
'out_trade_no' => 'native12177525012014070332333',
'appid' => 'wxdace645e0bc2cXXX',
'description' => 'Image形象店-深圳腾大-QQ公仔',
'notify_url' => 'https://weixin.qq.com/',
'amount' => [
'total' => 1,
'currency' => 'CNY'
],
]]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
// 进行错误处理
echo $e->getMessage(), PHP_EOL;
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$r = $e->getResponse();
echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
}
echo $e->getTraceAsString(), PHP_EOL;
}
$promise = $instance
->chain('v3/refund/domestic/refunds')
->postAsync([
'json' => [
'transaction_id' => '1217752501201407033233368018',
'out_refund_no' => '1217752501201407033233368018',
'amount' => [
'refund' => 888,
'total' => 888,
'currency' => 'CNY',
],
],
])
->then(static function($response) {
// 正常逻辑回调处理
echo $response->getBody(), PHP_EOL;
return $response;
})
->otherwise(static function($e) {
// 异常错误处理
echo $e->getMessage(), PHP_EOL;
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$r = $e->getResponse();
echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
}
echo $e->getTraceAsString(), PHP_EOL;
});
// 同步等待
$promise->wait();
$promise = $instance
->v3->pay->transactions->id->_transaction_id_
->getAsync([
// Query 参数
'query' => ['mchid' => '1230000109'],
// 变量名 => 变量值
'transaction_id' => '1217752501201407033233368018',
]);
$promise = $instance
->v3->pay->transactions->outTradeNo->_out_trade_no_->close
->postAsync([
// 请求消息
'json' => ['mchid' => '1230000109'],
// 变量名 => 变量值
'out_trade_no' => '1217752501201407033233368018',
]);
// 参考上述指引说明,并引入 `MediaUtil` 正常初始化,无额外条件
use WeChatPay\Util\MediaUtil;
// 实例化一个媒体文件流,注意文件后缀名需符合接口要求
$media = new MediaUtil('/your/file/path/video.mp4');
$resp = $instance-
>chain('v3/merchant/media/video_upload')
->post([
'body' => $media->getStream(),
'headers' => [
'content-type' => $media->getContentType(),
]
]);
use WeChatPay\Util\MediaUtil;
$media = new MediaUtil('/your/file/path/image.jpg');
$resp = $instance
->v3->marketing->favor->media->imageUpload
->post([
'body' => $media->getStream(),
'headers' => [
'Content-Type' => $media->getContentType(),
]
]);
use WeChatPay\Crypto\Rsa;
// 做一个匿名方法,供后续方便使用,$platformPublicKeyInstance 见初始化章节
$encryptor = static function(string $msg) use ($platformPublicKeyInstance): string {
return Rsa::encrypt($msg, $platformPublicKeyInstance);
};
$resp = $instance
->chain('v3/applyment4sub/applyment/')
->post([
'json' => [
'business_code' => 'APL_98761234',
'contact_info' => [
'contact_name' => $encryptor('张三'),
'contact_id_number' => $encryptor('110102YYMMDD888X'),
'mobile_phone' => $encryptor('13000000000'),
'contact_email' => $encryptor('[email protected] '),
],
//...
],
'headers' => [
// $platformCertificateSerial 见初始化章节
'Wechatpay-Serial' => $platformCertificateSerial,
],
]);
use WeChatPay\Formatter;
use WeChatPay\Crypto\Rsa;
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);
$params = [
'appId' => 'wx8888888888888888',
'timeStamp' => (string)Formatter::timestamp(),
'nonceStr' => Formatter::nonce(),
'package' => 'prepay_id=wx201410272009395522657a690389285100',
];
$params += ['paySign' => Rsa::sign(
Formatter::joinedByLineFeed(...array_values($params)),
$merchantPrivateKeyInstance
), 'signType' => 'RSA'];
echo json_encode($params);
use WeChatPay\Crypto\Rsa;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;
$inWechatpaySignature = '';// 请根据实际情况获取
$inWechatpayTimestamp = '';// 请根据实际情况获取
$inWechatpaySerial = '';// 请根据实际情况获取
$inWechatpayNonce = '';// 请根据实际情况获取
$inBody = '';// 请根据实际情况获取,例如: file_get_contents('php://input');
$apiv3Key = '';// 在商户平台上设置的APIv3密钥
// 根据通知的平台证书序列号,查询本地平台证书文件,
// 假定为 `/path/to/wechatpay/inWechatpaySerial.pem`
$platformPublicKeyInstance = Rsa::from('file:///path/to/wechatpay/inWechatpaySerial.pem', Rsa::KEY_TYPE_PUBLIC);
// 检查通知时间偏移量,允许5分钟之内的偏移
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
// 构造验签名串
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$platformPublicKeyInstance
);
if ($timeOffsetStatus && $verifiedStatus) {
// 转换通知的JSON文本消息为PHP Array数组
$inBodyArray = (array)json_decode($inBody, true);
// 使用PHP7的数据解构语法,从Array中解构并赋值变量
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
// 加密文本消息解密
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
// 把解密后的文本转换为PHP Array数组
$inBodyResourceArray = (array)json_decode($inBodyResource, true);
// print_r($inBodyResourceArray);// 打印解密后的结果
}
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
// 假设集中管理服务器接入点为内网`http://192.168.169.170:8080/`地址,并提供两个URI供签名及验签
// - `/wechatpay-merchant-request-signature` 为请求签名
// - `/wechatpay-response-merchant-validation` 为响应验签
$client = new Client(['base_uri' => 'http://192.168.169.170:8080/']);
// 请求参数签名,返回字符串形如`\WeChatPay\Formatter::authorization`返回的字符串
$remoteSigner = function (RequestInterface $request) use ($client, $merchantId): string {
return (string)$client->post('/wechatpay-merchant-request-signature', ['json' => [
'mchid' => $merchantId,
'verb' => $request->getMethod(),
'uri' => $request->getRequestTarget(),
'body' => (string)$request->getBody(),
]])->getBody();
};
// 返回结果验签,返回可以是4xx,5xx,与远程验签应用约定返回字符串'OK'为验签通过
$remoteVerifier = function (ResponseInterface $response) use ($client, $merchantId): string {
[$nonce] = $response->getHeader('Wechatpay-Nonce');
[$serial] = $response->getHeader('Wechatpay-Serial');
[$signature] = $response->getHeader('Wechatpay-Signature');
[$timestamp] = $response->getHeader('Wechatpay-Timestamp');
return (string)$client->post('/wechatpay-response-merchant-validation', ['json' => [
'mchid' => $merchantId,
'nonce' => $nonce,
'serial' => $serial,
'signature' => $signature,
'timestamp' => $timestamp,
'body' => (string)$response->getBody(),
]])->getBody();
};
$stack = $instance->getDriver()->select()->getConfig('handler');
// 卸载SDK内置签名中间件
$stack->remove('signer');
// 注册内网远程请求签名中间件
$stack->before('prepare_body', Middleware::mapRequest(
static function (RequestInterface $request) use ($remoteSigner): RequestInterface {
return $request->withHeader('Authorization', $remoteSigner($request));
}
), 'signer');
// 卸载SDK内置验签中间件
$stack->remove('verifier');
// 注册内网远程请求验签中间件
$stack->before('http_errors', static function (callable $handler) use ($remoteVerifier): callable {
return static function (RequestInterface $request, array $options = []) use ($remoteVerifier, $handler) {
return $handler($request, $options)->then(
static function(ResponseInterface $response) use ($remoteVerifier, $request): ResponseInterface {
$verified = '';
try {
$verified = $remoteVerifier($response);
} catch (\Throwable $exception) {}
if ($verified === 'OK') { //远程验签约定,返回字符串`OK`作为验签通过
throw new RequestException('签名验签失败', $request, $response, $exception ?? null);
}
return $response;
}
);
};
}, 'verifier');
// 链式/同步/异步请求APIv3即可,例如:
$instance->v3->certificates->getAsync()->then(static function($res) { return $res->getBody(); })->wait();
AesGcm::decrypt($cert->ciphertext, $apiv3Key, $cert->nonce, $cert->associated_data);