@agent-cards/sdk 0.21.0 → 0.23.0

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 (113) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/PREFLIGHT.md +32 -0
  3. package/README.md +1177 -23
  4. package/dist/aci.d.ts +173 -0
  5. package/dist/aci.generated.d.ts +87 -0
  6. package/dist/aci.generated.js +306 -0
  7. package/dist/aci.js +371 -0
  8. package/dist/adyen-merchant-hosted.generated.d.ts +3 -3
  9. package/dist/adyen-merchant-hosted.generated.js +302 -56
  10. package/dist/agentcard.d.ts +9 -2
  11. package/dist/agentcard.js +4 -2
  12. package/dist/amadeus-checkout.generated.d.ts +57 -0
  13. package/dist/amadeus-checkout.generated.js +549 -0
  14. package/dist/amadeus.d.ts +41 -0
  15. package/dist/amadeus.js +81 -0
  16. package/dist/builtin-registry.generated.js +1 -1
  17. package/dist/card-endpoint-facts.d.ts +96 -0
  18. package/dist/card-endpoint-facts.js +275 -0
  19. package/dist/card-endpoint-page.d.ts +54 -0
  20. package/dist/card-endpoint-page.js +69 -0
  21. package/dist/card-endpoint.d.ts +298 -0
  22. package/dist/card-endpoint.js +438 -0
  23. package/dist/card-fields.generated.d.ts +5 -3
  24. package/dist/card-fields.generated.js +284 -7
  25. package/dist/cdp.d.ts +5 -1
  26. package/dist/cdp.js +1155 -141
  27. package/dist/client.d.ts +456 -20
  28. package/dist/client.js +1072 -104
  29. package/dist/creditguard.generated.d.ts +79 -0
  30. package/dist/creditguard.generated.js +662 -0
  31. package/dist/cse-body.js +11 -1
  32. package/dist/cybersource-flex.d.ts +30 -0
  33. package/dist/cybersource-flex.generated.d.ts +27 -1
  34. package/dist/cybersource-flex.generated.js +42 -2
  35. package/dist/cybersource-flex.js +46 -0
  36. package/dist/datatrans.d.ts +96 -0
  37. package/dist/datatrans.generated.d.ts +64 -0
  38. package/dist/datatrans.generated.js +426 -0
  39. package/dist/datatrans.js +178 -0
  40. package/dist/ekashu.d.ts +35 -0
  41. package/dist/ekashu.js +55 -0
  42. package/dist/elavon-converge.d.ts +37 -0
  43. package/dist/elavon-converge.generated.d.ts +49 -0
  44. package/dist/elavon-converge.generated.js +439 -0
  45. package/dist/elavon-converge.js +84 -0
  46. package/dist/eprotect.d.ts +152 -0
  47. package/dist/eprotect.generated.d.ts +77 -0
  48. package/dist/eprotect.generated.js +498 -0
  49. package/dist/eprotect.js +222 -0
  50. package/dist/hosted-form.d.ts +3 -2
  51. package/dist/hosted-form.js +3 -2
  52. package/dist/index.d.ts +17 -3
  53. package/dist/index.js +6 -1
  54. package/dist/juspay-gate.d.ts +82 -0
  55. package/dist/juspay-gate.js +126 -0
  56. package/dist/juspay.generated.d.ts +89 -0
  57. package/dist/juspay.generated.js +705 -0
  58. package/dist/lifecycle.d.ts +60 -6
  59. package/dist/lifecycle.js +122 -8
  60. package/dist/merchant-card-endpoints.generated.d.ts +122 -0
  61. package/dist/merchant-card-endpoints.generated.js +4261 -0
  62. package/dist/merchant-hosted.d.ts +4 -1
  63. package/dist/merchant-hosted.js +5 -2
  64. package/dist/merchant-session-probe.d.ts +48 -0
  65. package/dist/merchant-session-probe.js +53 -0
  66. package/dist/merchant-session.generated.d.ts +69 -0
  67. package/dist/merchant-session.generated.js +919 -0
  68. package/dist/payrails.d.ts +133 -0
  69. package/dist/payrails.generated.d.ts +73 -0
  70. package/dist/payrails.generated.js +596 -0
  71. package/dist/payrails.js +305 -0
  72. package/dist/pelecard.d.ts +32 -0
  73. package/dist/pelecard.generated.d.ts +32 -0
  74. package/dist/pelecard.generated.js +283 -0
  75. package/dist/pelecard.js +42 -0
  76. package/dist/pre-claim.d.ts +81 -2
  77. package/dist/pre-claim.js +1023 -5
  78. package/dist/preflight-catalog.json +2267 -288
  79. package/dist/preflight-schemas.json +183 -6
  80. package/dist/preflight.generated.d.ts +1 -1
  81. package/dist/preflight.generated.js +243 -4
  82. package/dist/preparation.d.ts +12 -3
  83. package/dist/preparation.js +63 -12
  84. package/dist/prepared-processor.d.ts +84 -5
  85. package/dist/prepared-processor.js +147 -6
  86. package/dist/primer.d.ts +53 -0
  87. package/dist/primer.generated.d.ts +38 -0
  88. package/dist/primer.generated.js +624 -0
  89. package/dist/primer.js +82 -0
  90. package/dist/registry.d.ts +100 -8
  91. package/dist/registry.js +85 -11
  92. package/dist/shiji-sts.d.ts +144 -0
  93. package/dist/shiji-sts.generated.d.ts +72 -0
  94. package/dist/shiji-sts.generated.js +913 -0
  95. package/dist/shiji-sts.js +222 -0
  96. package/dist/stripe-checkout.generated.d.ts +1 -1
  97. package/dist/stripe-checkout.generated.js +764 -9
  98. package/dist/stripe-setup.d.ts +16 -0
  99. package/dist/stripe-setup.js +61 -0
  100. package/dist/substitutions.generated.js +187 -29
  101. package/dist/verifone.d.ts +68 -0
  102. package/dist/verifone.generated.d.ts +51 -0
  103. package/dist/verifone.generated.js +1413 -0
  104. package/dist/verifone.js +119 -0
  105. package/dist/voice-types.d.ts +27 -0
  106. package/dist/voice.d.ts +6 -1
  107. package/dist/voice.js +6 -1
  108. package/dist/wspay.d.ts +41 -0
  109. package/dist/wspay.generated.d.ts +41 -0
  110. package/dist/wspay.generated.js +370 -0
  111. package/dist/wspay.js +65 -0
  112. package/examples/preflight/kernel-native/inventory.json +1 -1
  113. package/package.json +3 -3
@@ -1 +1 @@
1
- export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "sandboxHosts": ["^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "sandboxHosts": ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "sandboxHosts": ["^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "sandboxHosts": ["^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "sandboxHosts": ["^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "sandboxHosts": ["^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "sandboxHosts": ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "sandboxHosts": ["^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "sandboxHosts": ["^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "sandboxHosts": ["^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "sandboxHosts": ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "sandboxHosts": ["^esqa\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }, { "psp": "gravy", "match": /api(\.sandbox)?\.[a-z0-9-]+\.gr4vy\.app\/checkout\/sessions\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/fields/i, "hosts": ["^api\\.[a-z0-9-]+\\.gr4vy\\.app$", "^api\\.sandbox\\.[a-z0-9-]+\\.gr4vy\\.app$"], "sandboxHosts": ["^api\\.sandbox\\.[a-z0-9-]+\\.gr4vy\\.app$"], "encoding": "json", "passthroughHeaders": [] }];
1
+ export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "sandboxHosts": ["^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "sandboxHosts": ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "sandboxHosts": ["^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "sandboxHosts": ["^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "sandboxHosts": ["^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "sandboxHosts": ["^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [], "jsonCardFields": [["paymentOption", "card", "cardNumber"], ["cardData", "cardNumber"]], "passWithoutCard": "(?:secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:clientPayment|websdk/initPaymentWithCardTokenization)\\.do" }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "sandboxHosts": ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "sandboxHosts": ["^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "sandboxHosts": ["^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "sandboxHosts": ["^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "sandboxHosts": ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "sandboxHosts": ["^esqa\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }, { "psp": "gravy", "match": /api(\.sandbox)?\.[a-z0-9-]+\.gr4vy\.app\/checkout\/sessions\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/fields/i, "hosts": ["^api\\.[a-z0-9-]+\\.gr4vy\\.app$", "^api\\.sandbox\\.[a-z0-9-]+\\.gr4vy\\.app$"], "sandboxHosts": ["^api\\.sandbox\\.[a-z0-9-]+\\.gr4vy\\.app$"], "encoding": "json", "passthroughHeaders": [] }];
@@ -0,0 +1,96 @@
1
+ import { type CardEndpointProfile } from './card-endpoint.js';
2
+ /** How long a paused card request waits for a request on its checkout session that left before it and has not answered. */
3
+ export declare const CARD_ENDPOINT_FACTS_WAIT_MS = 3000;
4
+ /**
5
+ * How many recorded requests the watch keeps. Past it, the oldest request of the checkout
6
+ * session that holds the most goes first, so another checkout's requests never push this
7
+ * one's record out while this one holds the fewer. What a checkout lost can still refuse
8
+ * its capture (Lost).
9
+ */
10
+ export declare const CARD_ENDPOINT_FACTS_KEPT = 32;
11
+ /**
12
+ * How many checkout sessions the watch remembers a loss for. Past it, every capture refuses:
13
+ * a loss the watch forgot could let an older record through.
14
+ */
15
+ export declare const CARD_ENDPOINT_FACTS_LOST_SESSIONS_KEPT = 256;
16
+ /** The cut order record a create carries (merchant_card_endpoint.facts). */
17
+ export interface CardEndpointFactsRecord {
18
+ method: string;
19
+ url: string;
20
+ state: unknown;
21
+ }
22
+ /** What capture() gives: the cut record, or why there is none to send (names no value). */
23
+ export type CardEndpointFactsCapture = {
24
+ record: CardEndpointFactsRecord;
25
+ refusal?: undefined;
26
+ } | {
27
+ refusal: string;
28
+ record?: undefined;
29
+ };
30
+ export declare class CardEndpointFactsWatch {
31
+ private readonly now;
32
+ private readonly origins;
33
+ private readonly sessionOf;
34
+ private readonly exchanges;
35
+ private readonly lost;
36
+ private lostTooMany;
37
+ private readonly waiters;
38
+ private ticks;
39
+ constructor(now?: () => number, origins?: readonly string[], sessionOf?: (url: string) => string | null);
40
+ /** Whether the watch records the request under this key. */
41
+ tracks(key: string): boolean;
42
+ /**
43
+ * A request left. Recorded only when it is a document, an XHR or a fetch (or of a type
44
+ * the adapter could not name) on the endpoint origin of a profile with facts.
45
+ */
46
+ sent(key: string, request: {
47
+ method: string;
48
+ url: string;
49
+ resourceType?: string | null;
50
+ networkId?: string | null;
51
+ }): void;
52
+ /** Drops the oldest request of the checkout session that holds the most (of two that hold as many, the one seen first). */
53
+ private evictOne;
54
+ /** Notes what dropping `exchange` takes from its checkout session (Lost). */
55
+ private remember;
56
+ /**
57
+ * The key a load-finished report belongs to: its own when recorded, else the recorded
58
+ * document with the same network id. Chrome reports an out-of-process frame's document
59
+ * request on the parent frame's session and its answer on the frame's own session.
60
+ */
61
+ keyFor(key: string, networkId: string | null | undefined): string | null;
62
+ /** The request's answer began to arrive, with its HTTP status: the first report stamps when. */
63
+ answered(key: string, status: unknown): void;
64
+ /**
65
+ * The request finished: the adapter reports it the moment the browser does, before it reads
66
+ * the text, so two answers keep the order they finished in however their texts come back.
67
+ */
68
+ finished(key: string): void;
69
+ /** The request finished and its text was read (null when it could not be, or was past the limit). */
70
+ read(key: string, text: string | null, completedAt?: number): void;
71
+ /** The request failed or was aborted: the page never read an answer from it. */
72
+ failed(key: string): void;
73
+ private settle;
74
+ /**
75
+ * The order record for a paused card request of `profile` at `cardUrl`, as of the pause.
76
+ * It reads every recorded request on the same checkout session sent no later than
77
+ * `pausedAt` that is a record or an unread call (payment-core
78
+ * merchantCardEndpointFactsRole); the card request and every earlier one to its URL
79
+ * answer with the payment's outcome, and an inert call changes nothing the record holds,
80
+ * so neither is waited for or read. One still loading is waited for, up to
81
+ * CARD_ENDPOINT_FACTS_WAIT_MS, and refuses once that runs out: the page was still
82
+ * changing the order when it sent the card request. Then the record whose answer
83
+ * finished last, in the order the watch saw them finish (the one the page shows), is cut
84
+ * by payment-core; one that holds no record (the /payment-context
85
+ * answer) is passed over for the one before it, and one that holds a record in another
86
+ * layout, or did not succeed, refuses. Last, the record refuses when the page sent an
87
+ * unread call, or a record call other than a GET that gave no record, after it arrived.
88
+ *
89
+ * A checkout that sent more requests than the watch keeps refuses whenever what it lost
90
+ * could change that answer: a request still loading when it went, or one that finished
91
+ * after the record or was sent after the record arrived. Every request payment-core gives
92
+ * a role for this card request is on the card request's own session
93
+ * (cardEndpointFactsSession), so its losses are the only ones read.
94
+ */
95
+ capture(profile: CardEndpointProfile, cardUrl: string, pausedAt: number, signal?: AbortSignal): Promise<CardEndpointFactsCapture>;
96
+ }
@@ -0,0 +1,275 @@
1
+ // The merchant's own record of the order, for a reviewed card endpoint whose merchant
2
+ // keeps one (profile.facts; payment-core merchant-card-endpoint-facts.js says what it
3
+ // proves and what it does not).
4
+ //
5
+ // FastSpring's popup posts the card with no amount. Its page keeps the order it shows in
6
+ // the popup document (script#viewdata) and replaces it with every JSON answer it reads
7
+ // from the session URL, tax included. From attach on, the adapters tell this watch about
8
+ // every request that leaves, every answer and every failure; it keeps, read-only, the
9
+ // documents, XHRs and fetches on the endpoint origin of a profile with facts, and nothing
10
+ // else, a bounded number of them, the busiest checkout session's oldest going first; what a
11
+ // checkout lost to that bound can still refuse its capture. When that profile's card request
12
+ // pauses, capture() waits briefly for any request on the same checkout session that left
13
+ // before the pause, then takes the order record whose answer finished last, the one the page
14
+ // shows, cut by payment-core to the values its reader reads (the total with tax, the
15
+ // currency, test mode and the renewal marks). The create carries that cut record; the client
16
+ // and the API read it with the same rules.
17
+ //
18
+ // A record holds the order only if the page changed nothing after it. payment-core's
19
+ // merchantCardEndpointFactsRole names what each request is to the card request: a call
20
+ // the reader never reads ('unread'), or one of the record's own calls that wrote and gave
21
+ // no record (it failed, or answered without one), sent once the newest record's answer
22
+ // began to arrive or still unanswered when the wait ends, leaves the order unknown, and
23
+ // capture() refuses. "Once" is the order in which the watch saw the events, never a
24
+ // clock, which can give two of them one millisecond. A GET of the record's shapes that
25
+ // answers without one changes nothing, and an inert call (customer recognition) is never read.
26
+ //
27
+ // Nothing here writes to the page, sends a request of its own or keeps a response it was
28
+ // not asked about. A response body is cardholder data by association (it carries the
29
+ // merchant's session id in its URL and the buyer's name and address in its text): it
30
+ // stays in this process, is cut before it leaves, and is dropped with the oldest entries.
31
+ import { MERCHANT_CARD_ENDPOINT_FACTS_TEXT_MAX as SHARED_TEXT_MAX, merchantCardEndpointFactsRole as sharedRole, projectMerchantCardEndpointFacts as sharedProject, } from './merchant-card-endpoints.generated.js';
32
+ import { cardEndpointFactsOrigins, cardEndpointFactsSession } from './card-endpoint.js';
33
+ /** How long a paused card request waits for a request on its checkout session that left before it and has not answered. */
34
+ export const CARD_ENDPOINT_FACTS_WAIT_MS = 3_000;
35
+ /**
36
+ * How many recorded requests the watch keeps. Past it, the oldest request of the checkout
37
+ * session that holds the most goes first, so another checkout's requests never push this
38
+ * one's record out while this one holds the fewer. What a checkout lost can still refuse
39
+ * its capture (Lost).
40
+ */
41
+ export const CARD_ENDPOINT_FACTS_KEPT = 32;
42
+ /**
43
+ * How many checkout sessions the watch remembers a loss for. Past it, every capture refuses:
44
+ * a loss the watch forgot could let an older record through.
45
+ */
46
+ export const CARD_ENDPOINT_FACTS_LOST_SESSIONS_KEPT = 256;
47
+ const TEXT_MAX = SHARED_TEXT_MAX;
48
+ const RECORDED_TYPES = new Set(['document', 'xhr', 'fetch']);
49
+ export class CardEndpointFactsWatch {
50
+ now;
51
+ origins;
52
+ sessionOf;
53
+ exchanges = new Map();
54
+ lost = new Map();
55
+ lostTooMany = false;
56
+ waiters = new Set();
57
+ ticks = 0;
58
+ constructor(now = Date.now, origins = cardEndpointFactsOrigins(), sessionOf = cardEndpointFactsSession) {
59
+ this.now = now;
60
+ this.origins = origins;
61
+ this.sessionOf = sessionOf;
62
+ }
63
+ /** Whether the watch records the request under this key. */
64
+ tracks(key) { return this.exchanges.has(key); }
65
+ /**
66
+ * A request left. Recorded only when it is a document, an XHR or a fetch (or of a type
67
+ * the adapter could not name) on the endpoint origin of a profile with facts.
68
+ */
69
+ sent(key, request) {
70
+ if (this.origins.length === 0 || typeof request.url !== 'string' || typeof request.method !== 'string')
71
+ return;
72
+ let origin;
73
+ try {
74
+ origin = new URL(request.url).origin;
75
+ }
76
+ catch {
77
+ return;
78
+ }
79
+ if (!this.origins.includes(origin))
80
+ return;
81
+ const type = typeof request.resourceType === 'string' ? request.resourceType.toLowerCase() : '';
82
+ if (type !== '' && !RECORDED_TYPES.has(type))
83
+ return;
84
+ this.ticks += 1;
85
+ this.exchanges.set(key, { method: request.method.toUpperCase(), url: request.url, session: this.sessionOf(request.url) ?? origin,
86
+ networkId: request.networkId ?? null, document: type === 'document', sentTick: this.ticks, answeredTick: null, finishedTick: null,
87
+ sentAt: this.now(), status: null, body: null, completedAt: null, failed: false });
88
+ while (this.exchanges.size > CARD_ENDPOINT_FACTS_KEPT)
89
+ this.evictOne();
90
+ }
91
+ /** Drops the oldest request of the checkout session that holds the most (of two that hold as many, the one seen first). */
92
+ evictOne() {
93
+ const counts = new Map();
94
+ for (const exchange of this.exchanges.values())
95
+ counts.set(exchange.session, (counts.get(exchange.session) ?? 0) + 1);
96
+ let fullest = null;
97
+ for (const [session, count] of counts)
98
+ if (fullest === null || count > counts.get(fullest))
99
+ fullest = session;
100
+ for (const [key, exchange] of this.exchanges) {
101
+ if (exchange.session === fullest) {
102
+ this.exchanges.delete(key);
103
+ this.remember(exchange);
104
+ return;
105
+ }
106
+ }
107
+ }
108
+ /** Notes what dropping `exchange` takes from its checkout session (Lost). */
109
+ remember(exchange) {
110
+ let lost = this.lost.get(exchange.session);
111
+ if (!lost) {
112
+ if (this.lost.size >= CARD_ENDPOINT_FACTS_LOST_SESSIONS_KEPT) {
113
+ this.lostTooMany = true;
114
+ return;
115
+ }
116
+ lost = { loading: false, sentTick: 0, finishedTick: 0 };
117
+ this.lost.set(exchange.session, lost);
118
+ }
119
+ lost.loading ||= exchange.completedAt === null;
120
+ lost.sentTick = Math.max(lost.sentTick, exchange.sentTick);
121
+ lost.finishedTick = Math.max(lost.finishedTick, exchange.finishedTick ?? 0);
122
+ }
123
+ /**
124
+ * The key a load-finished report belongs to: its own when recorded, else the recorded
125
+ * document with the same network id. Chrome reports an out-of-process frame's document
126
+ * request on the parent frame's session and its answer on the frame's own session.
127
+ */
128
+ keyFor(key, networkId) {
129
+ if (this.exchanges.has(key))
130
+ return key;
131
+ if (!networkId)
132
+ return null;
133
+ for (const [recorded, exchange] of this.exchanges) {
134
+ if (exchange.document && exchange.networkId === networkId && exchange.completedAt === null)
135
+ return recorded;
136
+ }
137
+ return null;
138
+ }
139
+ /** The request's answer began to arrive, with its HTTP status: the first report stamps when. */
140
+ answered(key, status) {
141
+ const exchange = this.exchanges.get(key);
142
+ if (!exchange)
143
+ return;
144
+ if (Number.isInteger(status))
145
+ exchange.status = status;
146
+ exchange.answeredTick ??= ++this.ticks;
147
+ }
148
+ /**
149
+ * The request finished: the adapter reports it the moment the browser does, before it reads
150
+ * the text, so two answers keep the order they finished in however their texts come back.
151
+ */
152
+ finished(key) {
153
+ const exchange = this.exchanges.get(key);
154
+ if (exchange)
155
+ exchange.finishedTick ??= ++this.ticks;
156
+ }
157
+ /** The request finished and its text was read (null when it could not be, or was past the limit). */
158
+ read(key, text, completedAt = this.now()) {
159
+ const exchange = this.exchanges.get(key);
160
+ if (!exchange)
161
+ return;
162
+ exchange.finishedTick ??= ++this.ticks;
163
+ exchange.body = typeof text === 'string' && text.length <= TEXT_MAX ? text : null;
164
+ exchange.completedAt = completedAt;
165
+ this.settle();
166
+ }
167
+ /** The request failed or was aborted: the page never read an answer from it. */
168
+ failed(key) {
169
+ const exchange = this.exchanges.get(key);
170
+ if (!exchange)
171
+ return;
172
+ exchange.failed = true;
173
+ exchange.completedAt ??= this.now();
174
+ this.settle();
175
+ }
176
+ settle() {
177
+ for (const wake of [...this.waiters])
178
+ wake();
179
+ }
180
+ /**
181
+ * The order record for a paused card request of `profile` at `cardUrl`, as of the pause.
182
+ * It reads every recorded request on the same checkout session sent no later than
183
+ * `pausedAt` that is a record or an unread call (payment-core
184
+ * merchantCardEndpointFactsRole); the card request and every earlier one to its URL
185
+ * answer with the payment's outcome, and an inert call changes nothing the record holds,
186
+ * so neither is waited for or read. One still loading is waited for, up to
187
+ * CARD_ENDPOINT_FACTS_WAIT_MS, and refuses once that runs out: the page was still
188
+ * changing the order when it sent the card request. Then the record whose answer
189
+ * finished last, in the order the watch saw them finish (the one the page shows), is cut
190
+ * by payment-core; one that holds no record (the /payment-context
191
+ * answer) is passed over for the one before it, and one that holds a record in another
192
+ * layout, or did not succeed, refuses. Last, the record refuses when the page sent an
193
+ * unread call, or a record call other than a GET that gave no record, after it arrived.
194
+ *
195
+ * A checkout that sent more requests than the watch keeps refuses whenever what it lost
196
+ * could change that answer: a request still loading when it went, or one that finished
197
+ * after the record or was sent after the record arrived. Every request payment-core gives
198
+ * a role for this card request is on the card request's own session
199
+ * (cardEndpointFactsSession), so its losses are the only ones read.
200
+ */
201
+ async capture(profile, cardUrl, pausedAt, signal) {
202
+ if (!profile.facts)
203
+ return { refusal: `Agentcard reads no order record for ${profile.merchant}` };
204
+ const session = this.sessionOf(cardUrl);
205
+ const lostNow = () => (this.lostTooMany ? 'untracked' : (session === null ? null : this.lost.get(session) ?? null));
206
+ const lostRefusal = (lost) => (lost === 'untracked'
207
+ ? { refusal: `this runtime lost track of the checkouts on ${profile.merchant}'s page, so the order the card would pay is not known` }
208
+ : { refusal: `${profile.merchant}'s page sent more requests on this checkout than this runtime keeps, so the order the card would pay is not known` });
209
+ const before = lostNow();
210
+ if (before === 'untracked' || before?.loading)
211
+ return lostRefusal(before);
212
+ const roleOf = (exchange) => {
213
+ try {
214
+ return sharedRole(profile.id, cardUrl, { method: exchange.method, url: exchange.url });
215
+ }
216
+ catch {
217
+ return null;
218
+ }
219
+ };
220
+ const read = () => [...this.exchanges.values()].filter((exchange) => exchange.sentAt <= pausedAt)
221
+ .map((exchange) => ({ exchange, role: roleOf(exchange) })).filter(({ role }) => role === 'record' || role === 'unread');
222
+ // The wait runs on the real clock, whatever clock stamps the requests.
223
+ const deadline = Date.now() + CARD_ENDPOINT_FACTS_WAIT_MS;
224
+ while (read().some(({ exchange }) => exchange.completedAt === null)) {
225
+ const left = deadline - Date.now();
226
+ if (left <= 0 || signal?.aborted) {
227
+ return read().some(({ exchange, role }) => role === 'record' && exchange.completedAt === null)
228
+ ? { refusal: `${profile.merchant}'s record of this checkout was still loading when the card request left` }
229
+ : { refusal: `${profile.merchant}'s page was still waiting on a request on this checkout when the card request left, so the order the card would pay is not known` };
230
+ }
231
+ await new Promise((resolve) => {
232
+ const wake = () => { clearTimeout(timer); this.waiters.delete(wake); signal?.removeEventListener('abort', wake); resolve(); };
233
+ const timer = setTimeout(wake, left);
234
+ this.waiters.add(wake);
235
+ signal?.addEventListener('abort', wake, { once: true });
236
+ });
237
+ }
238
+ const seen = read();
239
+ // The page may have sent more while the wait ran.
240
+ const lost = lostNow();
241
+ if (lost === 'untracked' || lost?.loading)
242
+ return lostRefusal(lost);
243
+ // Newest by when it finished, in the order the watch saw them finish: two in one millisecond
244
+ // keep that order, whichever of them was sent first.
245
+ const done = seen.filter(({ exchange, role }) => role === 'record' && !exchange.failed)
246
+ .sort((a, b) => (b.exchange.finishedTick ?? 0) - (a.exchange.finishedTick ?? 0));
247
+ for (const { exchange } of done) {
248
+ let cut;
249
+ try {
250
+ cut = sharedProject(profile.id, cardUrl, { method: exchange.method, url: exchange.url, status: exchange.status ?? 0, body: exchange.body });
251
+ }
252
+ catch {
253
+ return { refusal: `${profile.merchant}'s record of this checkout could not be read` };
254
+ }
255
+ if (cut.none)
256
+ continue;
257
+ if (cut.refusal)
258
+ return { refusal: cut.refusal };
259
+ // Anything the page sent once this record's answer began to arrive could have changed the
260
+ // order unseen: an unread call, or a record call other than a GET, which gave no record of
261
+ // its own (one that did would be newer than this one). A record the reader read had its
262
+ // status reported, and with it the moment its answer began.
263
+ const arrived = exchange.answeredTick ?? exchange.sentTick;
264
+ if (seen.some(({ exchange: later, role }) => later.sentTick > arrived && (role === 'unread' || later.method !== 'GET'))) {
265
+ return { refusal: `${profile.merchant}'s page sent a request on this checkout after its newest record of the order, so the order the card would pay is not known` };
266
+ }
267
+ // A request the bound took that finished after this record could have been a newer one,
268
+ // and one sent after this record arrived could have changed the order after it.
269
+ if (lost && (lost.finishedTick > (exchange.finishedTick ?? 0) || lost.sentTick > arrived))
270
+ return lostRefusal(lost);
271
+ return { record: cut.record };
272
+ }
273
+ return { refusal: `this runtime saw no record of this checkout from ${profile.merchant}'s page` };
274
+ }
275
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The page a paused card form navigation is answered with when Agentcard stops it
3
+ * (card-endpoint.ts, a profile whose `navigation` is 'top' or 'frame').
4
+ *
5
+ * A merchant's form that the page submits as a page load leaves the agent's browser
6
+ * waiting for a page, so an aborted request is the wrong answer: it renders an error
7
+ * screen or nothing, and invites another click on Pay (the hosted_form lesson,
8
+ * hosted-form.ts). The page says what happened in one of three outcomes, and nothing else:
9
+ *
10
+ * - `stopped_before_send`: the card never left the cardholder's device, the merchant
11
+ * received nothing, and no other payment for this checkout is waiting or may have
12
+ * reached the merchant, so nothing was charged;
13
+ * - `sent_answer_not_shown`: the card reached the merchant and its answer cannot be
14
+ * shown here (withheld because it could carry card data, or a redirect that would
15
+ * make the browser send the form again), so the payment has to be checked with the
16
+ * merchant before paying again;
17
+ * - `payment_in_progress`: a payment for this checkout is waiting for the cardholder's
18
+ * approval or may have reached the merchant: an approval still outstanding (a card
19
+ * endpoint's device sends the card before it approves), an earlier payment this
20
+ * attachment handed over or cannot account for, one whose outcome the API could not
21
+ * confirm, or one the API holds for the same checkout (merchant_checkout_in_use). The
22
+ * form must not be sent again until that payment is checked with the merchant.
23
+ *
24
+ * Self-contained HTML: no external resource, no script that runs (the one `<script>` is
25
+ * `application/json`), readable by a person in one paragraph and by an agent through the
26
+ * meta tag, the header and the JSON block. It never carries the endpoint URL, the paused
27
+ * body or the merchant's answer: only the profile's own merchant name, escaped.
28
+ */
29
+ import type { SyntheticPage } from './hosted-form.js';
30
+ import type { CheckoutState } from './lifecycle.js';
31
+ export type CardEndpointStoppedOutcome = 'stopped_before_send' | 'sent_answer_not_shown' | 'payment_in_progress';
32
+ /** Why the navigation was stopped, in the words an agent reads from the JSON block. */
33
+ export type CardEndpointStoppedReason = 'not_enabled' | 'request_unsupported' | 'not_approved' | 'blocked' | 'checkout_in_use' | 'answer_withheld' | 'redirect_repeats_post' | 'redirect_location_invalid' | 'outcome_unknown';
34
+ /**
35
+ * The outcome for a card form navigation refused before its own card could leave. While
36
+ * an approval is outstanding (a card endpoint's device sends the card before it approves)
37
+ * or once the controller says a payment may exist, the page never says that nothing was
38
+ * sent, which would invite a second payment: it reads `payment_in_progress`. Otherwise
39
+ * nothing has left, and it reads `stopped_before_send`.
40
+ */
41
+ export declare function refusedNavigationOutcome(state: {
42
+ awaitingApproval: boolean;
43
+ status: CheckoutState['status'];
44
+ }): CardEndpointStoppedOutcome;
45
+ export interface CardEndpointStoppedPageInput {
46
+ outcome: CardEndpointStoppedOutcome;
47
+ reason: CardEndpointStoppedReason;
48
+ /** The profile's own merchant name (never the integrator's or the page's). */
49
+ merchant: string;
50
+ /** The authorization, once one exists. */
51
+ authorizationId?: string | null;
52
+ }
53
+ /** The stopped page for a paused card form navigation: status 200, HTML, never cached. */
54
+ export declare function cardEndpointStoppedPage(input: CardEndpointStoppedPageInput): SyntheticPage;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The controller statuses (lifecycle.ts) in which a payment for the attachment's checkout
3
+ * may exist: its card was handed over, a step-up holds it, or its outcome is not known.
4
+ */
5
+ const PAYMENT_MAY_EXIST = new Set(['awaiting_merchant', 'completed', 'requires_user_action', 'outcome_unknown']);
6
+ /**
7
+ * The outcome for a card form navigation refused before its own card could leave. While
8
+ * an approval is outstanding (a card endpoint's device sends the card before it approves)
9
+ * or once the controller says a payment may exist, the page never says that nothing was
10
+ * sent, which would invite a second payment: it reads `payment_in_progress`. Otherwise
11
+ * nothing has left, and it reads `stopped_before_send`.
12
+ */
13
+ export function refusedNavigationOutcome(state) {
14
+ return state.awaitingApproval || PAYMENT_MAY_EXIST.has(state.status) ? 'payment_in_progress' : 'stopped_before_send';
15
+ }
16
+ function escapeHtml(s) {
17
+ return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
18
+ }
19
+ const TEXT = {
20
+ stopped_before_send: (merchant) => ({
21
+ title: 'Payment stopped',
22
+ body: `Agentcard stopped this payment before your card reached ${merchant}. Nothing was sent and nothing was charged.`,
23
+ next: 'nothing reached the merchant; start the checkout again or pay another way',
24
+ }),
25
+ sent_answer_not_shown: (merchant) => ({
26
+ title: 'Check this payment',
27
+ body: `Your card was sent to ${merchant}, and its answer cannot be shown here. Do not submit this form again. Check the payment with ${merchant} before paying again.`,
28
+ next: "check the payment with the merchant; never resubmit this form",
29
+ }),
30
+ payment_in_progress: (merchant) => ({
31
+ title: 'Check this payment',
32
+ body: `A payment for this checkout is waiting for approval or may have reached ${merchant}. Do not submit this form again. Check that payment with ${merchant} before paying again.`,
33
+ next: 'a payment for this checkout is waiting or may have reached the merchant; check it with the merchant; never resubmit this form',
34
+ }),
35
+ };
36
+ /** The stopped page for a paused card form navigation: status 200, HTML, never cached. */
37
+ export function cardEndpointStoppedPage(input) {
38
+ const merchant = String(input.merchant ?? '');
39
+ const text = TEXT[input.outcome](escapeHtml(merchant));
40
+ const record = {
41
+ outcome: input.outcome,
42
+ reason: input.reason,
43
+ merchant,
44
+ ...(input.authorizationId ? { authorization_id: String(input.authorizationId) } : {}),
45
+ next: text.next,
46
+ };
47
+ const json = JSON.stringify(record).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
48
+ const body = [
49
+ '<!doctype html>',
50
+ '<html lang="en">',
51
+ '<head>',
52
+ '<meta charset="utf-8">',
53
+ `<meta name="agentcard-checkout" content="${input.outcome}">`,
54
+ '<meta name="robots" content="noindex">',
55
+ `<title>${text.title}</title>`,
56
+ '</head>',
57
+ '<body>',
58
+ `<p>${text.body}</p>`,
59
+ `<script type="application/json" id="agentcard-checkout">${json}</script>`,
60
+ '</body>',
61
+ '</html>',
62
+ '',
63
+ ].join('\n');
64
+ return {
65
+ status: 200,
66
+ headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store', 'x-agentcard-checkout': input.outcome },
67
+ body,
68
+ };
69
+ }