@alipay/alipay-aipay 0.1.1-beta.1
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/README.md +1 -0
- package/bin/alipay-aipay.js +6 -0
- package/dist/commands/install.d.ts +10 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +92 -0
- package/dist/commands/install.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 +125 -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 +51 -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 +228 -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 +53 -0
- 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 +211 -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 +571 -0
- package/skill/references/onboarding/modules/app-release.md +411 -0
- package/skill/references/onboarding/modules/authorization.md +269 -0
- package/skill/references/onboarding/modules/error-handling.md +247 -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 +312 -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,58 @@
|
|
|
1
|
+
# 统一收单下单并支付页面接口 - C# 示例
|
|
2
|
+
|
|
3
|
+
## 请求示例
|
|
4
|
+
|
|
5
|
+
```csharp
|
|
6
|
+
using Aop.Api;
|
|
7
|
+
using Aop.Api.Request;
|
|
8
|
+
using Aop.Api.Response;
|
|
9
|
+
using Aop.Api.Domain;
|
|
10
|
+
|
|
11
|
+
// 初始化SDK(C#/.NET 使用 PKCS#1 格式私钥,Java 使用 PKCS#8 格式,请勿混用)
|
|
12
|
+
IAopClient alipayClient = new DefaultAopClient(GetAlipayConfig());
|
|
13
|
+
|
|
14
|
+
var request = new AlipayTradePagePayRequest();
|
|
15
|
+
request.SetNotifyUrl("https://your-domain.com/notify"); // 异步通知地址,必须传入
|
|
16
|
+
request.SetReturnUrl("https://your-domain.com/return"); // 同步跳转地址,必须传入
|
|
17
|
+
|
|
18
|
+
var model = new AlipayTradePagePayModel
|
|
19
|
+
{
|
|
20
|
+
OutTradeNo = "MERCHANT_ORDER_NO_PLACEHOLDER", // 商户订单号,必填
|
|
21
|
+
TotalAmount = "88.88", // 订单总金额,必填,单位为元
|
|
22
|
+
Subject = "Iphone6 16G", // 订单标题,必填
|
|
23
|
+
ProductCode = "FAST_INSTANT_TRADE_PAY", // 网站支付固定值,必填
|
|
24
|
+
};
|
|
25
|
+
request.SetBizModel(model);
|
|
26
|
+
|
|
27
|
+
// ⚠️ 网站支付必须使用 pageExecute(),不能使用 Execute()
|
|
28
|
+
// ⚠️ 第二个参数传 null(accessToken),第三个参数 "POST" 表示生成 POST 表单
|
|
29
|
+
AlipayTradePagePayResponse response = alipayClient.pageExecute(request, null, "POST");
|
|
30
|
+
string formHtml = response.Body;
|
|
31
|
+
// pageExecute 返回的是 HTML 表单字符串,前端需要渲染并自动提交
|
|
32
|
+
|
|
33
|
+
private static AlipayConfig GetAlipayConfig()
|
|
34
|
+
{
|
|
35
|
+
string privateKey = "<-- 请填写您的应用私钥,例如:MIIE...(脱敏示例) -->";
|
|
36
|
+
string alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIB...(脱敏示例) -->";
|
|
37
|
+
AlipayConfig alipayConfig = new AlipayConfig();
|
|
38
|
+
alipayConfig.ServerUrl = "https://openapi.alipay.com/gateway.do";
|
|
39
|
+
// 沙箱环境: alipayConfig.ServerUrl = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
|
|
40
|
+
alipayConfig.AppId = "<-- 请填写您的AppId,例如:20190xxxxxx -->";
|
|
41
|
+
alipayConfig.PrivateKey = privateKey;
|
|
42
|
+
alipayConfig.Format = "json";
|
|
43
|
+
alipayConfig.AlipayPublicKey = alipayPublicKey;
|
|
44
|
+
alipayConfig.Charset = "UTF-8";
|
|
45
|
+
alipayConfig.SignType = "RSA2";
|
|
46
|
+
return alipayConfig;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 响应示例
|
|
51
|
+
### 正常示例
|
|
52
|
+
```html
|
|
53
|
+
<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">
|
|
54
|
+
<input type="hidden" name="biz_content" value="{"out_trade_no":"MERCHANT_ORDER_NO_PLACEHOLDER","total_amount":"88.88","subject":"Iphone6 16G","product_code":"FAST_INSTANT_TRADE_PAY"}">
|
|
55
|
+
<input type="submit" value="立即支付" style="display:none" >
|
|
56
|
+
</form>
|
|
57
|
+
<script>document.forms[0].submit();</script>
|
|
58
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# APP支付接口 - C# 示例
|
|
2
|
+
|
|
3
|
+
## 请求示例
|
|
4
|
+
|
|
5
|
+
```csharp
|
|
6
|
+
using System;
|
|
7
|
+
using System.Collections.Generic;
|
|
8
|
+
using Aop.Api;
|
|
9
|
+
using Aop.Api.Request;
|
|
10
|
+
using Aop.Api.Response;
|
|
11
|
+
using Aop.Api.Domain;
|
|
12
|
+
using Aop.Api.Util;
|
|
13
|
+
namespace SdkDemoTest
|
|
14
|
+
{
|
|
15
|
+
public class AlipayTradeAppPay
|
|
16
|
+
{
|
|
17
|
+
public static void Main(string[] args)
|
|
18
|
+
{
|
|
19
|
+
// 初始化SDK
|
|
20
|
+
IAopClient alipayClient = new DefaultAopClient(GetAlipayConfig());
|
|
21
|
+
// 构造请求参数以调用接口
|
|
22
|
+
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
|
|
23
|
+
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
|
|
24
|
+
|
|
25
|
+
// 设置商户订单号
|
|
26
|
+
model.OutTradeNo = "MERCHANT_ORDER_NO_PLACEHOLDER";
|
|
27
|
+
|
|
28
|
+
// 设置订单总金额
|
|
29
|
+
model.TotalAmount = "9.00";
|
|
30
|
+
|
|
31
|
+
// 设置订单标题
|
|
32
|
+
model.Subject = "大乐透";
|
|
33
|
+
|
|
34
|
+
// 设置产品码
|
|
35
|
+
model.ProductCode = "QUICK_MSECURITY_PAY";
|
|
36
|
+
|
|
37
|
+
// 设置订单包含的商品列表信息
|
|
38
|
+
List<GoodsDetail> goodsDetail = new List<GoodsDetail>();
|
|
39
|
+
GoodsDetail goodsDetail0 = new GoodsDetail();
|
|
40
|
+
goodsDetail0.GoodsName = "ipad";
|
|
41
|
+
goodsDetail0.AlipayGoodsId = "20010001";
|
|
42
|
+
goodsDetail0.Quantity = 1;
|
|
43
|
+
goodsDetail0.Price = "2000";
|
|
44
|
+
goodsDetail0.GoodsId = "apple-01";
|
|
45
|
+
goodsDetail0.GoodsCategory = "34543238";
|
|
46
|
+
goodsDetail0.CategoriesTree = "124868003|126232002|126252004";
|
|
47
|
+
goodsDetail0.ShowUrl = "http://www.alipay.com/xxx.jpg";
|
|
48
|
+
goodsDetail.Add(goodsDetail0);
|
|
49
|
+
model.GoodsDetail = goodsDetail;
|
|
50
|
+
|
|
51
|
+
// 设置订单绝对超时时间
|
|
52
|
+
model.TimeExpire = "2016-12-31 10:05:00";
|
|
53
|
+
|
|
54
|
+
// 设置业务扩展参数
|
|
55
|
+
ExtendParams extendParams = new ExtendParams();
|
|
56
|
+
extendParams.SysServiceProviderId = "<SYS_SERVICE_PROVIDER_ID>";
|
|
57
|
+
extendParams.HbFqSellerPercent = "100";
|
|
58
|
+
extendParams.HbFqNum = "3";
|
|
59
|
+
extendParams.IndustryRefluxInfo = "{\"scene_code\":\"metro_tradeorder\",\"channel\":\"xxxx\",\"scene_data\":{\"asset_name\":\"ALIPAY\"}}";
|
|
60
|
+
extendParams.RoyaltyFreeze = "true";
|
|
61
|
+
extendParams.CardType = "S0JP0000";
|
|
62
|
+
model.ExtendParams = extendParams;
|
|
63
|
+
|
|
64
|
+
// 设置公用回传参数
|
|
65
|
+
model.PassbackParams = "merchantBizType%3d3C%26merchantBizNo%3dMERCHANT_BIZ_NO_PLACEHOLDER";
|
|
66
|
+
|
|
67
|
+
// 设置商户的原始订单号
|
|
68
|
+
model.MerchantOrderNo = "MERCHANT_ORDER_NO_PLACEHOLDER";
|
|
69
|
+
|
|
70
|
+
// 设置外部指定买家
|
|
71
|
+
ExtUserInfo extUserInfo = new ExtUserInfo();
|
|
72
|
+
extUserInfo.CertType = "IDENTITY_CARD";
|
|
73
|
+
extUserInfo.CertNo = "<CERT_NO>";
|
|
74
|
+
extUserInfo.Mobile = "<MOBILE>";
|
|
75
|
+
extUserInfo.Name = "李明";
|
|
76
|
+
extUserInfo.MinAge = "18";
|
|
77
|
+
extUserInfo.NeedCheckInfo = "F";
|
|
78
|
+
extUserInfo.IdentityHash = "<IDENTITY_HASH>";
|
|
79
|
+
model.ExtUserInfo = extUserInfo;
|
|
80
|
+
|
|
81
|
+
// 设置通知参数选项
|
|
82
|
+
List<String> queryOptions = new List<String>();
|
|
83
|
+
queryOptions.Add("hyb_amount");
|
|
84
|
+
queryOptions.Add("enterprise_pay_info");
|
|
85
|
+
model.QueryOptions = queryOptions;
|
|
86
|
+
|
|
87
|
+
request.SetBizModel(model);
|
|
88
|
+
// 第三方代调用模式下请设置app_auth_token
|
|
89
|
+
// request.PutOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
|
|
90
|
+
|
|
91
|
+
AlipayTradeAppPayResponse response = alipayClient.SdkExecute(request);
|
|
92
|
+
string orderStr = response.Body;
|
|
93
|
+
Console.WriteLine(orderStr);
|
|
94
|
+
|
|
95
|
+
if(!response.IsError)
|
|
96
|
+
{
|
|
97
|
+
Console.WriteLine("调用成功");
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
{
|
|
101
|
+
Console.WriteLine("调用失败");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private static AlipayConfig GetAlipayConfig()
|
|
106
|
+
{
|
|
107
|
+
string privateKey = "<-- 请填写您的应用私钥,例如:MIIE...(脱敏示例) -->";
|
|
108
|
+
string alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIB...(脱敏示例) -->";
|
|
109
|
+
AlipayConfig alipayConfig = new AlipayConfig();
|
|
110
|
+
alipayConfig.ServerUrl = "https://openapi.alipay.com/gateway.do";
|
|
111
|
+
alipayConfig.AppId = "<-- 请填写您的AppId,例如:20190xxxxxx -->";
|
|
112
|
+
alipayConfig.PrivateKey = privateKey;
|
|
113
|
+
alipayConfig.Format = "json";
|
|
114
|
+
alipayConfig.AlipayPublicKey = alipayPublicKey;
|
|
115
|
+
alipayConfig.Charset = "UTF-8";
|
|
116
|
+
alipayConfig.SignType = "RSA2";
|
|
117
|
+
return alipayConfig;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 响应示例
|
|
124
|
+
### 正常示例
|
|
125
|
+
```
|
|
126
|
+
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
|
|
127
|
+
```
|
|
128
|
+
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A2M 智能收产品接入示例 - C# 版本
|
|
3
|
+
*
|
|
4
|
+
* 本文件演示完整的智能收产品接入流程:
|
|
5
|
+
* 1. 返回 402 Payment-Needed Header
|
|
6
|
+
* 2. 验证 Payment-Proof 支付凭证
|
|
7
|
+
* 3. 发送履约回执确认
|
|
8
|
+
* 4. 返回资源内容
|
|
9
|
+
*
|
|
10
|
+
* 依赖安装:
|
|
11
|
+
* dotnet add package AlipaySDKNet.Standard
|
|
12
|
+
* dotnet add package Microsoft.AspNetCore.App
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
using System;
|
|
16
|
+
using System.Collections.Generic;
|
|
17
|
+
using System.Linq;
|
|
18
|
+
using System.Security.Cryptography;
|
|
19
|
+
using System.Text;
|
|
20
|
+
using System.Text.Json;
|
|
21
|
+
using System.Threading.Tasks;
|
|
22
|
+
using Microsoft.AspNetCore.Builder;
|
|
23
|
+
using Microsoft.AspNetCore.Hosting;
|
|
24
|
+
using Microsoft.AspNetCore.Http;
|
|
25
|
+
using Microsoft.Extensions.DependencyInjection;
|
|
26
|
+
using Aop.Api;
|
|
27
|
+
using Aop.Api.Request;
|
|
28
|
+
using Aop.Api.Response;
|
|
29
|
+
using Aop.Api.Domain;
|
|
30
|
+
|
|
31
|
+
namespace A2MPaymentDemo
|
|
32
|
+
{
|
|
33
|
+
class Program
|
|
34
|
+
{
|
|
35
|
+
// ==================== 配置信息(实际使用时请从配置中心读取)====================
|
|
36
|
+
private static readonly AlipayConfig AlipayConfig = new AlipayConfig
|
|
37
|
+
{
|
|
38
|
+
ServerUrl = "https://openapi.alipay.com/gateway.do",
|
|
39
|
+
AppId = "<APP_ID>",
|
|
40
|
+
PrivateKey = "<APP_PRIVATE_KEY>", // 请填写您的应用私钥(PKCS#1 格式)
|
|
41
|
+
AlipayPublicKey = "<ALIPAY_PUBLIC_KEY>", // 请填写您的支付宝公钥
|
|
42
|
+
Format = "json",
|
|
43
|
+
Charset = "UTF-8",
|
|
44
|
+
SignType = "RSA2"
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
private static readonly string SellerId = "<SELLER_ID_2088>"; // 商户 ID(2088 格式)
|
|
48
|
+
private static readonly string ServiceId = "<SERVICE_ID>"; // 商户服务 ID
|
|
49
|
+
private static readonly string MerchantPrivateKey = "<APP_PRIVATE_KEY>"; // 请填写您的应用私钥(用于商家签名)
|
|
50
|
+
|
|
51
|
+
private const string ResourcePath = "/demo/a2m/resource";
|
|
52
|
+
private const string GoodsName = "AI 生成内容服务";
|
|
53
|
+
|
|
54
|
+
// 初始化支付宝客户端
|
|
55
|
+
private static readonly IAopClient AlipayClient = new DefaultAopClient(AlipayConfig);
|
|
56
|
+
|
|
57
|
+
static void Main(string[] args)
|
|
58
|
+
{
|
|
59
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
60
|
+
var app = builder.Build();
|
|
61
|
+
|
|
62
|
+
// 路由处理
|
|
63
|
+
app.MapGet(ResourcePath, async (HttpRequest request, HttpResponse response) =>
|
|
64
|
+
{
|
|
65
|
+
// 获取 Payment-Proof Header
|
|
66
|
+
var paymentProof = request.Headers["Payment-Proof"].FirstOrDefault();
|
|
67
|
+
|
|
68
|
+
// 场景 1:用户未支付,返回 402 + Payment-Needed Header
|
|
69
|
+
if (string.IsNullOrWhiteSpace(paymentProof))
|
|
70
|
+
{
|
|
71
|
+
await CreatePaymentRequiredResponse(response);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 场景 2:用户已支付,验证 Payment-Proof 并返回资源
|
|
76
|
+
await VerifyPaymentAndDeliverResource(response, paymentProof);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
Console.WriteLine($"A2M 智能收服务已启动:http://localhost:5000{ResourcePath}");
|
|
80
|
+
Console.WriteLine("测试步骤:");
|
|
81
|
+
Console.WriteLine($"1. 无 Payment-Proof Header: curl http://localhost:5000{ResourcePath}");
|
|
82
|
+
Console.WriteLine($"2. 有 Payment-Proof Header: curl -H \"Payment-Proof: <value>\" http://localhost:5000{ResourcePath}");
|
|
83
|
+
|
|
84
|
+
app.Run();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ==================== 工具方法 ====================
|
|
88
|
+
|
|
89
|
+
/// <summary>
|
|
90
|
+
/// 格式化支付宝时间戳:yyyy-MM-dd HH:mm:ss
|
|
91
|
+
/// </summary>
|
|
92
|
+
private static string FormatAlipayTimestamp(DateTime? dateTime = null)
|
|
93
|
+
{
|
|
94
|
+
return (dateTime ?? DateTime.Now).ToString("yyyy-MM-dd HH:mm:ss");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// <summary>
|
|
98
|
+
/// Base64URL 编码
|
|
99
|
+
/// </summary>
|
|
100
|
+
private static string Base64UrlEncode(string data)
|
|
101
|
+
{
|
|
102
|
+
var bytes = Encoding.UTF8.GetBytes(data);
|
|
103
|
+
var base64 = Convert.ToBase64String(bytes);
|
|
104
|
+
return base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// <summary>
|
|
108
|
+
/// Base64URL 解码
|
|
109
|
+
/// </summary>
|
|
110
|
+
private static string Base64UrlDecode(string data)
|
|
111
|
+
{
|
|
112
|
+
// 补充 padding
|
|
113
|
+
var padding = data.Length % 4;
|
|
114
|
+
if (padding != 0)
|
|
115
|
+
{
|
|
116
|
+
data += new string('=', 4 - padding);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var base64 = data.Replace('-', '+').Replace('_', '/');
|
|
120
|
+
var bytes = Convert.FromBase64String(base64);
|
|
121
|
+
return Encoding.UTF8.GetString(bytes);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// <summary>
|
|
125
|
+
/// 生成商家签名(seller_signature)
|
|
126
|
+
/// </summary>
|
|
127
|
+
private static string GenerateSellerSignature(Dictionary<string, string> parameters, string privateKey)
|
|
128
|
+
{
|
|
129
|
+
// 1. 按 key 字典序排序
|
|
130
|
+
var sortedKeys = parameters.Keys.OrderBy(k => k).ToList();
|
|
131
|
+
|
|
132
|
+
// 2. 拼接签名内容
|
|
133
|
+
var signContent = new StringBuilder();
|
|
134
|
+
var first = true;
|
|
135
|
+
foreach (var key in sortedKeys)
|
|
136
|
+
{
|
|
137
|
+
var value = parameters[key];
|
|
138
|
+
if (!string.IsNullOrEmpty(value))
|
|
139
|
+
{
|
|
140
|
+
if (!first)
|
|
141
|
+
{
|
|
142
|
+
signContent.Append("&");
|
|
143
|
+
}
|
|
144
|
+
signContent.Append($"{key}={value}");
|
|
145
|
+
first = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 3. RSA2 签名
|
|
150
|
+
using (var rsa = new RSACryptoServiceProvider())
|
|
151
|
+
{
|
|
152
|
+
rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
|
|
153
|
+
var signature = rsa.SignData(Encoding.UTF8.GetBytes(signContent.ToString()), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
|
154
|
+
return Convert.ToBase64String(signature);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ==================== 智能收产品接入示例接口 ====================
|
|
159
|
+
|
|
160
|
+
/// <summary>
|
|
161
|
+
/// 创建 402 支付请求响应
|
|
162
|
+
/// </summary>
|
|
163
|
+
private static async Task CreatePaymentRequiredResponse(HttpResponse response)
|
|
164
|
+
{
|
|
165
|
+
try
|
|
166
|
+
{
|
|
167
|
+
// 1. 构造订单信息
|
|
168
|
+
var outTradeNo = $"ORDER_{DateTimeOffset.Now.ToUnixTimeMilliseconds()}";
|
|
169
|
+
var amount = "0.01";
|
|
170
|
+
var currency = "CNY";
|
|
171
|
+
var resourceId = ResourcePath;
|
|
172
|
+
var goodsName = GoodsName;
|
|
173
|
+
|
|
174
|
+
// 2. 计算支付截止时间(30 分钟后)
|
|
175
|
+
var payBefore = DateTimeOffset.Now.AddMinutes(30).ToString("o");
|
|
176
|
+
|
|
177
|
+
// 3. 生成商家签名
|
|
178
|
+
var signParams = new Dictionary<string, string>
|
|
179
|
+
{
|
|
180
|
+
["amount"] = amount,
|
|
181
|
+
["currency"] = currency,
|
|
182
|
+
["goods_name"] = goodsName,
|
|
183
|
+
["out_trade_no"] = outTradeNo,
|
|
184
|
+
["pay_before"] = payBefore,
|
|
185
|
+
["resource_id"] = resourceId,
|
|
186
|
+
["seller_id"] = SellerId,
|
|
187
|
+
["service_id"] = ServiceId
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
var sellerSignature = GenerateSellerSignature(signParams, MerchantPrivateKey);
|
|
191
|
+
|
|
192
|
+
// 4. 构造 Payment-Needed Header 内容
|
|
193
|
+
var paymentNeeded = new
|
|
194
|
+
{
|
|
195
|
+
protocol = new
|
|
196
|
+
{
|
|
197
|
+
out_trade_no = outTradeNo,
|
|
198
|
+
amount = amount,
|
|
199
|
+
currency = currency,
|
|
200
|
+
resource_id = resourceId,
|
|
201
|
+
pay_before = payBefore,
|
|
202
|
+
seller_signature = sellerSignature,
|
|
203
|
+
seller_sign_type = "RSA2",
|
|
204
|
+
seller_unique_id = SellerId
|
|
205
|
+
},
|
|
206
|
+
method = new
|
|
207
|
+
{
|
|
208
|
+
seller_name = "测试商户",
|
|
209
|
+
seller_id = SellerId,
|
|
210
|
+
seller_app_id = AlipayConfig.AppId,
|
|
211
|
+
goods_name = goodsName,
|
|
212
|
+
seller_unique_id_key = "seller_id",
|
|
213
|
+
service_id = ServiceId
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// 5. Base64URL 编码
|
|
218
|
+
var paymentNeededJson = JsonSerializer.Serialize(paymentNeeded, new JsonSerializerOptions
|
|
219
|
+
{
|
|
220
|
+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
221
|
+
});
|
|
222
|
+
var paymentNeededEncoded = Base64UrlEncode(paymentNeededJson);
|
|
223
|
+
|
|
224
|
+
// 6. 构造 402 响应
|
|
225
|
+
var responseBody = new
|
|
226
|
+
{
|
|
227
|
+
code = "Payment-Needed",
|
|
228
|
+
message = "需要支付",
|
|
229
|
+
out_trade_no = outTradeNo,
|
|
230
|
+
amount = amount,
|
|
231
|
+
currency = currency,
|
|
232
|
+
goods_name = goodsName
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
Console.WriteLine($"创建支付订单成功:outTradeNo={outTradeNo}, amount={amount}");
|
|
236
|
+
|
|
237
|
+
response.StatusCode = 402;
|
|
238
|
+
response.Headers["Payment-Needed"] = paymentNeededEncoded;
|
|
239
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
240
|
+
await response.WriteAsJsonAsync(responseBody);
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
catch (Exception ex)
|
|
244
|
+
{
|
|
245
|
+
Console.WriteLine($"创建订单失败:{ex.Message}");
|
|
246
|
+
response.StatusCode = 500;
|
|
247
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
248
|
+
await response.WriteAsJsonAsync(new
|
|
249
|
+
{
|
|
250
|
+
code = "CREATE_ORDER_ERROR",
|
|
251
|
+
message = $"创建订单失败:{ex.Message}"
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// <summary>
|
|
257
|
+
/// 验证支付凭证并交付资源
|
|
258
|
+
/// </summary>
|
|
259
|
+
private static async Task VerifyPaymentAndDeliverResource(HttpResponse response, string paymentProof)
|
|
260
|
+
{
|
|
261
|
+
try
|
|
262
|
+
{
|
|
263
|
+
// 1. 从 Payment-Proof 中解析订单信息
|
|
264
|
+
string paymentProofValue = null;
|
|
265
|
+
string tradeNo = null;
|
|
266
|
+
string clientSession = null;
|
|
267
|
+
|
|
268
|
+
try
|
|
269
|
+
{
|
|
270
|
+
var decodedProof = Base64UrlDecode(paymentProof);
|
|
271
|
+
using (var proofJson = JsonDocument.Parse(decodedProof))
|
|
272
|
+
{
|
|
273
|
+
var root = proofJson.RootElement;
|
|
274
|
+
|
|
275
|
+
if (root.TryGetProperty("protocol", out var protocol))
|
|
276
|
+
{
|
|
277
|
+
paymentProofValue = protocol.GetProperty("payment_proof").GetString();
|
|
278
|
+
tradeNo = protocol.GetProperty("trade_no").GetString();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (root.TryGetProperty("method", out var method))
|
|
282
|
+
{
|
|
283
|
+
clientSession = method.GetProperty("client_session").GetString();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 校验必要字段
|
|
288
|
+
if (string.IsNullOrWhiteSpace(paymentProofValue))
|
|
289
|
+
{
|
|
290
|
+
response.StatusCode = 400;
|
|
291
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
292
|
+
await response.WriteAsJsonAsync(new
|
|
293
|
+
{
|
|
294
|
+
code = "INVALID_PAYMENT_PROOF_FORMAT",
|
|
295
|
+
message = "Payment-Proof 格式错误:缺少 payment_proof"
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (string.IsNullOrWhiteSpace(tradeNo))
|
|
301
|
+
{
|
|
302
|
+
response.StatusCode = 400;
|
|
303
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
304
|
+
await response.WriteAsJsonAsync(new
|
|
305
|
+
{
|
|
306
|
+
code = "INVALID_PAYMENT_PROOF_FORMAT",
|
|
307
|
+
message = "Payment-Proof 格式错误:缺少 trade_no"
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch (Exception ex)
|
|
313
|
+
{
|
|
314
|
+
Console.WriteLine($"Payment-Proof 解析失败:{ex.Message}");
|
|
315
|
+
response.StatusCode = 400;
|
|
316
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
317
|
+
await response.WriteAsJsonAsync(new
|
|
318
|
+
{
|
|
319
|
+
code = "INVALID_PAYMENT_PROOF_FORMAT",
|
|
320
|
+
message = $"Payment-Proof 格式错误:{ex.Message}"
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 2. 调用支付宝 API 验证支付凭证
|
|
326
|
+
var request = new AlipayAipayAgentPaymentVerifyRequest();
|
|
327
|
+
request.BizContent = JsonSerializer.Serialize(new
|
|
328
|
+
{
|
|
329
|
+
payment_proof = paymentProofValue,
|
|
330
|
+
trade_no = tradeNo,
|
|
331
|
+
client_session = clientSession
|
|
332
|
+
}, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
|
333
|
+
|
|
334
|
+
var verifyResponse = await AlipayClient.ExecuteAsync(request);
|
|
335
|
+
|
|
336
|
+
// 3. 验证失败,返回错误
|
|
337
|
+
if (verifyResponse.Code != "10000")
|
|
338
|
+
{
|
|
339
|
+
Console.WriteLine($"支付凭证验证失败:{verifyResponse.SubMsg}");
|
|
340
|
+
response.StatusCode = 400;
|
|
341
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
342
|
+
await response.WriteAsJsonAsync(new
|
|
343
|
+
{
|
|
344
|
+
code = verifyResponse.SubCode ?? "VERIFY_FAILED",
|
|
345
|
+
message = verifyResponse.SubMsg ?? "支付凭证验证失败"
|
|
346
|
+
});
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 4. 验证成功,获取订单信息
|
|
351
|
+
var verifyTradeNo = verifyResponse.TradeNo;
|
|
352
|
+
var verifyOutTradeNo = verifyResponse.OutTradeNo;
|
|
353
|
+
var resourceId = verifyResponse.ResourceId;
|
|
354
|
+
var active = verifyResponse.Active;
|
|
355
|
+
|
|
356
|
+
Console.WriteLine($"支付凭证验证成功:tradeNo={verifyTradeNo}, outTradeNo={verifyOutTradeNo}");
|
|
357
|
+
|
|
358
|
+
// 5. 校验凭证有效性(active=true 表示凭证有效)
|
|
359
|
+
if (active != true)
|
|
360
|
+
{
|
|
361
|
+
Console.WriteLine($"支付凭证无效或已过期:outTradeNo={verifyOutTradeNo}");
|
|
362
|
+
response.StatusCode = 400;
|
|
363
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
364
|
+
await response.WriteAsJsonAsync(new
|
|
365
|
+
{
|
|
366
|
+
code = "INVALID_PAYMENT_PROOF",
|
|
367
|
+
message = "支付凭证无效或已过期"
|
|
368
|
+
});
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 6. 【TODO】查询订单是否存在(以数据库为准)
|
|
373
|
+
// 7. 【TODO】资源防串校验
|
|
374
|
+
// 8. 【TODO】履约防重放校验
|
|
375
|
+
|
|
376
|
+
// 9. 生成资源内容
|
|
377
|
+
var serviceResult = GenerateServiceResource(resourceId);
|
|
378
|
+
|
|
379
|
+
// 10. 【TODO】履约记录落库
|
|
380
|
+
// 11. 【TODO】保存待确认履约状态
|
|
381
|
+
// 实际生产中建议先保存 serviceResult 和 PENDING_CONFIRM 状态。
|
|
382
|
+
// fulfillment.confirm 成功后再标记 FULFILLED;如果确认失败,
|
|
383
|
+
// 允许同一笔 Payment-Proof 重试确认,避免误返回成功。
|
|
384
|
+
|
|
385
|
+
Console.WriteLine($"资源已生成,准备发送履约确认:outTradeNo={verifyOutTradeNo}, tradeNo={verifyTradeNo}");
|
|
386
|
+
|
|
387
|
+
// 12. 发送履约确认到支付宝,确认成功后才返回成功交付
|
|
388
|
+
var fulfillmentConfirmed = await SendFulfillmentConfirm(verifyTradeNo);
|
|
389
|
+
if (!fulfillmentConfirmed)
|
|
390
|
+
{
|
|
391
|
+
response.StatusCode = 502;
|
|
392
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
393
|
+
await response.WriteAsJsonAsync(new
|
|
394
|
+
{
|
|
395
|
+
code = "FULFILLMENT_CONFIRM_FAILED",
|
|
396
|
+
message = "资源已生成但履约确认失败,请稍后使用同一 Payment-Proof 重试"
|
|
397
|
+
});
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// 12.1 【TODO】履约确认成功后,再标记订单已履约
|
|
402
|
+
// order.FulfillStatus = "FULFILLED";
|
|
403
|
+
// order.FulfilledAt = DateTime.Now;
|
|
404
|
+
|
|
405
|
+
Console.WriteLine($"履约确认成功:outTradeNo={verifyOutTradeNo}, tradeNo={verifyTradeNo}");
|
|
406
|
+
|
|
407
|
+
// 13. 构造 Payment-Validation Header
|
|
408
|
+
var paymentValidation = new
|
|
409
|
+
{
|
|
410
|
+
trade_no = verifyTradeNo,
|
|
411
|
+
out_trade_no = verifyOutTradeNo,
|
|
412
|
+
validated = true,
|
|
413
|
+
resource_id = resourceId
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
var paymentValidationJson = JsonSerializer.Serialize(paymentValidation, new JsonSerializerOptions
|
|
417
|
+
{
|
|
418
|
+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
419
|
+
});
|
|
420
|
+
var paymentValidationEncoded = Base64UrlEncode(paymentValidationJson);
|
|
421
|
+
|
|
422
|
+
// 14. 返回资源内容
|
|
423
|
+
var responseBody = new
|
|
424
|
+
{
|
|
425
|
+
resource_id = resourceId,
|
|
426
|
+
content = serviceResult,
|
|
427
|
+
trade_no = verifyTradeNo,
|
|
428
|
+
out_trade_no = verifyOutTradeNo,
|
|
429
|
+
already_fulfilled = false,
|
|
430
|
+
fulfillment_confirmed = true
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
response.Headers["Payment-Validation"] = paymentValidationEncoded;
|
|
434
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
435
|
+
await response.WriteAsJsonAsync(responseBody);
|
|
436
|
+
}
|
|
437
|
+
catch (Exception ex)
|
|
438
|
+
{
|
|
439
|
+
Console.WriteLine($"支付凭证验证异常:{ex.Message}");
|
|
440
|
+
response.StatusCode = 500;
|
|
441
|
+
response.ContentType = "application/json; charset=utf-8";
|
|
442
|
+
await response.WriteAsJsonAsync(new
|
|
443
|
+
{
|
|
444
|
+
code = "VERIFY_FAILED",
|
|
445
|
+
message = $"支付凭证验证失败:{ex.Message}"
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/// <summary>
|
|
451
|
+
/// 生成服务资源内容
|
|
452
|
+
/// </summary>
|
|
453
|
+
private static object GenerateServiceResource(string resourceId)
|
|
454
|
+
{
|
|
455
|
+
return new
|
|
456
|
+
{
|
|
457
|
+
status = "success",
|
|
458
|
+
service_type = "AI_CONTENT_GENERATION",
|
|
459
|
+
resource_id = resourceId,
|
|
460
|
+
content = "这是 AI 生成的内容示例,可根据实际业务替换为任意数字服务内容",
|
|
461
|
+
generated_at = DateTime.Now.ToString("o")
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/// <summary>
|
|
466
|
+
/// 发送履约确认
|
|
467
|
+
/// </summary>
|
|
468
|
+
private static async Task<bool> SendFulfillmentConfirm(string tradeNo)
|
|
469
|
+
{
|
|
470
|
+
try
|
|
471
|
+
{
|
|
472
|
+
Console.WriteLine($"开始发送履约确认:tradeNo={tradeNo}");
|
|
473
|
+
|
|
474
|
+
var request = new AlipayAipayAgentFulfillmentConfirmRequest();
|
|
475
|
+
request.BizContent = JsonSerializer.Serialize(new
|
|
476
|
+
{
|
|
477
|
+
trade_no = tradeNo
|
|
478
|
+
}, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
|
479
|
+
|
|
480
|
+
var response = await AlipayClient.ExecuteAsync(request);
|
|
481
|
+
|
|
482
|
+
if (response.Code == "10000")
|
|
483
|
+
{
|
|
484
|
+
Console.WriteLine($"履约确认成功:tradeNo={tradeNo}");
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
else
|
|
488
|
+
{
|
|
489
|
+
Console.WriteLine($"履约确认失败:tradeNo={tradeNo}, errorCode={response.SubCode}, errorMsg={response.SubMsg}");
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
catch (Exception ex)
|
|
494
|
+
{
|
|
495
|
+
Console.WriteLine($"履约确认异常:tradeNo={tradeNo}, error={ex.Message}");
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|