@alipay/alipay-aipay 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/alipay-aipay.js +6 -0
- package/dist/commands/install.d.ts +11 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +182 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/prepare-tools.d.ts +5 -0
- package/dist/commands/prepare-tools.d.ts.map +1 -0
- package/dist/commands/prepare-tools.js +29 -0
- package/dist/commands/prepare-tools.js.map +1 -0
- package/dist/commands/uninstall.d.ts +10 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +82 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +155 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/alipay-cli.d.ts +15 -0
- package/dist/utils/alipay-cli.d.ts.map +1 -0
- package/dist/utils/alipay-cli.js +221 -0
- package/dist/utils/alipay-cli.js.map +1 -0
- package/dist/utils/config.d.ts +48 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +207 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/file.d.ts +55 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +154 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/gpg.d.ts +14 -0
- package/dist/utils/gpg.d.ts.map +1 -0
- package/dist/utils/gpg.js +247 -0
- package/dist/utils/gpg.js.map +1 -0
- package/dist/utils/jq.d.ts +14 -0
- package/dist/utils/jq.d.ts.map +1 -0
- package/dist/utils/jq.js +221 -0
- package/dist/utils/jq.js.map +1 -0
- package/dist/utils/npm.d.ts +17 -0
- package/dist/utils/npm.d.ts.map +1 -0
- package/dist/utils/npm.js +42 -0
- package/dist/utils/npm.js.map +1 -0
- package/dist/utils/shell.d.ts +11 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +54 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +48 -7
- package/skill/SKILL.md +118 -0
- package/skill/references/integration/flow.md +487 -0
- package/skill/references/integration/modules/alipay-sdk-reminder.md +256 -0
- package/skill/references/integration/modules/checklist.md +122 -0
- package/skill/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +68 -0
- package/skill/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +77 -0
- package/skill/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +74 -0
- package/skill/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +73 -0
- package/skill/references/integration/modules/code-examples/csharp/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +58 -0
- package/skill/references/integration/modules/code-examples/csharp/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +128 -0
- package/skill/references/integration/modules/code-examples/csharp/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.cs +500 -0
- package/skill/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +72 -0
- package/skill/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +80 -0
- package/skill/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +78 -0
- package/skill/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +77 -0
- package/skill/references/integration/modules/code-examples/java/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +66 -0
- package/skill/references/integration/modules/code-examples/java/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +135 -0
- package/skill/references/integration/modules/code-examples/java/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemoController.java +610 -0
- package/skill/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +51 -0
- package/skill/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +57 -0
- package/skill/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +55 -0
- package/skill/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +54 -0
- package/skill/references/integration/modules/code-examples/nodejs/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +77 -0
- package/skill/references/integration/modules/code-examples/nodejs/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
- package/skill/references/integration/modules/code-examples/nodejs/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.js +461 -0
- package/skill/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
- package/skill/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +67 -0
- package/skill/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +66 -0
- package/skill/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
- package/skill/references/integration/modules/code-examples/php/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +56 -0
- package/skill/references/integration/modules/code-examples/php/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +107 -0
- package/skill/references/integration/modules/code-examples/php/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.php +444 -0
- package/skill/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +57 -0
- package/skill/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
- package/skill/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +63 -0
- package/skill/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +62 -0
- package/skill/references/integration/modules/code-examples/python/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +48 -0
- package/skill/references/integration/modules/code-examples/python/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +43 -0
- package/skill/references/integration/modules/code-examples/python/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.py +465 -0
- package/skill/references/integration/modules/interface-guide.md +90 -0
- package/skill/references/integration/modules/product-decision.md +90 -0
- package/skill/references/integration/modules/sandbox/a2m-sandbox-test.md +270 -0
- package/skill/references/integration/modules/sandbox/alipay-sandbox-tool.md +133 -0
- package/skill/references/integration/modules/sandbox/sandbox-setup-guide.md +218 -0
- package/skill/references/integration/modules/scripts/local_402_sandbox_pay.py +659 -0
- package/skill/references/normal/alipay-cli-env.md +214 -0
- package/skill/references/normal/rejection-guide.md +37 -0
- package/skill/references/normal/scripts/common.sh +32 -0
- package/skill/references/normal/scripts/detect_dev_tool.sh +46 -0
- package/skill/references/onboarding/flow.md +573 -0
- package/skill/references/onboarding/modules/app-release.md +415 -0
- package/skill/references/onboarding/modules/authorization.md +269 -0
- package/skill/references/onboarding/modules/error-handling.md +251 -0
- package/skill/references/onboarding/modules/mcc-reference.md +209 -0
- package/skill/references/onboarding/modules/product-sign.md +304 -0
- package/skill/references/onboarding/modules/scripts/app.sh +705 -0
- package/skill/references/onboarding/modules/scripts/ar_sign_apply.sh +248 -0
- package/skill/references/onboarding/modules/scripts/auth.sh +505 -0
- package/skill/references/onboarding/modules/scripts/error_handler.sh +452 -0
- package/skill/references/onboarding/modules/scripts/query_sign_status.sh +146 -0
- package/skill/references/onboarding/modules/scripts/service.sh +267 -0
- package/skill/references/onboarding/modules/scripts/upload_screenshots.sh +152 -0
- package/skill/references/onboarding/modules/service-registration.md +294 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# 统一收单交易退款查询接口 - PHP 示例
|
|
2
|
+
|
|
3
|
+
## 请求示例
|
|
4
|
+
|
|
5
|
+
```php
|
|
6
|
+
<?php
|
|
7
|
+
require_once __DIR__ . '/aop/AopClient.php';
|
|
8
|
+
require_once __DIR__ . '/aop/request/AlipayTradeFastpayRefundQueryRequest.php';
|
|
9
|
+
|
|
10
|
+
// 初始化 SDK(PHP 使用 PKCS#1 格式私钥,Java 使用 PKCS#8 格式,请勿混用)
|
|
11
|
+
$aop = new AopClient();
|
|
12
|
+
$aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
|
|
13
|
+
// 沙箱环境: $aop->gatewayUrl = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do';
|
|
14
|
+
$aop->appId = '<-- 请填写您的AppId,例如:20190xxxxxx -->';
|
|
15
|
+
$aop->rsaPrivateKey = '<-- 请填写您的应用私钥(PKCS#1格式),例如:MIIE...(脱敏示例) -->';
|
|
16
|
+
$aop->alipayrsaPublicKey = '<-- 请填写您的支付宝公钥,例如:MIIB...(脱敏示例) -->';
|
|
17
|
+
$aop->format = 'json';
|
|
18
|
+
$aop->postCharset = 'UTF-8';
|
|
19
|
+
$aop->signType = 'RSA2';
|
|
20
|
+
|
|
21
|
+
// 构造请求参数
|
|
22
|
+
$request = new AlipayTradeFastpayRefundQueryRequest();
|
|
23
|
+
$bizContent = [
|
|
24
|
+
'out_trade_no' => 'MERCHANT_ORDER_NO_PLACEHOLDER', // 商户订单号
|
|
25
|
+
'out_request_no' => 'OUT_REQUEST_NO_PLACEHOLDER', // 退款请求号,必填
|
|
26
|
+
];
|
|
27
|
+
$request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
|
|
28
|
+
|
|
29
|
+
// 调用接口
|
|
30
|
+
$response = $aop->execute($request);
|
|
31
|
+
$responseApiName = str_replace('.', '_', $request->getApiMethodName()) . '_response';
|
|
32
|
+
$result = $response->$responseApiName;
|
|
33
|
+
|
|
34
|
+
if (!empty($result->code) && $result->code == '10000') {
|
|
35
|
+
// refund_status: REFUND_SUCCESS 表示退款成功
|
|
36
|
+
echo "退款状态: " . $result->refund_status . ", 退款金额: " . $result->refund_amount;
|
|
37
|
+
} else {
|
|
38
|
+
echo "调用失败: " . $result->sub_code . " - " . $result->sub_msg;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 响应示例
|
|
43
|
+
### 正常示例
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"code": "10000",
|
|
47
|
+
"msg": "Success",
|
|
48
|
+
"trade_no": "ALIPAY_TRADE_NO_PLACEHOLDER",
|
|
49
|
+
"out_trade_no": "MERCHANT_ORDER_NO_PLACEHOLDER",
|
|
50
|
+
"out_request_no": "OUT_REQUEST_NO_PLACEHOLDER",
|
|
51
|
+
"total_amount": "0.01",
|
|
52
|
+
"refund_amount": "0.01",
|
|
53
|
+
"refund_status": "REFUND_SUCCESS",
|
|
54
|
+
"gmt_refund_pay": "2014-11-27 15:45:57"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
### 异常示例
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"code": "20000",
|
|
61
|
+
"msg": "Service Currently Unavailable",
|
|
62
|
+
"sub_code": "isp.unknow-error",
|
|
63
|
+
"sub_msg": "系统繁忙"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 统一收单下单并支付页面接口 - PHP 示例
|
|
2
|
+
|
|
3
|
+
## 请求示例
|
|
4
|
+
|
|
5
|
+
```php
|
|
6
|
+
<?php
|
|
7
|
+
require_once __DIR__ . '/aop/AopClient.php';
|
|
8
|
+
require_once __DIR__ . '/aop/request/AlipayTradePagePayRequest.php';
|
|
9
|
+
|
|
10
|
+
// 初始化 SDK(PHP 使用 PKCS#1 格式私钥,Java 使用 PKCS#8 格式,请勿混用)
|
|
11
|
+
$aop = new AopClient();
|
|
12
|
+
$aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
|
|
13
|
+
// 沙箱环境: $aop->gatewayUrl = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do';
|
|
14
|
+
$aop->appId = '<-- 请填写您的AppId,例如:20190xxxxxx -->';
|
|
15
|
+
$aop->rsaPrivateKey = '<-- 请填写您的应用私钥(PKCS#1格式),例如:MIIE...(脱敏示例) -->';
|
|
16
|
+
$aop->alipayrsaPublicKey = '<-- 请填写您的支付宝公钥,例如:MIIB...(脱敏示例) -->';
|
|
17
|
+
$aop->format = 'json';
|
|
18
|
+
$aop->postCharset = 'UTF-8';
|
|
19
|
+
$aop->signType = 'RSA2';
|
|
20
|
+
|
|
21
|
+
// 构造请求参数
|
|
22
|
+
$request = new AlipayTradePagePayRequest();
|
|
23
|
+
$request->setNotifyUrl('https://your-domain.com/notify'); // 异步通知地址
|
|
24
|
+
$request->setReturnUrl('https://your-domain.com/return'); // 同步跳转地址
|
|
25
|
+
|
|
26
|
+
$bizContent = [
|
|
27
|
+
'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999), // 商户订单号
|
|
28
|
+
'total_amount' => '0.01', // 订单总金额
|
|
29
|
+
'subject' => '测试商品', // 订单标题
|
|
30
|
+
'product_code' => 'FAST_INSTANT_TRADE_PAY', // 网站支付固定值
|
|
31
|
+
];
|
|
32
|
+
$request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
|
|
33
|
+
|
|
34
|
+
// ⚠️ 网站支付必须使用 pageExecute(),不能使用 execute()
|
|
35
|
+
// ⚠️ 必须使用 POST 模式:pageExecute($request, 'POST')
|
|
36
|
+
$response = $aop->pageExecute($request, 'POST');
|
|
37
|
+
|
|
38
|
+
// pageExecute 返回的是 HTML 表单字符串,前端需要渲染并自动提交
|
|
39
|
+
echo $response;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
> **⚠️ 关键注意事项:**
|
|
43
|
+
> 1. 必须使用 `pageExecute()` 方法,不能使用 `execute()` 方法
|
|
44
|
+
> 2. 必须使用 POST 模式:`$aop->pageExecute($request, 'POST')`
|
|
45
|
+
> 3. 返回的 HTML 需要前端渲染并自动提交,禁止直接在服务端 redirect
|
|
46
|
+
> 4. PHP 使用 PKCS#1 格式私钥,Java 使用 PKCS#8 格式,请勿混用
|
|
47
|
+
|
|
48
|
+
## 响应示例
|
|
49
|
+
### 正常示例
|
|
50
|
+
```html
|
|
51
|
+
<form name="punchout_form" method="post" action="https://openapi.alipay.com/gateway.do?charset=UTF-8&method=alipay.trade.page.pay&format=json&sign=SIGN_PLACEHOLDER&version=1.0&app_id=20190xxxxxx&sign_type=RSA2×tamp=2014-07-24+03%3A07%3A50">
|
|
52
|
+
<input type="hidden" name="biz_content" value="{"out_trade_no":"MERCHANT_ORDER_NO_PLACEHOLDER","total_amount":"0.01","subject":"测试商品","product_code":"FAST_INSTANT_TRADE_PAY"}">
|
|
53
|
+
<input type="submit" value="立即支付" style="display:none" >
|
|
54
|
+
</form>
|
|
55
|
+
<script>document.forms[0].submit();</script>
|
|
56
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# APP支付接口 - PHP 示例
|
|
2
|
+
|
|
3
|
+
## 请求示例
|
|
4
|
+
|
|
5
|
+
```php
|
|
6
|
+
<?php
|
|
7
|
+
require_once '../aop/AopClient.php';
|
|
8
|
+
require_once '../aop/AopCertClient.php';
|
|
9
|
+
require_once '../aop/AopCertification.php';
|
|
10
|
+
require_once '../aop/AlipayConfig.php';
|
|
11
|
+
require_once '../aop/request/AlipayTradeAppPayRequest.php';
|
|
12
|
+
|
|
13
|
+
// 初始化SDK
|
|
14
|
+
$alipayClient = new AopClient(getAlipayConfig());
|
|
15
|
+
// 构造请求参数以调用接口
|
|
16
|
+
$request = new AlipayTradeAppPayRequest();
|
|
17
|
+
$model = array();
|
|
18
|
+
|
|
19
|
+
// 设置商户订单号
|
|
20
|
+
$model['out_trade_no'] = "MERCHANT_ORDER_NO_PLACEHOLDER";
|
|
21
|
+
|
|
22
|
+
// 设置订单总金额
|
|
23
|
+
$model['total_amount'] = "9.00";
|
|
24
|
+
|
|
25
|
+
// 设置订单标题
|
|
26
|
+
$model['subject'] = "大乐透";
|
|
27
|
+
|
|
28
|
+
// 设置产品码
|
|
29
|
+
$model['product_code'] = "QUICK_MSECURITY_PAY";
|
|
30
|
+
|
|
31
|
+
// 设置订单包含的商品列表信息
|
|
32
|
+
$goodsDetail = array();
|
|
33
|
+
$goodsDetail0 = array();
|
|
34
|
+
$goodsDetail0['goods_name'] = "ipad";
|
|
35
|
+
$goodsDetail0['alipay_goods_id'] = "20010001";
|
|
36
|
+
$goodsDetail0['quantity'] = 1;
|
|
37
|
+
$goodsDetail0['price'] = "2000";
|
|
38
|
+
$goodsDetail0['goods_id'] = "apple-01";
|
|
39
|
+
$goodsDetail0['goods_category'] = "34543238";
|
|
40
|
+
$goodsDetail0['categories_tree'] = "124868003|126232002|126252004";
|
|
41
|
+
$goodsDetail0['show_url'] = "http://www.alipay.com/xxx.jpg";
|
|
42
|
+
$goodsDetail[] = $goodsDetail0;
|
|
43
|
+
$model['goods_detail'] = $goodsDetail;
|
|
44
|
+
|
|
45
|
+
// 设置订单绝对超时时间
|
|
46
|
+
$model['time_expire'] = "2016-12-31 10:05:00";
|
|
47
|
+
|
|
48
|
+
// 设置业务扩展参数
|
|
49
|
+
$extendParams = array();
|
|
50
|
+
$extendParams['sys_service_provider_id'] = "<SYS_SERVICE_PROVIDER_ID>";
|
|
51
|
+
$extendParams['hb_fq_seller_percent'] = "100";
|
|
52
|
+
$extendParams['hb_fq_num'] = "3";
|
|
53
|
+
$extendParams['industry_reflux_info'] = "{\"scene_code\":\"metro_tradeorder\",\"channel\":\"xxxx\",\"scene_data\":{\"asset_name\":\"ALIPAY\"}}";
|
|
54
|
+
$extendParams['royalty_freeze'] = "true";
|
|
55
|
+
$extendParams['card_type'] = "S0JP0000";
|
|
56
|
+
$model['extend_params'] = $extendParams;
|
|
57
|
+
|
|
58
|
+
// 设置公用回传参数
|
|
59
|
+
$model['passback_params'] = "merchantBizType%3d3C%26merchantBizNo%3dMERCHANT_BIZ_NO_PLACEHOLDER";
|
|
60
|
+
|
|
61
|
+
// 设置商户的原始订单号
|
|
62
|
+
$model['merchant_order_no'] = "MERCHANT_ORDER_NO_PLACEHOLDER";
|
|
63
|
+
|
|
64
|
+
// 设置外部指定买家
|
|
65
|
+
$extUserInfo = array();
|
|
66
|
+
$extUserInfo['cert_type'] = "IDENTITY_CARD";
|
|
67
|
+
$extUserInfo['cert_no'] = "<CERT_NO>";
|
|
68
|
+
$extUserInfo['mobile'] = "<MOBILE>";
|
|
69
|
+
$extUserInfo['name'] = "李明";
|
|
70
|
+
$extUserInfo['min_age'] = "18";
|
|
71
|
+
$extUserInfo['need_check_info'] = "F";
|
|
72
|
+
$extUserInfo['identity_hash'] = "<IDENTITY_HASH>";
|
|
73
|
+
$model['ext_user_info'] = $extUserInfo;
|
|
74
|
+
|
|
75
|
+
// 设置通知参数选项
|
|
76
|
+
$queryOptions = array();
|
|
77
|
+
$queryOptions[] = "hyb_amount";
|
|
78
|
+
$queryOptions[] = "enterprise_pay_info";
|
|
79
|
+
$model['query_options'] = $queryOptions;
|
|
80
|
+
|
|
81
|
+
$request->setBizContent(json_encode($model,JSON_UNESCAPED_UNICODE));
|
|
82
|
+
// 如果是第三方代调用模式,请设置app_auth_token(应用授权令牌)
|
|
83
|
+
$orderStr = $alipayClient->sdkExecute($request, "<-- 请填写应用授权令牌 -->");
|
|
84
|
+
echo $orderStr;
|
|
85
|
+
|
|
86
|
+
function getAlipayConfig()
|
|
87
|
+
{
|
|
88
|
+
$privateKey = '<-- 请填写您的应用私钥,例如:MIIE...(脱敏示例) -->';
|
|
89
|
+
$alipayPublicKey = '<-- 请填写您的支付宝公钥,例如:MIIB...(脱敏示例) -->';
|
|
90
|
+
$alipayConfig = new AlipayConfig();
|
|
91
|
+
$alipayConfig->setServerUrl('https://openapi.alipay.com/gateway.do');
|
|
92
|
+
$alipayConfig->setAppId('<-- 请填写您的AppId,例如:20190xxxxxx -->');
|
|
93
|
+
$alipayConfig->setPrivateKey($privateKey);
|
|
94
|
+
$alipayConfig->setFormat('json');
|
|
95
|
+
$alipayConfig->setAlipayPublicKey($alipayPublicKey);
|
|
96
|
+
$alipayConfig->setCharset('UTF-8');
|
|
97
|
+
$alipayConfig->setSignType('RSA2');
|
|
98
|
+
return $alipayConfig;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 响应示例
|
|
103
|
+
### 正常示例
|
|
104
|
+
```
|
|
105
|
+
app_id=20190xxxxxx&biz_content=%7B%22time_expire%22%3A%222016-12-31+10%3A05%3A00%22%2C%22extend_params%22%3A%22%22%2C%22query_options%22%3A%22%5B%5C%22hyb_amount%5C%22%2C%5C%22enterprise_pay_info%5C%22%5D%22%2C%22subject%22%3A%22%E5%A4%A7%E4%B9%90%E9%80%8F%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22body%22%3A%22Iphone6+16G%22%2C%22passback_params%22%3A%22merchantBizType%253d3C%2526merchantBizNo%253dMERCHANT_BIZ_NO_PLACEHOLDER%22%2C%22specified_channel%22%3A%22pcredit%22%2C%22goods_detail%22%3A%22%22%2C%22merchant_order_no%22%3A%22MERCHANT_ORDER_NO_PLACEHOLDER%22%2C%22enable_pay_channels%22%3A%22pcredit%2CmoneyFund%2CdebitCardExpress%22%2C%22out_trade_no%22%3A%22MERCHANT_ORDER_NO_PLACEHOLDER%22%2C%22ext_user_info%22%3A%22%22%2C%22total_amount%22%3A%229.00%22%2C%22timeout_express%22%3A%2290m%22%2C%22disable_pay_channels%22%3A%22pcredit%2CmoneyFund%2CdebitCardExpress%22%2C%22agreement_sign_params%22%3A%22%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay&sign=SIGN_PLACEHOLDER&sign_type=RSA2×tamp=2014-07-24+03%3A07%3A50&version=1.0
|
|
106
|
+
```
|
|
107
|
+
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* A2M 智能收产品接入示例 - PHP 版本
|
|
4
|
+
*
|
|
5
|
+
* 本文件演示完整的智能收产品接入流程:
|
|
6
|
+
* 1. 返回 402 Payment-Needed Header
|
|
7
|
+
* 2. 验证 Payment-Proof 支付凭证
|
|
8
|
+
* 3. 发送履约回执确认
|
|
9
|
+
* 4. 返回资源内容
|
|
10
|
+
*
|
|
11
|
+
* 依赖安装:
|
|
12
|
+
* composer require alipay/alipay-sdk
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ==================== 配置信息(实际使用时请从配置中心读取)====================
|
|
16
|
+
define('ALIPAY_CONFIG', [
|
|
17
|
+
'appId' => '<APP_ID>',
|
|
18
|
+
'privateKey' => '<APP_PRIVATE_KEY>', // 请填写您的应用私钥(PKCS#1 格式)
|
|
19
|
+
'alipayPublicKey' => '<ALIPAY_PUBLIC_KEY>', // 请填写您的支付宝公钥
|
|
20
|
+
'gateway' => 'https://openapi.alipay.com/gateway.do',
|
|
21
|
+
'sellerId' => '<SELLER_ID_2088>', // 商户 ID(2088 格式)
|
|
22
|
+
'serviceId' => '<SERVICE_ID>', // 商户服务 ID
|
|
23
|
+
'merchantPrivateKey' => '<APP_PRIVATE_KEY>' // 请填写您的应用私钥(用于商家签名)
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
define('RESOURCE_CONFIG', [
|
|
27
|
+
'path' => '/demo/a2m/resource',
|
|
28
|
+
'goodsName' => 'AI 生成内容服务'
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 初始化 SDK
|
|
33
|
+
*/
|
|
34
|
+
function initAlipayClient() {
|
|
35
|
+
require_once 'aop/AopClient.php';
|
|
36
|
+
require_once 'aop/AlipayConfig.php';
|
|
37
|
+
|
|
38
|
+
$config = new AlipayConfig();
|
|
39
|
+
$config->setServerUrl(ALIPAY_CONFIG['gateway']);
|
|
40
|
+
$config->setAppId(ALIPAY_CONFIG['appId']);
|
|
41
|
+
$config->setPrivateKey(ALIPAY_CONFIG['privateKey']);
|
|
42
|
+
$config->setFormat('json');
|
|
43
|
+
$config->setAlipayPublicKey(ALIPAY_CONFIG['alipayPublicKey']);
|
|
44
|
+
$config->setCharset('UTF-8');
|
|
45
|
+
$config->setSignType('RSA2');
|
|
46
|
+
|
|
47
|
+
return new AopClient($config);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 格式化支付宝时间戳:yyyy-MM-dd HH:mm:ss
|
|
52
|
+
*/
|
|
53
|
+
function formatAlipayTimestamp($timestamp = null) {
|
|
54
|
+
if ($timestamp === null) {
|
|
55
|
+
$timestamp = time();
|
|
56
|
+
}
|
|
57
|
+
return date('Y-m-d H:i:s', $timestamp);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 生成商家签名(seller_signature)
|
|
62
|
+
*/
|
|
63
|
+
function generateSellerSignature($params, $privateKey) {
|
|
64
|
+
// 1. 按 key 字典序排序
|
|
65
|
+
ksort($params);
|
|
66
|
+
|
|
67
|
+
// 2. 拼接签名内容
|
|
68
|
+
$signContent = [];
|
|
69
|
+
foreach ($params as $key => $value) {
|
|
70
|
+
if ($value !== null && $value !== '') {
|
|
71
|
+
$signContent[] = "{$key}={$value}";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
$signString = implode('&', $signContent);
|
|
75
|
+
|
|
76
|
+
// 3. RSA2 签名
|
|
77
|
+
$privateKeyResource = openssl_pkey_get_private($privateKey);
|
|
78
|
+
openssl_sign($signString, $signature, $privateKeyResource, OPENSSL_ALGO_SHA256);
|
|
79
|
+
|
|
80
|
+
return base64_encode($signature);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Base64URL 编码
|
|
85
|
+
*/
|
|
86
|
+
function base64UrlEncode($data) {
|
|
87
|
+
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Base64URL 解码
|
|
92
|
+
*/
|
|
93
|
+
function base64UrlDecode($data) {
|
|
94
|
+
$padding = strlen($data) % 4;
|
|
95
|
+
if ($padding) {
|
|
96
|
+
$data .= str_repeat('=', 4 - $padding);
|
|
97
|
+
}
|
|
98
|
+
return base64_decode(strtr($data, '-_', '+/'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 发送 JSON 响应
|
|
103
|
+
*/
|
|
104
|
+
function jsonResponse($data, $statusCode = 200, $headers = []) {
|
|
105
|
+
http_response_code($statusCode);
|
|
106
|
+
header('Content-Type: application/json; charset=utf-8');
|
|
107
|
+
|
|
108
|
+
foreach ($headers as $key => $value) {
|
|
109
|
+
header("{$key}: {$value}");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
|
113
|
+
exit;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ==================== 智能收产品接入示例接口 ====================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 智能收产品统一接口
|
|
120
|
+
*
|
|
121
|
+
* 完整流程演示:
|
|
122
|
+
* 1. 不带 Payment-Proof Header:返回 HTTP 402 + Payment-Needed Header
|
|
123
|
+
* 2. 带 Payment-Proof Header:验证支付 → 自动履约 → 返回资源
|
|
124
|
+
*/
|
|
125
|
+
function handleResourceRequest() {
|
|
126
|
+
// 获取 Payment-Proof Header
|
|
127
|
+
$paymentProof = isset($_SERVER['HTTP_PAYMENT_PROOF']) ? $_SERVER['HTTP_PAYMENT_PROOF'] : null;
|
|
128
|
+
|
|
129
|
+
// 场景 1:用户未支付,返回 402 + Payment-Needed Header
|
|
130
|
+
if (empty($paymentProof) || trim($paymentProof) === '') {
|
|
131
|
+
createPaymentRequiredResponse();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 场景 2:用户已支付,验证 Payment-Proof 并返回资源
|
|
136
|
+
verifyPaymentAndDeliverResource($paymentProof);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 创建 402 支付请求响应
|
|
141
|
+
*/
|
|
142
|
+
function createPaymentRequiredResponse() {
|
|
143
|
+
try {
|
|
144
|
+
// 1. 构造订单信息
|
|
145
|
+
$outTradeNo = 'ORDER_' . time() . mt_rand(1000, 9999);
|
|
146
|
+
$amount = '0.01'; // 单位:元
|
|
147
|
+
$currency = 'CNY';
|
|
148
|
+
$resourceId = RESOURCE_CONFIG['path'];
|
|
149
|
+
$goodsName = RESOURCE_CONFIG['goodsName'];
|
|
150
|
+
|
|
151
|
+
// 2. 计算支付截止时间(30 分钟后)
|
|
152
|
+
$payBefore = date('c', time() + 30 * 60); // ISO 8601 格式
|
|
153
|
+
|
|
154
|
+
// 3. 生成商家签名
|
|
155
|
+
$sellerSignature = generateSellerSignature([
|
|
156
|
+
'amount' => $amount,
|
|
157
|
+
'currency' => $currency,
|
|
158
|
+
'goods_name' => $goodsName,
|
|
159
|
+
'out_trade_no' => $outTradeNo,
|
|
160
|
+
'pay_before' => $payBefore,
|
|
161
|
+
'resource_id' => $resourceId,
|
|
162
|
+
'seller_id' => ALIPAY_CONFIG['sellerId'],
|
|
163
|
+
'service_id' => ALIPAY_CONFIG['serviceId']
|
|
164
|
+
], ALIPAY_CONFIG['merchantPrivateKey']);
|
|
165
|
+
|
|
166
|
+
// 4. 构造 Payment-Needed Header 内容
|
|
167
|
+
$paymentNeeded = [
|
|
168
|
+
'protocol' => [
|
|
169
|
+
'out_trade_no' => $outTradeNo,
|
|
170
|
+
'amount' => $amount,
|
|
171
|
+
'currency' => $currency,
|
|
172
|
+
'resource_id' => $resourceId,
|
|
173
|
+
'pay_before' => $payBefore,
|
|
174
|
+
'seller_signature' => $sellerSignature,
|
|
175
|
+
'seller_sign_type' => 'RSA2',
|
|
176
|
+
'seller_unique_id' => ALIPAY_CONFIG['sellerId']
|
|
177
|
+
],
|
|
178
|
+
'method' => [
|
|
179
|
+
'seller_name' => '测试商户',
|
|
180
|
+
'seller_id' => ALIPAY_CONFIG['sellerId'],
|
|
181
|
+
'seller_app_id' => ALIPAY_CONFIG['appId'],
|
|
182
|
+
'goods_name' => $goodsName,
|
|
183
|
+
'seller_unique_id_key' => 'seller_id',
|
|
184
|
+
'service_id' => ALIPAY_CONFIG['serviceId']
|
|
185
|
+
]
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
// 5. Base64URL 编码
|
|
189
|
+
$paymentNeededEncoded = base64UrlEncode(json_encode($paymentNeeded, JSON_UNESCAPED_UNICODE));
|
|
190
|
+
|
|
191
|
+
// 6. 构造 402 响应
|
|
192
|
+
$response = [
|
|
193
|
+
'code' => 'Payment-Needed',
|
|
194
|
+
'message' => '需要支付',
|
|
195
|
+
'out_trade_no' => $outTradeNo,
|
|
196
|
+
'amount' => $amount,
|
|
197
|
+
'currency' => $currency,
|
|
198
|
+
'goods_name' => $goodsName
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
error_log("创建支付订单成功:outTradeNo={$outTradeNo}, amount={$amount}");
|
|
202
|
+
|
|
203
|
+
jsonResponse($response, 402, ['Payment-Needed' => $paymentNeededEncoded]);
|
|
204
|
+
|
|
205
|
+
} catch (Exception $e) {
|
|
206
|
+
error_log('创建订单失败:' . $e->getMessage());
|
|
207
|
+
jsonResponse([
|
|
208
|
+
'code' => 'CREATE_ORDER_ERROR',
|
|
209
|
+
'message' => '创建订单失败:' . $e->getMessage()
|
|
210
|
+
], 500);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 验证支付凭证并交付资源
|
|
216
|
+
*/
|
|
217
|
+
function verifyPaymentAndDeliverResource($paymentProof) {
|
|
218
|
+
try {
|
|
219
|
+
// 1. 从 Payment-Proof 中解析订单信息
|
|
220
|
+
$decodedProof = base64UrlDecode($paymentProof);
|
|
221
|
+
$proofJson = json_decode($decodedProof, true);
|
|
222
|
+
|
|
223
|
+
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
224
|
+
throw new Exception('Payment-Proof 解析失败:' . json_last_error_msg());
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 从 protocol 层获取 payment_proof 和 trade_no
|
|
228
|
+
$paymentProofValue = null;
|
|
229
|
+
$tradeNo = null;
|
|
230
|
+
$clientSession = null;
|
|
231
|
+
|
|
232
|
+
if (isset($proofJson['protocol'])) {
|
|
233
|
+
$paymentProofValue = $proofJson['protocol']['payment_proof'] ?? null;
|
|
234
|
+
$tradeNo = $proofJson['protocol']['trade_no'] ?? null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 从 method 层获取 client_session
|
|
238
|
+
if (isset($proofJson['method'])) {
|
|
239
|
+
$clientSession = $proofJson['method']['client_session'] ?? null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 校验必要字段
|
|
243
|
+
if (empty($paymentProofValue)) {
|
|
244
|
+
jsonResponse([
|
|
245
|
+
'code' => 'INVALID_PAYMENT_PROOF_FORMAT',
|
|
246
|
+
'message' => 'Payment-Proof 格式错误:缺少 payment_proof'
|
|
247
|
+
], 400);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (empty($tradeNo)) {
|
|
251
|
+
jsonResponse([
|
|
252
|
+
'code' => 'INVALID_PAYMENT_PROOF_FORMAT',
|
|
253
|
+
'message' => 'Payment-Proof 格式错误:缺少 trade_no'
|
|
254
|
+
], 400);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 2. 调用支付宝 API 验证支付凭证
|
|
258
|
+
$alipayClient = initAlipayClient();
|
|
259
|
+
$request = new \AlipayAipayAgentPaymentVerifyRequest();
|
|
260
|
+
|
|
261
|
+
$model = [
|
|
262
|
+
'payment_proof' => $paymentProofValue,
|
|
263
|
+
'trade_no' => $tradeNo,
|
|
264
|
+
'client_session' => $clientSession
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
$request->setBizContent(json_encode($model, JSON_UNESCAPED_UNICODE));
|
|
268
|
+
$responseResult = $alipayClient->execute($request);
|
|
269
|
+
$responseApiName = str_replace('.', '_', $request->getApiMethodName()) . '_response';
|
|
270
|
+
$verifyResponse = $responseResult->$responseApiName;
|
|
271
|
+
|
|
272
|
+
// 3. 验证失败,返回错误
|
|
273
|
+
if (empty($verifyResponse->code) || $verifyResponse->code !== '10000') {
|
|
274
|
+
error_log('支付凭证验证失败:' . ($verifyResponse->sub_msg ?? 'Unknown error'));
|
|
275
|
+
jsonResponse([
|
|
276
|
+
'code' => $verifyResponse->sub_code ?? 'VERIFY_FAILED',
|
|
277
|
+
'message' => $verifyResponse->sub_msg ?? '支付凭证验证失败'
|
|
278
|
+
], 400);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 4. 验证成功,获取订单信息
|
|
282
|
+
$verifyTradeNo = $verifyResponse->trade_no ?? null;
|
|
283
|
+
$verifyOutTradeNo = $verifyResponse->out_trade_no ?? null;
|
|
284
|
+
$resourceId = $verifyResponse->resource_id ?? null;
|
|
285
|
+
$active = $verifyResponse->active ?? null;
|
|
286
|
+
|
|
287
|
+
error_log("支付凭证验证成功:tradeNo={$verifyTradeNo}, outTradeNo={$verifyOutTradeNo}");
|
|
288
|
+
|
|
289
|
+
// 5. 校验凭证有效性(active=true 表示凭证有效)
|
|
290
|
+
if ($active !== true) {
|
|
291
|
+
error_log("支付凭证无效或已过期:outTradeNo={$verifyOutTradeNo}");
|
|
292
|
+
jsonResponse([
|
|
293
|
+
'code' => 'INVALID_PAYMENT_PROOF',
|
|
294
|
+
'message' => '支付凭证无效或已过期'
|
|
295
|
+
], 400);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 6. 【TODO】查询订单是否存在(以数据库为准)
|
|
299
|
+
// $order = $orderRepository->findByOutTradeNo($verifyOutTradeNo);
|
|
300
|
+
// if (!$order) {
|
|
301
|
+
// jsonResponse([
|
|
302
|
+
// 'code' => 'ORDER_NOT_FOUND',
|
|
303
|
+
// 'message' => '订单不存在'
|
|
304
|
+
// ], 404);
|
|
305
|
+
// }
|
|
306
|
+
|
|
307
|
+
// 7. 【TODO】资源防串校验
|
|
308
|
+
// if ($resourceId !== $order->getResourceId()) {
|
|
309
|
+
// jsonResponse([
|
|
310
|
+
// 'code' => 'RESOURCE_ID_MISMATCH',
|
|
311
|
+
// 'message' => '资源 ID 不匹配,可能存在资源串改风险'
|
|
312
|
+
// ], 403);
|
|
313
|
+
// }
|
|
314
|
+
|
|
315
|
+
// 8. 【TODO】履约防重放校验(数据库幂等控制)
|
|
316
|
+
// if ($order->getFulfillStatus() === 'FULFILLED') {
|
|
317
|
+
// jsonResponse([
|
|
318
|
+
// 'code' => 'ALREADY_FULFILLED',
|
|
319
|
+
// 'message' => '订单已履约,不重复提供',
|
|
320
|
+
// 'already_fulfilled' => true
|
|
321
|
+
// ], 200);
|
|
322
|
+
// }
|
|
323
|
+
|
|
324
|
+
// 9. 执行业务逻辑,生成资源内容
|
|
325
|
+
$serviceResult = generateServiceResource($resourceId);
|
|
326
|
+
|
|
327
|
+
// 10. 【TODO】履约记录落库(用于审计/售后/对账)
|
|
328
|
+
// $fulfillmentRecordRepository->save([...]);
|
|
329
|
+
|
|
330
|
+
// 11. 【TODO】保存待确认履约状态
|
|
331
|
+
// 实际生产中建议先保存 $serviceResult 和 PENDING_CONFIRM 状态。
|
|
332
|
+
// fulfillment.confirm 成功后再标记 FULFILLED;如果确认失败,
|
|
333
|
+
// 允许同一笔 Payment-Proof 重试确认,避免误返回成功。
|
|
334
|
+
// $orderRepository->update($verifyOutTradeNo, [
|
|
335
|
+
// 'orderStatus' => 'PAID',
|
|
336
|
+
// 'fulfillStatus' => 'PENDING_CONFIRM',
|
|
337
|
+
// 'tradeNo' => $verifyTradeNo,
|
|
338
|
+
// 'serviceResult' => $serviceResult
|
|
339
|
+
// ]);
|
|
340
|
+
|
|
341
|
+
error_log("资源已生成,准备发送履约确认:outTradeNo={$verifyOutTradeNo}, tradeNo={$verifyTradeNo}");
|
|
342
|
+
|
|
343
|
+
// 12. 发送履约确认到支付宝,确认成功后才返回成功交付
|
|
344
|
+
if (!sendFulfillmentConfirm($verifyTradeNo)) {
|
|
345
|
+
jsonResponse([
|
|
346
|
+
'code' => 'FULFILLMENT_CONFIRM_FAILED',
|
|
347
|
+
'message' => '资源已生成但履约确认失败,请稍后使用同一 Payment-Proof 重试'
|
|
348
|
+
], 502);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 12.1 【TODO】履约确认成功后,再标记订单已履约
|
|
352
|
+
// $orderRepository->update($verifyOutTradeNo, [
|
|
353
|
+
// 'fulfillStatus' => 'FULFILLED',
|
|
354
|
+
// 'fulfilledAt' => date('c')
|
|
355
|
+
// ]);
|
|
356
|
+
|
|
357
|
+
error_log("履约确认成功:outTradeNo={$verifyOutTradeNo}, tradeNo={$verifyTradeNo}");
|
|
358
|
+
|
|
359
|
+
// 13. 构造 Payment-Validation Header
|
|
360
|
+
$paymentValidation = [
|
|
361
|
+
'trade_no' => $verifyTradeNo,
|
|
362
|
+
'out_trade_no' => $verifyOutTradeNo,
|
|
363
|
+
'validated' => true,
|
|
364
|
+
'resource_id' => $resourceId
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
$paymentValidationEncoded = base64UrlEncode(json_encode($paymentValidation, JSON_UNESCAPED_UNICODE));
|
|
368
|
+
|
|
369
|
+
// 14. 返回资源内容
|
|
370
|
+
jsonResponse([
|
|
371
|
+
'resource_id' => $resourceId,
|
|
372
|
+
'content' => $serviceResult,
|
|
373
|
+
'trade_no' => $verifyTradeNo,
|
|
374
|
+
'out_trade_no' => $verifyOutTradeNo,
|
|
375
|
+
'already_fulfilled' => false,
|
|
376
|
+
'fulfillment_confirmed' => true
|
|
377
|
+
], 200, ['Payment-Validation' => $paymentValidationEncoded]);
|
|
378
|
+
|
|
379
|
+
} catch (Exception $e) {
|
|
380
|
+
error_log('支付凭证验证异常:' . $e->getMessage());
|
|
381
|
+
jsonResponse([
|
|
382
|
+
'code' => 'VERIFY_FAILED',
|
|
383
|
+
'message' => '支付凭证验证失败:' . $e->getMessage()
|
|
384
|
+
], 500);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 生成服务资源内容
|
|
390
|
+
*/
|
|
391
|
+
function generateServiceResource($resourceId) {
|
|
392
|
+
return json_encode([
|
|
393
|
+
'status' => 'success',
|
|
394
|
+
'service_type' => 'AI_CONTENT_GENERATION',
|
|
395
|
+
'resource_id' => $resourceId,
|
|
396
|
+
'content' => '这是 AI 生成的内容示例,可根据实际业务替换为任意数字服务内容',
|
|
397
|
+
'generated_at' => date('c')
|
|
398
|
+
], JSON_UNESCAPED_UNICODE);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* 发送履约确认
|
|
403
|
+
*/
|
|
404
|
+
function sendFulfillmentConfirm($tradeNo) {
|
|
405
|
+
try {
|
|
406
|
+
error_log("开始发送履约确认:tradeNo={$tradeNo}");
|
|
407
|
+
|
|
408
|
+
$alipayClient = initAlipayClient();
|
|
409
|
+
$request = new \AlipayAipayAgentFulfillmentConfirmRequest();
|
|
410
|
+
|
|
411
|
+
$request->setBizContent(json_encode([
|
|
412
|
+
'trade_no' => $tradeNo
|
|
413
|
+
], JSON_UNESCAPED_UNICODE));
|
|
414
|
+
|
|
415
|
+
$responseResult = $alipayClient->execute($request);
|
|
416
|
+
$responseApiName = str_replace('.', '_', $request->getApiMethodName()) . '_response';
|
|
417
|
+
$response = $responseResult->$responseApiName;
|
|
418
|
+
|
|
419
|
+
if (!empty($response->code) && $response->code === '10000') {
|
|
420
|
+
error_log("履约确认成功:tradeNo={$tradeNo}");
|
|
421
|
+
return true;
|
|
422
|
+
} else {
|
|
423
|
+
error_log("履约确认失败:tradeNo={$tradeNo}, errorCode=" .
|
|
424
|
+
($response->sub_code ?? 'Unknown') . ", errorMsg=" .
|
|
425
|
+
($response->sub_msg ?? 'Unknown'));
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
} catch (Exception $e) {
|
|
430
|
+
error_log("履约确认异常:tradeNo={$tradeNo}, error=" . $e->getMessage());
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ==================== 路由处理 ====================
|
|
436
|
+
|
|
437
|
+
// 简单路由
|
|
438
|
+
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
|
439
|
+
if ($requestUri === RESOURCE_CONFIG['path'] && $_SERVER['REQUEST_METHOD'] === 'GET') {
|
|
440
|
+
handleResourceRequest();
|
|
441
|
+
} else {
|
|
442
|
+
http_response_code(404);
|
|
443
|
+
echo json_encode(['code' => 'NOT_FOUND', 'message' => '接口不存在'], JSON_UNESCAPED_UNICODE);
|
|
444
|
+
}
|