@alipay/alipay-aipay 1.7.0 → 1.7.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.
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 +926 -0
  112. package/skill-pre/references/onboarding/flow.md +595 -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 +97 -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,281 @@
1
+ # Alipay-SDK Reminder
2
+
3
+ 以下各项为接入支付宝 SDK 的相关规则,**严禁在未阅读全部内容的情况下编写任何 SDK 相关代码**。每条规则均来自真实集成踩坑,请逐条阅读并确认后再继续,编写代码遵守规则。
4
+
5
+ ---
6
+
7
+ ## SDK 引入方式
8
+
9
+ SDK 的引入方式取决于对应语言的包管理方式和实际导出方式,**绝不能凭猜测选择**,必须通过查阅 SDK 文档或类型定义确定。
10
+
11
+ **各语言 SDK 引入方式参考:**
12
+
13
+ | 语言 | 包管理 | 引入方式 | 备注 |
14
+ |------|--------|---------|------|
15
+ | Java | Maven | `import com.alipay.api.*;` | 通过 Maven 依赖引入,包结构固定 |
16
+ | .NET | NuGet | `using Aop.Api;` 等 | 通过 NuGet 安装 `AlipaySDKNet.Standard`,命名空间固定 |
17
+ | PHP | Composer | `require_once '../aop/AopClient.php';` 等 | 通过 GitHub 仓库或 Composer 引入,类文件路径以实际目录为准 |
18
+ | Python | pip | `from alipay.aop.api.AlipayClientConfig import AlipayClientConfig`<br>`from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient` | 通过 pip 安装 `alipay-sdk-python`,模块路径以实际版本为准 |
19
+ | Node.js | npm | 需查阅类型定义确认 | 导入方式可能随版本变化,**必须验证**,详见下方「Node.js 导入格式判断」 |
20
+
21
+ ### Node.js 导入格式判断
22
+
23
+ `alipay-sdk`(Node.js)的导入方式取决于包的实际导出方式,**绝不能凭猜测选择**。
24
+
25
+ **判断流程:**
26
+
27
+ 1. **查看 package.json**:确认模块类型(ESM/CJS)和入口路径
28
+ ```bash
29
+ cat node_modules/alipay-sdk/package.json
30
+ ```
31
+ 重点看 `type`、`exports`、`main`、`types` 字段。
32
+
33
+ 2. **查看类型定义文件**:确认导出方式
34
+ ```bash
35
+ cat node_modules/alipay-sdk/dist/types/index.d.ts
36
+ ```
37
+ 注意:类型定义文件路径以 package.json 中 `types` 字段为准。
38
+
39
+ 3. **选择导入语法**:
40
+ - 命名导出(`export class AlipaySdk`)→ `import { AlipaySdk } from 'alipay-sdk'`
41
+ - 默认导出(`export default`)→ `import AlipaySdk from 'alipay-sdk'`
42
+ - 混合导出 → `import AlipaySdk, { AlipaySdkConfig } from 'alipay-sdk'`
43
+
44
+ **优先级**:类型定义(.d.ts)> 官方文档 > 源码
45
+
46
+ **注意:**
47
+ - package.json 的 `exports` 字段是入口路径配置,不是导出方式
48
+ - 包的导出方式可能随版本更新而改变,务必验证
49
+ - `code-examples/nodejs/` 中的 `require` / `import` 行只用于展示 SDK 调用位置,不作为当前项目可复制依据;生成或修改真实 Node.js 项目前,必须以目标项目实际安装的 `alipay-sdk` 包和项目模块制式为准重新确定导入方式
50
+
51
+ ## 运行与配置边界
52
+
53
+ 1. 沙箱与生产共用支付校验控制流,只切换已确认配置;生产路径不得存在 Mock、固定成功或跳过验签旁路。
54
+ 2. `alipayPublicKey` 公钥模式只用当前 SDK 的普通执行 API;证书 API 要求项目明确采用证书模式且同时配置应用、支付宝和支付宝根证书。
55
+ 3. Unix/macOS/Linux 从 runner 确认的项目根定位 `.alipay-sandbox.json`,禁止依赖 `cwd`、`getcwd()`、`Directory.GetCurrentDirectory()`、`user.dir` 或 Agent 启动目录。
56
+ 4. 配置缺失、模式错配或校验失败时明确失败;不得回退示例值、占位密钥、生产网关、非固定沙箱 `serviceId` 或无验签请求。
57
+
58
+ ---
59
+
60
+ ## 页面跳转类 API 方法选择
61
+
62
+ `alipay.trade.page.pay` 是页面跳转类 API,**严禁使用 `exec()` 方法**,必须使用页面跳转方法。各语言 SDK 的方法名不同,请按下表选择:
63
+
64
+ | 语言 | 页面跳转方法 | SDK 执行方法 | 页面跳转类 API 举例 |
65
+ |------|-------------|-------------|-------------------|
66
+ | Java | `alipayClient.pageExecute(request, "POST")` | `alipayClient.execute(request)` | `alipay.trade.page.pay` |
67
+ | .NET | `alipayClient.PageExecute(request, null, "POST")` | `alipayClient.Execute(request)` | 同上 |
68
+ | PHP | `$alipayClient->pageExecute($request, "POST")` | `$alipayClient->execute($request)` | 同上 |
69
+ | Node.js | `alipaySdk.pageExec('alipay.trade.page.pay', 'POST', {...})` | `alipaySdk.exec('alipay.trade.query', {...})` | 同上 |
70
+ | Python | `alipay_client.page_execute(request)` | `alipay_client.execute(request)` | 同上 |
71
+
72
+ **此外,APP 支付(`alipay.trade.app.pay`)属于客户端 SDK 调用类 API,需使用 `sdkExecute`/`sdkExec` 方法:**
73
+
74
+ | 语言 | 客户端 SDK 方法 |
75
+ |------|---------------|
76
+ | Java | `alipayClient.sdkExecute(request)` |
77
+ | .NET | `alipayClient.SdkExecute(request)` |
78
+ | PHP | `$alipayClient->sdkExecute($request)` |
79
+ | Node.js | `alipaySdk.sdkExecute('alipay.trade.app.pay', {...})` |
80
+
81
+ **Node.js 对比示例:**
82
+ ```javascript
83
+ // ✅ 正确 - 页面跳转类 API 使用 pageExec()
84
+ const htmlForm = alipaySdk.pageExec('alipay.trade.page.pay', 'POST', {
85
+ bizContent: { out_trade_no: '123', total_amount: '10.00', ... }
86
+ });
87
+ // 返回:<form action="...">...</form> HTML 字符串
88
+
89
+ // ❌ 错误 - 对页面跳转类 API 使用 exec()
90
+ const result = await alipaySdk.exec('alipay.trade.page.pay', {
91
+ bizContent: { out_trade_no: '123', total_amount: '10.00', ... }
92
+ });
93
+ // 返回:JSON 对象,无法用于页面跳转,这是最常见的集成错误!
94
+ ```
95
+
96
+ | 方法类型 | 用途 | 返回值 | 示例 |
97
+ | --- | --- | --- | --- |
98
+ | 页面跳转方法(`pageExec()`/`pageExecute()` 等) | 页面跳转类 API(如:支付) | HTML 表单字符串 | 网站支付 |
99
+ | `exec()` | 服务端 API(查询 / 退款) | 结构化数据(JSON 等) | 交易查询、退款 |
100
+
101
+ **对支付接口调用 exec() 将不会得到可用的支付表单,是最常见的集成错误之一。**
102
+
103
+ ### 前端支付表单处理
104
+
105
+ 页面跳转类 API 返回的是包含自动提交脚本的 HTML `<form>` 字符串,前端**必须将此 HTML 渲染到页面并自动提交表单**,禁止直接用 URL 跳转。
106
+
107
+ **❌ 错误写法(以浏览器端 JavaScript 为例)**:
108
+ ```javascript
109
+ // paymentHtml 是 HTML 表单字符串,不是 URL
110
+ if (data.paymentHtml) {
111
+ window.location.href = data.paymentHtml; // 页面只显示 ?method=alipay.trade.page.pay... 参数,无法跳转
112
+ }
113
+ ```
114
+
115
+ **✅ 正确写法**:
116
+ ```javascript
117
+ if (data.paymentHtml) {
118
+ const container = document.createElement('div');
119
+ container.innerHTML = data.paymentHtml;
120
+ container.style.position = 'fixed';
121
+ container.style.top = '0';
122
+ container.style.left = '0';
123
+ container.style.width = '100%';
124
+ container.style.height = '100%';
125
+ container.style.zIndex = '9999';
126
+ container.style.background = 'white';
127
+ document.body.appendChild(container);
128
+ const form = container.querySelector('form');
129
+ if (form) form.submit();
130
+ }
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 私钥格式
136
+
137
+ **沙箱返回两个私钥字段,必须按语言选对:**
138
+
139
+ | 字段 | 格式 | 使用场景 |
140
+ |------|------|---------|
141
+ | `appPrivateKey` | PKCS#8 格式 | ✅ Java 语言必须使用 |
142
+ | `appPrivatePkcsKey` | PKCS#1 格式 | ✅ 非 Java 语言(Python、Node.js、PHP、.NET 等)必须使用 |
143
+
144
+ **严禁以下误判行为:**
145
+
146
+ | 误判行为 | 为什么错 | 正确做法 |
147
+ |---------|---------|---------|
148
+ | 看到 `ERR_OSSL_UNSUPPORTED` 就推断私钥格式有问题 | 该错误也可能来自私钥复制污染、字段选错或 SDK 配置参数错误,不能证明私钥格式或运行时不兼容 | 先确认项目实际读取值与可信配置原值一致,再检查字段映射和 SDK 参数 |
149
+ | 向 SDK 配置写入 PEM 头尾、其他前后缀、包装行或说明文字 | 支付宝 SDK 会按配置自行处理密钥包装,手动添加会导致格式错误 | SDK 配置直接使用沙箱或生产配置中的原始私钥值,不做任何格式处理 |
150
+ | 将沙箱返回的 `appPrivatePkcsKey` 转换为 PKCS#8 格式 | 沙箱已同时返回两种格式,转换会引入不必要风险 | 非 Java 语言使用原始 `appPrivatePkcsKey` 值,不做转换 |
151
+ | 将沙箱返回的 `appPrivateKey` 转换为 PKCS#1 格式 | 沙箱已同时返回两种格式,转换会引入不必要风险 | Java 语言使用原始 `appPrivateKey` 值,不做转换 |
152
+ | 写入 SDK 配置前先对私钥做 Base64 解码/重新编码 | SDK 配置无需这些处理 | SDK 配置直接使用沙箱返回的原始字符串 |
153
+ | 因为字段名含 "Pkcs" 就认为是 PKCS#8 格式 | `appPrivatePkcsKey` 是 **PKCS#1** 格式,"Pkcs" 仅表示这是 PKCS 系列标准之一,不代表 PKCS#8 | 非 Java 语言使用 `appPrivatePkcsKey`(PKCS#1),Java 语言使用 `appPrivateKey`(PKCS#8) |
154
+
155
+ **核心原则:沙箱配置直接使用沙箱返回的对应私钥字段,不做格式转换;生产配置必须确认应用私钥格式与项目语言匹配。所有写入 SDK 配置的私钥值都使用不带 PEM 头尾或其他前后缀的原始密钥字符串,禁止手动添加 PEM 头尾、添加其他前后缀、编码处理或换行符调整。Java 语言使用 PKCS#8,非 Java 语言(Node.js、Python、PHP、.NET 等)使用 PKCS#1。**
156
+
157
+ **调用边界说明**:上述禁止加工规则针对持久化配置和 SDK 配置。若 A2M 等业务协议需要直接调用语言原生密码库生成业务签名,而该密码库明确要求 PEM 或密钥对象,可从已经校验一致的原始私钥临时构造调用入参;禁止写回配置、覆盖原始值,或把这种调用适配解释成私钥修复。支付宝 SDK 请求签名仍直接使用原始字段,不得预先包装。
158
+
159
+ ### 生产密钥替换与生产支付测试提醒
160
+
161
+ 当用户要求 Agent 直接替换生产环境密钥配置,或用户声称已经替换完成并要求启动生产环境支付测试时,必须先提醒并核对以下事项:
162
+
163
+ 1. `appId`、应用公钥、应用私钥必须属于同一套生产应用密钥,不得混用沙箱应用、其他生产应用或另一套重新生成的密钥。
164
+ 2. 基于项目编程语言确认应用私钥格式:Java 使用 PKCS#8;非 Java(Node.js、Python、PHP、.NET 等)使用 PKCS#1。
165
+ 3. 如果生产应用私钥格式与当前语言 SDK 要求不一致,请使用支付宝开放平台密钥工具进行私钥格式转换,再将转换后的原始私钥字符串写入项目配置。
166
+ 4. 不得把私钥输出到日志、对话总结、测试结果或完整支付表单中。
167
+
168
+ ### 连环误判链
169
+
170
+ 遇到 `ERR_OSSL_UNSUPPORTED` 时,agent 容易陷入以下**越错越远**的连环推理,**每一步都是错的**,必须识别并阻断:
171
+
172
+ ```
173
+ ❌ 连环误判链(严禁陷入):
174
+
175
+ 第 1 步(误判起点):看到 ERR_OSSL_UNSUPPORTED → 推断"私钥格式有问题"
176
+ ↓
177
+ 第 2 步(错误修复):认为"给 SDK 配置添加 PEM 头尾可以修复私钥"
178
+ → 改写 SDK 配置或原始私钥 → 错误仍在
179
+ ↓
180
+ 第 3 步(错误归因):认为"Node.js 18+ 用了 OpenSSL 3.0,旧格式不被支持"
181
+ ↓
182
+ 第 4 步(越错越远):认为"应该把原始私钥改成另一种格式"
183
+ → 将私钥转换为 PKCS#8 格式 → 错误更严重(注:Java 语言应直接使用沙箱返回的 `appPrivateKey` 字段,而非手动转换格式)
184
+ ```
185
+
186
+ **正确做法**:看到 `ERR_OSSL_UNSUPPORTED`,立即停止改写原始私钥或 SDK 配置,先核对项目读取值与可信配置原值,再按下方「验签失败排查原则」逐项检查。原生密码库所需的临时调用适配仍按上方“调用边界说明”处理,不能用于修复配置。
187
+
188
+ ---
189
+
190
+ ## 验签失败排查原则
191
+
192
+ 遇到 `invalid-signature`(验签出错)时,**严禁凭猜测归因到私钥格式或环境兼容性**,必须按以下顺序逐项排查:
193
+
194
+ ### 正确排查顺序
195
+
196
+ **原则:先验证实际请求内容,再怀疑密钥配置。**
197
+
198
+ 1. **检查实际发送、签名和验签参数的一致性**
199
+ - 支付宝验签时要求实际请求参数与签名内容匹配。如果 `return_url`、`notify_url` 等参数已传入支付请求但未正确参与签名,或者在签名后被改写,可能导致验签失败。用户明确关闭同步回跳时,未传 `return_url` 本身不应被当作验签失败原因。
200
+ - **排查方法**:比较实际发送给支付宝的请求参数、SDK 参与签名的参数和服务端验签时使用的参数,确认参数名、参数值和编码方式一致。
201
+ - 未关闭同步回跳:确认实际请求、签名内容和验签参数中的 `return_url` 值一致。
202
+ - 已关闭同步回跳:确认实际请求和签名内容均未包含 `return_url`。
203
+
204
+ 2. **检查私钥来源**
205
+ - 确认使用的是沙箱返回的正确私钥字段:Java 使用 `appPrivateKey`,非 Java 使用 `appPrivatePkcsKey`,而非自行生成或其他来源。
206
+
207
+ 3. **检查 signType 是否一致**
208
+ - 请求中的 `sign_type` 必须为 `RSA2`,与配置保持一致。
209
+
210
+ 4. **检查私钥格式完整性**
211
+ - 私钥内容无多余空格、换行符。
212
+
213
+ 5. **检查网关地址**
214
+ - 沙箱环境必须使用沙箱网关 `https://openapi-sandbox.dl.alipaydev.com/gateway.do`,生产环境使用 `https://openapi.alipay.com/gateway.do`。
215
+ - 五语言 A2M 示例默认读取 `ALIPAY_GATEWAY`,未设置时使用沙箱网关;只有生产部署才把该环境变量显式设置为生产网关。禁止把生产网关硬编码成沙箱联调默认值。
216
+
217
+ > **核心教训**:验签失败 ≠ 私钥问题。应先通过日志/输出验证实际请求行为(参数是否完整传入),而非基于技术传言做假设。
218
+
219
+ ---
220
+
221
+ ## 网站支付同步回跳
222
+
223
+ 用户未明确要求关闭同步回跳时,网站支付默认配置 `return_url`。该值不能只是一个格式正确的 URL,还必须对应目标项目内真实存在的 GET 路由和可正常渲染的结果页。
224
+
225
+ 1. 从项目实际运行配置确定协议、主机、端口和路径,禁止使用示例域名、猜测端口或未实现的路由。
226
+ 2. 结果页不存在时,必须在当前项目中实现,不得只填写 SDK 的 `return_url` 参数。
227
+ 3. 启动服务后对不带支付参数的精确 `return_url` 发起 GET 请求。该请求只验证路由和页面壳:没有订单上下文时展示安全的中性状态,不得放宽真实回跳请求的验签和订单归属校验。跟随有限次重定向,确认最终响应无 404、500、认证循环或重定向循环。单页应用直接刷新该路由仍必须返回应用页面。
228
+ 4. 检查 SDK 实际传入的 `return_url` 与已验证地址完全一致,防止域名、端口、协议或路径在配置映射中被替换。
229
+ 5. 回跳处理必须先验证支付宝同步参数签名,或使用服务端保存且与当前用户会话绑定的订单标识定位待查订单;不得信任可任意修改的订单号。
230
+ 6. 同步回跳参数不是支付成功的可信依据。页面应先展示“正在确认支付结果”,再通过服务端交易查询或已验签的异步通知状态展示最终结果。
231
+ 7. 当前 Agent 有浏览器或 UI 验证能力时,必须实际打开回跳页,确认非空白、无可见报错且关键状态内容已渲染。当前无该能力时,不追加用户确认,将回跳页 UI 证据标记为人工待验证;不得宣称页面渲染已验证或网站支付代码开发完成。
232
+
233
+ 回跳地址无法访问、路由不存在、页面无法渲染,或 SDK 实际值与已验证地址不一致时,必须先修正,不得宣称网站支付代码开发完成。
234
+
235
+ ---
236
+
237
+ ## 时间戳格式化
238
+
239
+ 支付宝要求时间戳格式为 `yyyy-MM-dd HH:mm:ss`(空格分隔,非 ISO 格式)。
240
+
241
+ ### Java
242
+ ```java
243
+ import java.text.SimpleDateFormat;
244
+ // ✅ 正确
245
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
246
+ // ❌ 错误:Instant.now().toString() → "2026-04-29T14:30:00.000Z"
247
+ ```
248
+
249
+ ### .NET (C#)
250
+ ```csharp
251
+ // ✅ 正确
252
+ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
253
+ // ❌ 错误:DateTime.Now.ToString("o") → "2026-04-29T14:30:00.0000000+08:00"
254
+ ```
255
+
256
+ ### PHP
257
+ ```php
258
+ // ✅ 正确
259
+ date("Y-m-d H:i:s");
260
+ // ❌ 错误:date("c") → "2026-04-29T14:30:00+08:00"
261
+ ```
262
+
263
+ ### Node.js
264
+ ```javascript
265
+ function formatAlipayTimestamp(date) {
266
+ const pad = (n) => n.toString().padStart(2, '0');
267
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
268
+ }
269
+
270
+ // ❌ 错误:new Date().toISOString() → "2026-04-29T14:30:00.000Z"
271
+ ```
272
+
273
+ ### Python
274
+ ```python
275
+ from datetime import datetime
276
+
277
+ def format_alipay_timestamp(dt: datetime) -> str:
278
+ return dt.strftime("%Y-%m-%d %H:%M:%S")
279
+
280
+ # ❌ 错误:datetime.isoformat() → "2026-04-29T14:30:00"
281
+ ```
@@ -0,0 +1,154 @@
1
+ # 支付宝支付产品代码开发校验清单
2
+
3
+ 本文档整理了支付宝支付集成过程中的通用校验项,仅包含必须校验和高危项目。代码实现后、集成收口前必须逐项核对;逐项核对不等于为每项安装工具或启动运行环境,自动执行边界以 `../flow.md` 步骤 8 为准,无法自动取得证据时标记为“人工待验证”。默认按第八节摘要模板输出,用户要求明细时再展开全部逐项结果。校验结果供参考,开发者务必按照支付宝最新开放平台文档进行检查。
4
+
5
+ ---
6
+
7
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:aipay,webpay,apppay -->
8
+ ## 一、密钥与安全校验
9
+
10
+ | 校验项 | 校验要求 | 说明 |
11
+ |----------|----------|----------|
12
+ | 私钥存储位置 | 私钥不得保存在客户端代码中 | 构造交易数据并签名必须在商家服务端完成,私钥绝对不能保存在商家APP客户端中,也不能从服务端下发。商户需妥善保存私钥,不建议在代码或配置中明文写入私钥,应当通过加密配置文件或密钥管理平台进行管理 |
13
+ | 私钥日志安全 | 私钥不得出现在日志中 | 私钥的保密等级比源码高,出现在日志中将增加私钥泄露的风险 |
14
+ | 私钥仓库安全 | 私钥不得上传公共仓库 | 私钥泄露将导致应用和支付宝交互的安全性完全丧失 |
15
+ | 沙箱配置后置校验 | 沙箱 appId、应用私钥、支付宝公钥接入项目后必须完成配置准确性校验 | `sandboxConfigState=READY` 时,Unix/macOS/Linux 必须确认商家服务端加载器直接读取已验证的 `.alipay-sandbox.json`,从同一 `appIds[0]` 选择当前语言字段且不在源码、普通日志或额外 `.env` 留下密钥副本;Windows 必须确认项目实际读取值属于本次手工申领和密钥配置的同一沙箱应用。待配置时本项直接不通过,代码只能保留会在配置缺失时明确失败的加载边界,禁止占位符、示例值或其他来源密钥 |
16
+ | 用户项目文件保护 | 既有项目结构、文件和无关支付能力均保留;新项目只创建在尚不存在或为空的目录 | 检查本轮变更不存在未经独立二次确认的既有文件删除、既有文件整体覆盖,或与已确认目标产品无关的修改;新建文件不属于既有文件整体覆盖。发现违规立即判定不通过并停止后续流程 |
17
+ | 沙箱配置防提交 | Unix/macOS/Linux 快速沙箱:配置未被 Git 跟踪,项目根目录 `.gitignore` 包含精确规则 `/.alipay-sandbox.json`,配置权限为 `0600`;Windows 手工沙箱:实际敏感配置未被 Git 跟踪并具有对应忽略保护,Windows 文件访问控制能核验时仅当前用户可访问,不能核验时标记人工待验证 | 禁止私钥、沙箱账号或密码随项目进入 Git 暂存区或公共仓库;缺少适用保护时判定不通过 |
18
+ | 沙箱配置稳定定位 | Unix/macOS/Linux 项目代码从已确认的规范化项目根定位 `.alipay-sandbox.json` | 禁止依赖 `cwd`、`getcwd()`、`Directory.GetCurrentDirectory()`、`user.dir` 或 Agent 启动目录 |
19
+ | SDK 公钥/证书模式一致 | 公钥模式使用普通执行 API;证书执行 API 只在应用公钥证书、支付宝公钥证书和支付宝根证书均已配置时使用 | 禁止公钥配置模式误用证书执行 API |
20
+ | 支付校验无旁路 | 生产源码中不存在 Mock、测试或沙箱开关可触达的跳过验签、固定成功或支付校验旁路 | 沙箱与生产必须共用相同验证控制流 |
21
+
22
+ ---
23
+
24
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:webpay,apppay -->
25
+ ## 二、异步通知校验
26
+
27
+ 先区分两种验收层级,再判断是否通过:
28
+
29
+ - **本地正式验收 / 本地生产参数验收模式**:允许暂时没有可公网访问的 HTTPS `notify_url`。这一层必须检查“通知处理代码是否已实现”和“支付结果是否可通过主动查询兜底确认”;公网可访问性标记为“人工待验证”,不作为阻断本地正式验收的单一条件。
30
+ - **真实生产上线**:必须具备可公网访问的 HTTPS `notify_url`,并完成验签、关键字段校验、幂等、成功响应 `success` 和补偿查询。未满足时不得判定生产就绪。
31
+
32
+ | 校验项 | 校验要求 | 说明 |
33
+ |----------|----------|----------|
34
+ | 验签必须执行 | 收到异步通知后必须先验签 | 确保通知来自支付宝 |
35
+ | 关键信息校验 | 检验 out_trade_no、total_amount、app_id、seller_id 等商户请求关键参数| 防止伪造订单、金额篡改等 |
36
+ | 交易状态判断 | 仅 `TRADE_SUCCESS` 或 `TRADE_FINISHED` 才算支付成功 | 其他状态不算支付成功 |
37
+ | 幂等处理 | 必须进行幂等处理,过滤重复的通知 | 同一笔订单可能收到多次异步通知 |
38
+ | 响应值 | 商户侧处理成功后需返回字符串 `success` | 否则支付宝会重试通知 |
39
+ | URL 格式 | notify_url 不能包含空格和 HTML 标签 | 否则可能导致通知失败 |
40
+ | 重定向 | notify_url 不能重定向 | 重定向会导致支付宝收不到 success |
41
+ | 外网可访问 | 真实生产上线时,notify_url 必须外网可访问;本地生产参数验收模式下标记为人工待验证 | 需确保支付宝服务器可访问;本地 `localhost`、局域网地址或 TLS 未就绪域名不阻塞本地正式验收 |
42
+
43
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:aipay,webpay,apppay -->
44
+ ## 三、适用接口覆盖校验
45
+
46
+ | 校验项 | 校验要求 | 说明 |
47
+ |----------|----------|----------|
48
+ | AI 网页应用收款/AI 移动应用收款 | 已实现产品下单、交易查询、退款、退款查询、关闭交易和异步通知处理代码 | 以产品和接口文档为准;缺少本地示例不得作为省略理由。公网通知联调是否完成单独在上一节判断 |
49
+ | AI 按量付费 | 已实现 402 协议、`alipay.aipay.agent.payment.verify` 和 `alipay.aipay.agent.fulfillment.confirm` | 不使用 AI 网页应用收款/AI 移动应用收款的通用收单接口 |
50
+ | 限定范围 | 用户明确排除的适用接口已列为待办 | 存在未实现的适用接口时不得判定完整集成 |
51
+
52
+ ---
53
+
54
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:webpay,apppay -->
55
+ ## 四、支付结果处理校验
56
+
57
+ | 校验项 | 校验要求 | 说明 |
58
+ |----------|----------|----------|
59
+ | 前台同步结果防范 | 前台同步跳转结果仅作通知,**不能作为支付成功的依据** | 必须以异步通知或查询接口结果为准 |
60
+ | AI 网页应用收款回跳默认行为 | 用户未明确关闭同步回跳时,已配置项目实际 `return_url` | 地址不得包含示例域名、占位符、猜测端口或未实现路由 |
61
+ | AI 网页应用收款回跳页可用性 | 启动服务后 GET 访问精确 `return_url`,并在 Agent 具备浏览器/UI 能力时验证页面正常渲染;当前环境无法取得 UI 证据时标记人工待验证 | 不得出现 404、500、空白页、可见报错、认证循环或重定向循环;单页应用直接刷新路由也必须可用 |
62
+ | AI 网页应用收款回跳状态展示 | 先验证同步参数签名,或使用与当前用户会话绑定的服务端订单标识;再通过服务端查询或已验签通知状态展示最终支付结果 | 同步回跳参数不得直接改写订单或作为支付成功依据,不得使用可任意修改的订单号访问其他订单 |
63
+ | AI 网页应用收款关闭同步回跳 | 仅当用户已明确表示不使用同步回跳时适用;SDK 请求未传 `return_url` | 用户未明确关闭时,缺少 `return_url` 必须修复为默认同步回跳分支;关闭分支必须保留异步通知、交易查询和商户订单查询页,且沙箱提示不得声称付款后会自动返回商户网站 |
64
+ | 支付成功判断 | 仅 `trade_status` 为 `TRADE_SUCCESS` 或 `TRADE_FINISHED` 时才认定付款成功 | 其他状态不算支付成功 |
65
+ | 主动查询兜底 | 主动查询订单状态必须作为支付结果确认的兜底逻辑 | 无论异步通知是否到达,都应具备主动查询订单状态的能力,防止因网络抖动、通知延迟或丢失导致的状态不一致 |
66
+ | 未知异常处理 | 网络超时或未知异常时,必须调用查询接口确认 | 不能简单推断为成功或失败 |
67
+ | 商家资损防范 | 不依赖前台同步结果,以异步通知或查询为准 | 防止用户未付款但商家判定成功 |
68
+ | 用户资损防范 | 未确认支付结果前不要求用户再次付款 | 防止用户已付款但商家判定失败导致重复扣款 |
69
+
70
+ > AI 按量付费不使用前台同步跳转结果和异步通知作为支付成功依据,需按下一节专项校验。
71
+
72
+ ---
73
+
74
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:aipay -->
75
+ ## 五、AI 按量付费专项校验
76
+
77
+ | 校验项 | 校验要求 | 说明 |
78
+ |----------|----------|----------|
79
+ | 订单持久化 | 返回 `Payment-Needed` 前必须持久化 `out_trade_no`、`resource_id`、金额、状态、过期时间 | 付款后携带 `Payment-Proof` 回来时必须能映射回商户本地订单 |
80
+ | Payment-Proof 验证 | 收到 `Payment-Proof` 后必须调用 `alipay.aipay.agent.payment.verify` | 验证成功只代表支付宝凭证有效,不代表本地订单已完成校验 |
81
+ | 本地订单匹配 | 验付成功后必须查询本地订单并校验状态 | 防止凭证有效但商户订单不存在、过期或状态异常 |
82
+ | 资源防串 | 必须校验本地订单的 `resource_id` 与验付结果/请求资源一致 | 防止用户通过篡改资源标识获取其他资源 |
83
+ | 金额一致性 | 必须校验本地订单金额与 `Payment-Needed` 账单金额和服务定价一致;若验付接口返回金额字段,再校验实际支付金额 | 防止金额篡改或错误定价 |
84
+ | 幂等履约 | 同一订单重复携带 `Payment-Proof` 时不得重复发放资源 | 应返回历史履约结果或可重试确认结果 |
85
+ | 履约确认 | 资源生成后必须调用 `alipay.aipay.agent.fulfillment.confirm` | 确认成功后再标记订单 `FULFILLED` 并返回成功交付 |
86
+ | 自动联调成功证据 | 必须同时具备 HTTP 200、非空 `resource_id` / `resourceId`、非空 `content`、无明确业务失败,以及当前五语言示例有效的 `Payment-Validation` | 204、空体、空对象、`VERIFY_FAILED`、履约失败或异常响应头均不得判为通过 |
87
+ | 敏感过程产物 | 单次联调的临时目录/文件权限不得宽于 `0700/0600`,命令结束时立即清理 | 禁止打印 Payment-Proof 或 Payment-Validation 原始值;失败后重新执行完整联调 |
88
+ | 沙箱 serviceId | 沙箱联调的 `serviceId` 固定为 `api_mock_service_id` | 不向用户索要正式 `serviceId`,不使用其他占位符 |
89
+ | 沙箱空字段边界 | 沙箱联调中字段为空可用于排查定位,生产环境关键字段为空必须按异常处理 | 不得把沙箱容错逻辑直接迁移成生产跳过校验 |
90
+ | 生产实现完整性 | 订单持久化、本地订单匹配、金额一致性、资源防串、幂等履约、待确认状态和确认失败重试均已实际实现 | 任一关键控制仍为 TODO、注释、内存演示或伪代码时只能判定沙箱演示可测试,不得判定生产就绪 |
91
+
92
+ ---
93
+
94
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:webpay,apppay -->
95
+ ## 六、退款校验
96
+
97
+ | 校验项 | 校验要求 | 说明 |
98
+ |----------|----------|----------|
99
+ | 退款使用场景 | 所有退款业务都必须调用退款接口 | 撤销接口不能用于退款业务,撤销接口仅用于在不确定支付结果情况下对交易发起撤销 |
100
+ | 退款是否成功判断 | 退款接口返回 `fund_change=Y` 或退款查询返回 `refund_status=REFUND_SUCCESS` 才算退款成功 | 其他状态不算退款成功 |
101
+ | 退款幂等保障 | 接口未退款成功且不明确交易情况时,重试请务必保证退款请求号 out_request_no 以及请求参数一致,避免发生多次退款 | 防止多次退款 |
102
+ | 新退款与重试区分 | 每次新退款使用新的 `out_request_no`;同一次退款的异常重试复用原编号、金额和请求参数 | 新的一次部分退款不能复用其他退款编号,重试也不能临时生成新编号 |
103
+ | 退款金额计算 | 使用十进制定点金额,并在调用前校验累计退款不超过原订单实付金额 | 禁止二进制浮点误差或超额退款 |
104
+
105
+ ---
106
+
107
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:aipay,webpay,apppay -->
108
+ ## 七、上线前校验
109
+
110
+ | 校验项 | 校验要求 | 说明 |
111
+ |----------|----------|----------|
112
+ | 网关地址切换 | 五语言 A2M 示例默认沙箱网关为 `https://openapi-sandbox.dl.alipaydev.com/gateway.do`;上线时显式切换为 `https://openapi.alipay.com/gateway.do` | 禁止把生产网关复制进沙箱运行配置 |
113
+ | APPID 切换 | 使用生产环境的 APPID | 与沙箱 APPID 不同 |
114
+ | 密钥切换 | 使用生产环境的应用私钥和支付宝公钥 | 与沙箱密钥不同 |
115
+ | 生产密钥对应关系 | `appId`、应用公钥、应用私钥属于同一套生产应用密钥 | 不得混用沙箱应用、其他生产应用或另一套重新生成的密钥 |
116
+ | 生产私钥格式 | 应用私钥格式与项目语言 SDK 匹配 | Java 使用 PKCS#8;非 Java 使用 PKCS#1。格式不匹配时使用支付宝开放平台密钥工具转换 |
117
+ | AI 按量付费 serviceId 替换 | 生产配置中不得保留 `api_mock_service_id` | 上线前替换为服务市场实际返回的真实 `serviceId` |
118
+ | 证书模式 | 只有明确采用证书模式时才切换为同一生产应用的完整证书链并使用证书执行 API | 应用公钥证书、支付宝公钥证书、支付宝根证书必须同时配置;公钥模式不得调用证书执行 API |
119
+ | 产品开通状态 | 确认产品已在生产环境开通 | 沙箱无需开通,生产环境需要申请开通 |
120
+ | 私钥安全 | 私钥不在代码、日志、公共仓库中明文存储 | 使用安全的方式管理私钥 |
121
+ | 日志安全 | 日志中不打印敏感信息(私钥、用户信息等) | 防止信息泄露 |
122
+ | HTTPS 通信 | 生产环境必须使用 HTTPS | 保证传输安全 |
123
+
124
+ ---
125
+
126
+ <!-- ALIPAY_AIPAY_CHECKLIST_PRODUCTS:aipay,webpay,apppay -->
127
+ ## 八、代码开发结果输出
128
+
129
+ > 内部逐项判断使用以下状态:
130
+ > - ✅ 通过:已实现且符合规范
131
+ > - ❌ 不通过:未实现或不符合规范,需整改
132
+ > - ⚠️ 部分通过:部分实现,需补充完善
133
+ > - ➖ 不适用:当前集成场景不涉及此项
134
+ > - 🟡 人工待验证:当前环境无法自动取得充分证据
135
+
136
+ 先按当前证据逐项判断;本轮可修复时先修复、重检,不渲染中间结果。项目稳定后按 `../flow.md` 执行唯一 `checklist-result`,由 runner 在同一次最终调用中校验 scope 并对同一项目快照执行固定 implementation audit。审计失败是阻断缺口,不能被 Agent 自报结论覆盖;可修复时先返回结构化 findings,项目变化后重新加载 checklist 阶段取得新 scope。
137
+
138
+ 最终 action、完整参数和失败转移只以 `../flow.md` 步骤 8 为准,本模块不复制命令。runner 只接受最终一轮实际证据:Unix/macOS/Linux 的 `sandboxConfigState` 复用脚本终态,Windows 复用步骤 1/3 的 `READY|VERIFY_PENDING`;待配置时不重跑或读取配置,并把配置及依赖动作列为未完成。
139
+
140
+ 各产品结论差异如下,对客正文只在消息目录维护:
141
+
142
+ | 产品 | checklist 内部校验事实 | 实际待办来源 |
143
+ |---|---|---|
144
+ | `AIPAY` | 通过必须绑定本轮 AI 按量付费服务端联调成功;只有完整 402、沙箱收银、Payment-Proof 重试和资源交付证据才能称沙箱测试通过 | 失败/部分通过项、人工待验证项、联调恢复动作 |
145
+ | `WEBPAY` | 未提供浏览器付款入口和完整指引时不得通过;公网 HTTPS `notify_url` 未联调只能称本地验收完成,不能称生产就绪 | 失败/部分通过项、人工待验证项、待用户体验事项 |
146
+ | `APPPAY` | 当前没有沙箱付款验证结论,不据此生成通过或失败,也不临场补充沙箱付款步骤 | 失败/部分通过项、人工待验证项 |
147
+
148
+ `failedItems`、`manualItems` 必须包含确定下一步,无对应项时传“无”。`overallResult=通过` 要求两者为“无”、`sandboxConfigState=READY`、`blockingDefectState=NONE`;“部分通过”至少有一类缺口;“未通过”必须列出失败项。`blockingDefectState` 只表示沙箱配置及其直接依赖以外的代码、安全或必要依据缺口;单纯 UI、浏览器或公网联调待验证只进入 `manualItems`。`full_process` 仅在沙箱待配置且无其他阻断,或 `READY` 且只剩非阻塞人工项时继续 Onboarding。
149
+
150
+ 内部保留适用项依据;明细按实际证据输出,不维护第二份模板。同一次代码开发只渲染一次;renderer stdout 是默认收口的唯一对客正文,前后不得追加摘要、风险或待办。
151
+
152
+ ---
153
+
154
+ > 📖 本文档内容整理自支付宝开放平台官方文档,如有更新请以官方文档为准。
@@ -0,0 +1,199 @@
1
+ # 异步通知处理 - C# 示例
2
+
3
+ 适用产品:AI 网页应用收款、AI 移动应用收款。AI 按量付费不使用异步通知。
4
+
5
+ ```csharp
6
+ using System;
7
+ using System.Collections.Generic;
8
+ using System.Globalization;
9
+ using System.Linq;
10
+ using System.Text.RegularExpressions;
11
+ using System.Threading.Tasks;
12
+ using Microsoft.AspNetCore.Mvc;
13
+
14
+ [ApiController]
15
+ public class AlipayNotifyController : ControllerBase
16
+ {
17
+ private readonly IAlipayNotifyVerifier _alipayNotifyVerifier;
18
+ private readonly IPaymentConfigRepository _paymentConfigRepository;
19
+ private readonly IOrderRepository _orderRepository;
20
+ private readonly INotifyEventRepository _notifyEventRepository;
21
+ private readonly INotifyLogger _notifyLogger;
22
+
23
+ public AlipayNotifyController(
24
+ IAlipayNotifyVerifier alipayNotifyVerifier,
25
+ IPaymentConfigRepository paymentConfigRepository,
26
+ IOrderRepository orderRepository,
27
+ INotifyEventRepository notifyEventRepository,
28
+ INotifyLogger notifyLogger)
29
+ {
30
+ _alipayNotifyVerifier = alipayNotifyVerifier;
31
+ _paymentConfigRepository = paymentConfigRepository;
32
+ _orderRepository = orderRepository;
33
+ _notifyEventRepository = notifyEventRepository;
34
+ _notifyLogger = notifyLogger;
35
+ }
36
+
37
+ [HttpPost("/alipay/notify")]
38
+ public async Task<IActionResult> Notify()
39
+ {
40
+ var paramsMap = new Dictionary<string, string>();
41
+ try
42
+ {
43
+ paramsMap = Request.Form.ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
44
+ var config = await _paymentConfigRepository.FindByAppId(paramsMap.GetValueOrDefault("app_id"));
45
+ if (config == null)
46
+ {
47
+ return Fail("alipay notify app config not found", paramsMap);
48
+ }
49
+
50
+ if (!VerifyNotifyWithAlipaySdk(paramsMap, config))
51
+ {
52
+ return Fail("alipay notify signature check failed", paramsMap);
53
+ }
54
+
55
+ var order = await _orderRepository.FindByOutTradeNo(paramsMap.GetValueOrDefault("out_trade_no"));
56
+ if (order == null || !BusinessFieldsMatch(order, paramsMap, config))
57
+ {
58
+ return Fail("alipay notify business check failed", paramsMap);
59
+ }
60
+
61
+ var inserted = await _notifyEventRepository.InsertOnce(
62
+ paramsMap.GetValueOrDefault("notify_id"),
63
+ paramsMap.GetValueOrDefault("trade_no"),
64
+ paramsMap.GetValueOrDefault("out_trade_no"),
65
+ paramsMap.GetValueOrDefault("trade_status"),
66
+ paramsMap);
67
+ if (!inserted)
68
+ {
69
+ return Content("success", "text/plain");
70
+ }
71
+
72
+ if (IsPaidTradeNotification(paramsMap))
73
+ {
74
+ await _orderRepository.MarkPaidIdempotently(order.OutTradeNo, paramsMap.GetValueOrDefault("trade_no"), paramsMap);
75
+ }
76
+ else
77
+ {
78
+ await _orderRepository.RecordNonPaidTradeEvent(order.OutTradeNo, paramsMap.GetValueOrDefault("trade_status"), paramsMap);
79
+ }
80
+
81
+ return Content("success", "text/plain");
82
+ }
83
+ catch (Exception)
84
+ {
85
+ SafeLogNotifyError("alipay notify unexpected exception", paramsMap);
86
+ return Content("fail", "text/plain");
87
+ }
88
+ }
89
+
90
+ private bool BusinessFieldsMatch(MerchantOrder order, Dictionary<string, string> paramsMap, AlipayNotifyConfig config)
91
+ {
92
+ return paramsMap.GetValueOrDefault("app_id") == config.AppId
93
+ && paramsMap.GetValueOrDefault("out_trade_no") == order.OutTradeNo
94
+ && AmountEquals(order.TotalAmount, paramsMap.GetValueOrDefault("total_amount"))
95
+ && ExpectedSellerMatches(config, paramsMap.GetValueOrDefault("seller_id"), paramsMap.GetValueOrDefault("seller_email"));
96
+ }
97
+
98
+ private static bool AmountEquals(decimal expected, string? actual)
99
+ {
100
+ var expectedAmount = NormalizeAmountText(expected);
101
+ var actualAmount = NormalizeAmountText(actual);
102
+ return expectedAmount != null && expectedAmount == actualAmount;
103
+ }
104
+
105
+ private static string? NormalizeAmountText(decimal value)
106
+ {
107
+ if (decimal.Round(value, 2) != value) return null;
108
+ return value.ToString("0.00", CultureInfo.InvariantCulture);
109
+ }
110
+
111
+ private static string? NormalizeAmountText(string? value)
112
+ {
113
+ if (string.IsNullOrWhiteSpace(value)) return null;
114
+ var match = Regex.Match(value.Trim(), @"^(\d+)(?:\.(\d{1,2}))?$");
115
+ if (!match.Success) return null;
116
+ var yuan = match.Groups[1].Value.TrimStart('0');
117
+ if (yuan.Length == 0) yuan = "0";
118
+ return $"{yuan}.{match.Groups[2].Value.PadRight(2, '0')}";
119
+ }
120
+
121
+ private static bool IsPaidTradeNotification(Dictionary<string, string> paramsMap)
122
+ {
123
+ var paidStatus = paramsMap.GetValueOrDefault("trade_status") == "TRADE_SUCCESS"
124
+ || paramsMap.GetValueOrDefault("trade_status") == "TRADE_FINISHED";
125
+ return paidStatus
126
+ && string.IsNullOrEmpty(paramsMap.GetValueOrDefault("out_biz_no"))
127
+ && string.IsNullOrEmpty(paramsMap.GetValueOrDefault("gmt_refund"))
128
+ && string.IsNullOrEmpty(paramsMap.GetValueOrDefault("refund_fee"));
129
+ }
130
+
131
+ private static bool ExpectedSellerMatches(AlipayNotifyConfig config, string? sellerId, string? sellerEmail)
132
+ {
133
+ var expectedSellerId = config.SellerId;
134
+ var expectedSellerEmail = config.SellerEmail;
135
+ return (!string.IsNullOrEmpty(expectedSellerId) && sellerId == expectedSellerId)
136
+ || (!string.IsNullOrEmpty(expectedSellerEmail) && sellerEmail == expectedSellerEmail);
137
+ }
138
+
139
+ private bool VerifyNotifyWithAlipaySdk(Dictionary<string, string> paramsMap, AlipayNotifyConfig config)
140
+ {
141
+ // 使用当前项目已安装的支付宝 SDK 验签能力校验 paramsMap["sign"]。
142
+ // _alipayNotifyVerifier.Verify(paramsMap, config) 必须封装目标项目当前 SDK 实际通知验签 API;落地前先查已安装包文档或类型定义,不能手写临时 RSA 验签。
143
+ return _alipayNotifyVerifier.Verify(paramsMap, config);
144
+ }
145
+
146
+ private IActionResult Fail(string message, Dictionary<string, string> paramsMap)
147
+ {
148
+ SafeLogNotifyError(message, paramsMap);
149
+ return Content("fail", "text/plain");
150
+ }
151
+
152
+ private void SafeLogNotifyError(string message, Dictionary<string, string> paramsMap)
153
+ {
154
+ try
155
+ {
156
+ _notifyLogger.Warn(message, SanitizeNotifyParams(paramsMap));
157
+ }
158
+ catch (Exception)
159
+ {
160
+ // 日志失败不能影响支付宝收到纯文本 fail。
161
+ }
162
+ }
163
+
164
+ private static Dictionary<string, string> SanitizeNotifyParams(Dictionary<string, string> paramsMap)
165
+ {
166
+ var safeParams = new Dictionary<string, string>(paramsMap);
167
+ safeParams.Remove("sign");
168
+ return safeParams;
169
+ }
170
+ }
171
+
172
+ // 以下接口对接商家项目的真实 SDK 验签、支付配置、订单仓储、通知事件表和脱敏日志。
173
+ // 生产环境必须使用持久化订单表和通知事件表;内存 Dictionary 只能用于非生产 demo。
174
+ public interface IAlipayNotifyVerifier { bool Verify(Dictionary<string, string> paramsMap, AlipayNotifyConfig config); }
175
+ public interface IPaymentConfigRepository { Task<AlipayNotifyConfig?> FindByAppId(string? appId); }
176
+ public interface IOrderRepository
177
+ {
178
+ Task<MerchantOrder?> FindByOutTradeNo(string? outTradeNo);
179
+ Task MarkPaidIdempotently(string outTradeNo, string? tradeNo, Dictionary<string, string> paramsMap);
180
+ Task RecordNonPaidTradeEvent(string outTradeNo, string? tradeStatus, Dictionary<string, string> paramsMap);
181
+ }
182
+ public interface INotifyEventRepository
183
+ {
184
+ Task<bool> InsertOnce(string? notifyId, string? tradeNo, string? outTradeNo, string? tradeStatus, Dictionary<string, string> paramsMap);
185
+ }
186
+ public interface INotifyLogger { void Warn(string message, Dictionary<string, string> paramsMap); }
187
+ public record AlipayNotifyConfig(string AppId, string AlipayPublicKey, string? SellerId, string? SellerEmail);
188
+ public record MerchantOrder(string OutTradeNo, decimal TotalAmount);
189
+ ```
190
+
191
+ 要点:
192
+
193
+ - 支付宝通知是 POST 表单,验签前必须保留完整表单参数。
194
+ - 验签必须调用当前项目支付宝 SDK 的实际验签 API,验签前不得丢字段或改值。
195
+ - `app_id`、`seller_id` / `seller_email` 从运行时支付配置校验,不从订单对象默认取值。
196
+ - 金额校验使用 `decimal` 或项目定点数工具,不要用浮点数直接比较。
197
+ - 只有付款成功状态且不是退款、关单、分账等事件时,才认定付款成功。
198
+ - 内存订单或内存幂等只能用于非生产 demo,生产必须使用持久化订单表和通知事件表。
199
+ - 处理成功后返回纯文本 `success`;异常、验签失败或业务校验失败返回 `fail`。