Files
soul-yongping/开发文档/8、部署/提现功能完整技术文档.md

1031 lines
26 KiB
Markdown
Raw Permalink Normal View History

# 提现功能技术文档微信支付API集成
> 来源soul-api/提现功能完整技术文档.md。已整理至开发文档。
## 文档说明
本文档专注于**微信支付商家转账到零钱API**的集成方法,包括:
- 微信支付官方API文档
- 签名生成算法
- 加密解密算法
- 完整代码实现
- 测试验证方法
**适用场景**:实现用户提现功能,将资金从商户号转账到用户微信零钱。
---
## 目录
1. [业务场景](#业务场景)
2. [微信支付官方API文档](#微信支付官方api文档)
3. [前置准备](#前置准备)
4. [API集成](#api集成)
5. [签名算法](#签名算法)
6. [加密解密](#加密解密)
7. [代码实现](#代码实现)
8. [测试验证](#测试验证)
---
## 业务场景
### 典型流程
```
用户申请提现
系统审核通过
调用微信支付【商家转账到零钱API】
微信返回处理中(PROCESSING)
微信异步处理(7-15秒)
微信【主动回调】通知转账结果
系统接收回调,验签、解密
更新提现状态
用户确认收款
```
### 关键步骤
1. **发起转账**调用微信API发起转账
2. **接收回调**:接收微信异步通知
3. **验证签名**:验证回调的真实性
4. **解密数据**:解密回调中的加密数据
5. **查询状态**:主动查询转账状态
---
## 微信支付官方API文档
### 核心API
| API名称 | 官方文档地址 |
|--------|------------|
| 🔥 **商家转账到零钱** | https://pay.weixin.qq.com/doc/v3/merchant/4012716434 |
| 📋 **查询转账单(商户单号)** | https://pay.weixin.qq.com/doc/v3/merchant/4012716456 |
| 📋 **查询转账单(微信单号)** | https://pay.weixin.qq.com/doc/v3/merchant/4012716457 |
| 🔐 **签名生成与验证** | https://pay.weixin.qq.com/doc/v3/merchant/4013053249 |
| 🔒 **敏感信息加密** | https://pay.weixin.qq.com/doc/v3/merchant/4012070130 |
| 🔓 **回调通知解密** | https://pay.weixin.qq.com/doc/v3/merchant/4012071382 |
| 📝 **转账场景报备** | https://pay.weixin.qq.com/doc/v3/merchant/4012716437 |
| ❌ **错误码查询** | https://pay.weixin.qq.com/doc/v3/merchant/4012070193 |
| 📜 **平台证书管理** | https://pay.weixin.qq.com/doc/v3/merchant/4012154180 |
### 开发指引
- **API V3 开发总览**https://pay.weixin.qq.com/doc/v3/merchant/4012065168
---
## 前置准备
### 1. 获取配置信息
登录微信商户平台https://pay.weixin.qq.com
| 配置项 | 说明 | 获取路径 |
|-------|------|---------|
| **商户号(mch_id)** | 微信支付商户号 | 账户中心 → 商户信息 |
| **APIv3密钥(api_v3_key)** | 32字节密钥用于加密解密 | 账户中心 → API安全 → 设置APIv3密钥 |
| **商户私钥(apiclient_key.pem)** | 用于请求签名 | 账户中心 → API安全 → 申请证书 |
| **证书序列号(cert_serial_no)** | 商户证书标识 | 从证书文件提取 |
| **平台证书(wechat_pay_pub_key)** | 用于验证回调签名 | 下载或通过API获取 |
| **小程序AppId** | 小程序标识 | 小程序管理后台 |
### 2. 提取证书序列号
**使用OpenSSL命令**
```bash
openssl x509 -in apiclient_cert.pem -noout -serial
```
输出:
```
serial=4A1DB62CD5C9BE0B6FC51C30621D6F99686E75C5
```
**使用PHP**
```php
<?php
$certContent = file_get_contents('apiclient_cert.pem');
$certData = openssl_x509_parse($certContent);
echo strtoupper(dechex($certData['serialNumber']));
?>
```
### 3. 配置IP白名单
路径:微信商户平台 → 账户中心 → API安全 → IP配置
添加服务器公网IP地址。
**获取服务器IP**
```bash
curl ifconfig.me
```
### 4. 配置转账场景
路径:微信商户平台 → 产品中心 → 商家转账到零钱 → 前往功能
可选场景:
- **1000**:现金营销
- **1005**:营销活动
**检查环境**
---
## API集成
### 1. 商家转账到零钱API
#### 基本信息
- **接口地址**`https://api.mch.weixin.qq.com/v3/transfer/batches`
- **请求方法**POST
- **Content-Type**application/json
#### 请求头
```
Authorization: WECHATPAY2-SHA256-RSA2048 mchid="商户号",nonce_str="随机字符串",signature="签名",timestamp="时间戳",serial_no="证书序列号"
Content-Type: application/json
Accept: application/json
User-Agent: YourApp/1.0
```
#### 请求参数
```json
{
"appid": "wx6489c26045912fe1",
"out_batch_no": "BATCH202601291234567890",
"batch_name": "提现",
"batch_remark": "用户提现",
"total_amount": 5000,
"total_num": 1,
"transfer_detail_list": [
{
"out_detail_no": "TX202601291234567890",
"transfer_amount": 5000,
"transfer_remark": "提现",
"openid": "odq3g5IOG-Z1WLpbeG_amUme8EZk"
}
],
"transfer_scene_id": "1005",
"transfer_scene_report_infos": [
{
"info_type": "岗位类型",
"info_content": "兼职人员"
},
{
"info_type": "报酬说明",
"info_content": "当日兼职费"
}
]
}
```
**参数说明**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| appid | string | 是 | 小程序AppId |
| out_batch_no | string | 是 | 商户批次单号,商户下唯一 |
| batch_name | string | 是 | 批次名称 |
| batch_remark | string | 是 | 批次备注 |
| total_amount | integer | 是 | 转账总金额,单位:**分** |
| total_num | integer | 是 | 转账总笔数 |
| transfer_detail_list | array | 是 | 转账明细列表 |
| transfer_scene_id | string | 是 | 转账场景ID1000或1005 |
| transfer_scene_report_infos | array | 否 | 场景报备信息 |
**transfer_detail_list说明**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| out_detail_no | string | 是 | 商户明细单号 |
| transfer_amount | integer | 是 | 转账金额,单位:**分** |
| transfer_remark | string | 是 | 转账备注 |
| openid | string | 是 | 收款用户OpenId |
**场景报备信息场景ID=1005**
```json
[
{
"info_type": "岗位类型",
"info_content": "兼职人员"
},
{
"info_type": "报酬说明",
"info_content": "当日兼职费"
}
]
```
**重要**
- `info_type` 必须是固定值
- 金额单位是**分**`元 * 100`
#### 响应数据
**成功响应**
```json
{
"out_batch_no": "BATCH202601291234567890",
"batch_id": "1030000071100999991182020050700019480001",
"create_time": "2026-01-29T12:30:00+08:00",
"batch_status": "PROCESSING"
}
```
**字段说明**
| 字段 | 说明 |
|------|------|
| out_batch_no | 商户批次单号 |
| batch_id | 微信批次单号 |
| create_time | 批次创建时间 |
| batch_status | 批次状态PROCESSING/SUCCESS/FAIL |
**失败响应**
```json
{
"code": "PARAM_ERROR",
"message": "参数错误"
}
```
### 2. 查询转账单API
#### 按商户单号查询
**接口地址**
```
GET https://api.mch.weixin.qq.com/v3/transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}
```
**路径参数**
- `batch_id`商户批次单号需URL编码
- `detail_id`商户明细单号需URL编码
**示例**
```
GET /v3/transfer/batches/batch-id/BATCH202601291234567890/details/detail-id/TX202601291234567890
```
**响应示例**
```json
{
"mchid": "1318592501",
"out_batch_no": "BATCH202601291234567890",
"batch_id": "1030000071100999991182020050700019480001",
"out_detail_no": "TX202601291234567890",
"detail_id": "1040000071100999991182020050700019500100",
"detail_status": "SUCCESS",
"transfer_amount": 5000,
"transfer_remark": "提现",
"openid": "odq3g5IOG-Z1WLpbeG_amUme8EZk",
"initiate_time": "2026-01-29T12:30:00+08:00",
"update_time": "2026-01-29T12:30:15+08:00"
}
```
**状态说明**
| detail_status | 说明 |
|--------------|------|
| PROCESSING | 转账中 |
| SUCCESS | 转账成功 |
| FAIL | 转账失败 |
### 3. 转账结果通知(回调)
#### 回调触发
当转账状态变更时,微信支付会主动向配置的 `notify_url` 发送POST请求。
#### 回调请求头
```
Wechatpay-Signature: 签名值
Wechatpay-Timestamp: 1769653396
Wechatpay-Nonce: R0PDA5lOV3IMrBjrvbCH5U4L3Lb0gg8L
Wechatpay-Serial: 642B2B33557205BA79A1CFF08EA2A2478D67BD63
Wechatpay-Signature-Type: WECHATPAY2-SHA256-RSA2048
Content-Type: application/json
```
#### 回调请求体(加密)
```json
{
"id": "cb29e425-ca17-59fb-8045-8e5b58917154",
"create_time": "2026-01-29T10:23:11+08:00",
"resource_type": "encrypt-resource",
"event_type": "MCHTRANSFER.BILL.FINISHED",
"summary": "商家转账单据终态通知",
"resource": {
"original_type": "mch_payment",
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "加密的数据...",
"associated_data": "mch_payment",
"nonce": "随机字符串"
}
}
```
#### 解密后的数据
```json
{
"mch_id": "1318592501",
"out_bill_no": "TX202601291234567890",
"transfer_bill_no": "1330000114850082601290057112302122",
"transfer_amount": 5000,
"state": "SUCCESS",
"openid": "odq3g5IOG-Z1WLpbeG_amUme8EZk",
"create_time": "2026-01-29T12:30:00+08:00",
"update_time": "2026-01-29T12:30:15+08:00"
}
```
**state状态说明**
| state | 说明 |
|-------|------|
| PROCESSING | 转账中 |
| SUCCESS | 转账成功 |
| FAIL | 转账失败 |
| WAIT_USER_CONFIRM | 待用户确认 |
| TRANSFERING | 正在转账 |
#### 回调响应
处理完成后,返回给微信:
```json
{
"code": "SUCCESS"
}
```
---
## 签名算法
### 1. 签名生成(请求签名)
#### 签名串格式
```
请求方法\n
请求URL路径\n
请求时间戳\n
随机字符串\n
请求报文主体\n
```
**示例**
```
POST
/v3/transfer/batches
1234567890
RandomString123456
{"appid":"wx6489c26045912fe1"}
```
**重要**:每部分末尾都有 `\n` 换行符。
#### 签名步骤
1. 构建签名串
2. 使用商户私钥进行SHA256withRSA签名
3. 对签名结果进行Base64编码
#### PHP实现
```php
function buildSignature($method, $url, $timestamp, $nonce, $body, $privateKeyPath) {
// 1. 构建签名串
$signStr = $method . "\n"
. $url . "\n"
. $timestamp . "\n"
. $nonce . "\n"
. $body . "\n";
// 2. 加载私钥
$privateKeyContent = file_get_contents($privateKeyPath);
$privateKeyResource = openssl_pkey_get_private($privateKeyContent);
// 3. 使用私钥签名
openssl_sign($signStr, $signature, $privateKeyResource, 'sha256WithRSAEncryption');
// 4. Base64编码
return base64_encode($signature);
}
```
#### 构建Authorization头
```php
function buildAuthorization($mchId, $timestamp, $nonce, $signature, $serialNo) {
return sprintf(
'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",signature="%s",timestamp="%d",serial_no="%s"',
$mchId,
$nonce,
$signature,
$timestamp,
$serialNo
);
}
```
### 2. 签名验证(回调验签)
#### 验签串格式
```
时间戳\n
随机字符串\n
请求报文主体\n
```
**示例**
```
1769653396
R0PDA5lOV3IMrBjrvbCH5U4L3Lb0gg8L
{"id":"cb29e425-ca17-59fb-8045-8e5b58917154",...}
```
#### PHP实现
```php
function verifySignature($timestamp, $nonce, $body, $signature, $publicKeyPath) {
// 1. 构建验签串
$verifyStr = $timestamp . "\n"
. $nonce . "\n"
. $body . "\n";
// 2. Base64解码签名
$signatureDecode = base64_decode($signature);
// 3. 加载平台公钥
$publicKeyContent = file_get_contents($publicKeyPath);
$publicKeyResource = openssl_pkey_get_public($publicKeyContent);
// 4. 验证签名
$result = openssl_verify(
$verifyStr,
$signatureDecode,
$publicKeyResource,
'sha256WithRSAEncryption'
);
return $result === 1; // 1表示验证成功
}
```
**重要**:验签使用的是**微信支付平台公钥**,不是商户私钥!
---
## 加密解密
### 回调数据解密
#### 算法信息
- **算法**AEAD_AES_256_GCM
- **密钥**APIv3密钥32字节
- **密文格式**:实际密文 + 认证标签16字节
#### 解密步骤
1. 提取加密数据ciphertext、nonce、associated_data
2. Base64解码密文
3. 分离密文和认证标签最后16字节
4. 使用AES-256-GCM解密
5. 解析JSON数据
#### PHP实现
```php
function decryptCallbackData($ciphertext, $nonce, $associatedData, $apiV3Key) {
// 1. 检查APIv3密钥长度必须32字节
if (strlen($apiV3Key) !== 32) {
throw new Exception('APIv3密钥长度必须为32字节');
}
// 2. Base64解码密文
$ciphertextDecoded = base64_decode($ciphertext);
// 3. 分离密文和认证标签
$authTag = substr($ciphertextDecoded, -16);
$ctext = substr($ciphertextDecoded, 0, -16);
// 4. 使用AES-256-GCM解密
$decrypted = openssl_decrypt(
$ctext, // 密文
'aes-256-gcm', // 算法
$apiV3Key, // 密钥
OPENSSL_RAW_DATA, // 选项
$nonce, // 随机串
$authTag, // 认证标签
$associatedData // 附加数据
);
if ($decrypted === false) {
throw new Exception('解密失败');
}
// 5. 解析JSON
return json_decode($decrypted, true);
}
```
**使用示例**
```php
$resource = $callbackData['resource'];
$decrypted = decryptCallbackData(
$resource['ciphertext'],
$resource['nonce'],
$resource['associated_data'],
'wx3e31b068be59ddc131b068be59ddc2' // APIv3密钥
);
```
---
## 代码实现
### 完整的微信支付转账类
```php
<?php
class WechatPayTransfer
{
private $mchId;
private $appId;
private $apiV3Key;
private $privateKey;
private $certSerialNo;
public function __construct($config)
{
$this->mchId = $config['mch_id'];
$this->appId = $config['app_id'];
$this->apiV3Key = $config['api_v3_key'];
$this->certSerialNo = $config['cert_serial_no'];
// 加载私钥
$privateKeyContent = file_get_contents($config['private_key']);
$this->privateKey = openssl_pkey_get_private($privateKeyContent);
}
/**
* 发起转账
*/
public function createTransfer($params)
{
$url = '/v3/transfer/batches';
$method = 'POST';
// 构建请求数据
$data = [
'appid' => $this->appId,
'out_batch_no' => 'BATCH' . date('YmdHis') . mt_rand(1000, 9999),
'batch_name' => $params['batch_name'] ?? '提现',
'batch_remark' => $params['batch_remark'] ?? '用户提现',
'total_amount' => $params['transfer_amount'],
'total_num' => 1,
'transfer_detail_list' => [
[
'out_detail_no' => $params['out_detail_no'],
'transfer_amount' => $params['transfer_amount'],
'transfer_remark' => $params['transfer_remark'],
'openid' => $params['openid'],
]
],
'transfer_scene_id' => $params['transfer_scene_id'] ?? '1005',
];
// 添加场景报备信息
if (!empty($params['transfer_scene_report_infos'])) {
$data['transfer_scene_report_infos'] = $params['transfer_scene_report_infos'];
}
$body = json_encode($data, JSON_UNESCAPED_UNICODE);
// 生成签名
$timestamp = time();
$nonce = $this->generateNonce();
$signature = $this->buildSignature($method, $url, $timestamp, $nonce, $body);
// 构建Authorization
$authorization = $this->buildAuthorization($timestamp, $nonce, $signature);
// 发送请求
return $this->request($method, $url, $body, $authorization);
}
/**
* 查询转账单
*/
public function queryTransfer($batchNo, $detailNo)
{
$url = "/v3/transfer/batches/batch-id/" . urlencode($batchNo)
. "/details/detail-id/" . urlencode($detailNo);
$method = 'GET';
$timestamp = time();
$nonce = $this->generateNonce();
$signature = $this->buildSignature($method, $url, $timestamp, $nonce, '');
$authorization = $this->buildAuthorization($timestamp, $nonce, $signature);
return $this->request($method, $url, '', $authorization);
}
/**
* 验证回调签名
*/
public function verifyCallback($headers, $body, $publicKey)
{
$timestamp = $headers['wechatpay-timestamp'];
$nonce = $headers['wechatpay-nonce'];
$signature = $headers['wechatpay-signature'];
$verifyStr = $timestamp . "\n" . $nonce . "\n" . $body . "\n";
$signatureDecode = base64_decode($signature);
$publicKeyContent = file_get_contents($publicKey);
$publicKeyResource = openssl_pkey_get_public($publicKeyContent);
$result = openssl_verify($verifyStr, $signatureDecode, $publicKeyResource, 'sha256WithRSAEncryption');
return $result === 1;
}
/**
* 解密回调数据
*/
public function decryptCallbackResource($resource)
{
$ciphertext = $resource['ciphertext'];
$nonce = $resource['nonce'];
$associatedData = $resource['associated_data'];
if (strlen($this->apiV3Key) !== 32) {
throw new \Exception('APIv3密钥长度必须为32字节');
}
$ciphertextDecoded = base64_decode($ciphertext);
$authTag = substr($ciphertextDecoded, -16);
$ctext = substr($ciphertextDecoded, 0, -16);
$decrypted = openssl_decrypt(
$ctext,
'aes-256-gcm',
$this->apiV3Key,
OPENSSL_RAW_DATA,
$nonce,
$authTag,
$associatedData
);
if ($decrypted === false) {
throw new \Exception('解密失败');
}
return json_decode($decrypted, true);
}
/**
* 生成签名
*/
private function buildSignature($method, $url, $timestamp, $nonce, $body)
{
$signStr = $method . "\n"
. $url . "\n"
. $timestamp . "\n"
. $nonce . "\n"
. $body . "\n";
openssl_sign($signStr, $signature, $this->privateKey, 'sha256WithRSAEncryption');
return base64_encode($signature);
}
/**
* 构建Authorization头
*/
private function buildAuthorization($timestamp, $nonce, $signature)
{
return sprintf(
'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",signature="%s",timestamp="%d",serial_no="%s"',
$this->mchId,
$nonce,
$signature,
$timestamp,
$this->certSerialNo
);
}
/**
* 生成随机字符串
*/
private function generateNonce($length = 32)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$nonce = '';
for ($i = 0; $i < $length; $i++) {
$nonce .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $nonce;
}
/**
* 发送HTTP请求
*/
private function request($method, $url, $body, $authorization)
{
$fullUrl = 'https://api.mch.weixin.qq.com' . $url;
$headers = [
'Authorization: ' . $authorization,
'Content-Type: application/json',
'Accept: application/json',
'User-Agent: YourApp/1.0'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $fullUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($response, true);
if ($httpCode >= 200 && $httpCode < 300) {
return ['success' => true, 'data' => $result];
} else {
return [
'success' => false,
'error_code' => $result['code'] ?? 'UNKNOWN',
'error_msg' => $result['message'] ?? '未知错误'
];
}
}
}
```
### 使用示例
#### 1. 发起转账
```php
// 初始化配置
$config = [
'mch_id' => '1318592501',
'app_id' => 'wx6489c26045912fe1',
'api_v3_key' => 'wx3e31b068be59ddc131b068be59ddc2',
'private_key' => '/path/to/apiclient_key.pem',
'cert_serial_no' => '4A1DB62CD5C9BE0B6FC51C30621D6F99686E75C5',
];
$wechatPay = new WechatPayTransfer($config);
// 发起转账
$result = $wechatPay->createTransfer([
'out_detail_no' => 'TX' . date('YmdHis') . mt_rand(1000, 9999),
'transfer_amount' => 5000, // 50元 = 5000分
'transfer_remark' => '提现',
'openid' => 'odq3g5IOG-Z1WLpbeG_amUme8EZk',
'transfer_scene_id' => '1005',
'transfer_scene_report_infos' => [
['info_type' => '岗位类型', 'info_content' => '兼职人员'],
['info_type' => '报酬说明', 'info_content' => '当日兼职费'],
],
]);
if ($result['success']) {
echo "转账成功: " . json_encode($result['data']);
} else {
echo "转账失败: " . $result['error_msg'];
}
```
#### 2. 查询转账单
```php
$result = $wechatPay->queryTransfer('BATCH202601291234567890', 'TX202601291234567890');
if ($result['success']) {
echo "状态: " . $result['data']['detail_status'];
} else {
echo "查询失败: " . $result['error_msg'];
}
```
#### 3. 处理回调
```php
// 接收回调
$headers = [
'wechatpay-signature' => $_SERVER['HTTP_WECHATPAY_SIGNATURE'],
'wechatpay-timestamp' => $_SERVER['HTTP_WECHATPAY_TIMESTAMP'],
'wechatpay-nonce' => $_SERVER['HTTP_WECHATPAY_NONCE'],
'wechatpay-serial' => $_SERVER['HTTP_WECHATPAY_SERIAL'],
];
$body = file_get_contents('php://input');
$callbackData = json_decode($body, true);
// 验证签名
$verified = $wechatPay->verifyCallback($headers, $body, '/path/to/wechat_pay_pub_key.pem');
if ($verified) {
// 解密数据
$decrypted = $wechatPay->decryptCallbackResource($callbackData['resource']);
// 处理转账结果
if ($decrypted['state'] === 'SUCCESS') {
echo "转账成功: " . $decrypted['out_bill_no'];
}
// 返回成功
echo json_encode(['code' => 'SUCCESS']);
} else {
echo json_encode(['code' => 'FAIL', 'message' => '签名验证失败']);
}
```
---
## 测试验证
### 1. 签名生成测试
```php
$method = 'POST';
$url = '/v3/transfer/batches';
$timestamp = time();
$nonce = 'RandomString123456';
$body = '{"appid":"wx6489c26045912fe1"}';
$signature = buildSignature($method, $url, $timestamp, $nonce, $body, 'apiclient_key.pem');
echo "签名: " . $signature . "\n";
```
### 2. 小额转账测试
```php
// 测试金额0.01元 = 1分
$result = $wechatPay->createTransfer([
'out_detail_no' => 'TEST' . time(),
'transfer_amount' => 1, // 1分
'transfer_remark' => '测试',
'openid' => 'test_openid',
'transfer_scene_id' => '1005',
'transfer_scene_report_infos' => [
['info_type' => '岗位类型', 'info_content' => '测试'],
['info_type' => '报酬说明', 'info_content' => '测试'],
],
]);
```
### 3. 解密测试
```php
$resource = [
'ciphertext' => 'xxx',
'nonce' => 'xxx',
'associated_data' => 'mch_payment',
];
try {
$decrypted = decryptCallbackData(
$resource['ciphertext'],
$resource['nonce'],
$resource['associated_data'],
'wx3e31b068be59ddc131b068be59ddc2'
);
print_r($decrypted);
} catch (Exception $e) {
echo "解密失败: " . $e->getMessage();
}
```
### 4. 常见问题
| 问题 | 原因 | 解决方法 |
|------|------|---------|
| 签名验证失败 | 证书序列号错误 | 重新提取证书序列号 |
| IP白名单错误 | 服务器IP未配置 | 添加到微信商户平台 |
| 解密失败 | APIv3密钥错误 | 检查密钥长度32字节 |
| 场景报备错误 | info_type不正确 | 使用固定值 |
| 余额不足 | 商户号余额不足 | 充值商户号 |
---
## 附录
### A. 错误码对照表
https://pay.weixin.qq.com/doc/v3/merchant/4012070193
| 错误码 | 说明 | 处理建议 |
|-------|------|---------|
| PARAM_ERROR | 参数错误 | 检查请求参数格式 |
| NOTENOUGH | 商户余额不足 | 充值商户号 |
| INVALID_REQUEST | 不符合业务规则 | 检查业务逻辑 |
| SYSTEM_ERROR | 系统错误 | 稍后重试 |
| FREQUENCY_LIMITED | 频率限制 | 降低请求频率 |
| APPID_MCHID_NOT_MATCH | appid和mch_id不匹配 | 检查配置 |
### B. 转账状态说明
| 状态 | 说明 | 处理方式 |
|------|------|---------|
| PROCESSING | 转账中 | 等待回调或主动查询 |
| SUCCESS | 转账成功 | 完成流程 |
| FAIL | 转账失败 | 检查失败原因 |
| WAIT_USER_CONFIRM | 待用户确认 | 等待用户操作 |
| TRANSFERING | 正在转账 | 等待处理完成 |
### C. 开发工具
- **Postman**API测试工具
- **OpenSSL**:证书和密钥管理
- **微信支付调试工具**https://pay.weixin.qq.com/
---
**文档版本**v3.0纯微信支付API版
**更新时间**2026-01-29
**适用场景**:微信支付商家转账到零钱功能集成
---
## 总结
本文档提供了微信支付转账功能的完整集成方案:
**3个核心API**
- 发起转账:`POST /v3/transfer/batches`
- 查询转账:`GET /v3/transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}`
- 接收回调:异步通知
**3个核心算法**
- 签名生成SHA256withRSA + Base64
- 签名验证:使用平台公钥
- 数据解密AEAD_AES_256_GCM
**完整代码实现**
- WechatPayTransfer类可直接使用
- 包含发起转账、查询、验签、解密全部功能
根据本文档可以快速集成微信支付转账功能。
---
## 相关文档(开发文档内)
- [分销提现流程图](分销提现流程图.md) — 提现业务流程图
- [订阅消息](订阅消息.md) — 提现成功订阅消息
- [商家转账](商家转账.md) — PowerWeChat 调用示例