@alipay/alipay-aipay 1.8.1 → 1.8.3
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/package.json +3 -3
- package/skill/VERSION +1 -1
- package/skill/references/integration/flow.md +42 -41
- package/skill/references/integration/modules/checklist.md +8 -10
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/skill/references/integration/modules/interface-contract/apppay-interface-contract.md +1 -1
- package/skill/references/integration/modules/interface-contract/notify-url-contract.md +5 -4
- package/skill/references/integration/modules/interface-contract/webpay-interface-contract.md +1 -1
- package/skill/references/integration/modules/interface-guide.md +1 -1
- package/skill/references/integration/modules/sandbox/sandbox-setup-guide.md +11 -9
- package/skill/references/normal/integration-worker.md +3 -3
- package/skill/references/normal/json/customer-messages.json +1 -1
- package/skill/references/normal/json/policy-rules.json +1 -1
- package/skill/references/normal/scripts/flow_loader.mjs +9 -0
- package/skill/references/normal/scripts/integration_message_runner.mjs +88 -16
- package/skill/references/normal/scripts/onboarding_message_runner.mjs +9 -5
- package/skill/references/normal/scripts/render_customer_message.mjs +150 -53
- package/skill/references/onboarding/flow.md +12 -6
- package/skill/references/runtime-entry.md +4 -2
- package/skill-pre/VERSION +1 -1
- package/skill-pre/references/integration/flow.md +43 -41
- package/skill-pre/references/integration/modules/checklist.md +10 -10
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/skill-pre/references/integration/modules/interface-contract/apppay-interface-contract.md +1 -1
- package/skill-pre/references/integration/modules/interface-contract/notify-url-contract.md +5 -4
- package/skill-pre/references/integration/modules/interface-contract/webpay-interface-contract.md +1 -1
- package/skill-pre/references/integration/modules/interface-guide.md +1 -1
- package/skill-pre/references/integration/modules/sandbox/sandbox-setup-guide.md +11 -9
- package/skill-pre/references/normal/integration-worker.md +3 -3
- package/skill-pre/references/normal/json/customer-messages.json +1 -1
- package/skill-pre/references/normal/json/policy-rules.json +1 -1
- package/skill-pre/references/normal/scripts/flow_loader.mjs +9 -0
- package/skill-pre/references/normal/scripts/integration_message_runner.mjs +88 -16
- package/skill-pre/references/normal/scripts/onboarding_message_runner.mjs +9 -5
- package/skill-pre/references/normal/scripts/render_customer_message.mjs +155 -53
- package/skill-pre/references/onboarding/flow.md +12 -6
- package/skill-pre/references/runtime-entry.md +4 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":1,"rules":[{"policyId":"P-MCP-CONTRACT-FROZEN","scope":"all","severity":"critical","text":"只用当前 Skill 已验证的 MCP 脚本契约;禁止推断、改名或尝试近似方法。","runtimeText":"仅用已验证脚本;禁推断或尝试近似方法。"},{"policyId":"P-AUTH-BEFORE-ONBOARDING","scope":"onboarding","severity":"critical","text":"登录、scope 和 MCC 校验通过前,禁止 onboarding 查询或写入。","runtimeText":"登录/scope/MCC 未通过前禁查询和写入。"},{"policyId":"P-SIGNING-WRITE","scope":"onboarding.signing","severity":"critical","text":"签约材料校验通过后直接提交,不增加确认;缺失或非法时禁止提交。"},{"policyId":"P-SERVICE-WRITE","scope":"onboarding.service","severity":"critical","text":"服务创建摘要送达后直接创建;服务修改须取得绑定当前脱敏摘要的明确确认。"},{"policyId":"P-APPLICATION-WRITE","scope":"onboarding.application","severity":"critical","text":"完整新建材料或非空候选后的明确“新建”已构成创建确认;快照未变时不追加确认,公钥确认后直接提审。"},{"policyId":"P-WRITE-UNKNOWN","scope":"onboarding.write","severity":"critical","text":"非幂等写入结果不明时只读核验;仍无法确认则进入 UNKNOWN,禁止重试写入。"},{"policyId":"P-PARTIAL-RESULT","scope":"onboarding","severity":"critical","text":"签约、服务、应用分支独立记录,失败不回滚或覆盖其他成功结果。"},{"policyId":"P-MATERIAL-CATEGORY","scope":"onboarding.materials","severity":"required","text":"每轮只展示一个材料类别;同类一次收齐,已提供材料不得重复索取。"},{"policyId":"P-KEY-NO-PRIVATE","scope":"onboarding.application","severity":"critical","text":"禁止请求、处理、保存或输出生产私钥,也不提供密钥生成命令。"},{"policyId":"P-KEY-PUBLIC-USER","scope":"onboarding.application","severity":"critical","text":"应用公钥只能由用户提供;缺少时禁止 createKeyConfirmPage,也不得补全、改写或添加 PEM 头尾。"},{"policyId":"P-AUTH-URL","scope":"onboarding.authorization","severity":"critical","text":"授权链接只由 auth.sh 构造并校验固定域名、路径和参数;禁用 verification_url。"},{"policyId":"P-KEY-URL","scope":"onboarding.application","severity":"critical","text":"公钥确认只展示 createKeyConfirmPage 返回并校验的裸 URL;禁止二维码链接和 alipays://。"},{"policyId":"P-AUTH-URL-AUTO-OPEN","scope":"onboarding.authorization","severity":"critical","text":"授权 URL 交付后调用受控 opener;失败时保留同一裸 URL 和复制兜底。"},{"policyId":"P-KEY-URL-AUTO-OPEN","scope":"onboarding.application","severity":"critical","text":"公钥 URL 交付后调用受控 opener;失败时保留同一裸 URL 和复制兜底。"},{"policyId":"P-KEY-TOOL-DOWNLOAD","scope":"onboarding.application","severity":"critical","text":"新建 WEBAPP 或 MOBILEAPP IOS|ANDROID|ALL 在首次等待或写入前必须展示 https://opendocs.alipay.com/isv/02kipk;HarmonyOS 除外。仅空候选且缺公钥时自动下载,非空候选只在用户要求时下载;只下载,不安装、启动或生成密钥。"},{"policyId":"P-A2M-SANDBOX","scope":"integration.aipay","severity":"critical","text":"AI 按量付费沙箱 serviceId 固定为 api_mock_service_id,SDK 使用沙箱网关;生产网关和真实 serviceId 只用于正式配置。","runtimeText":"A2M 沙箱固定 api_mock_service_id 与沙箱网关;生产值仅用于正式配置。"},{"policyId":"P-A2M-SUCCESS","scope":"integration.aipay","severity":"critical","text":"自动联调只有在 HTTP 200、非空可归属资源、无明确业务失败且适用 Payment-Validation 有效时通过。","runtimeText":"联调仅在 HTTP 200、资源可归属、无业务失败且适用回执有效时通过。"},{"policyId":"P-SANDBOX-CREDENTIAL-EXCEPTION","scope":"integration.sandbox","severity":"critical","text":"Unix/macOS/Linux 只允许快速沙箱实际返回且已校验的买家登录/支付密码在沙箱摘要和付款体验中经 stdin 临时渲染。Windows 手工沙箱允许用户在 sandbox.windows.manual_setup 后提供当前沙箱应用的 APPID、支付宝公钥、应用公钥、与当前开发语言匹配的应用私钥和沙箱账号信息;Agent
|
|
1
|
+
{"schemaVersion":1,"rules":[{"policyId":"P-MCP-CONTRACT-FROZEN","scope":"all","severity":"critical","text":"只用当前 Skill 已验证的 MCP 脚本契约;禁止推断、改名或尝试近似方法。","runtimeText":"仅用已验证脚本;禁推断或尝试近似方法。"},{"policyId":"P-AUTH-BEFORE-ONBOARDING","scope":"onboarding","severity":"critical","text":"登录、scope 和 MCC 校验通过前,禁止 onboarding 查询或写入。","runtimeText":"登录/scope/MCC 未通过前禁查询和写入。"},{"policyId":"P-SIGNING-WRITE","scope":"onboarding.signing","severity":"critical","text":"签约材料校验通过后直接提交,不增加确认;缺失或非法时禁止提交。"},{"policyId":"P-SERVICE-WRITE","scope":"onboarding.service","severity":"critical","text":"服务创建摘要送达后直接创建;服务修改须取得绑定当前脱敏摘要的明确确认。"},{"policyId":"P-APPLICATION-WRITE","scope":"onboarding.application","severity":"critical","text":"完整新建材料或非空候选后的明确“新建”已构成创建确认;快照未变时不追加确认,公钥确认后直接提审。"},{"policyId":"P-WRITE-UNKNOWN","scope":"onboarding.write","severity":"critical","text":"非幂等写入结果不明时只读核验;仍无法确认则进入 UNKNOWN,禁止重试写入。"},{"policyId":"P-PARTIAL-RESULT","scope":"onboarding","severity":"critical","text":"签约、服务、应用分支独立记录,失败不回滚或覆盖其他成功结果。"},{"policyId":"P-MATERIAL-CATEGORY","scope":"onboarding.materials","severity":"required","text":"每轮只展示一个材料类别;同类一次收齐,已提供材料不得重复索取。"},{"policyId":"P-KEY-NO-PRIVATE","scope":"onboarding.application","severity":"critical","text":"禁止请求、处理、保存或输出生产私钥,也不提供密钥生成命令。"},{"policyId":"P-KEY-PUBLIC-USER","scope":"onboarding.application","severity":"critical","text":"应用公钥只能由用户提供;缺少时禁止 createKeyConfirmPage,也不得补全、改写或添加 PEM 头尾。"},{"policyId":"P-AUTH-URL","scope":"onboarding.authorization","severity":"critical","text":"授权链接只由 auth.sh 构造并校验固定域名、路径和参数;禁用 verification_url。"},{"policyId":"P-KEY-URL","scope":"onboarding.application","severity":"critical","text":"公钥确认只展示 createKeyConfirmPage 返回并校验的裸 URL;禁止二维码链接和 alipays://。"},{"policyId":"P-AUTH-URL-AUTO-OPEN","scope":"onboarding.authorization","severity":"critical","text":"授权 URL 交付后调用受控 opener;失败时保留同一裸 URL 和复制兜底。"},{"policyId":"P-KEY-URL-AUTO-OPEN","scope":"onboarding.application","severity":"critical","text":"公钥 URL 交付后调用受控 opener;失败时保留同一裸 URL 和复制兜底。"},{"policyId":"P-KEY-TOOL-DOWNLOAD","scope":"onboarding.application","severity":"critical","text":"新建 WEBAPP 或 MOBILEAPP IOS|ANDROID|ALL 在首次等待或写入前必须展示 https://opendocs.alipay.com/isv/02kipk;HarmonyOS 除外。仅空候选且缺公钥时自动下载,非空候选只在用户要求时下载;只下载,不安装、启动或生成密钥。"},{"policyId":"P-A2M-SANDBOX","scope":"integration.aipay","severity":"critical","text":"AI 按量付费沙箱 serviceId 固定为 api_mock_service_id,SDK 使用沙箱网关;生产网关和真实 serviceId 只用于正式配置。","runtimeText":"A2M 沙箱固定 api_mock_service_id 与沙箱网关;生产值仅用于正式配置。"},{"policyId":"P-A2M-SUCCESS","scope":"integration.aipay","severity":"critical","text":"自动联调只有在 HTTP 200、非空可归属资源、无明确业务失败且适用 Payment-Validation 有效时通过。","runtimeText":"联调仅在 HTTP 200、资源可归属、无业务失败且适用回执有效时通过。"},{"policyId":"P-SANDBOX-CREDENTIAL-EXCEPTION","scope":"integration.sandbox","severity":"critical","text":"Unix/macOS/Linux 只允许快速沙箱实际返回且已校验的买家登录/支付密码在沙箱摘要和付款体验中经 stdin 临时渲染。Windows 手工沙箱允许用户在 sandbox.windows.manual_setup 后提供当前沙箱应用的 APPID、支付宝公钥、应用公钥、与当前开发语言匹配的应用私钥和沙箱账号信息;Agent 只能将其用于已确认目标项目的受保护配置,不得在普通日志或业务状态中复述或另存原文;除 AI 网页应用收款的登记沙箱摘要及最终三项买家配置外,不得在回复或摘要中复述原文。两类例外均不得扩展到生产环境账号、应用私钥或密码。 最终收口仅 AI 网页应用收款可从当前会话已校验且已交付的登记摘要展示沙箱买家测试账号、登录密码和支付密码三项,密码明文展示(含 Windows);按量付费及 APP 支付不展示最终沙箱配置区块;不得扩展到其他凭据、原始配置或生产信息。","runtimeText":"仅登记且已校验的快速沙箱或 Windows 手工沙箱资料可按 flow 用于受保护项目;快速沙箱买家密码按既有登记消息展示,Windows 仅网页收款登记摘要可明文展示;最终收口仅网页收款三项买家信息;其他原文禁复述、另存或扩展到生产值。"},{"policyId":"P-SENSITIVE-STATE","scope":"all","severity":"critical","text":"用户公钥仅用于当前 app key/verify-key/verify-key-and-audit 调用,禁止在回复、摘要、普通日志、跨会话状态或宽权限临时文件中复述或保存;除登记的沙箱例外外,私钥、token、Payment-Proof、签名串、完整支付表单和未脱敏响应同样不得进入这些位置;临时 URL 仅可由登记动作校验后在该动作正文展示。","runtimeText":"公钥限当前 app 调用且禁复述/保存;敏感值禁入回复/摘要/日志/状态/宽权限文件;临时 URL 仅由登记动作展示。"},{"policyId":"P-PROJECT-PROTECT","scope":"integration","severity":"critical","text":"不得清空、覆盖或删除用户既有项目;只在已确认目标路径和范围内做必要修改。","runtimeText":"禁清空/覆盖/删除既有项目;仅改已确认路径和范围。"},{"policyId":"P-CUSTOMER-OUTPUT-EXCLUSIVE","scope":"all","severity":"critical","text":"登记 runner/script 的 stdout 是唯一对客正文,禁止增删改写;阻塞消息送达后立即等待用户。","runtimeText":"登记 stdout 原文是唯一正文;阻塞送达后立即等待。"},{"policyId":"P-ONBOARDING-CONTROL-CHANNEL","scope":"onboarding","severity":"critical","text":"剔除 ALIPAY_AIPAY_INTERNAL 内部行后再处理 stdout;内部行禁止对客,正文为空时直接转移。","runtimeText":"内部行禁对客;正文为空按唯一终态转移。"},{"policyId":"P-AUTH-PAGE-DELIVERY-BARRIER","scope":"onboarding.authorization","severity":"critical","text":"`auth.page` stdout 必须先独立送达,再 opener 和有限轮询;不得合并 pending 或收口,也不等待确认。"},{"policyId":"P-KEY-PAGE-DELIVERY-BARRIER","scope":"onboarding.application","severity":"critical","text":"`application.key.page` stdout 必须先独立送达,再 opener 和有限轮询;不得合并 pending 或收口,也不等待确认。"},{"policyId":"P-DETERMINISTIC-HANDOFF","scope":"all","severity":"critical","text":"只执行 flow 动作处登记命令并按唯一终态立即转移;禁止追加命令重复解析,失败只走登记恢复。","runtimeText":"仅执行登记命令并按唯一终态转移;禁追加解析;失败只走登记恢复。"}],"officialUrls":{"aipaySite":"https://aipay.alipay.com/","aipaySkillSite":"https://aipay.alipay.com/?from=alipay-aipay-skill","merchantPlatform":"https://b.alipay.com/page/portal/home","openPlatform":"https://open.alipay.com/","openPlatformHome":"https://open.alipay.com","technicalSupport":"https://opensupport.alipay.com/support/intelligent-services?form=payskill","keyToolManual":"https://opendocs.alipay.com/isv/02kipk","keyToolMac":"https://ideservice.alipay.com/ide/getPluginUrl.htm?clientType=assistant&platform=mac&channelType=WEB","keyToolWindows":"https://ideservice.alipay.com/ide/getPluginUrl.htm?clientType=assistant&platform=win&channelType=WEB","androidSandboxAlipay":"https://mdn.alipayobjects.com/sandboxsys/afts/img/_itHRrdOD9oAAAAAAAAAAAAADgSLAQBr/original","sandboxToolFallback":"https://open.alipay.com/develop/sandbox/tool","sandboxGateway":"https://openapi-sandbox.dl.alipaydev.com/gateway.do","productionGateway":"https://openapi.alipay.com/gateway.do","openPlatformPaySkill":"https://open.alipay.com?form=payskill","traditionalPaymentSkill":"https://github.com/alipay/ai/tree/main/skills","dingtalkSupportGroupQr":"https://mdn.alipayobjects.com/huamei_5fajmi/afts/img/MlFpRKmYyLIAAAAARJAAAAgADlahAQFr/original","wecomCommunityGroupQr":"https://mdn.alipayobjects.com/huamei_5fajmi/afts/img/Cz0TTZVO7_4AAAAAQDAAAAgADlahAQFr/fmt.webp"},"authorizationProducts":[{"productName":"AI 按量付费","salesCode":"I1080300001000160457","scope":"app:all,machine_pay:write,agmnt:write"},{"productName":"AI 网页应用收款","salesCode":"I1080300001000041203","scope":"app:all,fast_instant_trade_pay:write"},{"productName":"AI 移动应用收款","salesCode":"I1080300001000041313","scope":"app:all,auth_alipay_apppay:write"}],"temporaryUrlAllowlist":{"authorization":{"host":"aipay.alipay.com","path":"/cli-auth","requiredQueryParams":["deviceCode","productCode","mccCode"],"optionalQueryParams":["platform"]},"publicKeyConfirmation":[{"host":"aipay.alipay.com","path":"/public-key-confirm","requiredQueryParams":["keyConfirmToken"],"optionalQueryParams":[]},{"host":"open.alipay.com","path":"/ai/ai-resource-center/public-key-confirm","requiredQueryParams":["keyConfirmToken"],"optionalQueryParams":[]}],"windowsOnboardingRedirect":{"host":"aipay.alipay.com","path":"/","requiredQueryParams":["from","reason","platform_id"],"optionalQueryParams":[]}}}
|
|
@@ -160,6 +160,15 @@ export function loadFlowBundle(flow, phase, { productType } = {}) {
|
|
|
160
160
|
if (flow === 'integration' && phase === 'checklist') {
|
|
161
161
|
if (!productType) throw new Error('integration checklist phase 缺少 product-type');
|
|
162
162
|
blocks.push(`## 当前产品适用校验清单\n\n${checklistForProduct(productType).markdown}`);
|
|
163
|
+
if (productType === 'webpay') {
|
|
164
|
+
const catalog = JSON.parse(fs.readFileSync(path.join(scriptDir, '../json/customer-messages.json'), 'utf8'));
|
|
165
|
+
const field = catalog.messages.find((message) => message.messageId === 'integration.checklist.result')
|
|
166
|
+
?.variables.find((variable) => variable.name === 'webpayExperienceResult');
|
|
167
|
+
if (!Array.isArray(field?.values) || field.values.length !== 3 || field.values.some((value) => typeof value !== 'string')) {
|
|
168
|
+
throw new Error('网页收款 checklist 缺少合法体验结果枚举');
|
|
169
|
+
}
|
|
170
|
+
blocks.push(`## WEBPAY 结果参数精确枚举\n\nwebpayExperienceResult 仅接受以下原值,按步骤 6 实际证据选择,不改写:\n${field.values.map((value) => `- ${JSON.stringify(value)}`).join('\n')}`);
|
|
171
|
+
}
|
|
163
172
|
}
|
|
164
173
|
return [
|
|
165
174
|
`<!-- ALIPAY_AIPAY_REFERENCE_BUNDLE flow=${flow} phase=${phase}; 仅供内部执行,禁止对客 -->`,
|
|
@@ -750,9 +750,83 @@ export function inspectImplementation(variant, projectPath, snapshot = null) {
|
|
|
750
750
|
|
|
751
751
|
findings.sort((left, right) => `${left.code}:${left.path || ''}:${left.line || 0}`
|
|
752
752
|
.localeCompare(`${right.code}:${right.path || ''}:${right.line || 0}`));
|
|
753
|
+
for (const finding of findings) {
|
|
754
|
+
finding.id = crypto.createHash('sha256')
|
|
755
|
+
.update(JSON.stringify([variant, currentSnapshot.fingerprint, finding]))
|
|
756
|
+
.digest('hex');
|
|
757
|
+
}
|
|
753
758
|
return { projectPath: canonicalPath, variant, findings };
|
|
754
759
|
}
|
|
755
760
|
|
|
761
|
+
function reviewedAuditFindings(audit, rawReview, snapshot) {
|
|
762
|
+
if (rawReview === undefined) return audit.findings.map((finding) => ({ ...finding, reviewDecision: 'UNREVIEWED' }));
|
|
763
|
+
if (typeof rawReview !== 'string' || rawReview.length > 65536) throw new Error('audit-review 必须是受控 JSON 数组');
|
|
764
|
+
let reviews;
|
|
765
|
+
try { reviews = JSON.parse(rawReview); } catch { throw new Error('audit-review 不是合法 JSON'); }
|
|
766
|
+
if (!Array.isArray(reviews)) throw new Error('audit-review 必须是 JSON 数组');
|
|
767
|
+
const findings = new Map(audit.findings.map((finding) => [finding.id, finding]));
|
|
768
|
+
const reviewedIds = new Set();
|
|
769
|
+
const decisions = new Map();
|
|
770
|
+
const excluded = new Set();
|
|
771
|
+
const sources = new Set(implementationSourceFiles(snapshot.canonicalPath, snapshot.sources, snapshot.contents));
|
|
772
|
+
for (const review of reviews) {
|
|
773
|
+
if (!review || typeof review !== 'object' || Array.isArray(review)
|
|
774
|
+
|| Object.keys(review).some((key) => !['findingId', 'decision', 'evidence'].includes(key))
|
|
775
|
+
|| !findings.has(review.findingId) || reviewedIds.has(review.findingId)) {
|
|
776
|
+
throw new Error('audit-review 必须逐项绑定当前产品和项目快照的唯一 findingId');
|
|
777
|
+
}
|
|
778
|
+
reviewedIds.add(review.findingId);
|
|
779
|
+
if (!['FALSE_POSITIVE', 'CONFIRMED', 'UNRESOLVED'].includes(review.decision)) {
|
|
780
|
+
throw new Error('audit-review decision 仅支持 FALSE_POSITIVE、CONFIRMED、UNRESOLVED');
|
|
781
|
+
}
|
|
782
|
+
decisions.set(review.findingId, review.decision);
|
|
783
|
+
if (!Array.isArray(review.evidence) || review.evidence.length === 0 || review.evidence.length > 20) {
|
|
784
|
+
throw new Error('audit-review 每项必须提供当前生产代码的定位和核对依据');
|
|
785
|
+
}
|
|
786
|
+
for (const evidence of review.evidence) {
|
|
787
|
+
if (!evidence || typeof evidence !== 'object' || Array.isArray(evidence)
|
|
788
|
+
|| Object.keys(evidence).some((key) => !['path', 'line', 'reason'].includes(key))
|
|
789
|
+
|| typeof evidence.path !== 'string' || !evidence.path || evidence.path.includes('\\')
|
|
790
|
+
|| path.posix.isAbsolute(evidence.path) || path.win32.isAbsolute(evidence.path)
|
|
791
|
+
|| evidence.path.split('/').some((part) => !part || part === '.' || part === '..')
|
|
792
|
+
|| !Number.isInteger(evidence.line) || evidence.line < 1
|
|
793
|
+
|| typeof evidence.reason !== 'string' || evidence.reason.trim().length < 12 || evidence.reason.length > 2000
|
|
794
|
+
|| /[\u0000-\u001f\u007f]/u.test(evidence.reason)) {
|
|
795
|
+
throw new Error('audit-review evidence 必须包含规范化相对 path、有效 line 和具体单行 reason');
|
|
796
|
+
}
|
|
797
|
+
const file = path.resolve(snapshot.canonicalPath, evidence.path);
|
|
798
|
+
const source = snapshot.contents.get(file)?.toString('utf8');
|
|
799
|
+
const lines = source === undefined ? [] : stripSourceComments(source, path.extname(file).toLowerCase()).split(/\r?\n/u);
|
|
800
|
+
if (!sources.has(file) || !lines?.[evidence.line - 1]?.trim()) {
|
|
801
|
+
throw new Error('audit-review evidence 必须指向当前快照内非空的生产源码行');
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
// The runner validates the snapshot and locations; the Agent owns the code reasoning.
|
|
805
|
+
// Unreviewed, confirmed and unresolved findings remain blocking.
|
|
806
|
+
if (review.decision === 'FALSE_POSITIVE') excluded.add(review.findingId);
|
|
807
|
+
}
|
|
808
|
+
return audit.findings.filter((finding) => !excluded.has(finding.id))
|
|
809
|
+
.map((finding) => ({ ...finding, reviewDecision: decisions.get(finding.id) || 'UNREVIEWED' }));
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function mergeAuditItems(failedItems, findings) {
|
|
813
|
+
let items = failedItems && failedItems !== '无' ? [failedItems] : [];
|
|
814
|
+
if (items[0]?.trim().startsWith('[')) {
|
|
815
|
+
try {
|
|
816
|
+
const parsed = JSON.parse(items[0]);
|
|
817
|
+
if (Array.isArray(parsed) && parsed.every((item) => typeof item === 'string')) items = parsed;
|
|
818
|
+
} catch { /* Preserve plain text. */ }
|
|
819
|
+
}
|
|
820
|
+
items = items.flatMap((item) => item.split(';')).map((item) => item.trim()).filter(Boolean);
|
|
821
|
+
for (const finding of findings) {
|
|
822
|
+
const location = finding.path ? `(${finding.path}${finding.line ? `:${finding.line}` : ''})` : '';
|
|
823
|
+
const prefix = finding.reviewDecision === 'CONFIRMED' ? '' : '静态扫描疑点尚未排除:';
|
|
824
|
+
const item = `${prefix}${finding.message}${location}`;
|
|
825
|
+
if (!items.includes(item)) items.push(item);
|
|
826
|
+
}
|
|
827
|
+
return [...new Set(items)].join(';');
|
|
828
|
+
}
|
|
829
|
+
|
|
756
830
|
function scanProjectRoute(productType, projectPath, projectSelection) {
|
|
757
831
|
if (!['aipay', 'webpay', 'apppay'].includes(productType)) {
|
|
758
832
|
throw new Error('ROUTE_INSPECT_ERROR:productType 非法');
|
|
@@ -1255,18 +1329,20 @@ function runServiceNotReady(args) {
|
|
|
1255
1329
|
|
|
1256
1330
|
function runSafetyCloseout(args) {
|
|
1257
1331
|
const variant = required(args, 'variant');
|
|
1258
|
-
if (
|
|
1259
|
-
return requireCustomerMessageDelivery('integration.safety.closeout', renderMessage('integration.safety.closeout', variant, {
|
|
1260
|
-
acceptanceSummary: required(args, 'acceptance-summary')
|
|
1261
|
-
}));
|
|
1332
|
+
if (variant !== 'APPPAY') throw new Error('步骤 7 提醒仅适用于 APPPAY;AIPAY、WEBPAY 直接进入最终 checklist,上线提醒由最终 summary 输出');
|
|
1333
|
+
return requireCustomerMessageDelivery('integration.safety.closeout', renderMessage('integration.safety.closeout', variant, {}));
|
|
1262
1334
|
}
|
|
1263
1335
|
|
|
1264
1336
|
function runWindowsSandboxSummary(args) {
|
|
1337
|
+
const productName = required(args, 'product-name');
|
|
1265
1338
|
const environmentRows = required(args, 'environment-rows');
|
|
1266
1339
|
const passwordSaveHint = '已写入本地配置文件,默认不在对话中展开';
|
|
1267
1340
|
const lines = environmentRows.split('\n');
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1341
|
+
if (productName === 'AI 网页应用收款' && lines.slice(2).some((line) => line.includes(passwordSaveHint))) {
|
|
1342
|
+
throw new Error('网页收款沙箱摘要必须展示已校验的买家密码明文');
|
|
1343
|
+
}
|
|
1344
|
+
if (productName !== 'AI 网页应用收款' && (lines[2] !== `| 沙箱买家登录密码 | ${passwordSaveHint} |`
|
|
1345
|
+
|| lines[3] !== `| 沙箱买家支付密码 | ${passwordSaveHint} |`)) {
|
|
1270
1346
|
throw new Error('Windows 沙箱摘要的两个密码行必须只使用固定本地保存提示');
|
|
1271
1347
|
}
|
|
1272
1348
|
const summary = renderMessage('sandbox.environment.summary', 'DEFAULT', {
|
|
@@ -1290,6 +1366,7 @@ function checklistInputFromArgs(args) {
|
|
|
1290
1366
|
'overall-result': 'overallResult',
|
|
1291
1367
|
'failed-items': 'failedItems',
|
|
1292
1368
|
'manual-items': 'manualItems',
|
|
1369
|
+
'sandbox-summary': 'sandboxSummary',
|
|
1293
1370
|
'sandbox-result': 'sandboxResult',
|
|
1294
1371
|
'webpay-experience-result': 'webpayExperienceResult',
|
|
1295
1372
|
'sandbox-config-state': 'sandboxConfigState',
|
|
@@ -1343,26 +1420,21 @@ async function runChecklistResult(args) {
|
|
|
1343
1420
|
}
|
|
1344
1421
|
const input = checklistInputFromArgs(args) || await readStdin();
|
|
1345
1422
|
const audit = inspectImplementation(variant, projectPath, snapshot);
|
|
1346
|
-
|
|
1423
|
+
const findings = reviewedAuditFindings(audit, args['audit-review'], snapshot);
|
|
1424
|
+
if (findings.length === 0 && finalizesAuditFindings) {
|
|
1347
1425
|
throw new Error('finalize-audit-findings 仅用于收口实际 audit findings');
|
|
1348
1426
|
}
|
|
1349
|
-
if (
|
|
1427
|
+
if (findings.length > 0) {
|
|
1350
1428
|
if (args['finalize-audit-findings'] !== 'true') {
|
|
1351
1429
|
process.stderr.write(`${internalPrefix}CHECKLIST_AUDIT=${JSON.stringify({
|
|
1352
1430
|
state: 'REPAIR_REQUIRED',
|
|
1353
|
-
findings
|
|
1431
|
+
findings
|
|
1354
1432
|
})}\n`);
|
|
1355
1433
|
return null;
|
|
1356
1434
|
}
|
|
1357
|
-
const auditItems = audit.findings.map((finding) => {
|
|
1358
|
-
const location = finding.path ? `(${finding.path}${finding.line ? `:${finding.line}` : ''})` : '';
|
|
1359
|
-
return `${finding.message}${location}`;
|
|
1360
|
-
}).join(';');
|
|
1361
1435
|
input.overallResult = '未通过';
|
|
1362
1436
|
input.blockingDefectState = 'PRESENT';
|
|
1363
|
-
input.failedItems = input.failedItems
|
|
1364
|
-
? `${input.failedItems};${auditItems}`
|
|
1365
|
-
: auditItems;
|
|
1437
|
+
input.failedItems = mergeAuditItems(input.failedItems, findings);
|
|
1366
1438
|
if (input.executionMode === '完整接入') {
|
|
1367
1439
|
input.nextFlowReminder = '代码开发校验通过后将自动进入支付产品-产品开通;未通过时停留在代码开发。';
|
|
1368
1440
|
}
|
|
@@ -1065,14 +1065,17 @@ async function runMaterialCollect(args) {
|
|
|
1065
1065
|
}, { productType });
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
-
function renderApplicationCandidates(applicationType, applicationCandidates, selectionState = 'initial') {
|
|
1068
|
+
function renderApplicationCandidates(applicationType, applicationCandidates, selectionState = 'initial', includeIntro = selectionState === 'initial') {
|
|
1069
1069
|
const onlineCount = applicationCandidates.filter((candidate) => candidate?.status === 'ON_LINE').length;
|
|
1070
1070
|
const variant = applicationCandidates.length === 0
|
|
1071
1071
|
? 'EMPTY'
|
|
1072
1072
|
: onlineCount === 0
|
|
1073
1073
|
? 'PENDING_ONLY'
|
|
1074
1074
|
: 'ONLINE_WITH_OPTIONAL_PENDING';
|
|
1075
|
-
const
|
|
1075
|
+
const candidateFacts = renderMessage('application.candidates.facts', variant, { applicationType, applicationCandidates });
|
|
1076
|
+
const facts = includeIntro
|
|
1077
|
+
? `${renderMessage('application.configuration.intro', 'DEFAULT', {})}\n\n${candidateFacts}`
|
|
1078
|
+
: candidateFacts;
|
|
1076
1079
|
if (applicationCandidates.length === 0) return facts;
|
|
1077
1080
|
const selectionVariant = `${onlineCount === 0 ? 'PENDING' : 'ONLINE'}_${applicationType === 'WEBAPP' ? 'WEB' : 'MOBILE'}_OR_NEW`;
|
|
1078
1081
|
const selection = renderMessage('application.candidate.select', selectionVariant, {});
|
|
@@ -1168,8 +1171,8 @@ async function runApplicationRecheck(args) {
|
|
|
1168
1171
|
const previousCanonical = canonicalCandidates(input.previousCandidates);
|
|
1169
1172
|
const currentCanonical = canonicalCandidates(input.currentCandidates);
|
|
1170
1173
|
// Rendering both snapshots applies the same content and sensitivity checks even on the silent path.
|
|
1171
|
-
renderApplicationCandidates(applicationType, input.previousCandidates);
|
|
1172
|
-
const output = renderApplicationCandidates(applicationType, input.currentCandidates);
|
|
1174
|
+
renderApplicationCandidates(applicationType, input.previousCandidates, 'initial', false);
|
|
1175
|
+
const output = renderApplicationCandidates(applicationType, input.currentCandidates, 'initial', false);
|
|
1173
1176
|
if (previousCanonical === currentCanonical) {
|
|
1174
1177
|
process.stderr.write(`${internalPrefix}APPLICATION_RECHECK_RESULT=UNCHANGED\n`);
|
|
1175
1178
|
return '';
|
|
@@ -1237,10 +1240,11 @@ function closeoutInputFromArgs(args) {
|
|
|
1237
1240
|
};
|
|
1238
1241
|
const optionalMapping = {
|
|
1239
1242
|
'sandbox-config-state': 'sandboxConfigState',
|
|
1243
|
+
'sandbox-summary': 'sandboxSummary',
|
|
1240
1244
|
'manual-verification-items': 'manualVerificationItems'
|
|
1241
1245
|
};
|
|
1242
1246
|
for (const [argName, fieldName] of Object.entries(optionalMapping)) {
|
|
1243
|
-
if (Object.prototype.hasOwnProperty.call(args, argName)) input[fieldName] = required(args, argName);
|
|
1247
|
+
if (Object.prototype.hasOwnProperty.call(args, argName)) input[fieldName] = argName === 'sandbox-summary' ? optional(args, argName) : required(args, argName);
|
|
1244
1248
|
}
|
|
1245
1249
|
return input;
|
|
1246
1250
|
}
|
|
@@ -18,7 +18,7 @@ const placeholderPattern = /\{\{([A-Za-z][A-Za-z0-9_]*)\}\}/g;
|
|
|
18
18
|
const allowedVariableTypes = new Set([
|
|
19
19
|
'applicationCandidates',
|
|
20
20
|
'command', 'customerErrorDetails', 'customerErrorText', 'enum', 'enumList', 'existingPath', 'mccCode', 'officialTemporaryUrl', 'path',
|
|
21
|
-
'runtimeUrl', 'sandboxCredential', 'sandboxTableRows', 'serviceCandidates', 'mccCandidates', 'singleLine', 'string'
|
|
21
|
+
'runtimeUrl', 'sandboxCredential', 'sandboxTableRows', 'sandboxSummary', 'serviceCandidates', 'mccCandidates', 'singleLine', 'string'
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
24
|
function allowedVariableNames(definitions) {
|
|
@@ -547,6 +547,9 @@ function validateValue(definition, value, policy) {
|
|
|
547
547
|
case 'sandboxCredential':
|
|
548
548
|
if (value.length > 256 || /[\r\n]/.test(value)) throw new Error(`变量 ${definition.name} 不是合法沙箱凭据`);
|
|
549
549
|
break;
|
|
550
|
+
case 'sandboxSummary':
|
|
551
|
+
if (value.length > 12000 || /[\u0000-\u0009\u000b-\u001f\u007f]/u.test(value)) throw new Error('沙箱摘要包含非法内容');
|
|
552
|
+
break;
|
|
550
553
|
case 'sandboxTableRows':
|
|
551
554
|
if (value.length > 4096 || !value.split('\n').every((line) => /^\| [^|]+ \| [^|]+ \|$/.test(line))) {
|
|
552
555
|
throw new Error(`变量 ${definition.name} 不是合法沙箱摘要行`);
|
|
@@ -806,70 +809,137 @@ function validateWriteConfirmation(variantName, variables, normalized) {
|
|
|
806
809
|
validateServicePayload(variables, normalized);
|
|
807
810
|
}
|
|
808
811
|
|
|
812
|
+
// Replay only the registered sandbox summary already delivered in this conversation.
|
|
813
|
+
// No config contents, keys, commands or free-form Markdown are accepted here.
|
|
814
|
+
function sandboxReplayBlock(original, productName, sandboxConfigState) {
|
|
815
|
+
// This optional display must never prevent delivery of the actual branch results.
|
|
816
|
+
try {
|
|
817
|
+
if (typeof original !== 'string' || !original || productName !== 'AI 网页应用收款') return '';
|
|
818
|
+
if (sandboxConfigState !== 'READY') return '';
|
|
819
|
+
const lines = original.trim().split('\n');
|
|
820
|
+
const configPath = lines[5]?.match(/^\| 配置文件目录 \| `([^`]+)` \|$/u)?.[1];
|
|
821
|
+
const environmentRows = lines.slice(6, 10).join('\n');
|
|
822
|
+
if (!configPath) throw new Error('必须复用本轮登记的完整沙箱摘要');
|
|
823
|
+
const input = { productName, configPath, environmentRows };
|
|
824
|
+
const summary = renderMessage('sandbox.environment.summary', 'DEFAULT', input);
|
|
825
|
+
const normalize = (text) => text.trim().replace(/\n{2,}/gu, '\n\n');
|
|
826
|
+
const valid = ['DEFAULT', 'WINDOWS'].some((variant) => {
|
|
827
|
+
const reminder = renderMessage('sandbox.environment.reminder', variant, {});
|
|
828
|
+
return normalize(original) === normalize(`${summary}\n\n${reminder}`)
|
|
829
|
+
|| normalize(original) === normalize(`${summary}\n${reminder}`);
|
|
830
|
+
});
|
|
831
|
+
if (!valid) throw new Error('沙箱摘要不匹配登记正文,禁止改写或追加内容');
|
|
832
|
+
return renderMessage('sandbox.environment.summary', 'FINAL', input);
|
|
833
|
+
} catch {
|
|
834
|
+
return '';
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function productionConfigSourceLines() {
|
|
839
|
+
return [
|
|
840
|
+
'1、确认上线条件:',
|
|
841
|
+
'- 前往支付宝 AI 付站点(https://aipay.alipay.com/?from=alipay-aipay-skill)→ 一站式接入,查看产品开通情况。',
|
|
842
|
+
'- 如本次流程新建支付宝应用,进入一站式接入 → 选择产品 → 密钥配置,确认新建的支付宝应用“已上线”;使用其他生产应用时,也需确认该应用已上线。',
|
|
843
|
+
'',
|
|
844
|
+
'2、生产环境的支付宝应用配置替换到项目中:',
|
|
845
|
+
'- 修改项目中的支付宝网关为 https://openapi.alipay.com/gateway.do。',
|
|
846
|
+
'- 把生产环境支付宝应用配置写入业务系统生产环境变量、受保护配置文件或密钥管理系统。',
|
|
847
|
+
'- appId、支付宝公钥、应用私钥需要配到项目中。应用私钥不要泄露、不要暴露给 Agent;应用公钥用于在支付宝侧配置,无需额外配到项目中。',
|
|
848
|
+
'- 确保应用公钥与应用私钥配对,appId、该应用公钥及对应的支付宝公钥与目标生产应用配置一致,不混用沙箱或其他应用的配置。',
|
|
849
|
+
];
|
|
850
|
+
}
|
|
851
|
+
|
|
809
852
|
function productionSpecificLines(productName) {
|
|
810
853
|
if (productName === 'AI 按量付费') {
|
|
811
854
|
return [
|
|
812
|
-
'
|
|
813
|
-
'
|
|
814
|
-
'
|
|
815
|
-
];
|
|
816
|
-
}
|
|
817
|
-
if (productName === 'AI 移动应用收款') {
|
|
818
|
-
return [
|
|
819
|
-
'4. AI 移动应用收款 `notify_url`:生成生产 `orderStr` 时传入商家已部署的公网 HTTPS `notify_url`;未传不会触发异步通知。',
|
|
820
|
-
'5. AI 移动应用收款验收:`notify_url` 完成验签,校验 `app_id`/`out_trade_no`/`total_amount`/`trade_status`,幂等处理并返回 `success`;支付结果以异步通知或 `alipay.trade.query` 补偿查询为准。客户端 SDK 使用服务端 `orderStr` 调起支付,客户端结果只做展示,不能作为付款成功依据。'
|
|
855
|
+
'', '3、AI 按量付费服务配置:',
|
|
856
|
+
'- 把沙箱 `api_mock_service_id` 替换为真实 `serviceId`。获取方式:登录支付宝 AI 付站点(https://aipay.alipay.com/?from=alipay-aipay-skill)→ 控制台 → 服务管理 → 复制审核已通过的目标服务 ID。',
|
|
857
|
+
'- `seller_id`/`seller_unique_id` 填收款商户 PID/2088,`seller_app_id` 填生产 `appId`。PID 获取方式:登录支付宝商家平台(https://b.alipay.com/page/portal/home)→ 点击头像 → 复制账户名下方 2088 开头的商家 ID。',
|
|
821
858
|
];
|
|
822
859
|
}
|
|
860
|
+
const mobile = productName === 'AI 移动应用收款';
|
|
823
861
|
return [
|
|
824
|
-
'
|
|
825
|
-
'
|
|
826
|
-
'
|
|
862
|
+
'', `3、${productName}支付结果确认:`,
|
|
863
|
+
'- `notify_url` 用于商家服务端接收支付宝发来的支付结果异步通知,不是前端页面或支付宝生成的 URL。',
|
|
864
|
+
'- 如使用异步通知,下单时传入 `notify_url`;它应是商家已部署、支付宝服务端可通过公网访问的 HTTPS 通知接口地址。',
|
|
865
|
+
'- 如不配置 `notify_url`,不会触发该地址的异步通知;商家服务端需通过 `alipay.trade.query` 主动查单。',
|
|
866
|
+
mobile
|
|
867
|
+
? '- 支付结果以验签通过的异步通知或服务端交易查询结果为准;APP 客户端支付结果不能单独作为付款成功依据。'
|
|
868
|
+
: '- 支付结果以验签通过的异步通知或 `alipay.trade.query` 查询结果为准;同步回跳或前台页面不能作为付款成功依据。',
|
|
869
|
+
'- 项目正式对客前,请完成生产环境支付验证。',
|
|
870
|
+
...(mobile ? [] : ['', '4、AI 网页应用收款限制:', '- 如需解除 AI 网页应用收款的收款限制,请前往支付宝商家平台 → 签约管理补全收款信息:https://b.alipay.com/page/ar-center/ar-manage'])
|
|
827
871
|
];
|
|
828
872
|
}
|
|
829
873
|
|
|
830
|
-
function
|
|
831
|
-
return [
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
874
|
+
function summaryItems(value) {
|
|
875
|
+
if (!value || value === '无') return [];
|
|
876
|
+
let items = [value];
|
|
877
|
+
if (value.trim().startsWith('[')) {
|
|
878
|
+
try {
|
|
879
|
+
const parsed = JSON.parse(value);
|
|
880
|
+
if (Array.isArray(parsed) && parsed.every((item) => typeof item === 'string')) items = parsed;
|
|
881
|
+
} catch { /* Preserve validated plain text. */ }
|
|
882
|
+
}
|
|
883
|
+
return [...new Set(items.flatMap((item) => item.split(';')).map((item) => item.trim()).filter(Boolean))];
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Completion inputs contain actual gaps only; reminders are delivered elsewhere.
|
|
887
|
+
function validateChecklistItemClassification(variables, fields) {
|
|
888
|
+
const notifyReminders = new Set([
|
|
889
|
+
'公网 HTTPS notify_url 联调人工待验证:上线前配置公网通知地址并完成验签联调',
|
|
890
|
+
'公网 HTTPS notify_url 联调待部署后验证'
|
|
891
|
+
]);
|
|
892
|
+
for (const field of fields) {
|
|
893
|
+
const value = variables[field];
|
|
894
|
+
const items = summaryItems(value);
|
|
895
|
+
if (value && value !== '无' && items.length === 0) {
|
|
896
|
+
throw new Error(`${field} 没有实际缺口时必须传“无”,不能使用空列表影响完成判定`);
|
|
897
|
+
}
|
|
898
|
+
for (const item of items) {
|
|
899
|
+
if (notifyReminders.has(item) || /^沙箱付款待用户体验(?::.*)?$/u.test(item)) {
|
|
900
|
+
throw new Error(`${field} 只能包含实际缺口;通用通知地址提醒和可选沙箱付款体验不计入完成判定。请重新分类并按实际证据填写结果,不重跑已完成检查`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
836
904
|
}
|
|
837
905
|
|
|
838
906
|
function productionReadinessBlock(productName, context) {
|
|
839
907
|
const statusLines = [];
|
|
840
|
-
if (
|
|
908
|
+
if (context.integrationOnly) {
|
|
909
|
+
statusLines.push('- 本轮仅进行代码开发;上线前请确认生产配置并完成验证。');
|
|
910
|
+
} else if (!context.productBranchesComplete) {
|
|
841
911
|
statusLines.push('- 还不能正式收款,产品签约、服务市场或应用发布仍有上表待办。');
|
|
842
912
|
}
|
|
843
913
|
if (context.integrationResult === '不在本轮执行范围') {
|
|
844
914
|
statusLines.push('- 本轮只完成产品开通流程收口,不代表业务系统已经具备收款代码;先完成支付产品-代码开发、项目部署和生产参数验收。');
|
|
845
915
|
} else if (context.integrationResult === '部分完成,沙箱配置待完成') {
|
|
846
|
-
statusLines.push('-
|
|
916
|
+
statusLines.push('- 沙箱配置尚未完成,相关测试未执行;后续上线条件按生产配置和实际验证结果另行确认。');
|
|
847
917
|
} else if (context.integrationResult === '部分完成,人工待验证') {
|
|
848
918
|
statusLines.push('- 代码开发还有人工待验证项;完成后才能判断是否可进入真实生产上线。');
|
|
919
|
+
} else if (context.integrationResult === '未通过' && !(context.integrationOnly && context.failedItems && context.failedItems !== '无')) {
|
|
920
|
+
statusLines.push('- 代码开发仍有未完成项,请先按本轮校验结果修复。');
|
|
849
921
|
}
|
|
850
|
-
if (
|
|
851
|
-
|
|
852
|
-
'【当前状态】',
|
|
853
|
-
...statusLines,
|
|
854
|
-
'',
|
|
855
|
-
'【上线前有以下关键点】',
|
|
856
|
-
...productionConfigSourceLines(),
|
|
857
|
-
...productionSpecificLines(productName),
|
|
858
|
-
'',
|
|
859
|
-
'【提示】',
|
|
860
|
-
'- 如找不到配置替换位置,可以试试对 Agent 说:“我要上线了,帮我找一下项目里支付宝生产配置应该改哪些地方”。让 Agent 定位文件/变量名,但不要把生产应用私钥、账号密码、支付凭证或未脱敏通知内容直接发给 Agent。'
|
|
861
|
-
].join('\n');
|
|
922
|
+
if (context.integrationOnly && context.failedItems && context.failedItems !== '无') {
|
|
923
|
+
statusLines.push(`- ${context.failedItems}`);
|
|
862
924
|
}
|
|
925
|
+
const integrationGapAlreadyShown = context.productBranchesComplete && ['部分完成,人工待验证', '部分完成,沙箱配置待完成'].includes(context.integrationResult);
|
|
926
|
+
if (context.remainingActions && context.remainingActions !== '无' && !integrationGapAlreadyShown) {
|
|
927
|
+
statusLines.push(...summaryItems(context.remainingActions).map((item) => `- 下一步:${item}`));
|
|
928
|
+
}
|
|
929
|
+
if (statusLines.length === 0) {
|
|
930
|
+
statusLines.push('- 代码开发和产品开通流程已完成,但当前 Agent 没有替你切生产配置,也不会默认发起真实生产交易。');
|
|
931
|
+
}
|
|
932
|
+
const manualItems = summaryItems(context.manualVerificationItems);
|
|
933
|
+
const manualLines = manualItems.length
|
|
934
|
+
? ['', '上线前请完成本轮尚未验证的项目:', ...manualItems.map((item) => `- ${item}`)]
|
|
935
|
+
: [];
|
|
863
936
|
return [
|
|
864
|
-
'【当前状态】',
|
|
865
|
-
|
|
866
|
-
'',
|
|
867
|
-
'
|
|
868
|
-
...productionConfigSourceLines(),
|
|
869
|
-
...productionSpecificLines(productName),
|
|
870
|
-
'',
|
|
937
|
+
'【当前状态】', ...statusLines, '',
|
|
938
|
+
...(context.sandboxBlock ? [context.sandboxBlock, ''] : []),
|
|
939
|
+
'【上线前有以下关键点】', '',
|
|
940
|
+
...productionConfigSourceLines(), ...productionSpecificLines(productName), ...manualLines, '',
|
|
871
941
|
'【提示】',
|
|
872
|
-
'-
|
|
942
|
+
'- 如找不到项目上线前生产配置的替换位置,可以试试对 Agent 说:“我要上线了,帮我找一下项目里支付宝生产配置要改哪些地方”。让 Agent 帮助您定位文件/变量名,但不要把生产应用私钥、账号密码、支付凭证或未脱敏通知内容直接发给 Agent。'
|
|
873
943
|
].join('\n');
|
|
874
944
|
}
|
|
875
945
|
|
|
@@ -1355,7 +1425,12 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1355
1425
|
}
|
|
1356
1426
|
validateVariantInputShape(messageId, variantName, variables);
|
|
1357
1427
|
for (const definition of message.variables) {
|
|
1358
|
-
|
|
1428
|
+
if (definition.name === 'sandboxSummary') {
|
|
1429
|
+
try { normalized.sandboxSummary = validateValue(definition, variables.sandboxSummary, policy); }
|
|
1430
|
+
catch { normalized.sandboxSummary = ''; }
|
|
1431
|
+
} else {
|
|
1432
|
+
normalized[definition.name] = definition.derived ? '' : validateValue(definition, variables[definition.name], policy);
|
|
1433
|
+
}
|
|
1359
1434
|
}
|
|
1360
1435
|
} catch (error) {
|
|
1361
1436
|
throw addManagedEntrypointHint(messageId, error);
|
|
@@ -1373,6 +1448,14 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1373
1448
|
if (messageId === 'onboarding.mcc.candidates') {
|
|
1374
1449
|
normalized.candidateTable = mccCandidateTable(variantName, variables.mccCandidates, telemetryContext.productType);
|
|
1375
1450
|
}
|
|
1451
|
+
if (messageId === 'sandbox.environment.summary' && variantName === 'FINAL') {
|
|
1452
|
+
if (variables.productName !== 'AI 网页应用收款') throw new Error('最终沙箱测试配置仅适用于 AI 网页应用收款');
|
|
1453
|
+
const buyerRows = variables.environmentRows.split('\n').slice(1);
|
|
1454
|
+
if (buyerRows.some((row) => row.includes('已写入本地配置文件,默认不在对话中展开'))) {
|
|
1455
|
+
throw new Error('网页收款最终沙箱测试配置必须展示已校验的买家密码明文');
|
|
1456
|
+
}
|
|
1457
|
+
normalized.sandboxBuyerRows = buyerRows.join('\n');
|
|
1458
|
+
}
|
|
1376
1459
|
if (messageId === 'application.operation.result') {
|
|
1377
1460
|
if (variantName === 'ALIPAY_PUBLIC_KEY_MANUAL_CONFIGURATION') {
|
|
1378
1461
|
if (variables.actualStatus !== '需人工配置') {
|
|
@@ -1468,6 +1551,7 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1468
1551
|
}[variables.integrationStatus];
|
|
1469
1552
|
}
|
|
1470
1553
|
if (messageId === 'integration.checklist.result') {
|
|
1554
|
+
validateChecklistItemClassification(variables, ['failedItems', 'manualItems']);
|
|
1471
1555
|
const sandboxPending = variables.sandboxConfigState !== 'READY';
|
|
1472
1556
|
const hasBlockingDefect = variables.blockingDefectState === 'PRESENT';
|
|
1473
1557
|
if (variantName === 'AIPAY' && !variables.sandboxResult) throw new Error('AI 按量付费 checklist 缺少沙箱联调结果');
|
|
@@ -1494,8 +1578,8 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1494
1578
|
if (variables.overallResult === '未通过' && !hasFailedItems) {
|
|
1495
1579
|
throw new Error('代码开发结果为未通过时必须列出未通过项');
|
|
1496
1580
|
}
|
|
1497
|
-
if (variables.overallResult
|
|
1498
|
-
throw new Error('
|
|
1581
|
+
if (hasBlockingDefect && variables.overallResult !== '未通过') {
|
|
1582
|
+
throw new Error('存在代码、安全或必要依据阻塞缺口时,代码开发结果必须为未通过');
|
|
1499
1583
|
}
|
|
1500
1584
|
if (variables.overallResult !== '通过' && !sandboxPending && !hasBlockingDefect && !manualOnly) {
|
|
1501
1585
|
throw new Error('沙箱已就绪且没有阻塞缺口时,非通过结果只能来自人工待验证项');
|
|
@@ -1521,9 +1605,22 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1521
1605
|
if (variantName === 'WEBPAY' && variables.overallResult === '通过' && variables.webpayExperienceResult === '未提供浏览器付款入口或完整操作指引,需先补齐') {
|
|
1522
1606
|
throw new Error('AI 网页应用收款未交付付款入口时不得标记代码开发通过');
|
|
1523
1607
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1608
|
+
normalized.checklistDetails = variables.executionMode === '仅代码开发'
|
|
1609
|
+
? ''
|
|
1610
|
+
: [
|
|
1611
|
+
...(hasFailedItems ? [`未完成项:${normalized.failedItems}`] : []),
|
|
1612
|
+
...(hasManualItems ? [`人工待验证:${normalized.manualItems}`] : [])
|
|
1613
|
+
].join('\n');
|
|
1614
|
+
const productName = { AIPAY: 'AI 按量付费', WEBPAY: 'AI 网页应用收款', APPPAY: 'AI 移动应用收款' }[variantName];
|
|
1615
|
+
normalized.launchGuidance = variables.executionMode === '仅代码开发'
|
|
1616
|
+
? productionReadinessBlock(productName, {
|
|
1617
|
+
productBranchesComplete: false,
|
|
1618
|
+
integrationOnly: true,
|
|
1619
|
+
failedItems: variables.failedItems,
|
|
1620
|
+
integrationResult: variables.overallResult === '通过' ? '已完成' : sandboxPending ? '部分完成,沙箱配置待完成' : hasBlockingDefect ? '未通过' : '部分完成,人工待验证',
|
|
1621
|
+
manualVerificationItems: variables.manualItems,
|
|
1622
|
+
sandboxBlock: sandboxReplayBlock(normalized.sandboxSummary, productName, variables.sandboxConfigState)
|
|
1623
|
+
}) : '';
|
|
1527
1624
|
}
|
|
1528
1625
|
if (messageId === 'materials.category.collect') {
|
|
1529
1626
|
const expectedStatus = {
|
|
@@ -1575,6 +1672,7 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1575
1672
|
normalized.specialQualificationHint = variables.specialQualificationHint || '';
|
|
1576
1673
|
}
|
|
1577
1674
|
if (messageId === 'process.partial_result') {
|
|
1675
|
+
validateChecklistItemClassification(variables, ['remainingActions', 'manualVerificationItems']);
|
|
1578
1676
|
const statusQueryHintDefinition = definitions.get('statusQueryHint');
|
|
1579
1677
|
const statusQueryHint = statusQueryHintDefinition?.values?.[0];
|
|
1580
1678
|
if (!statusQueryHintDefinition?.derived || !statusQueryHint) {
|
|
@@ -1597,12 +1695,10 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1597
1695
|
if (onboardingOnly) {
|
|
1598
1696
|
if (variables.sandboxConfigState !== undefined) throw new Error('onboarding_only 禁止传入代码开发沙箱状态');
|
|
1599
1697
|
if (hasManualVerificationItems) throw new Error('onboarding_only 禁止传入代码开发人工待验证项');
|
|
1600
|
-
normalized.manualVerificationLine = '';
|
|
1601
1698
|
normalized.sandboxRecoveryHint = '';
|
|
1602
1699
|
} else if (variables.integrationResult === '已完成') {
|
|
1603
1700
|
if (variables.sandboxConfigState !== 'READY') throw new Error('完整代码开发结果必须绑定 READY 沙箱状态');
|
|
1604
1701
|
if (hasManualVerificationItems) throw new Error('代码开发已完成时禁止传入人工待验证项');
|
|
1605
|
-
normalized.manualVerificationLine = '';
|
|
1606
1702
|
normalized.sandboxRecoveryHint = '';
|
|
1607
1703
|
} else if (variables.integrationResult === '部分完成,沙箱配置待完成') {
|
|
1608
1704
|
if (hasManualVerificationItems) throw new Error('沙箱待配置结果不通过人工待验证项表达');
|
|
@@ -1612,11 +1708,9 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1612
1708
|
}[variables.sandboxConfigState];
|
|
1613
1709
|
if (!normalized.sandboxRecoveryHint) throw new Error('沙箱待配置结果必须绑定 CREATE_PENDING 或 VERIFY_PENDING');
|
|
1614
1710
|
if (variables.remainingActions === '无') throw new Error('沙箱待配置时必须保留剩余待办');
|
|
1615
|
-
normalized.manualVerificationLine = '';
|
|
1616
1711
|
} else if (variables.integrationResult === '部分完成,人工待验证') {
|
|
1617
1712
|
if (variables.sandboxConfigState !== 'READY') throw new Error('人工待验证结果必须绑定 READY 沙箱状态');
|
|
1618
1713
|
if (!hasManualVerificationItems) throw new Error('人工待验证结果必须列出人工待验证项');
|
|
1619
|
-
normalized.manualVerificationLine = `人工待验证:${normalized.manualVerificationItems}`;
|
|
1620
1714
|
normalized.sandboxRecoveryHint = '';
|
|
1621
1715
|
} else {
|
|
1622
1716
|
throw new Error('full_process 进入产品开通收口时只允许已完成、沙箱待配置或人工待验证');
|
|
@@ -1640,7 +1734,10 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1640
1734
|
!hasManualVerificationItems;
|
|
1641
1735
|
normalized.productionReadinessBlock = productionReadinessBlock(variables.productName, {
|
|
1642
1736
|
productBranchesComplete,
|
|
1643
|
-
|
|
1737
|
+
remainingActions: normalized.remainingActions,
|
|
1738
|
+
integrationResult: variables.integrationResult,
|
|
1739
|
+
manualVerificationItems: variables.manualVerificationItems,
|
|
1740
|
+
sandboxBlock: sandboxReplayBlock(onboardingOnly ? '' : normalized.sandboxSummary, variables.productName, variables.sandboxConfigState)
|
|
1644
1741
|
});
|
|
1645
1742
|
const declaresComplete = variables.processResultTitle === '支付产品-产品开通已完成';
|
|
1646
1743
|
if (branchesComplete !== declaresComplete) {
|
|
@@ -1653,7 +1750,7 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
|
|
|
1653
1750
|
if (messageId === 'service.create.summary') validateServiceCreateSummary(variantName, variables, normalized);
|
|
1654
1751
|
if (messageId === 'onboarding.write.confirm') validateWriteConfirmation(variantName, variables, normalized);
|
|
1655
1752
|
let rendered = variant.templateLines.join('\n').replace(placeholderPattern, (_, name) => normalized[name]);
|
|
1656
|
-
if (messageId === 'application.operation.result' || messageId === 'process.partial_result' || messageId === 'error.backend' || messageId === 'error.cli') {
|
|
1753
|
+
if (messageId === 'application.operation.result' || messageId === 'process.partial_result' || messageId === 'integration.checklist.result' || messageId === 'sandbox.environment.summary' || messageId === 'error.backend' || messageId === 'error.cli') {
|
|
1657
1754
|
rendered = normalizeProcessPartialResultSpacing(rendered);
|
|
1658
1755
|
}
|
|
1659
1756
|
if (message.blocking && allowedRepliesFor(catalog, message).length > 0) {
|