@alipay/alipay-aipay 1.7.0 → 1.7.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.
Files changed (169) hide show
  1. package/README.md +30 -0
  2. package/dist/commands/install-current.js +15 -7
  3. package/dist/commands/install.js +20 -16
  4. package/dist/commands/uninstall.js +8 -3
  5. package/dist/commands/update.js +34 -16
  6. package/dist/index.js +10 -4
  7. package/dist/utils/channels.js +67 -0
  8. package/dist/utils/config.js +15 -11
  9. package/dist/utils/file.js +23 -10
  10. package/dist/utils/npm.js +19 -33
  11. package/package.json +8 -3
  12. package/skill/SKILL.md +2 -0
  13. package/skill/references/normal/json/release-channel.json +1 -0
  14. package/skill/references/normal/scripts/self_update.mjs +62 -17
  15. package/skill/references/normal/self-update.md +3 -1
  16. package/skill-pre/SKILL.md +65 -0
  17. package/skill-pre/VERSION +1 -0
  18. package/skill-pre/references/integration/flow.md +602 -0
  19. package/skill-pre/references/integration/modules/alipay-sdk-reminder.md +281 -0
  20. package/skill-pre/references/integration/modules/checklist.md +154 -0
  21. package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +199 -0
  22. package/skill-pre/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
  23. package/skill-pre/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 +83 -0
  24. package/skill-pre/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 +80 -0
  25. package/skill-pre/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
  26. package/skill-pre/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 +62 -0
  27. package/skill-pre/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 +132 -0
  28. package/skill-pre/references/integration/modules/code-examples/csharp/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +640 -0
  29. package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +184 -0
  30. package/skill-pre/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
  31. package/skill-pre/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 +96 -0
  32. package/skill-pre/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 +81 -0
  33. package/skill-pre/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
  34. package/skill-pre/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 +69 -0
  35. package/skill-pre/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 +138 -0
  36. package/skill-pre/references/integration/modules/code-examples/java/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemoController.md +623 -0
  37. package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +135 -0
  38. package/skill-pre/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
  39. package/skill-pre/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 +63 -0
  40. package/skill-pre/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 +59 -0
  41. package/skill-pre/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
  42. package/skill-pre/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 +84 -0
  43. package/skill-pre/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 +71 -0
  44. package/skill-pre/references/integration/modules/code-examples/nodejs/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +498 -0
  45. package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +157 -0
  46. package/skill-pre/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
  47. package/skill-pre/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 +73 -0
  48. package/skill-pre/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 +69 -0
  49. package/skill-pre/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
  50. package/skill-pre/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 +59 -0
  51. package/skill-pre/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 +110 -0
  52. package/skill-pre/references/integration/modules/code-examples/php/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +806 -0
  53. package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +128 -0
  54. package/skill-pre/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
  55. package/skill-pre/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 +70 -0
  56. package/skill-pre/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 +65 -0
  57. package/skill-pre/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
  58. package/skill-pre/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 +51 -0
  59. package/skill-pre/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 +47 -0
  60. package/skill-pre/references/integration/modules/code-examples/python/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +541 -0
  61. package/skill-pre/references/integration/modules/interface-contract/aipay-interface-contract.md +52 -0
  62. package/skill-pre/references/integration/modules/interface-contract/apppay-interface-contract.md +45 -0
  63. package/skill-pre/references/integration/modules/interface-contract/common-trade-contract.md +42 -0
  64. package/skill-pre/references/integration/modules/interface-contract/notify-url-contract.md +68 -0
  65. package/skill-pre/references/integration/modules/interface-contract/webpay-interface-contract.md +44 -0
  66. package/skill-pre/references/integration/modules/interface-guide.md +61 -0
  67. package/skill-pre/references/integration/modules/product-decision.md +73 -0
  68. package/skill-pre/references/integration/modules/sandbox/a2m-sandbox-test.md +235 -0
  69. package/skill-pre/references/integration/modules/sandbox/alipay-sandbox-tool.md +142 -0
  70. package/skill-pre/references/integration/modules/sandbox/sandbox-setup-guide.md +239 -0
  71. package/skill-pre/references/integration/modules/scripts/local_402_sandbox_pay.py +900 -0
  72. package/skill-pre/references/integration/modules/scripts/sandbox_config.sh +347 -0
  73. package/skill-pre/references/integration/modules/troubleshooting-flow.md +28 -0
  74. package/skill-pre/references/normal/alipay-cli-env.md +169 -0
  75. package/skill-pre/references/normal/conversation-telemetry.md +101 -0
  76. package/skill-pre/references/normal/full-process-routing.md +92 -0
  77. package/skill-pre/references/normal/integration-worker.md +90 -0
  78. package/skill-pre/references/normal/json/customer-messages.json +1 -0
  79. package/skill-pre/references/normal/json/managed-jq-assets.json +1 -0
  80. package/skill-pre/references/normal/json/policy-rules.json +1 -0
  81. package/skill-pre/references/normal/json/release-channel.json +1 -0
  82. package/skill-pre/references/normal/rejection-guide.md +41 -0
  83. package/skill-pre/references/normal/scripts/agent_platform.mjs +43 -0
  84. package/skill-pre/references/normal/scripts/alipay_cli_env.mjs +34 -0
  85. package/skill-pre/references/normal/scripts/alipay_cli_env.sh +55 -0
  86. package/skill-pre/references/normal/scripts/alipay_cli_refresh.mjs +129 -0
  87. package/skill-pre/references/normal/scripts/auth_runtime_runner.mjs +60 -0
  88. package/skill-pre/references/normal/scripts/checklist_sections.mjs +43 -0
  89. package/skill-pre/references/normal/scripts/common.sh +365 -0
  90. package/skill-pre/references/normal/scripts/conversation_telemetry.mjs +105 -0
  91. package/skill-pre/references/normal/scripts/detect_dev_tool.sh +79 -0
  92. package/skill-pre/references/normal/scripts/flow_loader.mjs +237 -0
  93. package/skill-pre/references/normal/scripts/integration_context_runner.mjs +151 -0
  94. package/skill-pre/references/normal/scripts/integration_message_runner.mjs +1456 -0
  95. package/skill-pre/references/normal/scripts/interaction_reply.mjs +140 -0
  96. package/skill-pre/references/normal/scripts/managed_jq.mjs +215 -0
  97. package/skill-pre/references/normal/scripts/onboarding_discovery_runner.mjs +387 -0
  98. package/skill-pre/references/normal/scripts/onboarding_message_runner.mjs +1425 -0
  99. package/skill-pre/references/normal/scripts/onboarding_recovery_runner.mjs +100 -0
  100. package/skill-pre/references/normal/scripts/open_official_url.sh +205 -0
  101. package/skill-pre/references/normal/scripts/platform_compat.mjs +42 -0
  102. package/skill-pre/references/normal/scripts/preview_runtime.mjs +151 -0
  103. package/skill-pre/references/normal/scripts/project_route_inspector.sh +132 -0
  104. package/skill-pre/references/normal/scripts/python_script_runner.mjs +58 -0
  105. package/skill-pre/references/normal/scripts/render_customer_message.mjs +1792 -0
  106. package/skill-pre/references/normal/scripts/runtime.mjs +271 -0
  107. package/skill-pre/references/normal/scripts/self_update.mjs +376 -0
  108. package/skill-pre/references/normal/scripts/telemetry.mjs +2102 -0
  109. package/skill-pre/references/normal/self-update.md +7 -0
  110. package/skill-pre/references/normal/telemetry.md +70 -0
  111. package/skill-pre/references/onboarding/assets/keygen.html +927 -0
  112. package/skill-pre/references/onboarding/flow.md +607 -0
  113. package/skill-pre/references/onboarding/modules/app-release.md +90 -0
  114. package/skill-pre/references/onboarding/modules/authorization.md +43 -0
  115. package/skill-pre/references/onboarding/modules/error-handling.md +62 -0
  116. package/skill-pre/references/onboarding/modules/product-sign.md +49 -0
  117. package/skill-pre/references/onboarding/modules/scripts/app.sh +1485 -0
  118. package/skill-pre/references/onboarding/modules/scripts/ar_sign_apply.sh +331 -0
  119. package/skill-pre/references/onboarding/modules/scripts/auth.sh +1040 -0
  120. package/skill-pre/references/onboarding/modules/scripts/download_key_tool.sh +4 -0
  121. package/skill-pre/references/onboarding/modules/scripts/error_handler.sh +841 -0
  122. package/skill-pre/references/onboarding/modules/scripts/mcc.sh +1085 -0
  123. package/skill-pre/references/onboarding/modules/scripts/network_retry.sh +135 -0
  124. package/skill-pre/references/onboarding/modules/scripts/query_sign_status.sh +105 -0
  125. package/skill-pre/references/onboarding/modules/scripts/service.sh +528 -0
  126. package/skill-pre/references/onboarding/modules/scripts/sign_status_parser.sh +58 -0
  127. package/skill-pre/references/onboarding/modules/scripts/upload_screenshots.sh +208 -0
  128. package/skill-pre/references/onboarding/modules/scripts/upload_special_license.sh +195 -0
  129. package/skill-pre/references/onboarding/modules/service-registration.md +40 -0
  130. package/skill-pre/references/runtime-entry.md +99 -0
  131. package/dist/commands/install-current.d.ts +0 -4
  132. package/dist/commands/install-current.d.ts.map +0 -1
  133. package/dist/commands/install-current.js.map +0 -1
  134. package/dist/commands/install.d.ts +0 -10
  135. package/dist/commands/install.d.ts.map +0 -1
  136. package/dist/commands/install.js.map +0 -1
  137. package/dist/commands/prepare-tools.d.ts +0 -5
  138. package/dist/commands/prepare-tools.d.ts.map +0 -1
  139. package/dist/commands/prepare-tools.js.map +0 -1
  140. package/dist/commands/uninstall.d.ts +0 -10
  141. package/dist/commands/uninstall.d.ts.map +0 -1
  142. package/dist/commands/uninstall.js.map +0 -1
  143. package/dist/commands/update.d.ts +0 -10
  144. package/dist/commands/update.d.ts.map +0 -1
  145. package/dist/commands/update.js.map +0 -1
  146. package/dist/index.d.ts +0 -2
  147. package/dist/index.d.ts.map +0 -1
  148. package/dist/index.js.map +0 -1
  149. package/dist/types.d.ts +0 -42
  150. package/dist/types.d.ts.map +0 -1
  151. package/dist/types.js.map +0 -1
  152. package/dist/utils/alipay-cli.d.ts +0 -20
  153. package/dist/utils/alipay-cli.d.ts.map +0 -1
  154. package/dist/utils/alipay-cli.js.map +0 -1
  155. package/dist/utils/config.d.ts +0 -52
  156. package/dist/utils/config.d.ts.map +0 -1
  157. package/dist/utils/config.js.map +0 -1
  158. package/dist/utils/file.d.ts +0 -77
  159. package/dist/utils/file.d.ts.map +0 -1
  160. package/dist/utils/file.js.map +0 -1
  161. package/dist/utils/jq.d.ts +0 -17
  162. package/dist/utils/jq.d.ts.map +0 -1
  163. package/dist/utils/jq.js.map +0 -1
  164. package/dist/utils/npm.d.ts +0 -17
  165. package/dist/utils/npm.d.ts.map +0 -1
  166. package/dist/utils/npm.js.map +0 -1
  167. package/dist/utils/shell.d.ts +0 -24
  168. package/dist/utils/shell.d.ts.map +0 -1
  169. package/dist/utils/shell.js.map +0 -1
@@ -0,0 +1,640 @@
1
+ # AI 按量付费完整接入示例 - C#
2
+
3
+ 以下代码展示严格验付和幂等履约的完整控制流。生成项目代码时,请按当前项目结构拆分并绑定真实持久化实现。
4
+
5
+ ```csharp
6
+ /*
7
+ * A2M 智能收产品接入示例 - C# 版本
8
+ *
9
+ * 本示例展示 A2M 核心协议、严格验付和幂等履约流程。
10
+ * 必须通过 ConfigureOrderRepository 绑定项目真实持久化实现;端口未绑定时明确失败,
11
+ * 不使用内存订单、固定资源或验付字段缺失兜底。
12
+ *
13
+ * 本文件演示 A2M 核心协议调用流程:
14
+ * 1. 返回 402 Payment-Needed Header
15
+ * 2. 验证 Payment-Proof 支付凭证
16
+ * 3. 发送履约回执确认
17
+ * 4. 返回资源内容
18
+ *
19
+ * 依赖安装:
20
+ * dotnet add package AlipaySDKNet.Standard
21
+ */
22
+
23
+ using System;
24
+ using System.Collections.Generic;
25
+ using System.Globalization;
26
+ using System.Linq;
27
+ using System.Security.Cryptography;
28
+ using System.Text;
29
+ using System.Text.Json;
30
+ using System.Threading.Tasks;
31
+ using Microsoft.AspNetCore.Builder;
32
+ using Microsoft.AspNetCore.Hosting;
33
+ using Microsoft.AspNetCore.Http;
34
+ using Microsoft.Extensions.DependencyInjection;
35
+ using Aop.Api;
36
+ using Aop.Api.Request;
37
+ using Aop.Api.Response;
38
+ using Aop.Api.Domain;
39
+
40
+ namespace A2MPaymentDemo
41
+ {
42
+ public interface IOrderRepository
43
+ {
44
+ // OrderSnapshot 映射 PENDING_PAYMENT/PAID 与 UNFULFILLED/PENDING_CONFIRM/FULFILLED。
45
+ Task CreatePendingAsync(OrderSnapshot order);
46
+ Task<OrderSnapshot> FindByOutTradeNoAsync(string outTradeNo);
47
+ Task<FulfillmentPreparation> PrepareFulfillmentAsync(FulfillmentRequest request);
48
+ Task MarkFulfilledAsync(string outTradeNo, string tradeNo);
49
+ }
50
+
51
+ public sealed class OrderSnapshot
52
+ {
53
+ public string OutTradeNo { get; init; }
54
+ public string Amount { get; init; }
55
+ public string Currency { get; init; }
56
+ public string ResourceId { get; init; }
57
+ public string GoodsName { get; init; }
58
+ public string PayBefore { get; init; }
59
+ public string OrderStatus { get; init; }
60
+ public string FulfillStatus { get; init; }
61
+ }
62
+
63
+ public sealed class FulfillmentRequest
64
+ {
65
+ public string OutTradeNo { get; init; }
66
+ public string TradeNo { get; init; }
67
+ public string ExpectedAmount { get; init; }
68
+ public string ExpectedResourceId { get; init; }
69
+ public Func<string> CreateResource { get; init; }
70
+ }
71
+
72
+ public sealed class FulfillmentPreparation
73
+ {
74
+ public string State { get; init; }
75
+ public string ServiceResult { get; init; }
76
+ }
77
+
78
+ class Program
79
+ {
80
+ // ==================== 配置信息(实际使用时请从配置中心读取)====================
81
+ private static readonly AlipayConfig AlipayConfig = new AlipayConfig
82
+ {
83
+ // 默认用于本 Skill 的快速沙箱联调;生产部署时设置 ALIPAY_GATEWAY=https://openapi.alipay.com/gateway.do
84
+ ServerUrl = Environment.GetEnvironmentVariable("ALIPAY_GATEWAY")
85
+ ?? "https://openapi-sandbox.dl.alipaydev.com/gateway.do",
86
+ AppId = "<APP_ID>",
87
+ PrivateKey = "<APP_PRIVATE_KEY>", // 请填写您的应用私钥(PKCS#1 格式)
88
+ AlipayPublicKey = "<ALIPAY_PUBLIC_KEY>", // 请填写您的支付宝公钥
89
+ Format = "json",
90
+ Charset = "UTF-8",
91
+ SignType = "RSA2"
92
+ };
93
+
94
+ private static readonly string SellerId = "<SELLER_ID_2088>"; // 商户 ID(2088 格式)
95
+ private static readonly string ServiceId = Environment.GetEnvironmentVariable("ALIPAY_SERVICE_ID")
96
+ ?? "api_mock_service_id"; // 仅用于沙箱联调;上线前替换为服务市场真实 serviceId
97
+ private static readonly string MerchantPrivateKey = "<APP_PRIVATE_KEY>"; // 请填写您的应用私钥(用于商家签名)
98
+
99
+ private const string ResourcePath = "/demo/a2m/resource";
100
+ private const string GoodsName = "AI 生成内容服务";
101
+ private const string SandboxGateway = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
102
+ private static IOrderRepository OrderRepository;
103
+
104
+ // 初始化支付宝客户端
105
+ private static readonly IAopClient AlipayClient = new DefaultAopClient(AlipayConfig);
106
+
107
+ /// <summary>
108
+ /// 在项目组合根绑定真实数据库实现。实现必须使用事务和唯一约束。
109
+ /// </summary>
110
+ public static void ConfigureOrderRepository(IOrderRepository repository)
111
+ {
112
+ OrderRepository = repository ?? throw new ArgumentNullException(nameof(repository));
113
+ }
114
+
115
+ private static IOrderRepository RequireOrderRepository()
116
+ {
117
+ return OrderRepository
118
+ ?? throw new InvalidOperationException("尚未绑定项目真实 IOrderRepository,禁止回退到内存订单");
119
+ }
120
+
121
+ static void Main(string[] args)
122
+ {
123
+ var builder = WebApplication.CreateBuilder(args);
124
+ var app = builder.Build();
125
+
126
+ // 路由处理
127
+ app.MapGet(ResourcePath, async (HttpRequest request, HttpResponse response) =>
128
+ {
129
+ // 获取 Payment-Proof Header
130
+ var paymentProof = request.Headers["Payment-Proof"].FirstOrDefault();
131
+
132
+ // 场景 1:用户未支付,返回 402 + Payment-Needed Header
133
+ if (string.IsNullOrWhiteSpace(paymentProof))
134
+ {
135
+ await CreatePaymentRequiredResponse(response);
136
+ return;
137
+ }
138
+
139
+ // 场景 2:用户已支付,验证 Payment-Proof 并返回资源
140
+ await VerifyPaymentAndDeliverResource(response, paymentProof);
141
+ });
142
+
143
+ Console.WriteLine($"A2M 按量付费服务已启动:http://localhost:5000{ResourcePath}");
144
+ Console.WriteLine("测试步骤:");
145
+ Console.WriteLine($"1. 无 Payment-Proof Header: curl http://localhost:5000{ResourcePath}");
146
+ Console.WriteLine($"2. 有 Payment-Proof Header: curl -H \"Payment-Proof: <value>\" http://localhost:5000{ResourcePath}");
147
+
148
+ app.Run();
149
+ }
150
+
151
+ // ==================== 工具方法 ====================
152
+
153
+ /// <summary>
154
+ /// 格式化支付宝时间戳:yyyy-MM-dd HH:mm:ss
155
+ /// </summary>
156
+ private static string FormatAlipayTimestamp(DateTime? dateTime = null)
157
+ {
158
+ return (dateTime ?? DateTime.Now).ToString("yyyy-MM-dd HH:mm:ss");
159
+ }
160
+
161
+ /// <summary>
162
+ /// Base64URL 编码
163
+ /// </summary>
164
+ private static string Base64UrlEncode(string data)
165
+ {
166
+ var bytes = Encoding.UTF8.GetBytes(data);
167
+ var base64 = Convert.ToBase64String(bytes);
168
+ return base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
169
+ }
170
+
171
+ /// <summary>
172
+ /// Base64URL 解码
173
+ /// </summary>
174
+ private static string Base64UrlDecode(string data)
175
+ {
176
+ // 补充 padding
177
+ var padding = data.Length % 4;
178
+ if (padding != 0)
179
+ {
180
+ data += new string('=', 4 - padding);
181
+ }
182
+
183
+ var base64 = data.Replace('-', '+').Replace('_', '/');
184
+ var bytes = Convert.FromBase64String(base64);
185
+ return Encoding.UTF8.GetString(bytes);
186
+ }
187
+
188
+ /// <summary>
189
+ /// 生成商家签名(seller_signature)
190
+ /// </summary>
191
+ private static string GenerateSellerSignature(Dictionary<string, string> parameters, string privateKey)
192
+ {
193
+ // 1. 按 key 字典序排序
194
+ var sortedKeys = parameters.Keys.OrderBy(k => k).ToList();
195
+
196
+ // 2. 拼接签名内容
197
+ var signContent = new StringBuilder();
198
+ var first = true;
199
+ foreach (var key in sortedKeys)
200
+ {
201
+ var value = parameters[key];
202
+ if (!string.IsNullOrEmpty(value))
203
+ {
204
+ if (!first)
205
+ {
206
+ signContent.Append("&");
207
+ }
208
+ signContent.Append($"{key}={value}");
209
+ first = false;
210
+ }
211
+ }
212
+
213
+ // 3. RSA2 签名
214
+ using (var rsa = RSA.Create())
215
+ {
216
+ rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateKey), out _);
217
+ var signature = rsa.SignData(Encoding.UTF8.GetBytes(signContent.ToString()), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
218
+ return Convert.ToBase64String(signature);
219
+ }
220
+ }
221
+
222
+ // ==================== 智能收产品接入示例接口 ====================
223
+
224
+ /// <summary>
225
+ /// 创建 402 支付请求响应
226
+ /// </summary>
227
+ private static async Task CreatePaymentRequiredResponse(HttpResponse response)
228
+ {
229
+ try
230
+ {
231
+ // 1. 构造订单信息
232
+ var outTradeNo = $"ORDER_{DateTimeOffset.Now.ToUnixTimeMilliseconds()}_{Guid.NewGuid():N}"[..40];
233
+ var amount = "0.01";
234
+ var currency = "CNY";
235
+ var resourceId = ResourcePath;
236
+ var goodsName = GoodsName;
237
+
238
+ // 2. 计算支付截止时间(30 分钟后)
239
+ var payBefore = DateTimeOffset.Now.AddMinutes(30).ToString("o");
240
+
241
+ // 3. 生成商家签名
242
+ var signParams = new Dictionary<string, string>
243
+ {
244
+ ["amount"] = amount,
245
+ ["currency"] = currency,
246
+ ["goods_name"] = goodsName,
247
+ ["out_trade_no"] = outTradeNo,
248
+ ["pay_before"] = payBefore,
249
+ ["resource_id"] = resourceId,
250
+ ["seller_id"] = SellerId,
251
+ ["service_id"] = ServiceId
252
+ };
253
+
254
+ var sellerSignature = GenerateSellerSignature(signParams, MerchantPrivateKey);
255
+
256
+ // 4. 必须在返回 402 前持久化;项目实现负责事务与 outTradeNo 唯一约束。
257
+ await RequireOrderRepository().CreatePendingAsync(new OrderSnapshot
258
+ {
259
+ OutTradeNo = outTradeNo,
260
+ Amount = NormalizeAmount(amount),
261
+ Currency = currency,
262
+ ResourceId = resourceId,
263
+ GoodsName = goodsName,
264
+ PayBefore = payBefore,
265
+ OrderStatus = "PENDING_PAYMENT",
266
+ FulfillStatus = "UNFULFILLED"
267
+ });
268
+
269
+ // 5. 持久化成功后构造 Payment-Needed Header 内容
270
+ var paymentNeeded = new
271
+ {
272
+ protocol = new
273
+ {
274
+ out_trade_no = outTradeNo,
275
+ amount = amount,
276
+ currency = currency,
277
+ resource_id = resourceId,
278
+ pay_before = payBefore,
279
+ seller_signature = sellerSignature,
280
+ seller_sign_type = "RSA2",
281
+ seller_unique_id = SellerId
282
+ },
283
+ method = new
284
+ {
285
+ seller_name = "测试商户",
286
+ seller_id = SellerId,
287
+ seller_app_id = AlipayConfig.AppId,
288
+ goods_name = goodsName,
289
+ seller_unique_id_key = "seller_id",
290
+ service_id = ServiceId
291
+ }
292
+ };
293
+
294
+ // 5. Base64URL 编码
295
+ var paymentNeededJson = JsonSerializer.Serialize(paymentNeeded, new JsonSerializerOptions
296
+ {
297
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
298
+ });
299
+ var paymentNeededEncoded = Base64UrlEncode(paymentNeededJson);
300
+
301
+ // 6. 构造 402 响应
302
+ var responseBody = new
303
+ {
304
+ code = "Payment-Needed",
305
+ message = "需要支付",
306
+ out_trade_no = outTradeNo,
307
+ amount = amount,
308
+ currency = currency,
309
+ goods_name = goodsName
310
+ };
311
+
312
+ Console.WriteLine($"创建支付订单成功:outTradeNo={outTradeNo}, amount={amount}");
313
+
314
+ response.StatusCode = 402;
315
+ response.Headers["Payment-Needed"] = paymentNeededEncoded;
316
+ response.ContentType = "application/json; charset=utf-8";
317
+ await response.WriteAsJsonAsync(responseBody);
318
+
319
+ }
320
+ catch (Exception ex)
321
+ {
322
+ Console.WriteLine($"创建订单失败:{ex.Message}");
323
+ response.StatusCode = 500;
324
+ response.ContentType = "application/json; charset=utf-8";
325
+ await response.WriteAsJsonAsync(new
326
+ {
327
+ code = "CREATE_ORDER_ERROR",
328
+ message = $"创建订单失败:{ex.Message}"
329
+ });
330
+ }
331
+ }
332
+
333
+ /// <summary>
334
+ /// 验证支付凭证并交付资源
335
+ /// </summary>
336
+ private static async Task VerifyPaymentAndDeliverResource(HttpResponse response, string paymentProof)
337
+ {
338
+ try
339
+ {
340
+ // 1. 从 Payment-Proof 中解析订单信息
341
+ string paymentProofValue = null;
342
+ string tradeNo = null;
343
+ string clientSession = null;
344
+
345
+ try
346
+ {
347
+ var decodedProof = Base64UrlDecode(paymentProof);
348
+ using (var proofJson = JsonDocument.Parse(decodedProof))
349
+ {
350
+ var root = proofJson.RootElement;
351
+
352
+ if (root.TryGetProperty("protocol", out var protocol))
353
+ {
354
+ paymentProofValue = protocol.GetProperty("payment_proof").GetString();
355
+ tradeNo = protocol.GetProperty("trade_no").GetString();
356
+ }
357
+
358
+ if (root.TryGetProperty("method", out var method)
359
+ && method.TryGetProperty("client_session", out var clientSessionValue))
360
+ {
361
+ clientSession = clientSessionValue.GetString();
362
+ }
363
+ }
364
+
365
+ // 校验必要字段
366
+ if (string.IsNullOrWhiteSpace(paymentProofValue))
367
+ {
368
+ await CreatePaymentRequiredResponse(response);
369
+ return;
370
+ }
371
+
372
+ if (string.IsNullOrWhiteSpace(tradeNo))
373
+ {
374
+ await CreatePaymentRequiredResponse(response);
375
+ return;
376
+ }
377
+ }
378
+ catch (Exception ex)
379
+ {
380
+ Console.WriteLine($"Payment-Proof 解析失败:{ex.Message}");
381
+ await CreatePaymentRequiredResponse(response);
382
+ return;
383
+ }
384
+
385
+ // 2. 调用支付宝 API 验证支付凭证
386
+ var request = new AlipayAipayAgentPaymentVerifyRequest();
387
+ var verifyBizContent = new Dictionary<string, string>
388
+ {
389
+ ["payment_proof"] = paymentProofValue,
390
+ ["trade_no"] = tradeNo
391
+ };
392
+ if (!string.IsNullOrWhiteSpace(clientSession))
393
+ {
394
+ verifyBizContent["client_session"] = clientSession;
395
+ }
396
+ request.BizContent = JsonSerializer.Serialize(verifyBizContent);
397
+
398
+ var verifyResponse = AlipayClient.Execute(request);
399
+
400
+ // 3. 验证失败,返回错误
401
+ if (verifyResponse.Code != "10000")
402
+ {
403
+ Console.WriteLine($"支付凭证验证失败:{verifyResponse.SubMsg}");
404
+ await CreatePaymentRequiredResponse(response);
405
+ return;
406
+ }
407
+
408
+ // 4. 验证成功后,以支付宝响应和本地持久化订单共同完成严格校验。
409
+ var returnedTradeNo = verifyResponse.TradeNo ?? "";
410
+ var verifyOutTradeNo = verifyResponse.OutTradeNo;
411
+ var returnedAmount = verifyResponse.Amount ?? "";
412
+ var returnedResourceId = verifyResponse.ResourceId ?? "";
413
+ var active = verifyResponse.Active;
414
+ var repository = RequireOrderRepository();
415
+ var order = string.IsNullOrWhiteSpace(verifyOutTradeNo)
416
+ ? null
417
+ : await repository.FindByOutTradeNoAsync(verifyOutTradeNo);
418
+ var sandboxMode = IsExactSandboxMode();
419
+ var verifyTradeNo = !string.IsNullOrWhiteSpace(returnedTradeNo)
420
+ ? returnedTradeNo
421
+ : sandboxMode ? tradeNo : "";
422
+ var verifyAmount = !string.IsNullOrWhiteSpace(returnedAmount)
423
+ ? returnedAmount
424
+ : sandboxMode && order != null ? order.Amount : "";
425
+ var resourceIdVerified = !string.IsNullOrWhiteSpace(returnedResourceId)
426
+ ? returnedResourceId
427
+ : sandboxMode && order != null ? order.ResourceId : "";
428
+
429
+ Console.WriteLine($"支付凭证验证成功:tradeNo={verifyTradeNo}, outTradeNo={verifyOutTradeNo}");
430
+
431
+ if (active != true
432
+ || string.IsNullOrWhiteSpace(verifyTradeNo)
433
+ || verifyTradeNo != tradeNo
434
+ || string.IsNullOrWhiteSpace(verifyOutTradeNo)
435
+ || string.IsNullOrWhiteSpace(resourceIdVerified))
436
+ {
437
+ await CreatePaymentRequiredResponse(response);
438
+ return;
439
+ }
440
+
441
+ var amountMatches = order != null && AmountsEqual(order.Amount, verifyAmount);
442
+ var resourceMatches = order != null
443
+ && order.ResourceId == ResourcePath
444
+ && order.ResourceId == resourceIdVerified;
445
+ var fulfillmentInProgress = order != null
446
+ && new[] { "PENDING_CONFIRM", "FULFILLED" }.Contains(order.FulfillStatus);
447
+ var orderUsable = order != null
448
+ && order.Currency == "CNY"
449
+ && new[] { "PENDING_PAYMENT", "PAID" }.Contains(order.OrderStatus)
450
+ && new[] { "UNFULFILLED", "PENDING_CONFIRM", "FULFILLED" }.Contains(order.FulfillStatus)
451
+ && (fulfillmentInProgress || IsFuture(order.PayBefore));
452
+ if (!amountMatches || !resourceMatches || !orderUsable)
453
+ {
454
+ await CreatePaymentRequiredResponse(response);
455
+ return;
456
+ }
457
+
458
+ var fulfillment = await repository.PrepareFulfillmentAsync(new FulfillmentRequest
459
+ {
460
+ OutTradeNo = verifyOutTradeNo,
461
+ TradeNo = verifyTradeNo,
462
+ ExpectedAmount = NormalizeAmount(verifyAmount),
463
+ ExpectedResourceId = resourceIdVerified,
464
+ CreateResource = () => GenerateServiceResource(resourceIdVerified)
465
+ });
466
+ if (fulfillment == null
467
+ || (fulfillment.State != "PENDING_CONFIRM" && fulfillment.State != "FULFILLED")
468
+ || string.IsNullOrWhiteSpace(fulfillment.ServiceResult))
469
+ {
470
+ throw new InvalidOperationException(
471
+ "IOrderRepository.PrepareFulfillmentAsync 未返回已持久化的履约结果");
472
+ }
473
+
474
+ if (fulfillment.State == "FULFILLED")
475
+ {
476
+ await SendSuccessfulResource(response, verifyTradeNo, verifyOutTradeNo,
477
+ resourceIdVerified, fulfillment.ServiceResult, true);
478
+ return;
479
+ }
480
+
481
+ Console.WriteLine($"资源已生成,准备发送履约确认:outTradeNo={verifyOutTradeNo}, tradeNo={verifyTradeNo}");
482
+
483
+ // 12. 发送履约确认到支付宝,确认成功后才返回成功交付
484
+ var fulfillmentConfirmed = SendFulfillmentConfirm(verifyTradeNo);
485
+ if (!fulfillmentConfirmed)
486
+ {
487
+ response.StatusCode = 502;
488
+ response.ContentType = "application/json; charset=utf-8";
489
+ await response.WriteAsJsonAsync(new
490
+ {
491
+ code = "FULFILLMENT_CONFIRM_FAILED",
492
+ message = "资源已生成但履约确认失败,请稍后使用同一 Payment-Proof 重试"
493
+ });
494
+ return;
495
+ }
496
+
497
+ await repository.MarkFulfilledAsync(verifyOutTradeNo, verifyTradeNo);
498
+ await SendSuccessfulResource(response, verifyTradeNo, verifyOutTradeNo,
499
+ resourceIdVerified, fulfillment.ServiceResult, false);
500
+ }
501
+ catch (Exception ex)
502
+ {
503
+ Console.WriteLine($"支付凭证验证异常:{ex.Message}");
504
+ response.StatusCode = 500;
505
+ response.ContentType = "application/json; charset=utf-8";
506
+ await response.WriteAsJsonAsync(new
507
+ {
508
+ code = "VERIFY_FAILED",
509
+ message = $"支付凭证验证失败:{ex.Message}"
510
+ });
511
+ }
512
+ }
513
+
514
+ private static async Task SendSuccessfulResource(
515
+ HttpResponse response,
516
+ string tradeNo,
517
+ string outTradeNo,
518
+ string resourceId,
519
+ string serviceResult,
520
+ bool alreadyFulfilled)
521
+ {
522
+ var paymentValidation = new
523
+ {
524
+ trade_no = tradeNo,
525
+ out_trade_no = outTradeNo,
526
+ validated = true,
527
+ resource_id = resourceId
528
+ };
529
+ response.Headers["Payment-Validation"] = Base64UrlEncode(
530
+ JsonSerializer.Serialize(paymentValidation));
531
+ response.ContentType = "application/json; charset=utf-8";
532
+ await response.WriteAsJsonAsync(new
533
+ {
534
+ resource_id = resourceId,
535
+ content = serviceResult,
536
+ trade_no = tradeNo,
537
+ out_trade_no = outTradeNo,
538
+ already_fulfilled = alreadyFulfilled,
539
+ fulfillment_confirmed = true
540
+ });
541
+ }
542
+
543
+ /// <summary>
544
+ /// 生成服务资源内容
545
+ /// </summary>
546
+ private static string GenerateServiceResource(string resourceId)
547
+ {
548
+ return JsonSerializer.Serialize(new
549
+ {
550
+ status = "success",
551
+ service_type = "AI_CONTENT_GENERATION",
552
+ resource_id = resourceId,
553
+ content = "这是 AI 生成的内容示例,可根据实际业务替换为任意数字服务内容",
554
+ generated_at = DateTime.Now.ToString("o")
555
+ });
556
+ }
557
+
558
+ private static string NormalizeAmount(string value)
559
+ {
560
+ if (!TryParseAmount(value, out var amount))
561
+ {
562
+ throw new ArgumentException("金额格式非法", nameof(value));
563
+ }
564
+ return amount.ToString("0.00", CultureInfo.InvariantCulture);
565
+ }
566
+
567
+ private static bool AmountsEqual(string left, string right)
568
+ {
569
+ return TryParseAmount(left, out var leftAmount)
570
+ && TryParseAmount(right, out var rightAmount)
571
+ && leftAmount == rightAmount;
572
+ }
573
+
574
+ private static bool TryParseAmount(string value, out decimal amount)
575
+ {
576
+ amount = 0;
577
+ if (string.IsNullOrWhiteSpace(value)
578
+ || !System.Text.RegularExpressions.Regex.IsMatch(value.Trim(), @"^\d+(?:\.\d{1,2})?$"))
579
+ {
580
+ return false;
581
+ }
582
+ return decimal.TryParse(value, NumberStyles.AllowDecimalPoint,
583
+ CultureInfo.InvariantCulture, out amount) && amount >= 0;
584
+ }
585
+
586
+ private static bool IsFuture(string value)
587
+ {
588
+ return DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture,
589
+ DateTimeStyles.RoundtripKind, out var deadline)
590
+ && deadline > DateTimeOffset.Now;
591
+ }
592
+
593
+ private static bool IsExactSandboxMode()
594
+ {
595
+ return AlipayConfig.ServerUrl == SandboxGateway && ServiceId == "api_mock_service_id";
596
+ }
597
+
598
+ /// <summary>
599
+ /// 发送履约确认
600
+ /// </summary>
601
+ private static bool SendFulfillmentConfirm(string tradeNo)
602
+ {
603
+ if (string.IsNullOrWhiteSpace(tradeNo))
604
+ {
605
+ Console.WriteLine("履约确认失败:tradeNo 为空");
606
+ return false;
607
+ }
608
+
609
+ try
610
+ {
611
+ Console.WriteLine($"开始发送履约确认:tradeNo={tradeNo}");
612
+
613
+ var request = new AlipayAipayAgentFulfillmentConfirmRequest();
614
+ request.BizContent = JsonSerializer.Serialize(new
615
+ {
616
+ trade_no = tradeNo
617
+ }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
618
+
619
+ var response = AlipayClient.Execute(request);
620
+
621
+ if (response.Code == "10000")
622
+ {
623
+ Console.WriteLine($"履约确认成功:tradeNo={tradeNo}");
624
+ return true;
625
+ }
626
+ else
627
+ {
628
+ Console.WriteLine($"履约确认失败:tradeNo={tradeNo}, errorCode={response.SubCode}, errorMsg={response.SubMsg}");
629
+ return false;
630
+ }
631
+ }
632
+ catch (Exception ex)
633
+ {
634
+ Console.WriteLine($"履约确认异常:tradeNo={tradeNo}, error={ex.Message}");
635
+ return false;
636
+ }
637
+ }
638
+ }
639
+ }
640
+ ```