@agent-cards/sdk 0.21.0 → 0.22.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 (103) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/PREFLIGHT.md +30 -0
  3. package/README.md +1070 -21
  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 +283 -49
  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.d.ts +177 -0
  18. package/dist/card-endpoint.js +286 -0
  19. package/dist/card-fields.generated.d.ts +5 -3
  20. package/dist/card-fields.generated.js +284 -7
  21. package/dist/cdp.d.ts +5 -1
  22. package/dist/cdp.js +841 -135
  23. package/dist/client.d.ts +403 -19
  24. package/dist/client.js +949 -98
  25. package/dist/creditguard.generated.d.ts +79 -0
  26. package/dist/creditguard.generated.js +662 -0
  27. package/dist/cse-body.js +11 -1
  28. package/dist/datatrans.d.ts +96 -0
  29. package/dist/datatrans.generated.d.ts +64 -0
  30. package/dist/datatrans.generated.js +426 -0
  31. package/dist/datatrans.js +178 -0
  32. package/dist/elavon-converge.d.ts +37 -0
  33. package/dist/elavon-converge.generated.d.ts +49 -0
  34. package/dist/elavon-converge.generated.js +439 -0
  35. package/dist/elavon-converge.js +84 -0
  36. package/dist/eprotect.d.ts +152 -0
  37. package/dist/eprotect.generated.d.ts +77 -0
  38. package/dist/eprotect.generated.js +498 -0
  39. package/dist/eprotect.js +222 -0
  40. package/dist/hosted-form.d.ts +3 -2
  41. package/dist/hosted-form.js +3 -2
  42. package/dist/index.d.ts +17 -3
  43. package/dist/index.js +6 -1
  44. package/dist/juspay-gate.d.ts +82 -0
  45. package/dist/juspay-gate.js +126 -0
  46. package/dist/juspay.generated.d.ts +89 -0
  47. package/dist/juspay.generated.js +705 -0
  48. package/dist/lifecycle.d.ts +57 -6
  49. package/dist/lifecycle.js +119 -8
  50. package/dist/merchant-card-endpoints.generated.d.ts +63 -0
  51. package/dist/merchant-card-endpoints.generated.js +1103 -0
  52. package/dist/merchant-hosted.d.ts +4 -1
  53. package/dist/merchant-hosted.js +5 -2
  54. package/dist/merchant-session-probe.d.ts +48 -0
  55. package/dist/merchant-session-probe.js +53 -0
  56. package/dist/merchant-session.generated.d.ts +69 -0
  57. package/dist/merchant-session.generated.js +919 -0
  58. package/dist/payrails.d.ts +133 -0
  59. package/dist/payrails.generated.d.ts +73 -0
  60. package/dist/payrails.generated.js +596 -0
  61. package/dist/payrails.js +305 -0
  62. package/dist/pelecard.d.ts +32 -0
  63. package/dist/pelecard.generated.d.ts +32 -0
  64. package/dist/pelecard.generated.js +283 -0
  65. package/dist/pelecard.js +42 -0
  66. package/dist/pre-claim.d.ts +72 -2
  67. package/dist/pre-claim.js +857 -5
  68. package/dist/preflight-catalog.json +2218 -287
  69. package/dist/preflight-schemas.json +179 -6
  70. package/dist/preflight.generated.d.ts +1 -1
  71. package/dist/preflight.generated.js +233 -4
  72. package/dist/preparation.d.ts +5 -2
  73. package/dist/preparation.js +36 -10
  74. package/dist/prepared-processor.d.ts +84 -5
  75. package/dist/prepared-processor.js +147 -6
  76. package/dist/primer.d.ts +53 -0
  77. package/dist/primer.generated.d.ts +38 -0
  78. package/dist/primer.generated.js +624 -0
  79. package/dist/primer.js +82 -0
  80. package/dist/registry.d.ts +100 -8
  81. package/dist/registry.js +85 -11
  82. package/dist/shiji-sts.d.ts +144 -0
  83. package/dist/shiji-sts.generated.d.ts +72 -0
  84. package/dist/shiji-sts.generated.js +913 -0
  85. package/dist/shiji-sts.js +222 -0
  86. package/dist/stripe-checkout.generated.d.ts +1 -1
  87. package/dist/stripe-checkout.generated.js +699 -9
  88. package/dist/stripe-setup.d.ts +16 -0
  89. package/dist/stripe-setup.js +61 -0
  90. package/dist/substitutions.generated.js +187 -29
  91. package/dist/verifone.d.ts +68 -0
  92. package/dist/verifone.generated.d.ts +51 -0
  93. package/dist/verifone.generated.js +1401 -0
  94. package/dist/verifone.js +119 -0
  95. package/dist/voice-types.d.ts +27 -0
  96. package/dist/voice.d.ts +6 -1
  97. package/dist/voice.js +6 -1
  98. package/dist/wspay.d.ts +41 -0
  99. package/dist/wspay.generated.d.ts +41 -0
  100. package/dist/wspay.generated.js +370 -0
  101. package/dist/wspay.js +65 -0
  102. package/examples/preflight/kernel-native/inventory.json +1 -1
  103. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 26a574d3944a8fbf0c12ddc9378d2ae3772bd2cdc78f95a5db1b61c037ee5474
3
+ // artifact-sha256: f3bee23e52b029d04c67ea1b9249334bd649c09b46367ee3c8afcb841a7cb50d
4
4
  // src/deep-freeze.js
5
5
  function deepFreeze(value) {
6
6
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -205,9 +205,10 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
205
205
  // Accept.js. PAN@ securePaymentContainerRequest.data.token.cardNumber.
206
206
  // Auth is body-level (merchantAuthentication: apiLoginID + clientKey) — nothing to replay.
207
207
  // NOTE: prod is api2, not api. `api.authorize.net` is the server-side AIM/API host and is
208
- // deliberately NOT allowlisted — a browser never posts a PAN there. Legacy SIM/DPM
209
- // (secure2.authorize.net/gateway/transact.dll) is excluded for the same reason plus: it is
210
- // retired, and it is a top-level form POST rather than an XHR.
208
+ // deliberately NOT allowlisted: a browser never posts a PAN there. SIM and DPM
209
+ // (secure2.authorize.net/gateway/transact.dll) are deprecated by Authorize.Net, not retired, and
210
+ // post the card as a top-level form navigation rather than an XHR: the separate
211
+ // authorize_net_dpm entry (mode hosted_form) covers them, on hosts this entry never shares.
211
212
  // Coverage caveat: AcceptUI (the hosted lightbox) renders its fields inside an
212
213
  // accept.authorize.net iframe, so that POST is cross-origin to the merchant page and an
213
214
  // in-page hook cannot observe it. This entry covers the Accept.js custom-fields path, which
@@ -232,6 +233,35 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
232
233
  encoding: "json",
233
234
  passthroughHeaders: ["^accept$"]
234
235
  },
236
+ {
237
+ psp: "worldpay_eprotect",
238
+ // Worldpay eProtect (Vantiv / Litle PayPage), not Access Worldpay above.
239
+ // The first card request that is a GET: the vendor's eProtect-api / litle-api scripts (and the
240
+ // Worldpay iframe, which runs them) inject ONE JSONP <script> whose query carries the PAN and CVV
241
+ // RSA-encrypted under the vendor's per-environment key. eprotect.js holds the query grammar,
242
+ // the pinned keys and the answer rules; recognition also requires isEprotectTokenRequest, so
243
+ // ppstats, script files, payframes and every other /eProtect/paypage query stay unrecognized.
244
+ // The alternation is wrapped (the EBANX rule) and the path has a right-edge guard for the SDK's
245
+ // unanchored test.
246
+ match: "(?:request\\.eprotect\\.vantivcnp\\.com|request\\.eprotect\\.vantivprelive\\.com)/eProtect/paypage(?![\\w/.-])",
247
+ hosts: ["^request\\.eprotect\\.vantivcnp\\.com$", "^request\\.eprotect\\.vantivprelive\\.com$"],
248
+ // Pre-live (certification) host: sandbox rows only, and only the guide's test cards on the device.
249
+ sandboxHosts: ["^request\\.eprotect\\.vantivprelive\\.com$"],
250
+ hostEnvironmentPinned: true,
251
+ // Ships dark: served only to an SDK that declares this capability, and gated off for every org
252
+ // until an operator allowlists one (CHECKOUT_GATED_PROCESSOR_ORGS).
253
+ capability: "worldpay_eprotect_jsonp",
254
+ // Merchant deadlines are 10 to 15 s with a 5 s failover, so the cardholder approves before Pay.
255
+ preparationRequired: true,
256
+ // `encoding` describes the query, since the request has no body. No captured header is ever
257
+ // replayed, and the card rides in the query of one GET whose answer is JSONP.
258
+ encoding: "form",
259
+ passthroughHeaders: [],
260
+ request: { method: "GET", cardIn: "query", response: "jsonp" },
261
+ // The api3 failover host re-sends the same query after 5 s. The SDK aborts it; it is never a
262
+ // destination, so it is in no `hosts`, manifest, CSP or relay list.
263
+ abortHosts: ["^secondary\\.eprotect\\.vantivcnp\\.com$"]
264
+ },
235
265
  {
236
266
  psp: "nuvei",
237
267
  // Safecharge Fields. PAN@ paymentOption.card.cardNumber.
@@ -247,7 +277,20 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
247
277
  hosts: ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"],
248
278
  sandboxHosts: ["^ppp-test\\.safecharge\\.com$"],
249
279
  encoding: "json",
250
- passthroughHeaders: []
280
+ passthroughHeaders: [],
281
+ // Where Nuvei's own scripts put the card, read from safecharge.js and
282
+ // fields/sfc_inner_card.min.js: paymentOption.card on all three endpoints,
283
+ // and cardData when Nuvei Fields' getToken posts cardTokenization.do. The
284
+ // initPayment and clientPayment endpoints also pay without a card: with a
285
+ // ccTempToken the merchant's server issued (Air France completes its Nuvei
286
+ // payments this way, behind Amadeus), a saved userPaymentOptionId, a wallet
287
+ // or an APM. createPayment sends each of those to initPayment first and then
288
+ // to clientPayment, so both continue untouched. A tokenization without a
289
+ // card is refused. So is a payment without a card once the attachment has
290
+ // handed Nuvei the card: its token may be one Nuvei minted from the
291
+ // approved card (checkout client.ts withoutCard).
292
+ jsonCardFields: [["paymentOption", "card", "cardNumber"], ["cardData", "cardNumber"]],
293
+ passWithoutCard: "(?:secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:clientPayment|websdk/initPaymentWithCardTokenization)\\.do"
251
294
  },
252
295
  // bluesnap: DELIBERATELY NOT SHIPPED — it cannot be intercepted at all. Its tokenizer is a PUT, and isCardRequest
253
296
  // gates on POST — but accepting PUT is not enough on its own. hpf.js fires the
@@ -517,6 +560,139 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
517
560
  encoding: "json",
518
561
  passthroughHeaders: ["^authorization$", "^x-gp-version$"]
519
562
  },
563
+ {
564
+ psp: "authorize_net_dpm",
565
+ // Authorize.Net Direct Post Method and SIM with a relay response: deprecated by Authorize.Net, still
566
+ // served, end of life "to be determined" (upgrade guide; SIM guide ch. 3 and app. C; sdk-php 1.9.9
567
+ // AuthorizeNetDPM, sdk-dotnet 1.8.1 DPMFormGenerator, sdk-ruby 1.8.1, sdk-java 1.8.0). The merchant's
568
+ // own card form (DPM), or Authorize.Net's hosted form the merchant frames (SIM, x_show_form=pf_receipt),
569
+ // posts ONE form navigation, application/x-www-form-urlencoded, to /gateway/transact.dll with the
570
+ // plaintext card in x_card_num, x_exp_date (one field: MMYY, MM/YY, MM-YY, MMYYYY, MM/YYYY or
571
+ // MM-YYYY) and, only when the merchant collects it, x_card_code. Beside it rides the merchant's
572
+ // fingerprint: x_login (the public API Login ID), x_fp_sequence, x_fp_timestamp and x_fp_hash, an HMAC
573
+ // over login^sequence^timestamp^amount^currency under the merchant's Signature Key. Authorize.Net
574
+ // refuses it after 1 hour (97), spends it once (98) and refuses a changed amount (99). It POSTs the
575
+ // result to x_relay_url server to server, then answers whichever browser submitted with the
576
+ // merchant's relay page. No cookie, no auth header and no encryption, so this is Tranzila's
577
+ // hosted_form: the cardholder's device rebuilds and submits the same form and the paused navigation
578
+ // resolves with the synthetic page. No relay and no prepare(): the create refuses a fingerprint more
579
+ // than 40 minutes old or more than 15 minutes ahead instead (authorize-net-dpm.js, with the rest of
580
+ // the template policy). Authorize.Net reads field names without regard to case (live forms post
581
+ // x_Background_URL and x_cust_ID), so every key comparison for this entry ignores case.
582
+ // The same URL without a card continues untouched: SIM step 1 (x_show_form=PAYMENT_FORM), an eCheck
583
+ // (x_method=ECHECK) and Visa Checkout on the SIM form. NOT here: api2 and apitest (Accept.js, the
584
+ // authorize_net entry); never armed: cardpresent., api., accept., captcha., account. and
585
+ // sandbox.authorize.net. Hosts are the three literal labels. test.authorize.net is Authorize.Net's
586
+ // sandbox gateway, pinned to test rows and documented test cards (S7).
587
+ // Ships dark: served only to a client that declares authorize_net_dpm_form, gated off for every org
588
+ // until an operator allowlists one, and never sent by autopilot (EXCLUDED_OPERATIONS).
589
+ capability: "authorize_net_dpm_form",
590
+ match: "(secure2|secure|test)\\.authorize\\.net/gateway/transact\\.dll(?![\\w/.-])",
591
+ hosts: ["^secure2\\.authorize\\.net$", "^secure\\.authorize\\.net$", "^test\\.authorize\\.net$"],
592
+ sandboxHosts: ["^test\\.authorize\\.net$"],
593
+ hostEnvironmentPinned: true,
594
+ // The endpoint is the bare URL: a query string or a fragment, even an empty one, is not this
595
+ // endpoint (recognition.js resolveRecognizerForUrl), so the create, the vault and the enclave all
596
+ // refuse it. transact.dll can read a query parameter as a form field, which would carry x_tran_key,
597
+ // x_type or x_relay_url past the template policy, and the URL is stored with the row.
598
+ bareEndpoint: true,
599
+ encoding: "form",
600
+ passthroughHeaders: [],
601
+ caseInsensitiveFormKeys: true,
602
+ cardFields: ["x_card_num"],
603
+ passWithoutCard: "(secure2|secure|test)\\.authorize\\.net/gateway/transact\\.dll",
604
+ mode: "hosted_form",
605
+ hostedForm: {
606
+ required: ["x_login", "x_amount", "x_fp_sequence", "x_fp_timestamp", "x_fp_hash", "x_relay_response"],
607
+ // One composed expiry field, written in the width and separator of the agent's dummy.
608
+ card: { number: "x_card_num", expiry: "x_exp_date", cvc: "x_card_code" },
609
+ // Filled only when the template carries the key; never added.
610
+ optional: ["cvc"],
611
+ amount: { sum: "x_amount", currency: "x_currency_code" },
612
+ // Authorize.Net spends a fingerprint once, so the fingerprint is the submission.
613
+ dedupe: ["x_login", "x_fp_sequence", "x_fp_timestamp", "x_fp_hash"],
614
+ // Absent or empty also means AUTH_CAPTURE.
615
+ sale: { key: "x_type", values: ["AUTH_CAPTURE"] },
616
+ callbackUrls: ["x_relay_url", "x_receipt_link_url", "x_cancel_url"],
617
+ policy: "authorize_net_dpm"
618
+ }
619
+ },
620
+ {
621
+ psp: "primer",
622
+ // Primer Web SDK v2 (Headless Checkout and hosted fields): Printful (printful.com) loads
623
+ // sdk.primer.io/web/v2.63.0; GetYourGuide (getyourguide.com) tokenizes with Primer's hosted fields and
624
+ // Primer routes the token to Adyen, Checkout.com or J.P. Morgan server side. PAN@ paymentInstrument.number,
625
+ // with cvv, expirationMonth (MM) and expirationYear (YYYY) beside it. Primer's hidden api-controller iframe
626
+ // (origin https://sdk.primer.io) resolves the core's "$.cardNumber-card" placeholders and POSTs plaintext
627
+ // JSON to `${pciUrl}/payment-instruments`, with the session's primer-client-token and no client-side
628
+ // encryption. pciUrl comes from the session: sdk.api.sandbox.primer.io (Primer's iOS client-token fixture),
629
+ // sdk.api.primer.io (production, inferred from the same naming and DNS; confirmed on first observation).
630
+ // The same URL also tokenizes PayPal, Apple Pay, Google Pay and Klarna with no card: jsonCardFields and
631
+ // passWithoutCard let those continue untouched. The siblings on the host (/{id}/exchange for a saved card,
632
+ // /{id}/vault, /payments, /3ds, /three-d-secure, /client-session) never carry a new card, and the
633
+ // right-edge boundary and the exact-URL rule in resolveRecognizerForUrl keep them out. primer.js judges the
634
+ // whole request, refuses a body that asks Primer to save the card (tokenType MULTI_USE, paymentFlow VAULT),
635
+ // Primer Checkout's primer-js SDK (its request gives up after 30 s) and a preferredNetwork that is not the
636
+ // card's own, and writes only the card fields. Staging and dev hosts are deliberately absent, and the
637
+ // sandbox host is pinned (S7): a sandbox row sends only documented test cards there, a live row never
638
+ // points at it. The request names no amount: the token is charged later.
639
+ // Ships dark: served only to a client that declares `primer_payment_instruments`, gated off for every
640
+ // org until an operator allowlists one, and never replayed by autopilot (EXCLUDED_OPERATIONS). A client
641
+ // session can save the card with no sign in the request, so the API also refuses every live row at
642
+ // create until the cardholder can consent to that save; only a test row pays.
643
+ capability: "primer_payment_instruments",
644
+ match: "sdk\\.api(?:\\.sandbox)?\\.primer\\.io/payment-instruments(?![\\w/.-])",
645
+ hosts: ["^sdk\\.api\\.primer\\.io$", "^sdk\\.api\\.sandbox\\.primer\\.io$"],
646
+ sandboxHosts: ["^sdk\\.api\\.sandbox\\.primer\\.io$"],
647
+ hostEnvironmentPinned: true,
648
+ encoding: "json",
649
+ // What Primer's controller sends beside the body; primer.js checks their shapes before any replay.
650
+ passthroughHeaders: [
651
+ "^primer-client-token$",
652
+ "^primer-sdk-client$",
653
+ "^primer-sdk-version$",
654
+ "^primer-sdk-checkout-session-id$",
655
+ "^x-api-version$",
656
+ "^x-request-id$"
657
+ ],
658
+ jsonCardFields: [["paymentInstrument", "number"]],
659
+ passWithoutCard: "sdk\\.api(?:\\.sandbox)?\\.primer\\.io/payment-instruments"
660
+ },
661
+ {
662
+ psp: "amadeus_checkout",
663
+ // Outpayce (Amadeus Payment Platform): Air France and KLM, one shared release.
664
+ // PAN@ data.mopdata.pan (JSON, plaintext inside TLS). Two card-bearing XHRs on one host:
665
+ // 1. Checkout Web SDK: the SDK hands {PPID, data, action} to its tunnel iframe
666
+ // (prd.payment.amadeus.com/checkout/sdk/<v>/form-fields/tunnel.html), which swaps the pan and
667
+ // CVV iframes' ids for the typed digits and posts it to ARIAPP/pay: text/plain, no cookies, a
668
+ // 30 s timeout, HTTP 200 only.
669
+ // 2. Data Collection Page (DCP): the page at /1ASIATP/ATPDCP/land/?DCPID= posts
670
+ // {DCPID, action, data} to ATPDCP/collect, same-origin JSON, the whole card in one body.
671
+ // ONE URL, MANY ACTIONS. Only an add carrying a card mopid's pan, or a store carrying a pan, is a
672
+ // card request (amadeus-body.js, which card-fields.js asks for this psp); load, bin, prepare,
673
+ // obfees, installments, tdsinit, verify, fetch, cancel, the wallet actions and DCP bin_check,
674
+ // expired and back carry at most the agent's dummy and continue untouched (passWithoutCard).
675
+ // amadeus-checkout.js holds the exact-URL rule, the whole-request check, both fills and the answer
676
+ // judge (sources there). Hosts are the two literal paypages hosts, never a wildcard label:
677
+ // paypages.dev (1ASIATPD), the airline intranet hosts, the playground, paypages.*.payment.outpayce.com
678
+ // (resolves, but no shipped SDK allowlists it) and the hosted page's own origin are absent, and the
679
+ // test host takes no DCP store until one is observed. The test host is pinned (S7): a create
680
+ // refuses it on a live row, and the device sends it only documented test cards.
681
+ // Ships dark: served only to a client that declares amadeus_checkout_actions.v1 (one that judges
682
+ // these bodies and prepares before the page's first request), gated off for every org until an
683
+ // operator allowlists one, prepared before Pay (S4: the tunnel abandons the add after 30 s), and
684
+ // never replayed by autopilot (EXCLUDED_OPERATIONS).
685
+ capability: "amadeus_checkout_actions.v1",
686
+ match: "(?:paypages\\.payment\\.amadeus\\.com/1ASIATP/(?:ARIAPP/pay|ATPDCP/collect)|paypages\\.test\\.payment\\.amadeus\\.com/1ASIATP[UMK]?/ARIAPP/pay)(?![\\w/.-])",
687
+ hosts: ["^paypages\\.payment\\.amadeus\\.com$", "^paypages\\.test\\.payment\\.amadeus\\.com$"],
688
+ sandboxHosts: ["^paypages\\.test\\.payment\\.amadeus\\.com$"],
689
+ hostEnvironmentPinned: true,
690
+ preparationRequired: true,
691
+ encoding: "json",
692
+ passthroughHeaders: ["^amadeus-checkout-sdk-version$", "^amadeus-checkout-sdk-host$", "^amadeus-checkout-sdk-flavor$"],
693
+ jsonCardFields: [["data", "mopdata", "pan"]],
694
+ passWithoutCard: "(?:paypages\\.payment\\.amadeus\\.com/1ASIATP/(?:ARIAPP/pay|ATPDCP/collect)|paypages\\.test\\.payment\\.amadeus\\.com/1ASIATP[UMK]?/ARIAPP/pay)"
695
+ },
520
696
  // ── Encrypted on the device under a pinned key ────────────────────────────
521
697
  {
522
698
  psp: "fiserv",
@@ -573,6 +749,38 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
573
749
  fields: ["keyId", "encryptedKey", "encryptionBlock", "aesIv"]
574
750
  }
575
751
  },
752
+ {
753
+ psp: "verifone",
754
+ // Verifone eCommerce Secure Card Capture (verifone.js), The Container Store's card path. The
755
+ // merchant's page encrypts the card with OpenPGP under the merchant's own Verifone key and
756
+ // posts the blob to the MERCHANT's server, which calls Verifone: nothing card-bearing goes
757
+ // from the browser to a Verifone host. So this entry recognizes no URL and names no host: a
758
+ // Verifone card request is found only through a reviewed merchant profile
759
+ // (verifone-profiles.js), whose endpoint, body rules and pinned key (verifone-keys.js) the
760
+ // backend, the SDK and the Vault page read by the profile's id (verifone.js). `hosts` is empty
761
+ // and `match` never matches, so the registry, the enclave's destinations and the SDK's card
762
+ // patterns gain nothing, and resolveRecognizerForUrl never resolves a URL to verifone. The
763
+ // blob is a 15-minute bearer at the merchant and a 3-D Secure challenge needs the cardholder,
764
+ // so the cardholder approves every payment: dark until an org is allowlisted, and autopilot
765
+ // refuses verifone at all three gates.
766
+ capability: "verifone_card_capture",
767
+ // A Verifone card is encrypted only for a request the cardholder already approved through a
768
+ // prepare() for the same reviewed profile: the backend refuses an unprepared create (409
769
+ // preparation_required) and the SDK refuses locally before it prompts, as for Fiserv.
770
+ preparationRequired: true,
771
+ match: "(?!)",
772
+ hosts: [],
773
+ encoding: "json",
774
+ // The browser continues the merchant's own request with its own headers; none is stored.
775
+ passthroughHeaders: [],
776
+ clientSideEncrypted: true,
777
+ mode: "cse",
778
+ cse: {
779
+ // Where the blob sits is each profile's leg (holder and field), never one path for the psp.
780
+ at: "merchant_profile",
781
+ fields: ["encryptedCard"]
782
+ }
783
+ },
576
784
  {
577
785
  psp: "gravy",
578
786
  // Gravy (Gr4vy) payment orchestration, Secure Fields. PAN@ payment_method.number,
@@ -658,9 +866,369 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
658
866
  hostEnvironmentPinned: true,
659
867
  encoding: "jwe",
660
868
  passthroughHeaders: []
869
+ },
870
+ {
871
+ psp: "juspay",
872
+ // Juspay Express Checkout, the merchant-page card flow Swiggy web ships (reviewed bundle
873
+ // web-payments.7cf02545077d488.js, SHA-256 08336f74599b7f756e4b17ed8747af8caa5d248879f7c0dd3d89f0abd8347504).
874
+ // The page tokenizes the card at /card/tokenize and pays at /txns with the token; juspay.js
875
+ // holds the whole protocol and says why only /txns is taken over.
876
+ // - Paused: the POST /txns that carries a synthetic token the SDK answered the placeholder
877
+ // tokenize with (never the tokenize itself: a real ctkn_ needs no merchant secret and pays
878
+ // any order of that merchant for 15 minutes). The SDK answers the placeholder tokenize and
879
+ // refuses the /v2/card/fingerprint lookup, so the placeholder never reaches Juspay.
880
+ // - deviceSteps.tokenize: the one other request the cardholder's device sends, the real
881
+ // card's tokenize, on the paused request's own host (request-policy.js
882
+ // assertAllowedDeviceStep). Definitions-only: the served row does not carry it.
883
+ // - NOT here, on purpose: api.juspay.io and api-ns1.juspay.in. Juspay operates both and
884
+ // serves the same API there, but no reviewed page was seen sending a card to either; the
885
+ // SDK refuses a card-bearing request to them instead of pausing it.
886
+ // - Headers: x-merchantid and x-routing-id only. client_auth_token can be scoped to a
887
+ // customer and Swiggy's card legs never send it; the merchant's API key must never reach
888
+ // a browser, so authorization is never passed.
889
+ // - Dark: served only to a client that declares juspay_txns.v1, and off for every org
890
+ // until one is allowlisted. The sandbox host takes only documented test cards (S7).
891
+ // - Autopilot never replays it: the enclave sends one request and Juspay needs two.
892
+ capability: "juspay_txns.v1",
893
+ match: "(api|sandbox)\\.juspay\\.in/txns(?![\\w/-])",
894
+ hosts: ["^api\\.juspay\\.in$", "^sandbox\\.juspay\\.in$"],
895
+ sandboxHosts: ["^sandbox\\.juspay\\.in$"],
896
+ hostEnvironmentPinned: true,
897
+ encoding: "form",
898
+ passthroughHeaders: ["^x-merchantid$", "^x-routing-id$"],
899
+ deviceSteps: { tokenize: "(api|sandbox)\\.juspay\\.in/card/tokenize" }
900
+ },
901
+ {
902
+ psp: "datatrans",
903
+ // Datatrans / PCI Proxy Secure Fields 2.0.0 in TOKENIZE mode (field.js 3.24.602):
904
+ // British Airways's Nexus checkout (merchantId 3000024893) and legacy ba-sell (3000025858). PAN@
905
+ // cardNumber (form). Datatrans's own card-number iframe document on pay.datatrans.com sends the
906
+ // whole card as one jQuery XHR to its relative URL `paymentField`: form-encoded, with the CVV read
907
+ // from its same-origin CVV frame and the expiry (expm, expy) from the merchant page, plus mode,
908
+ // formId, paymentMethod, merchantId and usage; no amount, key, cookie or signature. The server keys
909
+ // the form by formId in the body, and the replay gate (PCI Proxy's public sandbox merchant) showed
910
+ // it accepts that body from another client with no cookie, origin or referer.
911
+ // paymentField/cardInfo is a 12-digit BIN lookup on the same host and must never match: the
912
+ // right-edge boundary and the exact-URL rule in resolveRecognizerForUrl keep it (and every query,
913
+ // port and case variant) out. Wallet, bank-account and CVV-only posts share the URL and carry no
914
+ // cardNumber: cardFields and passWithoutCard let them continue untouched. The GET that loads the
915
+ // iframe document shares the path, and the POST gate keeps it out. datatrans.js judges the whole
916
+ // request, refuses usage=REGISTRATION (card on file) until that decision is made and a
917
+ // paymentMethod that is not the card's own, and writes only cardNumber, cvv, expm and expy.
918
+ // Hosts are the two literal labels (never *.datatrans.com: api., 3d. and admin. are other
919
+ // services, and PCI Proxy's custom domains are not recognized); the sandbox host is pinned (S7):
920
+ // it takes only documented test cards, and a live row never points at it.
921
+ // Ships dark: served only to a client that declares `datatrans_secure_fields_tokenize`, gated off
922
+ // for every org until an operator allowlists one, and never replayed by autopilot
923
+ // (EXCLUDED_OPERATIONS).
924
+ capability: "datatrans_secure_fields_tokenize",
925
+ match: "pay(?:\\.sandbox)?\\.datatrans\\.com/upp/payment/SecureFields/paymentField(?:[?#]|$)",
926
+ hosts: ["^pay\\.datatrans\\.com$", "^pay\\.sandbox\\.datatrans\\.com$"],
927
+ sandboxHosts: ["^pay\\.sandbox\\.datatrans\\.com$"],
928
+ hostEnvironmentPinned: true,
929
+ encoding: "form",
930
+ passthroughHeaders: [],
931
+ // the replay gate needed none: no x-requested-with, cookie, origin or referer
932
+ cardFields: ["cardNumber"],
933
+ passWithoutCard: "pay(?:\\.sandbox)?\\.datatrans\\.com/upp/payment/SecureFields/paymentField"
934
+ },
935
+ {
936
+ psp: "pelecard",
937
+ // Pelecard Iframe/Redirect 2.0, the hosted payment page Uman tourist tax (aumantax.com)
938
+ // loads in an iframe. PAN@ root CreditCardNumber, expiry MMYY @ CreditCardDate, CVV @ CvvNumber.
939
+ // Pelecard's own page (/PaymentGW?transactionId=<guid>) posts the flat SubmitModel JSON with one
940
+ // jQuery XHR (Payment.js sendAjax, sha256 75c37541...a669caea) to generalParameters.urlTransaction,
941
+ // the same host's /PaymentGW/Transaction: no cookie, no auth header, no client-side encryption.
942
+ // The amount, currency, action type (J4 charge, J2 registration, J5 hold) and CreateToken are bound
943
+ // to the TransactionId at the merchant's server-side init and never ride in the request.
944
+ // The same URL also serves the wallet, Bit, PIN pad and bank tabs with no card: jsonCardFields and
945
+ // passWithoutCard let those continue untouched. pelecard.js checks the exact URL (also in
946
+ // resolveRecognizerForUrl), validates the whole body, writes only the card fields, and refuses a
947
+ // saved token, a track, installments, a buyer-typed total and a body that asks to keep the card.
948
+ // NOT here: /PaymentGW/TimeOutTransaction (the dwell timer posts the same card fields there),
949
+ // /PaymentGW/CheckCreditCard, EndOfProcess, init and the other PaymentGW routes; gateway20qa
950
+ // (Pelecard QA); the legacy 1.0 page on gateway.pelecard.biz (/PayProcess/Transaction, a top-level
951
+ // form). Hosts are the two literal labels, never a wildcard. No sandboxHosts: Pelecard's public test
952
+ // terminals run on the same gateway21 host as live ones (the terminal bound to the TransactionId
953
+ // decides, and neither the request nor the page names it), so the S7 host rule has no test-only
954
+ // host to pin, and the device's test-card rule cannot tell a test payment from a live one here.
955
+ // A live row can therefore send a real card to a payment page created on a public test terminal,
956
+ // whose transactions anyone holding Pelecard's published demo credentials can read back.
957
+ // Ships dark: served only to a client that declares `pelecard_transaction`, gated off for every org
958
+ // until an operator allowlists one, and never replayed by autopilot (EXCLUDED_OPERATIONS).
959
+ capability: "pelecard_transaction",
960
+ match: "(?:gateway21|gateway20)\\.pelecard\\.biz/PaymentGW/Transaction(?![\\w/.-])",
961
+ hosts: ["^gateway21\\.pelecard\\.biz$", "^gateway20\\.pelecard\\.biz$"],
962
+ encoding: "json",
963
+ // jQuery's own same-origin marker, pinned to `XMLHttpRequest` by pelecard.js; nothing else rides.
964
+ passthroughHeaders: ["^x-requested-with$"],
965
+ jsonCardFields: [["CreditCardNumber"]],
966
+ passWithoutCard: "(?:gateway21|gateway20)\\.pelecard\\.biz/PaymentGW/Transaction"
967
+ },
968
+ {
969
+ psp: "aci",
970
+ // ACI Worldwide Open Payment Platform, COPYandPAY widget (Halfords). PAN@ card.number (form, dot keys).
971
+ // Each PCI iframe (/v1/pciIframe.html) posts the one field it holds, as a jQuery form XHR, to
972
+ // POST /v1/checkouts/{id} (no suffix); ACI merges them on the checkout. aci.js holds the leg
973
+ // rules, the fill and the answer judge (sources there). The id segment is the documented
974
+ // <32 hex>.<cluster>-vm-tx<nn>; (?![\w/.-]) is the right edge for the SDK's unanchored test and
975
+ // keeps /payment, /bins and /registration out of `match`. Hosts are the four literal hosts ACI
976
+ // serves the widget from, never a wildcard label; white-label OPP hosts need their own evidence.
977
+ // Ships dark: served only to a client that declares aci_split_card.v1 (one that holds the
978
+ // sibling legs below), gated off for every org until an operator allowlists one, and never
979
+ // replayed by autopilot (EXCLUDED_OPERATIONS).
980
+ capability: "aci_split_card.v1",
981
+ match: "(?:eu-prod\\.oppwa\\.com|oppwa\\.com|eu-test\\.oppwa\\.com|test\\.oppwa\\.com)/v1/checkouts/[0-9A-F]{32}\\.[a-z0-9]{1,16}-vm-tx[0-9]{1,3}(?![\\w/.-])",
982
+ hosts: ["^eu-prod\\.oppwa\\.com$", "^oppwa\\.com$", "^eu-test\\.oppwa\\.com$", "^test\\.oppwa\\.com$"],
983
+ // eu-test and test.oppwa.com are ACI's INTEGRATOR_TEST hosts. Pinned (S7): a create refuses a
984
+ // test host on a live row and a live host on a test row, and the device sends only documented
985
+ // test cards (ACI's own included, aci.js ACI_TEST_PANS) to a test host.
986
+ sandboxHosts: ["^eu-test\\.oppwa\\.com$", "^test\\.oppwa\\.com$"],
987
+ hostEnvironmentPinned: true,
988
+ // ACI's widget abandons Pay after its 60 s channel timeout, so the cardholder unlocks first (S4).
989
+ preparationRequired: true,
990
+ encoding: "form",
991
+ passthroughHeaders: [],
992
+ // no auth header; X-Requested-With is not replayed (it would force a CORS preflight)
993
+ cardFields: ["card.number"],
994
+ // One card, several legs on one checkout id. `absorb` legs (the page's CVC and expiry, dummy
995
+ // values) are held and answered with the approved leg's answer, never forwarded; `pass` legs
996
+ // (the holder) continue untouched; `siblingKeys` may ride any leg. `continuation` is the merchant
997
+ // form's /payment submission, which continues with `remove` taken out after `brandField` is
998
+ // checked against the approved card's network and every other key against the ones the widget
999
+ // sends (aci.js prepareAciContinuation).
1000
+ splitCard: {
1001
+ absorb: ["card.cvv", "card.expiryMonth", "card.expiryYear"],
1002
+ pass: ["card.holder"],
1003
+ siblingKeys: ["customParameters[SHOPPER_EndToEndIdentity]"],
1004
+ continuation: {
1005
+ match: "(?:eu-prod\\.oppwa\\.com|oppwa\\.com|eu-test\\.oppwa\\.com|test\\.oppwa\\.com)/v1/checkouts/[0-9A-F]{32}\\.[a-z0-9]{1,16}-vm-tx[0-9]{1,3}/payment(?![\\w/.-])",
1006
+ remove: ["card.expiryMonth", "card.expiryYear"],
1007
+ brandField: "paymentBrand"
1008
+ }
1009
+ }
1010
+ },
1011
+ {
1012
+ psp: "creditguard",
1013
+ // Hyp CreditGuard PPS, the hosted page merchants frame at https://pps.creditguard.co.il/?txId=<uuid>
1014
+ // (SeboCalm). Its Angular transaction service posts the card to /services/v1/txn/<txId>/process as
1015
+ // same-origin JSON, no auth header, no cookie: the txId is the only credential and the submit is the
1016
+ // sale itself. PAN@ root `cardNumber` (expMonth MM, expYear YY, cvv, personalId) in plaintext, or one
1017
+ // js-jose JWE in root `jwePayload` when the transaction carries a jwePublicKey (creditguard.js keeps
1018
+ // that variant behind a flag). Sources: templates/cgt-basic/main.js on pps and ppsuat, reviewed
1019
+ // 2026-09-25. /card/details, /card/details/dcc, /cc/ddc/init, /cg3ds/*, /otp/request and
1020
+ // /applepay|paybox|pcp/process share the prefix, carry the agent's placeholder or no card, and fall
1021
+ // out at the exact path; (?![\w/-]) is the right edge for the SDK's unanchored test. pps2 is a
1022
+ // production host (cg-safecharge-3ds envConfig: pps, pps2, cgmpi = prod). The legacy MPI page
1023
+ // (cgmpi.creditguard.co.il), pps-dev, ppsqa, pps-qa1 and cguat2 are deliberately absent.
1024
+ // creditguard.js judges the whole request before a card is written and fills only the card fields.
1025
+ // Ships dark: served only to a client that declares `creditguard_pps_process`, gated off for every
1026
+ // org until an operator allowlists one, never replayed by autopilot (EXCLUDED_OPERATIONS).
1027
+ capability: "creditguard_pps_process",
1028
+ match: "(?:pps|pps2|ppsuat)\\.creditguard\\.co\\.il/services/v1/txn/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/process(?![\\w/-])",
1029
+ hosts: ["^pps\\.creditguard\\.co\\.il$", "^pps2\\.creditguard\\.co\\.il$", "^ppsuat\\.creditguard\\.co\\.il$"],
1030
+ // ppsuat is CreditGuard's UAT page. Pinned (S7): a create refuses it on a live row and pps or pps2
1031
+ // on a test row, and the device sends only documented test cards to it.
1032
+ sandboxHosts: ["^ppsuat\\.creditguard\\.co\\.il$"],
1033
+ hostEnvironmentPinned: true,
1034
+ // S4: the submit names no amount, so a card is sent only after the cardholder approved a
1035
+ // prepare() that read the transaction (GET /services/v1/txn/<txId>, creditguard.js).
1036
+ preparationRequired: true,
1037
+ encoding: "json",
1038
+ // The device sends Content-Type (and Accept) JSON itself; nothing the page sent is replayed.
1039
+ passthroughHeaders: [],
1040
+ // A /process body without either (Google Pay, a saved cardId, a js-jose object fallback) is not a
1041
+ // new card, and with no passWithoutCard it is refused instead of paused.
1042
+ jsonCardFields: [["cardNumber"], ["jwePayload"]]
1043
+ },
1044
+ {
1045
+ psp: "payrails",
1046
+ // Payrails web SDK v6 (assets.payrails.io/web-sdk/v6/payrails.js 6.5.0), Blacklane's consumer
1047
+ // booking checkout: cardForm.tokenize() POSTs vaultConfiguration.links.saveInstrument.href. Body
1048
+ // (JSON.stringify, sent as text/plain): {holderReference, paymentMethod:'card', storeInstrument,
1049
+ // futureUsage, data:{encryptedData, vaultProviderConfigId}}. encryptedData is a compact JWE
1050
+ // (RSA-OAEP-256 + A256CBC-HS512, no kid) of {holderName, cardNumber, expiryMonth, expiryYear,
1051
+ // securityCode, holderReference}, built in the assets.payrails.io secure frame under the
1052
+ // session's vaultConfiguration.encryptionPublicKey. That key is per session, reaches us only
1053
+ // through the agent's page and cannot be pinned, so the mode is device_encrypt: the cardholder's
1054
+ // device encrypts the real card and sends this request itself (payrails-replay.js), and the
1055
+ // ciphertext never returns to the page. Dynamic payment-method forms save non-card instruments
1056
+ // on the same URL with no data.encryptedData: jsonCardFields claims only a card save and
1057
+ // passWithoutCard lets the rest continue untouched. payrails.js holds the request guard, the
1058
+ // card-on-file policy, the session-key and workspace checks and the answer rules. NOT here:
1059
+ // /public/payment/instruments/tokenize (web-cse used directly), api.payrails.io and the
1060
+ // execution authorize links (card-form payments, stored-card CVC), and assets.payrails.io.
1061
+ // `capability` keeps the entry out of BUILTIN_REGISTRY and every org until one opts in; the API
1062
+ // serves it only to a client that also asks for modes=device_encrypt. The two staging hosts are
1063
+ // pinned to sandbox rows (S7). Autopilot refuses it by operation (payrails.device_encrypted_card).
1064
+ capability: "payrails_device_encrypt",
1065
+ match: "(?:api-pub|api-pub\\.staging|sandbox-api-pub\\.staging)\\.payrails\\.io/public/payment/instruments(?![\\w/.-])",
1066
+ hosts: ["^api-pub\\.payrails\\.io$", "^api-pub\\.staging\\.payrails\\.io$", "^sandbox-api-pub\\.staging\\.payrails\\.io$"],
1067
+ sandboxHosts: ["^api-pub\\.staging\\.payrails\\.io$", "^sandbox-api-pub\\.staging\\.payrails\\.io$"],
1068
+ hostEnvironmentPinned: true,
1069
+ encoding: "json",
1070
+ // body shape; the SDK sends text/plain, replayed verbatim
1071
+ passthroughHeaders: ["^authorization$", "^x-idempotency-key$", "^x-client-version$", "^x-client-type$"],
1072
+ jsonCardFields: [["data", "encryptedData"]],
1073
+ passWithoutCard: "(?:api-pub|api-pub\\.staging|sandbox-api-pub\\.staging)\\.payrails\\.io/public/payment/instruments",
1074
+ clientSideEncrypted: true,
1075
+ // older SDKs read this and refuse locally
1076
+ mode: "device_encrypt",
1077
+ deviceEncrypt: {
1078
+ scheme: "payrails-jwe",
1079
+ // RSA-OAEP-256 + A256CBC-HS512, compact
1080
+ key: "session",
1081
+ // from the init payload the runtime captured, never pinned
1082
+ at: ["data", "encryptedData"],
1083
+ keyBits: [2048, 3072, 4096],
1084
+ // e = 65537; the wrapped key's length must equal the modulus length
1085
+ futureUsage: ["CardOnFile", "UnscheduledCardOnFile"],
1086
+ expiryYear: "YY"
1087
+ // Blacklane's single MM/YY field
1088
+ }
1089
+ },
1090
+ {
1091
+ psp: "elavon_converge",
1092
+ // Elavon Converge Hosted Payment Page (redirect, pageType hpp), NY DMV. Converge's
1093
+ // own AngularJS page posts the card as same-origin JSON to /hosted-payments/service/payment/hpp/process.
1094
+ // PAN@ paymentData.ssl_card_number (digits), expiry@ paymentData.ssl_exp_date (MMYY), CVC@
1095
+ // paymentData.ssl_cvv2cvc2. No client-side encryption; the session rides in the body (`context`).
1096
+ // Sources: assets/hpp-2.505.07.js, byte-identical on both hosts, reviewed 2026-09-25. The same URL
1097
+ // carries wallet, ACH and loan payments and the follow-ups after 3DS2, Pay Defense or a surcharge
1098
+ // quote: the Converge body gate decides (elavon-converge.js convergeBodyVerdict, which card-fields.js
1099
+ // asks first, so the SDK's generated copy carries it). The lightbox (hpm/process), Checkout.js
1100
+ // (hpe/process) and every other hpp/ call fall out at the exact path; (?![\w/.%-]) is the right edge
1101
+ // for the SDK's unanchored test. Hosts are two literal labels, never *.convergepay.com (www and demo
1102
+ // are the merchant web UI).
1103
+ // Ships dark: served only to a client that declares `converge_hpp_process`, gated off for every org
1104
+ // until an operator allowlists one, never replayed by autopilot (EXCLUDED_OPERATIONS).
1105
+ capability: "converge_hpp_process",
1106
+ match: "api(?:\\.demo)?\\.convergepay\\.com/hosted-payments/service/payment/hpp/process(?![\\w/.%-])",
1107
+ hosts: ["^api\\.convergepay\\.com$", "^api\\.demo\\.convergepay\\.com$"],
1108
+ // api.demo is Converge's demo environment. Pinned (S7): a create refuses it on a live row and the
1109
+ // production host on a test row, and the device sends only documented test cards to it.
1110
+ sandboxHosts: ["^api\\.demo\\.convergepay\\.com$"],
1111
+ hostEnvironmentPinned: true,
1112
+ encoding: "json",
1113
+ // The device sends Content-Type JSON itself; no Origin, Cookie, Referer or XSRF header is replayed.
1114
+ passthroughHeaders: [],
1115
+ jsonCardFields: [["paymentData", "ssl_card_number"]],
1116
+ // An electronic check or financing payment on the same URL carries no card and continues untouched.
1117
+ passWithoutCard: "api(?:\\.demo)?\\.convergepay\\.com/hosted-payments/service/payment/hpp/process"
1118
+ },
1119
+ {
1120
+ psp: "wspay",
1121
+ // WSPay Components (WSPay's build of Monri Components 1.6.0), Valamar's booking payment.
1122
+ // PAN@ payment_method.data.pan, expiry YYMM @ .expiration_date, cvv @ .cvv. The processor
1123
+ // iframe (/v2/js/payment-method-component-dynamic.html) XHRs POST /v2/payment/{client_secret}/confirm
1124
+ // with `Authorization: WP3-v2-Client <authenticity token>` (proxy /dist/monri.js sha256 33fb5b45...).
1125
+ // /confirm is the only card route the proxy serves: /v2/payment/{cs}/token and /v2/temp-tokenize
1126
+ // answer 404 exactly like bogus control paths (card-free probes, 2026-09-23). The same URL also
1127
+ // carries saved_card and temp_card_id bodies with no new card, so wspay.js validates the whole
1128
+ // body, writes only the three card fields and never uses the generic walk; there is no `compose`
1129
+ // on purpose (it widens what the generic walk would call a card). The client_secret segment is a
1130
+ // per-payment bearer secret. (?![\w/-]) is the right edge for the SDK's unanchored test.
1131
+ // NOT here: ipg.monri.com / ipgtest.monri.com (Monri direct: no CORS headers, behind Cloudflare,
1132
+ // needs the relay; a separate `monri` entry), ipgdev.monri.com, the ipg[test].webteh.hr hosts in
1133
+ // the bundle's whitelist (they do not resolve) and form[test].wspay.biz (WSPay Form, another product).
1134
+ // Ships dark: the recognizer is served only to a client that declares `wspay_confirm` for an
1135
+ // org an operator allowlisted, the processor is gated off for every other org, and autopilot
1136
+ // never replays it (EXCLUDED_OPERATIONS). GET /coverage and POST /coverage/assess describe it
1137
+ // on the same rule; only the preflight rules, which identify its scripts and frames, name
1138
+ // WSPay to every client. A confirm that asks WSPay to keep the card on file is refused unless
1139
+ // the caller carries the cardholder's consent (wspay.js).
1140
+ capability: "wspay_confirm",
1141
+ match: "proxy(?:-test)?\\.wspay\\.biz/v2/payment/[A-Za-z0-9_-]{1,128}/confirm(?![\\w/-])",
1142
+ hosts: ["^proxy\\.wspay\\.biz$", "^proxy-test\\.wspay\\.biz$"],
1143
+ // proxy-test is WSPay's test platform. Pinned (S7): a create refuses it on a live row and
1144
+ // proxy.wspay.biz on a test row, and the device sends it only the shared public network test
1145
+ // cards (test-pans.js), never a real card. WSPay's own test card list is unverified.
1146
+ sandboxHosts: ["^proxy-test\\.wspay\\.biz$"],
1147
+ hostEnvironmentPinned: true,
1148
+ encoding: "json",
1149
+ // The iframe's own Accept is load-bearing (Worldpay precedent); cookie/origin/referer never ride.
1150
+ passthroughHeaders: ["^authorization$", "^accept$"]
1151
+ },
1152
+ {
1153
+ psp: "shiji_sts",
1154
+ // Shiji Payment Solutions STS, the card leg of the MyCheck Wallet V3 (Shiji Group) that La Quinta
1155
+ // by Wyndham loads in its booking page. sts.proxy.v2.1.js encrypts the PAN digits and the "MM/YY"
1156
+ // expiry, each on its own, with JSEncrypt (RSAES-PKCS1-v1_5) under the RSA-1024 key it hardcodes,
1157
+ // and fetch-POSTs application/json {accessToken, pan, expires, keySequence?} to /v1/token/getTokenId;
1158
+ // after MyCheck's payment-methods call it posts {accessToken, cvv, keySequence?} to
1159
+ // /v1/token/getCvvId. ONE entry covers both paths: they share the host, and resolveRecognizerForUrl
1160
+ // takes the first definition by host, so a second entry would never be reached. shiji-sts.js judges
1161
+ // which a request is: getTokenId is the one prompt, getCvvId a companion that never prompts and is
1162
+ // continued only when bound to the getTokenId the runtime continued. The device encrypts under the
1163
+ // key shiji-sts.js pins (SPKI SHA-256 c7846a0f...5a469ff7), never one from a row, a request or a
1164
+ // page, and the runtime swaps the ciphertexts at the body root: cse.at '' is the root, which the
1165
+ // Adyen substitution refuses, so only substituteShijiSts writes this body. No auth header: the
1166
+ // accessToken rides in the body. NOT allowlisted: iframe.vault.cn.{prod,uat}.shijipayment.cn (in the
1167
+ // proxy's table, never chosen by the wallet) and api.vault.eu.prod.shijipayment.com (not the
1168
+ // browser's host). `capability` keeps the entry out of BUILTIN_REGISTRY, serves it only to a client
1169
+ // that declares shiji_sts_pinned_key, and leaves it off for every org until one is opted in. The UAT
1170
+ // host is the sandbox host and the environment is pinned (S7): UAT only on sandbox rows, with
1171
+ // documented test cards. Every getTokenId is refused as card on file (a hotel guarantee) until the
1172
+ // consent decision is made (shiji-sts.js SHIJI_STS_KEEP_ON_FILE). `(?![\w/-])` ends the path as
1173
+ // on mercado_pago and paysafe: a consumer that tests `match` unanchored never takes
1174
+ // /v1/token/getTokenIdX or /v1/token/getTokenId/x for a card request.
1175
+ capability: "shiji_sts_pinned_key",
1176
+ encryptedCardFields: { number: "pan", expiry: "expires", cvc: "cvv" },
1177
+ match: "iframe\\.vault\\.eu\\.(?:prod|uat)\\.shijipayment\\.com/v1/token/(?:getTokenId|getCvvId)(?![\\w/-])",
1178
+ hosts: ["^iframe\\.vault\\.eu\\.prod\\.shijipayment\\.com$", "^iframe\\.vault\\.eu\\.uat\\.shijipayment\\.com$"],
1179
+ sandboxHosts: ["^iframe\\.vault\\.eu\\.uat\\.shijipayment\\.com$"],
1180
+ hostEnvironmentPinned: true,
1181
+ encoding: "json",
1182
+ passthroughHeaders: [],
1183
+ clientSideEncrypted: true,
1184
+ mode: "cse",
1185
+ cse: { scheme: "rsa-pkcs1v15", at: "", fields: ["pan", "expires", "cvv"] }
1186
+ },
1187
+ {
1188
+ psp: "worldline_mycheckout",
1189
+ // Worldline Connect MyCheckout, the hosted payment page DHL Express's MyDHL+ sends its card
1190
+ // payments to. The page submits the card as a top-level form POST to /checkout on its own host
1191
+ // (a Document navigation, like Tranzila's), so the cardholder's device rebuilds that form with
1192
+ // the real card and submits it itself (mode hosted_form) and the paused navigation is
1193
+ // answered with the submission notice. The same URL takes the page's cancel and every non-card
1194
+ // payment product; only a body with cardNumber is a card request, and the rest continue
1195
+ // untouched (cardFields, passWithoutCard). A card body is taken over only in the one plain
1196
+ // shape worldline-mycheckout.js admits (worldlineMyCheckoutRequestRefusal): no card kept for
1197
+ // later, no wallet, a covered card product. The form names no amount (the merchant fixed it
1198
+ // when it created the hosted checkout), so the agent's amount is the price the cardholder
1199
+ // approves. `payment.` is the subdomain Worldline says always works, on pay1 (Europe) and pay2
1200
+ // (US) and their pre-production twins; a merchant's own MyCheckout subdomain is not admitted.
1201
+ // resolveRecognizerForUrl takes only the four endpoints written exactly. `capability` keeps
1202
+ // the entry out of BUILTIN_REGISTRY, serves it only to a client that declares
1203
+ // worldline_mycheckout_form, and leaves it off for every org until one is opted in; the
1204
+ // pre-production hosts take only test rows and documented test cards (hostEnvironmentPinned).
1205
+ capability: "worldline_mycheckout_form",
1206
+ match: "payment\\.pay[12](?:\\.preprod)?\\.checkout\\.worldline-solutions\\.com/checkout(?![\\w/.-])",
1207
+ hosts: [
1208
+ "^payment\\.pay1\\.checkout\\.worldline-solutions\\.com$",
1209
+ "^payment\\.pay2\\.checkout\\.worldline-solutions\\.com$",
1210
+ "^payment\\.pay1\\.preprod\\.checkout\\.worldline-solutions\\.com$",
1211
+ "^payment\\.pay2\\.preprod\\.checkout\\.worldline-solutions\\.com$"
1212
+ ],
1213
+ sandboxHosts: ["^payment\\.pay1\\.preprod\\.checkout\\.worldline-solutions\\.com$", "^payment\\.pay2\\.preprod\\.checkout\\.worldline-solutions\\.com$"],
1214
+ hostEnvironmentPinned: true,
1215
+ encoding: "form",
1216
+ passthroughHeaders: [],
1217
+ cardFields: ["cardNumber"],
1218
+ passWithoutCard: "payment\\.pay[12](?:\\.preprod)?\\.checkout\\.worldline-solutions\\.com/checkout",
1219
+ mode: "hosted_form",
1220
+ hostedForm: {
1221
+ required: ["publicMerchantId", "token", "requestToken", "idempotenceKey", "doPayment"],
1222
+ // expiryDate is one MM/YY field; the device writes it in the page's own delimiter.
1223
+ card: { number: "cardNumber", expiry: "expiryDate", cvc: "cvv" },
1224
+ // The merchant and the hosted checkout: one prompt per hosted checkout.
1225
+ dedupe: ["publicMerchantId", "token"],
1226
+ // A checked "remember" asks Worldline to keep the card for later purchases.
1227
+ token: "remember"
1228
+ }
661
1229
  }
662
1230
  ]);
663
- var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, ...definition }) => definition));
1231
+ var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, deviceSteps, deviceEncrypt, ...definition }) => definition));
664
1232
  var PSP_CLIENT_TABLE = deepFreeze(PROCESSOR_DEFINITIONS.map((definition) => ({
665
1233
  psp: definition.psp,
666
1234
  match: new RegExp(definition.match, "i"),
@@ -669,21 +1237,30 @@ var PSP_CLIENT_TABLE = deepFreeze(PROCESSOR_DEFINITIONS.map((definition) => ({
669
1237
  headers: definition.passthroughHeaders.map((source) => new RegExp(source, "i")),
670
1238
  ...definition.sandboxHosts ? { sandboxHosts: definition.sandboxHosts.map((source) => new RegExp(source, "i")) } : {},
671
1239
  ...definition.hostEnvironmentPinned ? { hostEnvironmentPinned: true } : {},
1240
+ ...definition.request ? { request: definition.request } : {},
672
1241
  ...definition.mode ? { mode: definition.mode } : {},
673
1242
  ...definition.clientSideEncrypted ? { clientSideEncrypted: true } : {},
674
1243
  ...definition.compose ? { compose: definition.compose } : {},
675
1244
  ...definition.encryptedCardFields ? { cse: { fields: definition.encryptedCardFields } } : {},
676
- ...definition.hostedForm ? { form: definition.hostedForm.card } : {}
1245
+ ...definition.caseInsensitiveFormKeys ? { caseInsensitiveFormKeys: true } : {},
1246
+ // The device's copy of the form's card keys (forms.js hostedFormPairs), with the card roles a
1247
+ // template may lack (`optional`), so the vault fills exactly what the definition names.
1248
+ ...definition.hostedForm ? { form: {
1249
+ ...definition.hostedForm.card,
1250
+ ...definition.hostedForm.optional ? { optional: definition.hostedForm.optional } : {}
1251
+ } } : {},
1252
+ ...definition.deviceSteps ? { deviceSteps: Object.fromEntries(Object.entries(definition.deviceSteps).map(([step, source]) => [step, new RegExp(source, "i")])) } : {},
1253
+ ...definition.deviceEncrypt ? { deviceEncrypt: definition.deviceEncrypt } : {}
677
1254
  })));
678
1255
  function buildBuiltinRegistry(recognizers) {
679
- return recognizers.filter((definition) => !definition.capability).map(({ cse, hostedForm, ...definition }) => ({
1256
+ return recognizers.filter((definition) => !definition.capability).map(({ cse, hostedForm, deviceEncrypt, ...definition }) => ({
680
1257
  ...definition,
681
1258
  match: new RegExp(definition.match, "i"),
682
1259
  passthroughHeaders: definition.passthroughHeaders.map((source) => new RegExp(source, "i"))
683
1260
  }));
684
1261
  }
685
1262
  var BUILTIN_REGISTRY = deepFreeze(buildBuiltinRegistry(PSP_RECOGNIZERS));
686
- var CHECKOUT_MODES = deepFreeze(["token", "cse", "hosted_form"]);
1263
+ var CHECKOUT_MODES = deepFreeze(["token", "cse", "hosted_form", "card_endpoint", "device_encrypt"]);
687
1264
  function recognizerByPsp(psp) {
688
1265
  return typeof psp === "string" ? PSP_CLIENT_TABLE.find((entry) => entry.psp === psp) ?? null : null;
689
1266
  }
@@ -694,6 +1271,116 @@ function trustedClientRecognizer(pspOrEntry) {
694
1271
  throw new Error("Refusing to pay: unknown payment processor.");
695
1272
  return definition;
696
1273
  }
1274
+ // src/datatrans.js
1275
+ var DATATRANS_HOSTS = Object.freeze(["pay.datatrans.com", "pay.sandbox.datatrans.com"]);
1276
+ var DATATRANS_REFUSAL_REASONS = Object.freeze(["unsupported", "card_on_file", "brand_mismatch", "unregistered_merchant"]);
1277
+ var DATATRANS_REGISTRATION_POLICIES = Object.freeze(["refuse", "disclose", "agentcard_single_use"]);
1278
+ var DATATRANS_REVIEWED_MERCHANT_IDS = Object.freeze({
1279
+ "pay.datatrans.com": Object.freeze([]),
1280
+ "pay.sandbox.datatrans.com": Object.freeze(["1110022968", "1100007006"])
1281
+ });
1282
+ var DATATRANS_PAYMENT_METHODS = Object.freeze(["VIS", "ECA", "AMX", "DIN", "DIS", "JCB", "CUP", "MAU", "UAP", "DNK"]);
1283
+ var DATATRANS_TEST_PANS = Object.freeze([
1284
+ "4242424242424242",
1285
+ "4900000000000086",
1286
+ "4900000000000003",
1287
+ "5404000000000001",
1288
+ "5200000000000007",
1289
+ "5200000000000080",
1290
+ "375811111111115",
1291
+ "375000000000007",
1292
+ "375811111111123",
1293
+ "36168002586009",
1294
+ "36167719110012",
1295
+ "6011000000000004",
1296
+ "3569990010030442",
1297
+ "3569990010030400",
1298
+ "6362970000457013",
1299
+ "3841000000000007",
1300
+ "6200104444444447",
1301
+ "6210946888060008",
1302
+ "6759000000000018",
1303
+ "6759000000000026",
1304
+ "4000001000000034",
1305
+ "4000001000000018",
1306
+ "4000001000000042",
1307
+ "4000001000000026",
1308
+ "5100001000000022",
1309
+ "5100001000000048",
1310
+ "5100001000000014",
1311
+ "5100001000000030",
1312
+ "340000100000024",
1313
+ "340000100000040",
1314
+ "340000100000016",
1315
+ "340000100000032",
1316
+ "36000010000022",
1317
+ "3550998599961037"
1318
+ ]);
1319
+ // src/amadeus-checkout.js
1320
+ var AMADEUS_CARD_URLS = Object.freeze({
1321
+ production: Object.freeze({
1322
+ sdk: Object.freeze(["https://paypages.payment.amadeus.com/1ASIATP/ARIAPP/pay"]),
1323
+ dcp: Object.freeze(["https://paypages.payment.amadeus.com/1ASIATP/ATPDCP/collect"])
1324
+ }),
1325
+ // pdt, uat, mig and skl. DCP was seen on the production path only, so the test host takes no store.
1326
+ sandbox: Object.freeze({
1327
+ sdk: Object.freeze([
1328
+ "https://paypages.test.payment.amadeus.com/1ASIATP/ARIAPP/pay",
1329
+ "https://paypages.test.payment.amadeus.com/1ASIATPU/ARIAPP/pay",
1330
+ "https://paypages.test.payment.amadeus.com/1ASIATPM/ARIAPP/pay",
1331
+ "https://paypages.test.payment.amadeus.com/1ASIATPK/ARIAPP/pay"
1332
+ ]),
1333
+ dcp: Object.freeze([])
1334
+ })
1335
+ });
1336
+ var AMADEUS_MERCHANT_EXPIRY_POLICIES = Object.freeze(["refuse", "fill"]);
1337
+ var AMADEUS_REFUSAL_REASONS = Object.freeze([
1338
+ "unsupported",
1339
+ "card_on_file",
1340
+ "split_tender",
1341
+ "installments",
1342
+ "currency_conversion",
1343
+ "amadeus_3ds",
1344
+ "saved_card",
1345
+ "vendor_mismatch",
1346
+ "header_binding",
1347
+ "merchant_held_expiry",
1348
+ "dcp_field_config_unreviewed"
1349
+ ]);
1350
+ var MESSAGES = Object.freeze({
1351
+ unsupported: "Refusing to pay: unsupported Amadeus card request.",
1352
+ card_on_file: "Refusing to pay: this Amadeus payment would let the merchant keep your card on file, which needs your consent first. Nothing was sent.",
1353
+ split_tender: "Refusing to pay: this Amadeus payment splits the total across more than one payment method. Nothing was sent.",
1354
+ installments: "Refusing to pay: this Amadeus payment asks to pay in installments. Nothing was sent.",
1355
+ currency_conversion: "Refusing to pay: this Amadeus payment converts the total into another currency. Nothing was sent.",
1356
+ amadeus_3ds: "Refusing to pay: this Amadeus payment set up its card check for a different card. Nothing was sent.",
1357
+ saved_card: "Refusing to pay: this Amadeus payment uses a card saved with the merchant, not your Vault card. Nothing was sent.",
1358
+ vendor_mismatch: "Refusing to pay: this Amadeus payment names a card network that is not your card's. Nothing was sent.",
1359
+ header_binding: "Refusing to pay: this Amadeus card form does not belong to the merchant you approved. Nothing was sent.",
1360
+ merchant_held_expiry: "Refusing to pay: this merchant sends your card's expiry date separately, which Agentcard cannot fill yet. Nothing was sent.",
1361
+ dcp_field_config_unreviewed: "Refusing to pay: Agentcard has not reviewed this merchant's Amadeus card form yet. Nothing was sent."
1362
+ });
1363
+ var SDK_FLAVORS = Object.freeze(["Core-Sdk", "Angular-Web-Sdk"]);
1364
+ var LABEL = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
1365
+ var SDK_HOST = new RegExp(`^${LABEL}(?:\\.${LABEL})+(?::[0-9]{1,5})?$`, "i");
1366
+ var SDK_HEADERS = Object.freeze(["amadeus-checkout-sdk-version", "amadeus-checkout-sdk-host", "amadeus-checkout-sdk-flavor"]);
1367
+ var SDK_DATA_KEYS = Object.freeze(["mopid", "mopdata", "fingerprint"]);
1368
+ var SDK_MOPDATA_KEYS = Object.freeze(["pan", "CVV", "holdername", "expmonth", "expyear", "vendor", "address", "contact"]);
1369
+ var SDK_MOPDATA_REFUSALS = Object.freeze({
1370
+ prefill_index: "saved_card",
1371
+ installmentPlansID: "installments",
1372
+ selectedInstallmentPlan: "installments",
1373
+ options: "currency_conversion",
1374
+ tdsSessionId: "amadeus_3ds",
1375
+ tdsDeviceInformation: "amadeus_3ds"
1376
+ });
1377
+ var AMADEUS_DCP_REVIEWED_KEYS = Object.freeze(["vendor", "pan", "expdate", "owner", "cvv", "address", "city", "zipCode", "country", "state"]);
1378
+ var DCP_TEXT_KEYS = Object.freeze(["address", "city", "zipCode", "country", "state"]);
1379
+ var NETWORKS = Object.freeze(["visa", "mastercard", "amex"]);
1380
+ var DECLINE_LABELS = Object.freeze(["label_error_payment", "label_error_bin", "label_error_bin_vendor", "label_error_bin_type"]);
1381
+ var EXPIRED_LABELS = Object.freeze(["session_timeout", "ppid_expired"]);
1382
+ var REF_KEYS = Object.freeze(["recloc", "orderId", "mopId", "pri", "approvalcode", "externalId", "paymentMerchantReference", "prr"]);
1383
+ var PAYMENT_INFORMATION_KEYS = Object.freeze(["approvalcode", "pri", "externalId", "mopId"]);
697
1384
  // src/recognition.js
698
1385
  var PATTERN_WITHOUT_CARD_FIELDS = Object.freeze({
699
1386
  stripe: "api\\.stripe\\.com/v1/(payment_methods|tokens|setup_intents/seti_\\w+/confirm|payment_intents/pi_\\w+/confirm)"
@@ -704,6 +1391,7 @@ var WITHOUT_CHECKOUT_SESSIONS = Object.freeze({
704
1391
  passWithoutCard: "api\\.stripe\\.com/v1/(sources|confirmation_tokens)"
705
1392
  })
706
1393
  });
1394
+ var ONLY_WITH_CARD_FIELDS = Object.freeze(["amadeus_checkout"]);
707
1395
  // src/request-policy.js
708
1396
  var HEADER_NEVER_FORWARD = /^(cookie|set-cookie|authorization2|proxy-authorization|host|content-length|origin|referer)$/i;
709
1397
  var ALLOWED_MEDIA_TYPES = {
@@ -738,6 +1426,8 @@ function contentTypeFor(pspOrEntry, headers) {
738
1426
  function replayHeaders(pspOrEntry, captured = {}) {
739
1427
  const rec = trustedClientRecognizer(pspOrEntry);
740
1428
  const headers = { "content-type": contentTypeFor(rec, captured) };
1429
+ if (rec.psp === "creditguard")
1430
+ headers.accept = "application/json";
741
1431
  for (const [name, value] of Object.entries(captured)) {
742
1432
  if (rec.headers.some((pattern) => pattern.test(name)) && !HEADER_NEVER_FORWARD.test(name))
743
1433
  headers[name] = value;