@agent-cards/checkout 0.19.0 → 0.21.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 (106) hide show
  1. package/README.md +6 -753
  2. package/cdp.d.ts +1 -0
  3. package/cdp.js +2 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +2 -0
  6. package/package.json +33 -33
  7. package/playwright.d.ts +1 -0
  8. package/playwright.js +2 -0
  9. package/preflight.d.ts +1 -0
  10. package/preflight.js +2 -0
  11. package/CHANGELOG.md +0 -132
  12. package/PREFLIGHT.md +0 -312
  13. package/dist/adyen-merchant-hosted.generated.d.ts +0 -277
  14. package/dist/adyen-merchant-hosted.generated.js +0 -1902
  15. package/dist/adyen.generated.d.ts +0 -24
  16. package/dist/adyen.generated.js +0 -64
  17. package/dist/attachment.d.ts +0 -11
  18. package/dist/attachment.js +0 -50
  19. package/dist/braintree.d.ts +0 -2
  20. package/dist/braintree.generated.d.ts +0 -10
  21. package/dist/braintree.generated.js +0 -302
  22. package/dist/braintree.js +0 -2
  23. package/dist/builtin-registry.generated.d.ts +0 -2
  24. package/dist/builtin-registry.generated.js +0 -1
  25. package/dist/card-fields.generated.d.ts +0 -3
  26. package/dist/card-fields.generated.js +0 -46
  27. package/dist/cdp.d.ts +0 -192
  28. package/dist/cdp.js +0 -2393
  29. package/dist/checkout-com.generated.d.ts +0 -4
  30. package/dist/checkout-com.generated.js +0 -183
  31. package/dist/client.d.ts +0 -849
  32. package/dist/client.js +0 -1754
  33. package/dist/cse-body.d.ts +0 -25
  34. package/dist/cse-body.js +0 -41
  35. package/dist/fiserv.d.ts +0 -65
  36. package/dist/fiserv.generated.d.ts +0 -73
  37. package/dist/fiserv.generated.js +0 -830
  38. package/dist/fiserv.js +0 -104
  39. package/dist/hosted-form.d.ts +0 -44
  40. package/dist/hosted-form.js +0 -78
  41. package/dist/index.d.ts +0 -18
  42. package/dist/index.js +0 -10
  43. package/dist/lifecycle.d.ts +0 -179
  44. package/dist/lifecycle.js +0 -395
  45. package/dist/mercado-checkout.d.ts +0 -20
  46. package/dist/mercado-checkout.generated.d.ts +0 -52
  47. package/dist/mercado-checkout.generated.js +0 -198
  48. package/dist/mercado-checkout.js +0 -108
  49. package/dist/merchant-handoff.d.ts +0 -54
  50. package/dist/merchant-handoff.js +0 -100
  51. package/dist/merchant-hosted.d.ts +0 -140
  52. package/dist/merchant-hosted.js +0 -170
  53. package/dist/merchant-total-watch.d.ts +0 -115
  54. package/dist/merchant-total-watch.js +0 -268
  55. package/dist/merchant-total.d.ts +0 -257
  56. package/dist/merchant-total.js +0 -383
  57. package/dist/owned-shop.generated.d.ts +0 -24
  58. package/dist/owned-shop.generated.js +0 -108
  59. package/dist/paysafe.generated.d.ts +0 -12
  60. package/dist/paysafe.generated.js +0 -87
  61. package/dist/playwright.d.ts +0 -3
  62. package/dist/playwright.js +0 -3
  63. package/dist/pre-claim.d.ts +0 -123
  64. package/dist/pre-claim.js +0 -386
  65. package/dist/preflight-capabilities.generated.d.ts +0 -1253
  66. package/dist/preflight-capabilities.generated.js +0 -1929
  67. package/dist/preflight-catalog.json +0 -4727
  68. package/dist/preflight-playwright.d.ts +0 -34
  69. package/dist/preflight-playwright.js +0 -355
  70. package/dist/preflight-schemas.json +0 -1122
  71. package/dist/preflight.d.ts +0 -1
  72. package/dist/preflight.generated.d.ts +0 -1965
  73. package/dist/preflight.generated.js +0 -570
  74. package/dist/preflight.js +0 -2
  75. package/dist/preparation.d.ts +0 -38
  76. package/dist/preparation.js +0 -191
  77. package/dist/prepared-processor.d.ts +0 -43
  78. package/dist/prepared-processor.js +0 -172
  79. package/dist/recurly.generated.d.ts +0 -1
  80. package/dist/recurly.generated.js +0 -87
  81. package/dist/registry.d.ts +0 -121
  82. package/dist/registry.js +0 -310
  83. package/dist/spreedly.generated.d.ts +0 -10
  84. package/dist/spreedly.generated.js +0 -332
  85. package/dist/stripe-checkout.d.ts +0 -81
  86. package/dist/stripe-checkout.generated.d.ts +0 -82
  87. package/dist/stripe-checkout.generated.js +0 -1093
  88. package/dist/stripe-checkout.js +0 -140
  89. package/dist/substitute.d.ts +0 -38
  90. package/dist/substitute.js +0 -23
  91. package/dist/substitutions.generated.d.ts +0 -11
  92. package/dist/substitutions.generated.js +0 -818
  93. package/examples/existing-browser.mjs +0 -63
  94. package/examples/preflight/classify-direct.mjs +0 -21
  95. package/examples/preflight/classify-kernel.mjs +0 -30
  96. package/examples/preflight/inspect-browser.mjs +0 -44
  97. package/examples/preflight/kernel-native/README.md +0 -112
  98. package/examples/preflight/kernel-native/documented-adapters.json +0 -113
  99. package/examples/preflight/kernel-native/inventory.json +0 -233
  100. package/examples/preflight/kernel-native/qualification.mjs +0 -182
  101. package/examples/preflight/kernel-profile.empty.json +0 -11
  102. package/examples/preflight/mollie-hosted.observations.json +0 -23
  103. package/examples/preflight/mollie-hosted.result.json +0 -103
  104. package/examples/preflight/stripe-script.direct.result.json +0 -92
  105. package/examples/preflight/stripe-script.observations.json +0 -16
  106. package/examples/preflight/stripe-script.result.json +0 -87
@@ -1,1093 +0,0 @@
1
- // @ts-nocheck
2
- // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 9edf78da59b3bdfe310b4422e06d333cd164497cdb4070db59ffed348492198a
4
- // src/deep-freeze.js
5
- function deepFreeze(value) {
6
- if (value && typeof value === "object" && !Object.isFrozen(value)) {
7
- for (const child of Object.values(value))
8
- deepFreeze(child);
9
- Object.freeze(value);
10
- }
11
- return value;
12
- }
13
- // src/definitions.js
14
- var PROCESSOR_DEFINITIONS = deepFreeze([
15
- {
16
- psp: "spreedly",
17
- // Native iframe new-card tokenization only; spreedly.js pins the query and body.
18
- match: "core\\.spreedly\\.com/v1/payment_methods/restricted\\.json",
19
- hosts: ["^core\\.spreedly\\.com$"],
20
- encoding: "json",
21
- passthroughHeaders: ["^spreedly-environment-key$"]
22
- },
23
- {
24
- psp: "shopify",
25
- match: "(checkout\\.pci\\.shopifyinc\\.com|deposit\\.[a-z0-9-]+\\.shopifycs\\.com)/sessions",
26
- hosts: ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"],
27
- encoding: "json",
28
- passthroughHeaders: ["^shopify-identification-signature$"]
29
- },
30
- {
31
- psp: "stripe",
32
- // Every endpoint Stripe.js sends a card to, read from its own controller
33
- // bundle: `tokens` (createToken), `payment_methods` (createPaymentMethod),
34
- // `sources` (createSource, the older API), `confirmation_tokens`
35
- // (createConfirmationToken: the page collects the card and confirms on its
36
- // own server), and the intent confirmations that carry the card inline as
37
- // `payment_method_data[card][number]` (confirmPayment, confirmSetup). The
38
- // modern Payment Element mostly confirms inline or through a confirmation
39
- // token, so matching the tokenizers alone never paused it. Observed live on
40
- // Anthropic's own billing page (platform.claude.com/settings/billing),
41
- // which posts to /v1/setup_intents/{seti_...}/confirm, the "save a card"
42
- // flow; /v1/payment_intents/{pi_...}/confirm is the same shape for "pay
43
- // now". Stripe Checkout Sessions confirm at
44
- // /v1/payment_pages/{cs_...}/confirm: Checkout Elements (initCheckout)
45
- // sends the card there inline, while hosted and embedded Checkout send only
46
- // the payment method they created first. The id segment is matched
47
- // narrowly (seti_/pi_/cs_ + word chars) rather than `.*` so the pattern
48
- // cannot be widened by a crafted path. A client that does not check
49
- // cardFields is served the pattern it knew before sources, confirmation
50
- // tokens and Checkout Sessions joined (recognition.js recognizersForClient).
51
- 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)",
52
- hosts: ["^api\\.stripe\\.com$"],
53
- encoding: "form",
54
- // Elements' own surface markers; without these Stripe rejects the surface.
55
- passthroughHeaders: ["^authorization$", "^stripe-version$", "^x-stripe-client-user-agent$"],
56
- // The same confirmations also run WITHOUT a card: a page that created the
57
- // method first confirms with `payment_method=pm_...`. Only a body that
58
- // carries one of these fields is a card request (card-fields.js). Any
59
- // other request on these endpoints has no card to approve, so it is
60
- // refused (requestWithoutCard) instead of paused for the cardholder,
61
- // unless passWithoutCard below names its endpoint.
62
- cardFields: ["card[number]", "payment_method_data[card][number]"],
63
- // A confirmation token that wraps a saved method, a source for a bank, or a
64
- // Checkout Session confirm paying with the method hosted or embedded
65
- // Checkout created first carries no card and was never ours: it continues
66
- // untouched, as it did before these endpoints were recognized.
67
- passWithoutCard: "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)"
68
- },
69
- {
70
- psp: "braintree",
71
- match: "payments(\\.sandbox)?\\.braintree-api\\.com/graphql",
72
- hosts: ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"],
73
- sandboxHosts: ["^payments\\.sandbox\\.braintree-api\\.com$"],
74
- encoding: "json",
75
- passthroughHeaders: ["^authorization$", "^braintree-version$"]
76
- },
77
- {
78
- psp: "checkout_com",
79
- // Flow 1.226.0's card controller selects api or card-acquisition-gateway
80
- // for the same card-token body and Bearer public-key Authorization.
81
- // Native source: checkout-web-components.checkout.com/pages/1.226.0/index.html.
82
- // Endpoint recognition does not establish consumer-wallet, CVV-only,
83
- // reusable-token, 3DS or merchant payment completion compatibility.
84
- match: "(api|card-acquisition-gateway)(\\.sandbox)?\\.checkout\\.com/tokens",
85
- hosts: [
86
- "^api\\.checkout\\.com$",
87
- "^api\\.sandbox\\.checkout\\.com$",
88
- "^card-acquisition-gateway\\.checkout\\.com$",
89
- "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"
90
- ],
91
- sandboxHosts: ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"],
92
- encoding: "json",
93
- passthroughHeaders: ["^authorization$"]
94
- },
95
- // NOT here, on purpose: VGS Collect (Very Good Security; Wolt's add-a-card
96
- // call through `<tenant>.<live|sandbox>.verygoodproxy.com`). Served for a
97
- // few hours on 2026-09-03 and withdrawn the same night: the proxy aliases
98
- // only submissions that originate in VGS's own iframe (its Collect session
99
- // handshake), so a byte-identical replay from the cardholder's device, 4 s
100
- // after capture, with the iframe's own headers and origin, is forwarded
101
- // un-aliased and the merchant answers 400 "Failed to process card". A
102
- // recognizer that pauses a call the vault cannot finish only blocks the
103
- // agent's browser from adding a card at all, so the entry is gone from
104
- // every registry; the page keeps its on-file wording machinery for the
105
- // merchant that does allow it.
106
- {
107
- psp: "adyen",
108
- encryptedCardFields: { number: "encryptedCardNumber", month: "encryptedExpiryMonth", year: "encryptedExpiryYear", cvc: "encryptedSecurityCode" },
109
- // The Sessions-flow payments call only (adyen-web's makePayment): the
110
- // card rides in paymentMethod as four JWE blobs the vault page produces
111
- // under the merchant's Adyen public key (services/checkout-cse-adyen.ts),
112
- // and the runtime substitutes them into the paused body. binLookup,
113
- // analytics and paymentDetails share these hosts, carry no card, and must
114
- // never raise an approval; the hosts test pins them refused. The host
115
- // alternation covers the regional live hosts (live-us, live-au, live-apse,
116
- // live-in, live-nea): `checkoutshopper-[a-z]+` never matched those, so
117
- // merchants on them were silently never paused.
118
- match: "(checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com|([a-z0-9-]+\\.)*adyenpayments\\.com)/checkoutshopper/v1/sessions/[A-Za-z0-9_-]+/payments",
119
- hosts: ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"],
120
- // The sandbox marker matches ONLY the TEST hostname, a subset of the first
121
- // host source (which matches checkoutshopper-test and every checkoutshopper-live
122
- // regional host). checkoutshopper-live* and *.adyenpayments.com are live.
123
- // Load-bearing for Adyen although Adyen does not pin its environment: the
124
- // Adyen TEST platform rule (backend.js adyenLivePlatform) reads this marker
125
- // on the server's authorization create and cardholder legs
126
- // (services/checkout-cse-adyen.ts in the backend), on the device and in the
127
- // enclave. Dropping or widening it moves which payments count as Adyen TEST.
128
- sandboxHosts: ["^checkoutshopper-test\\.adyen\\.com$"],
129
- encoding: "json",
130
- passthroughHeaders: [],
131
- clientSideEncrypted: true,
132
- // kept: older SDKs read this and refuse locally
133
- mode: "cse",
134
- cse: {
135
- at: "paymentMethod",
136
- fields: ["encryptedCardNumber", "encryptedExpiryMonth", "encryptedExpiryYear", "encryptedSecurityCode"],
137
- // adyen-web stamps `paymentMethod.brand` from the digits typed into its
138
- // Card component, i.e. the agent's dummy. Proven on Adyen TEST
139
- // (2026-09-02): a Mastercard dummy swapped for the vault's Visa
140
- // ciphertext was refused 422/109 "Invalid variant"; without `brand`
141
- // Adyen reads it off the decrypted PAN and authorises.
142
- remove: ["brand"]
143
- }
144
- },
145
- {
146
- psp: "tranzila",
147
- // Tranzila's hosted form (direct.tranzila.com/<terminal>/iframenew.php,
148
- // embedded by the merchant page) submits the card as a top-level form
149
- // POST to /process/n/ (directng: /process), application/x-www-form-
150
- // urlencoded, with the card in ccno/expmonth/expyear/mycvv and the
151
- // Israeli ID number in myid. The paused request is a Document navigation,
152
- // not an XHR: the runtime cannot resume it with the real card, so the
153
- // cardholder's device rebuilds and submits the SAME form itself, top-level
154
- // from the vault origin (mode hosted_form), and the runtime resolves the
155
- // paused navigation with a synthetic page once the device says the form
156
- // left. The iframe page itself and Tranzila's /done/ result page share
157
- // these hosts, carry no card, and are refused by `match`. Served only to
158
- // SDKs that ask for hosted_form on sync: an older SDK would arm these
159
- // hosts, pause the navigation, get a 400 and abort the frame.
160
- match: "(direct\\.tranzila\\.com/process/n/|directng\\.tranzila\\.com/process/?)",
161
- hosts: ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"],
162
- encoding: "form",
163
- passthroughHeaders: [],
164
- mode: "hosted_form",
165
- hostedForm: {
166
- required: ["sum", "supplier"],
167
- card: { number: "ccno", month: "expmonth", year: "expyear", cvc: "mycvv", nationalId: "myid" },
168
- amount: { sum: "sum", currency: "currency" },
169
- // supplier (the terminal) + benid (the iframe session) + the amount:
170
- // one submission, whatever nonce the form regenerated on a re-click.
171
- dedupe: ["supplier", "benid", "sum", "currency"],
172
- // One plain sale, reporting to the merchant the person sees: tranmode
173
- // A (or absent) only (V verifies, K charges a token, C refunds, D
174
- // forces), no TranzilaTK in either direction (`1` asks for a reusable
175
- // token back, a token charges one), and every *_url_address on the
176
- // merchant's host (Tranzila redirects the device there and POSTs the
177
- // result there with the card's last four, expiry and ID number).
178
- sale: { key: "tranmode", values: ["A"] },
179
- token: "TranzilaTK",
180
- callbackUrls: ["success_url_address", "fail_url_address", "notify_url_address"]
181
- }
182
- },
183
- // ── Verified tokenizers (plaintext PAN in the body) ───────────────────────
184
- // Each entry below was checked against the PSP's own shipped browser SDK or a
185
- // live probe, then run through resolveRecognizerForUrl with positive, sibling
186
- // and spoof URLs. Two rules the vectors keep proving, worth restating before
187
- // anyone edits one of these:
188
- // 1. Every dot in `hosts` is escaped. `^api.mercadopago.com$` admits the
189
- // REGISTRABLE domain apiamercadopago.com; the escape is the allowlist.
190
- // 2. `hosts` is pinned to literal labels, never widened to a wildcard label
191
- // (`[a-z0-9-]+\.psp\.com`). Several of these vendors run wildcard DNS,
192
- // so "the hostname resolves" is not evidence that it receives cards.
193
- {
194
- psp: "square",
195
- // PAN@ card_data.number. POST /v2/card-nonce (URL always carries ?_=&version=).
196
- match: "pci-connect\\.squareup(?:sandbox)?\\.com/v2/card-nonce",
197
- hosts: ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"],
198
- sandboxHosts: ["^pci-connect\\.squareupsandbox\\.com$"],
199
- encoding: "json",
200
- passthroughHeaders: []
201
- // auth = client_id in body; no auth header
202
- },
203
- {
204
- psp: "authorize_net",
205
- // Accept.js. PAN@ securePaymentContainerRequest.data.token.cardNumber.
206
- // Auth is body-level (merchantAuthentication: apiLoginID + clientKey) — nothing to replay.
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.
211
- // Coverage caveat: AcceptUI (the hosted lightbox) renders its fields inside an
212
- // accept.authorize.net iframe, so that POST is cross-origin to the merchant page and an
213
- // in-page hook cannot observe it. This entry covers the Accept.js custom-fields path, which
214
- // does run in the merchant page. /xml/v1/request.api is Authorize.Net's single monolithic
215
- // endpoint (request type is chosen by the body root key), so the path pin admits sibling
216
- // request types on the same URL — the body, not the URL, is what distinguishes them.
217
- match: "(api2|apitest)\\.authorize\\.net/xml/v1/request\\.api",
218
- hosts: ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"],
219
- sandboxHosts: ["^apitest\\.authorize\\.net$"],
220
- encoding: "json",
221
- // JSON body; Content-Type is sometimes text/plain (preflight dodge), replayed as-is
222
- passthroughHeaders: []
223
- },
224
- {
225
- psp: "worldpay",
226
- // Access Worldpay. PAN@ root key `cardNumber`. Accept is load-bearing:
227
- // the two paths need DIFFERENT vendor media types; `application/json` → HTTP 406.
228
- match: "(try\\.)?access\\.worldpay\\.com/(sessions/card|verifiedTokens/sessions)",
229
- hosts: ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"],
230
- sandboxHosts: ["^try\\.access\\.worldpay\\.com$"],
231
- // try.* is Worldpay's try/test host
232
- encoding: "json",
233
- passthroughHeaders: ["^accept$"]
234
- },
235
- {
236
- psp: "nuvei",
237
- // Safecharge Fields. PAN@ paymentOption.card.cardNumber.
238
- // THREE PAN-bearing endpoints, not one. Nuvei picks between them from
239
- // `sourceApplication`, and the covered one was the WRONG half of that
240
- // branch: WEB_SDK (merchant holds the raw PAN in their own page) posts
241
- // cardTokenization.do, while WEB_SDK_FIELDS (hosted fields, the
242
- // PCI-conscious integration) posts websdk/initPaymentWithCardTokenization.do
243
- // and clientPayment.do. Confirmed against sfc_inner_card.min.js, whose
244
- // initPayment() opens initPaymentWithCardTokenization.do unconditionally
245
- // with no branch back to the covered path.
246
- match: "(secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:cardTokenization|clientPayment|websdk/initPaymentWithCardTokenization)\\.do",
247
- hosts: ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"],
248
- sandboxHosts: ["^ppp-test\\.safecharge\\.com$"],
249
- encoding: "json",
250
- passthroughHeaders: []
251
- },
252
- // bluesnap: DELIBERATELY NOT SHIPPED — it cannot be intercepted at all. Its tokenizer is a PUT, and isCardRequest
253
- // gates on POST — but accepting PUT is not enough on its own. hpf.js fires the
254
- // same token PUT SEVERAL TIMES per checkout (blur and submit each send a delta
255
- // body), so intercepting it would raise one approval prompt per fire and hand the
256
- // device partial card data. An entry that cannot actually be intercepted does not
257
- // belong in an allowlist that advertises interception: it reads as support we do
258
- // not have, and on the CDP path it arms Fetch.enable, gets refused by the POST
259
- // gate, and is then continued straight to the PSP while the integrator believes
260
- // the card was protected. Ship it when the multi-fire semantics are handled.
261
- {
262
- psp: "airwallex",
263
- // PAN@ payment_method.card.number (payment_intents/confirm,
264
- // payment_consents/verify) / card.number (payment_methods/create).
265
- // ⚠ reference/bin/lookup is EXCLUDED. It egresses the PAN in an `x-pan` request
266
- // HEADER, and it is a POST — so matching it would not merely "pause" it, it would
267
- // intercept it and raise a real approval prompt for a bin lookup whose body holds
268
- // no card. Handling header-borne PANs needs its own design; until then this
269
- // header-aware handling, not the body cardFieldPath path. `x-pan` is deliberately NOT a
270
- // passthrough header, so today the replay carries no PAN at all (inert, never a leak).
271
- // `card/tokenize` was dropped from the candidate: an OPTIONS preflight on BOTH pci-api
272
- // hosts returns the same 404 as a bogus control path, while real POST-only routes
273
- // (payment_methods/create, payment_consents/create) answer 200 + Allow-Methods: POST.
274
- // The trailing (\?|$) is a no-op server-side (pathname has no query) — it is the
275
- // right-edge boundary for the SDK's UNANCHORED client-side test of the full URL.
276
- match: "(checkout(\\.sandbox)?\\.airwallex\\.com|pci-api(\\.sandbox)?\\.airwallex\\.com)/api/v1/pa/(payment_intents/[^/?#]+/confirm|payment_consents/[^/?#]+/verify|payment_methods/create)(\\?|$)",
277
- hosts: [
278
- "^checkout\\.airwallex\\.com$",
279
- "^checkout\\.sandbox\\.airwallex\\.com$",
280
- "^pci-api\\.airwallex\\.com$",
281
- "^pci-api\\.sandbox\\.airwallex\\.com$"
282
- ],
283
- sandboxHosts: ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"],
284
- encoding: "json",
285
- passthroughHeaders: ["^client-secret$", "^authorization$", "^x-auth-token$", "^x-on-behalf-of$", "^x-api-version$"]
286
- },
287
- {
288
- psp: "rapyd",
289
- // PAN@ required_fields.number (hosted collect page) OR payment_method.fields.number
290
- // (checkout page — it renders on checkout.rapyd.net/?token=checkout_… and posts cross-origin to
291
- // api.rapyd.net). The checkout-id segment is pinned to the documented `checkout_` prefix: a bare
292
- // [^/?#]+ also admitted /v1/checkout/collect and the encoded-slash smuggle
293
- // /v1/checkout/checkout_x%2Fadmin. ⚠ /v1/checkout/{id} is ALSO the documented GET Retrieve
294
- // Checkout Page (signature-authed, server-to-server, no card) — the POST gate is what splits them.
295
- match: "(api|sandboxapi)\\.rapyd\\.net/v1/(?:hosted/collect/card/[^/?#]+/payment_method|checkout/checkout_[A-Za-z0-9]+)(?:[?#]|$)",
296
- hosts: ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"],
297
- sandboxHosts: ["^sandboxapi\\.rapyd\\.net$"],
298
- encoding: "json",
299
- passthroughHeaders: []
300
- // auth rides the opaque hosted-page / checkout token in the URL
301
- },
302
- {
303
- psp: "dlocal",
304
- // Smart Fields cvault. PAN@ pan. Do NOT match /cvault/cvv/store (CVV only).
305
- // Hosts are two literal labels (no wildcard/`[a-z]+` label), so there is no
306
- // multi-tenant surface: `ppmcc` is dlocal-operated, not merchant-registrable.
307
- match: "ppmcc(-sandbox)?\\.dlocal\\.com/cvault/credit-card/temporal",
308
- hosts: ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"],
309
- sandboxHosts: ["^ppmcc-sandbox\\.dlocal\\.com$"],
310
- encoding: "json",
311
- passthroughHeaders: ["^x-fields-api-key$", "^x-uow$", "^x-dlocal-infrav2$"]
312
- },
313
- {
314
- psp: "ebanx",
315
- compose: { card_due_date: "MM/YYYY" },
316
- // PAN@ card.card_number. POST /ws/token. Body content-type is text/plain
317
- // (CORS-preflight dodge) — replay it verbatim; encoding:json describes the body shape.
318
- // The two domains SHARE one `/ws/token` tail on purpose: a bare top-level `|` (the shape
319
- // this entry arrived in) degrades to `(^A)|(B$)` under any caller that anchors without
320
- // re-grouping, admitting both `api.ebanx.com/ws/tokenizer_admin` and
321
- // `evil.com/api-diamond.ebanxpay.com/ws/token`. Keep the alternation wrapped.
322
- //
323
- // `api.ebanxpay.com` / `sandbox.ebanxpay.com` are the hosts a STOCK integration
324
- // actually uses: every published EBANX JS SDK (1.6.0 through 1.11.0, verified
325
- // against the live bundles) hardcodes
326
- // path: () => config.isLive() ? 'https://api.ebanxpay.com/' : 'https://sandbox.ebanxpay.com/'
327
- // Without them we recognized only the `.ebanx.com` and `api-diamond` variants, so
328
- // an ordinary EBANX checkout was invisible to us and the cardholder was never asked.
329
- match: "(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\\.ebanx\\.com|(?:api-diamond|api|sandbox)\\.ebanxpay\\.com)/ws/token",
330
- hosts: [
331
- "^customer\\.ebanx\\.com$",
332
- "^customer-frontier\\.ebanx\\.com$",
333
- "^api\\.ebanx\\.com$",
334
- "^sandbox\\.ebanx\\.com$",
335
- "^api-local-latam\\.ebanx\\.com$",
336
- "^sandbox-local-latam\\.ebanx\\.com$",
337
- "^api-diamond\\.ebanxpay\\.com$",
338
- "^api\\.ebanxpay\\.com$",
339
- "^sandbox\\.ebanxpay\\.com$"
340
- ],
341
- sandboxHosts: ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"],
342
- encoding: "json",
343
- passthroughHeaders: []
344
- },
345
- {
346
- psp: "mercado_pago",
347
- // PAN@ card_number. POST api.mercadopago.com/v1/card_tokens — the ONLY
348
- // PAN egress in sdk.mercadopago.com/js/v2 (verified against the live bundle). No sandbox host:
349
- // test mode is the TEST-* public_key. Auth + attribution ride the QUERY (the SDK's
350
- // defaultQueries appends public_key, locale, js_version, referer), so replay the URL verbatim
351
- // and never give `match` a trailing `$` — the SDK tests it unanchored against the raw URL.
352
- // Same endpoint also serves saved-card {card_id,security_code} (no PAN) → check body before swap.
353
- // (?![\w/-]) pins the exact endpoint: the `/card_tokens/{id}` sibling is a PUT carrying only
354
- // {security_code,expiration_*} (no PAN), and the guard stops the SDK pausing it or /{id}/clone.
355
- match: "api\\.mercadopago\\.com/v1/card_tokens(?![\\w/-])",
356
- hosts: ["^api\\.mercadopago\\.com$"],
357
- encoding: "json",
358
- passthroughHeaders: ["^x-product-id$"]
359
- // SDK sets X-Product-Id on the tokenize POST
360
- },
361
- {
362
- psp: "payu",
363
- // GPO Secure Forms. PAN@ card.number.
364
- match: "(secure\\.payu\\.com|(merch-prod|secure)\\.snd\\.payu\\.com)/api/front/tokens",
365
- hosts: ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"],
366
- sandboxHosts: ["^(merch-prod|secure)\\.snd\\.payu\\.com$"],
367
- // .snd. is PayU's sandbox
368
- encoding: "json",
369
- // Native checkout attaches a bearer to tokenization; preserve it on replay.
370
- passthroughHeaders: ["^authorization$"]
371
- },
372
- {
373
- psp: "razorpay",
374
- // PAN@ card[number] (form). Body carries risk/fingerprint fields — replay
375
- // body verbatim, swap only card[*]. Auth = ?key_id= (or ?x_entity_id=) in the URL.
376
- // THREE sinks, all POST, all carrying the SAME payload object. checkout-frame.js builds
377
- // them as ib(r,"payments/create/ajax") and ib(r,"payments/create/"+(fees?"fees":"checkout")):
378
- // /ajax XHR, standard flow
379
- // /checkout form-POST redirect flow
380
- // /fees form-POST, customer-fee-bearer merchants
381
- // `fees` is NOT optional: it is the same card body behind a boolean (vS(this.r, t.fees)),
382
- // so dropping it silently un-intercepts every fee-bearer merchant.
383
- // The optional `standard_checkout/` segment appears when a session_token is present
384
- // (builder: api + "v1/" + "standard_checkout" + "/" + path).
385
- // NOT matched, deliberately: the pg_router shape (api + "pg_router/" + "v1/" +
386
- // "standard_checkout" + path — note the MISSING slash before the path). Every ib() call
387
- // site passes only 2 args, so that flag is always false and the shape is unreachable for
388
- // card paths. If Razorpay ever enables it we go inert (fail-closed), never leaky.
389
- // NOT allowlisted, deliberately: api-<region>.razorpay.com. checkout.js only builds that
390
- // when the merchant page sets Razorpay.config.api="" — page-controlled input must never
391
- // widen a card allowlist.
392
- match: "api\\.razorpay\\.com/v1/(?:standard_checkout/)?payments/create/(?:ajax|checkout|fees)",
393
- hosts: ["^api\\.razorpay\\.com$"],
394
- encoding: "form",
395
- passthroughHeaders: ["^x-razorpay-sessionid$", "^x-customer-access-token$"]
396
- },
397
- {
398
- psp: "mollie",
399
- // Components v1+v2. PAN@ cardNumber. No sandbox host (body `testmode` boolean).
400
- match: "api\\.cc\\.mollie\\.com/v1/card-tokens",
401
- hosts: ["^api\\.cc\\.mollie\\.com$"],
402
- encoding: "json",
403
- passthroughHeaders: [],
404
- compose: { cardExpiryDate: "MM/YY" }
405
- },
406
- {
407
- psp: "paysafe",
408
- // Paysafe.js. PAN@ card.cardNum, expiry@ card.cardExpiry.{month,year}.
409
- // `paymenthub/v1/singleusepaymenthandles` was pinned from the docs and
410
- // has NEVER been observed in a capture; every run of the live SDK posts to
411
- // `/js/api/v1/tokenize` instead, from hosted.paysafe.com's own iframe. Both are kept:
412
- // the SUPH path may be reachable on integrations we have not exercised.
413
- // Auth travels as `x-paysafe-credentials` on the observed path (NOT `Authorization`,
414
- // which the docs describe for SUPH), so both are declared — replaying the wrong one
415
- // gets the tokenization rejected.
416
- // Token boundary is `(?![\w/-])`, not `(?!/)`: the server anchors `match` with ^(?:…)$
417
- // so either works there, but the SDK compiles `match` with `i` ALONE (unanchored), and
418
- // `(?!/)` lets `…/singleusepaymenthandlesEVIL` satisfy the browser-side pause test.
419
- // NOTE: legacy Paysafe.js v1 (/customervault/v1/singleusetokens, same two hosts) is
420
- // deliberately NOT covered here — it needs its own verification pass before adding.
421
- // Checkout 1.8 uses a separate hosted origin, with the same nested card shape.
422
- // Verified from https://hosted.test.paysafe.com/checkout/1.8.0/main.bundle.js
423
- // and its byte-identical production sibling (SHA-256 e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c).
424
- // Its native LIVE/TEST environment map selects exactly hosted[.test].paysafe.com.
425
- // Keep host/path pairs separate: details/log are not card requests;
426
- // card/tokenize uses a saved paymentToken, and 3DS-specific paths are unverified.
427
- match: "(?:api(\\.test)?\\.paysafe\\.com/(?:paymenthub/v1/singleusepaymenthandles|js/api/v1/tokenize)|hosted(\\.test)?\\.paysafe\\.com/checkout/api/v1/tokenize)(?![\\w/-])",
428
- hosts: ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"],
429
- sandboxHosts: ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"],
430
- encoding: "json",
431
- passthroughHeaders: ["^authorization$", "^x-paysafe-credentials$", "^correlationid$"]
432
- },
433
- {
434
- psp: "recurly",
435
- // PAN@ number (form-encoded POST). recurly.js hardcodes exactly TWO API bases:
436
- // "https://api.recurly.com/js/v1" and "https://api.eu.recurly.com/js/v1". EU is auto-selected
437
- // when the public key starts with `fra-`. No sandbox host: sandbox sites reuse api.recurly.com
438
- // with a sandbox public key.
439
- // WARNING: never widen these to [a-z0-9-]+\.recurly\.com. *.recurly.com is WILDCARD DNS (every
440
- // merchant subdomain resolves to the same LB), so a broad host pattern is a card-exfil hole.
441
- // (?![\w-]) excludes the live /js/v1/tokens sibling (iban/bacs/becs bank accounts + 3DS action
442
- // results, no PAN); /js/v1/payment_methods/token is APM/wallet ({currency,amount}) and is
443
- // excluded by the anchored path too.
444
- // WARNING: the opt-in `cors:false` legacy path tokenizes over a JSONP GET, which the POST gate
445
- // in isCardRequest cannot intercept. Default is `cors:true` (XHR POST), so this entry is live.
446
- match: "api(\\.eu)?\\.recurly\\.com/js/v1/token(?![\\w-])",
447
- hosts: ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"],
448
- encoding: "form",
449
- passthroughHeaders: ["^recurly-credential-checkout-hostname$"]
450
- // only in configure({hostname}) mode
451
- },
452
- {
453
- psp: "moneris",
454
- compose: { input_exp: "MMYY" },
455
- // Hosted Tokenization. VERIFIED live against hpp_common.js on all six
456
- // hosts: the iframe does `request.open('POST', 'request.php')` (XHR, relative to
457
- // /HPPtoken/index.php) with Content-Type application/x-www-form-urlencoded and
458
- // PAN@ input_data. It sets no auth header (the HT profile is the ?id= on the iframe
459
- // URL), so passthroughHeaders is correctly empty. hpp_common.js references exactly
460
- // one URL and one method, so there is no second tokenization path to cover.
461
- // NOTE: do NOT add Moneris Checkout (chkt/) as a separate entry. It shares these
462
- // hosts and resolveRecognizerForUrl .find()s on host first, so one would permanently
463
- // shadow the other; chkt/ instead falls out at the path check (refused, not leaked).
464
- match: "(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\\.moneris\\.com/HPPtoken/request\\.php",
465
- hosts: [
466
- "^www3\\.moneris\\.com$",
467
- "^esqa\\.moneris\\.com$",
468
- "^gateway\\.moneris\\.com$",
469
- "^gatewayqa\\.moneris\\.com$",
470
- "^gatewayt\\.moneris\\.com$",
471
- "^gatewaydev\\.moneris\\.com$"
472
- ],
473
- // Live: www3, gateway. Test/QA/dev: esqa, gatewayqa, gatewayt, gatewaydev.
474
- sandboxHosts: ["^esqa\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"],
475
- encoding: "form",
476
- passthroughHeaders: []
477
- },
478
- {
479
- psp: "bambora",
480
- // Bambora NA / Worldline Custom Checkout. PAN@ number. POST. Content-Type is
481
- // text/plain (preflight dodge) — replay verbatim; `encoding: 'json'` describes the body shape.
482
- // No sandbox host: test mode rides the same host, decided by merchant id + API key.
483
- // api.bam.shift4api.net is the post-migration host (Dec-2026 cutover); both hosts are pinned
484
- // exact — never widen to the docs' *.bam.shift4api.net wildcard. api.na.bambora.com CNAMEs to
485
- // the legacy beanstream.com origin; do NOT add www.beanstream.com, the browser SDK never calls it.
486
- match: "(api\\.na\\.bambora\\.com|api\\.bam\\.shift4api\\.net)/scripts/tokenization/tokens",
487
- hosts: ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"],
488
- encoding: "json",
489
- passthroughHeaders: []
490
- },
491
- {
492
- psp: "global_payments",
493
- // GlobalPayments.js, two gateways behind one PSP id. PAN@ card.number on
494
- // both (verified in the shipped js.globalpay.com/v1/globalpayments.js, actionTokenize$3/$4).
495
- // Heartland/Portico: prod api.heartlandportico.com/SecureSubmit.v1/api/token, sandbox
496
- // cert.api2.heartlandportico.com/Hps.Exchange.PosGateway.Hpf.v1/api/token. The host/path
497
- // pairing is ASYMMETRIC (prod is not api2, sandbox is not SecureSubmit.v1). That is the
498
- // vendor's own domains$4/urls$4 table, not a typo, so do not "correct" it. Auth is ?api_key=
499
- // in the URL, so there is nothing to replay in headers. api2.heartlandportico.com is
500
- // deliberately absent: it is the server-side PHP SDK's endpoint, never a browser call.
501
- // GP-API/UCP: apis[.sandbox][.eu].globalpay.com/ucp/[merchants/{id}/]payment-methods, auth via
502
- // Authorization Bearer + X-GP-Version. All four hosts verified live and serving cross-origin
503
- // POST. accounts/{id}/ is deliberately NOT accepted: the tokenizer only ever prefixes
504
- // merchants/{id}/, so allowing it would widen the path with no endpoint behind it.
505
- // Both gateways POST application/json, so isCardRequest's POST gate lets them through.
506
- 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)",
507
- hosts: [
508
- "^api\\.heartlandportico\\.com$",
509
- "^cert\\.api2\\.heartlandportico\\.com$",
510
- "^apis\\.globalpay\\.com$",
511
- "^apis\\.sandbox\\.globalpay\\.com$",
512
- "^apis\\.eu\\.globalpay\\.com$",
513
- "^apis\\.sandbox\\.eu\\.globalpay\\.com$"
514
- ],
515
- // Heartland/Portico cert.* is the certification (sandbox) host; GP-API sandbox is apis.sandbox[.eu].
516
- sandboxHosts: ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"],
517
- encoding: "json",
518
- passthroughHeaders: ["^authorization$", "^x-gp-version$"]
519
- },
520
- // ── Encrypted on the device under a pinned key ────────────────────────────
521
- {
522
- psp: "fiserv",
523
- // Fiserv Commerce Hub card capture from the Secure Data Capture (SDC) frame,
524
- // the request Home Depot's OrangePay checkout sends (payment-fields.js and
525
- // sdk-secure 3.8.14). The frame always encrypts: it seals the card with
526
- // AES-256-GCM under a fresh key, wraps that key with RSA-OAEP-SHA256 under the
527
- // public key the page passed it, and posts the envelope as
528
- // source.encryptionData. It picks its endpoint by the page's auth type
529
- // (iframe.js @85480): an AccessToken (Security Credentials) posts here, to
530
- // card-capture; a Checkout Sessions JWT posts to /ch/checkouts/v1/payment-link
531
- // instead. payment-link also carries the card, and it is left out on purpose:
532
- // `match` names card-capture only, so a Sessions merchant's capture is never
533
- // paused and fails as it does today. Fastlane (PaymentToken), ACH
534
- // (PaymentCheck), gift and EBT captures share this URL, carry no Vault card,
535
- // and the SDK continues them unpaused (fiserv.js isFiservPausableBody).
536
- // The Wave 0 sandbox gate (2026-09-24, connect-cert, Fiserv simulator cards)
537
- // settled the mode: card-capture refuses a plaintext source.card (400
538
- // GATEWAY 302 "Missing or Invalid Encryption Data") and captures the frame's
539
- // own envelope with no Origin, so the device builds that envelope under a
540
- // key Agentcard pinned (fiserv-keys.js), the runtime writes its four members
541
- // over the paused ones, and the capture continues from the agent's browser.
542
- // The AccessToken the agent can read was refused on charges and tokens at
543
- // Fiserv's edge in that sandbox. Every other Commerce Hub path is refused
544
- // here: charges, tokens, security/keys, payment-link, and the non-production
545
- // hosts (connect-dev, -qa, -uat, -test); resolveRecognizerForUrl also takes
546
- // only the two endpoints written exactly (fiserv-environment.js). The
547
- // envelope carries no timestamp or session binding, so it is approved by the
548
- // cardholder every time: dark until an org is allowlisted, and autopilot
549
- // refuses fiserv at all three gates.
550
- capability: "fiserv_card_capture",
551
- // A Fiserv card is sealed only for a capture the cardholder already approved
552
- // through a prepare(): the backend refuses an unprepared create (409
553
- // preparation_required), the internal buy loop never mints a Fiserv round,
554
- // and the SDK refuses locally before it prompts.
555
- preparationRequired: true,
556
- match: "connect(?:-cert)?\\.fiservapis\\.com/ch/payments-vas/v1/card-capture(?![\\w/.-])",
557
- hosts: ["^connect\\.fiservapis\\.com$", "^connect-cert\\.fiservapis\\.com$"],
558
- sandboxHosts: ["^connect-cert\\.fiservapis\\.com$"],
559
- // A live row reaches connect only and a test row connect-cert only, and
560
- // connect-cert receives only documented test cards (device-replay.js).
561
- hostEnvironmentPinned: true,
562
- encoding: "json",
563
- // The browser continues the capture with its own headers, so only the ones
564
- // the backend checks against the body and the merchant are kept:
565
- // Auth-Token-Type, the merchant and terminal ids, and the embedding frame's
566
- // origin the SDC names (x-integration-origin, self-asserted). The
567
- // AccessToken, the API key, Client-Request-Id and Timestamp are never stored.
568
- passthroughHeaders: ["^auth-token-type$", "^x-integration-merchant-id$", "^x-integration-terminal-id$", "^x-integration-origin$"],
569
- clientSideEncrypted: true,
570
- mode: "cse",
571
- cse: {
572
- at: "source.encryptionData",
573
- fields: ["keyId", "encryptedKey", "encryptionBlock", "aesIv"]
574
- }
575
- }
576
- ]);
577
- var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, ...definition }) => definition));
578
- var PSP_CLIENT_TABLE = deepFreeze(PROCESSOR_DEFINITIONS.map((definition) => ({
579
- psp: definition.psp,
580
- match: new RegExp(definition.match, "i"),
581
- hosts: definition.hosts.map((source) => new RegExp(source, "i")),
582
- encoding: definition.encoding,
583
- headers: definition.passthroughHeaders.map((source) => new RegExp(source, "i")),
584
- ...definition.sandboxHosts ? { sandboxHosts: definition.sandboxHosts.map((source) => new RegExp(source, "i")) } : {},
585
- ...definition.hostEnvironmentPinned ? { hostEnvironmentPinned: true } : {},
586
- ...definition.mode ? { mode: definition.mode } : {},
587
- ...definition.clientSideEncrypted ? { clientSideEncrypted: true } : {},
588
- ...definition.compose ? { compose: definition.compose } : {},
589
- ...definition.encryptedCardFields ? { cse: { fields: definition.encryptedCardFields } } : {},
590
- ...definition.hostedForm ? { form: definition.hostedForm.card } : {}
591
- })));
592
- function buildBuiltinRegistry(recognizers) {
593
- return recognizers.filter((definition) => !definition.capability).map(({ cse, hostedForm, ...definition }) => ({
594
- ...definition,
595
- match: new RegExp(definition.match, "i"),
596
- passthroughHeaders: definition.passthroughHeaders.map((source) => new RegExp(source, "i"))
597
- }));
598
- }
599
- var BUILTIN_REGISTRY = deepFreeze(buildBuiltinRegistry(PSP_RECOGNIZERS));
600
- var CHECKOUT_MODES = deepFreeze(["token", "cse", "hosted_form"]);
601
- function recognizerByPsp(psp) {
602
- return typeof psp === "string" ? PSP_CLIENT_TABLE.find((entry) => entry.psp === psp) ?? null : null;
603
- }
604
- function trustedClientRecognizer(pspOrEntry) {
605
- const psp = typeof pspOrEntry === "string" ? pspOrEntry : pspOrEntry?.psp;
606
- const definition = recognizerByPsp(psp);
607
- if (!definition)
608
- throw new Error("Refusing to pay: unknown payment processor.");
609
- return definition;
610
- }
611
- // src/recognition.js
612
- var PATTERN_WITHOUT_CARD_FIELDS = Object.freeze({
613
- stripe: "api\\.stripe\\.com/v1/(payment_methods|tokens|setup_intents/seti_\\w+/confirm|payment_intents/pi_\\w+/confirm)"
614
- });
615
- var WITHOUT_CHECKOUT_SESSIONS = Object.freeze({
616
- stripe: Object.freeze({
617
- match: "api\\.stripe\\.com/v1/(payment_methods|tokens|sources|confirmation_tokens|setup_intents/seti_\\w+/confirm|payment_intents/pi_\\w+/confirm)",
618
- passWithoutCard: "api\\.stripe\\.com/v1/(sources|confirmation_tokens)"
619
- })
620
- });
621
- // src/request-policy.js
622
- var HEADER_NEVER_FORWARD = /^(cookie|set-cookie|authorization2|proxy-authorization|host|content-length|origin|referer)$/i;
623
- var ALLOWED_MEDIA_TYPES = {
624
- json: ["application/json", "text/plain"],
625
- form: ["application/x-www-form-urlencoded"]
626
- };
627
- function contentTypeFor(pspOrEntry, headers) {
628
- const rec = trustedClientRecognizer(pspOrEntry);
629
- const encoding = rec && rec.encoding === "form" ? "form" : "json";
630
- const allowed = ALLOWED_MEDIA_TYPES[encoding];
631
- const fallback = allowed[0];
632
- const entry = Object.entries(headers || {}).find(([k]) => /^content-type$/i.test(k));
633
- const raw = entry && entry[1];
634
- if (typeof raw !== "string")
635
- return fallback;
636
- const base = raw.split(";")[0].trim().toLowerCase();
637
- const worldpaySessionType = rec.psp === "worldpay" && encoding === "json" && base === "application/vnd.worldpay.sessions-v1.hal+json";
638
- if (!allowed.includes(base) && !worldpaySessionType)
639
- return fallback;
640
- const charset = raw.match(/;\s*charset=([\w-]+)/i);
641
- return charset ? `${base}; charset=${charset[1]}` : base;
642
- }
643
- function replayHeaders(pspOrEntry, captured = {}) {
644
- const rec = trustedClientRecognizer(pspOrEntry);
645
- const headers = { "content-type": contentTypeFor(rec, captured) };
646
- for (const [name, value] of Object.entries(captured)) {
647
- if (rec.headers.some((pattern) => pattern.test(name)) && !HEADER_NEVER_FORWARD.test(name))
648
- headers[name] = value;
649
- }
650
- return headers;
651
- }
652
- var replayPolicy = Object.freeze({ contentTypeFor, HEADER_NEVER_FORWARD });
653
- // src/stripe-checkout.js
654
- var STRIPE_CHECKOUT_OPERATION = "stripe.device_checkout_confirm.v1";
655
- var STRIPE_CHECKOUT_CONTEXT_HEADER = "x-agentcard-stripe-checkout-context";
656
- var SESSION = /^cs_(?:test|live)_[A-Za-z0-9]{1,200}(?![\s\S])/;
657
- var KEY = /^pk_(?:test|live)_[A-Za-z0-9_]{1,256}(?![\s\S])/;
658
- var SYNTHETIC = /^pm_agentcard_checkout_[A-Za-z0-9_-]{16,80}(?![\s\S])/;
659
- var STRIPE_CHECKOUT_VALIDATION_CODES = Object.freeze({
660
- unclassified: "unclassified",
661
- request_shape: "request_shape",
662
- request_body_bounds: "request_body_bounds",
663
- request_headers: "request_headers",
664
- form_encoding: "form_encoding",
665
- form_field_unknown: "form_field_unknown",
666
- form_field_duplicate: "form_field_duplicate",
667
- form_value_bounds: "form_value_bounds",
668
- context_invalid: "context_invalid",
669
- session_binding: "session_binding",
670
- publishable_key: "publishable_key",
671
- mode_binding: "mode_binding",
672
- attribution_session: "attribution_session",
673
- tokenization_fields: "tokenization_fields",
674
- billing_email: "billing_email",
675
- billing_binding: "billing_binding",
676
- billing_conflict: "billing_conflict",
677
- expected_amount: "expected_amount",
678
- payment_method_type: "payment_method_type",
679
- synthetic_payment_method: "synthetic_payment_method",
680
- required_final_fields: "required_final_fields"
681
- });
682
- var validationCodes = /* @__PURE__ */ new WeakMap();
683
- var reject = (code = "unclassified") => {
684
- const error = new Error("stripe_checkout_rejected");
685
- validationCodes.set(error, code);
686
- throw error;
687
- };
688
- function stripeCheckoutValidationCode(error) {
689
- const code = validationCodes.get(error);
690
- return Object.values(STRIPE_CHECKOUT_VALIDATION_CODES).find((value) => value === code) ?? STRIPE_CHECKOUT_VALIDATION_CODES.unclassified;
691
- }
692
- var object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
693
- var opaque = (value) => typeof value === "string" && value.length <= 8192 && !/[\x00-\x1f\x7f]/.test(value);
694
- var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
695
- function validateStripeCheckoutIdentity(value) {
696
- if (!object(value) || !["test", "production"].includes(value.environment ?? "test") || value.environment === null || !["sessionId", "publishableKey", "livemode"].some((key) => Object.hasOwn(value, key)))
697
- reject("mode_binding");
698
- const environment = value.environment ?? "test", livemode = environment === "production";
699
- const mode = livemode ? "live" : "test";
700
- if (Object.hasOwn(value, "sessionId") && (typeof value.sessionId !== "string" || !SESSION.test(value.sessionId) || !value.sessionId.startsWith(`cs_${mode}_`)) || Object.hasOwn(value, "publishableKey") && (typeof value.publishableKey !== "string" || !KEY.test(value.publishableKey) || !value.publishableKey.startsWith(`pk_${mode}_`)) || Object.hasOwn(value, "livemode") && value.livemode !== livemode)
701
- reject("mode_binding");
702
- return Object.freeze({ environment, livemode });
703
- }
704
- var TEST_CARDS = ["4242424242424242", "4000000000000002", "4000002500003155"];
705
- var SYNTHETIC_CARDS = [...TEST_CARDS, "4111111111111111"];
706
- function validateStripeCheckoutCard(card, environment = "test") {
707
- if (!["test", "production"].includes(environment) || !object(card) || typeof card.number !== "string" || typeof card.cvc !== "string" || !/^(?:0?[1-9]|1[0-2])(?![\s\S])/.test(String(card.month)) || !/^(?:\d{2}|\d{4})(?![\s\S])/.test(String(card.year)) || card.name !== void 0 && !opaque(card.name))
708
- reject();
709
- if (environment === "test") {
710
- if (!TEST_CARDS.includes(card.number) || card.cvc !== "123")
711
- reject();
712
- }
713
- else {
714
- if (!/^\d{12,19}(?![\s\S])/.test(card.number) || /^(\d)\1+(?![\s\S])/.test(card.number) || !/^\d{3,4}(?![\s\S])/.test(card.cvc) || SYNTHETIC_CARDS.includes(card.number))
715
- reject();
716
- let sum = 0, double = false;
717
- for (let i = card.number.length - 1; i >= 0; i--) {
718
- let digit = Number(card.number[i]);
719
- if (double) {
720
- digit *= 2;
721
- if (digit > 9)
722
- digit -= 9;
723
- }
724
- sum += digit;
725
- double = !double;
726
- }
727
- if (sum % 10 !== 0)
728
- reject();
729
- }
730
- return true;
731
- }
732
- function isStripeCheckoutHostedMode(value) {
733
- return value === "hosted" || value === "hosted_page";
734
- }
735
- function encodeAscii(value) {
736
- let result = "", bits = 0, count = 0;
737
- for (const char of value) {
738
- bits = bits << 8 | char.charCodeAt(0);
739
- count += 8;
740
- while (count >= 6) {
741
- count -= 6;
742
- result += alphabet[bits >>> count & 63];
743
- }
744
- }
745
- return result + (count ? alphabet[bits << 6 - count & 63] : "");
746
- }
747
- function context(value) {
748
- if (!object(value) || Object.keys(value).length !== 3 || value.version !== 1 || !["version", "session_id", "synthetic_payment_method_id"].every((key) => Object.hasOwn(value, key)) || !SESSION.test(value.session_id) || !SYNTHETIC.test(value.synthetic_payment_method_id))
749
- reject("context_invalid");
750
- return { version: 1, session_id: value.session_id, synthetic_payment_method_id: value.synthetic_payment_method_id };
751
- }
752
- function encodeStripeCheckoutContext(value) {
753
- return encodeAscii(JSON.stringify(context(value)));
754
- }
755
- function hasStripeCheckoutMarker(request) {
756
- return object(request?.headers) && Object.keys(request.headers).some((key) => key.toLowerCase() === STRIPE_CHECKOUT_CONTEXT_HEADER);
757
- }
758
- function parseStripeCheckoutContext(request) {
759
- const entries = Object.entries(request?.headers ?? {}).filter(([key]) => key.toLowerCase() === STRIPE_CHECKOUT_CONTEXT_HEADER);
760
- if (!entries.length)
761
- return null;
762
- if (entries.length !== 1 || typeof entries[0][1] !== "string" || !/^[A-Za-z0-9_-]{1,1024}$/.test(entries[0][1]))
763
- reject("context_invalid");
764
- const encoded = entries[0][1];
765
- let text = "", bits = 0, count = 0;
766
- for (const char of encoded) {
767
- bits = bits << 6 | alphabet.indexOf(char);
768
- count += 6;
769
- if (count >= 8) {
770
- count -= 8;
771
- text += String.fromCharCode(bits >>> count & 255);
772
- }
773
- }
774
- let value;
775
- try {
776
- value = context(JSON.parse(text));
777
- }
778
- catch {
779
- reject("context_invalid");
780
- }
781
- if (encodeStripeCheckoutContext(value) !== encoded)
782
- reject("context_invalid");
783
- return value;
784
- }
785
- function endpoint(raw) {
786
- let url;
787
- try {
788
- url = new URL(raw);
789
- }
790
- catch {
791
- return null;
792
- }
793
- if (url.origin !== "https://api.stripe.com" || url.port || url.username || url.password || url.search || url.hash)
794
- return null;
795
- return url;
796
- }
797
- function isStripeCheckoutEndpoint(raw) {
798
- const url = endpoint(raw);
799
- const match = url && /^\/v1\/payment_pages\/(cs_(?:test|live)_[A-Za-z0-9]+)\/confirm$/.exec(url.pathname);
800
- return !!match && SESSION.test(match[1]);
801
- }
802
- var finalFields = /* @__PURE__ */ new Set([
803
- "payment_method",
804
- "key",
805
- "expected_amount",
806
- "expected_payment_method_type",
807
- "payment_method_data[billing_details][email]",
808
- "init_checksum",
809
- "js_checksum",
810
- "version",
811
- "rv_timestamp",
812
- "eid",
813
- "link_brand",
814
- "guid",
815
- "muid",
816
- "sid",
817
- "passive_captcha_token",
818
- "passive_captcha_ekey",
819
- "pxcts",
820
- "pxvid",
821
- "px3",
822
- ...[
823
- "checkout_config_id",
824
- "checkout_session_id",
825
- "client_session_id",
826
- "merchant_integration_source",
827
- "merchant_integration_version",
828
- "payment_method_selection_flow"
829
- ].map((key) => `client_attribution_metadata[${key}]`),
830
- ...["shipping_rate_amount", "subtotal", "total_discount_amount", "total_exclusive_tax", "total_inclusive_tax"].map((key) => `last_displayed_line_item_group_details[${key}]`)
831
- ]);
832
- var addressFields = ["city", "country", "line1", "line2", "postal_code", "state"];
833
- var tokenFields = /* @__PURE__ */ new Set([
834
- "type",
835
- "key",
836
- "card[number]",
837
- "card[cvc]",
838
- "card[exp_month]",
839
- "card[exp_year]",
840
- "billing_details[name]",
841
- "billing_details[email]",
842
- "billing_details[phone]",
843
- "allow_redisplay",
844
- ...addressFields.map((key) => `billing_details[address][${key}]`),
845
- "guid",
846
- "muid",
847
- "sid",
848
- "payment_user_agent",
849
- "referrer",
850
- "time_on_page",
851
- ...[
852
- "checkout_config_id",
853
- "checkout_session_id",
854
- "client_session_id",
855
- "merchant_integration_source",
856
- "merchant_integration_version",
857
- "payment_method_selection_flow"
858
- ].map((key) => `client_attribution_metadata[${key}]`)
859
- ]);
860
- function form(request, fields) {
861
- if (!object(request) || request.method !== "POST" || typeof request.body !== "string")
862
- reject("request_shape");
863
- if (request.body.length > 2e4)
864
- reject("request_body_bounds");
865
- if (!object(request.headers) || Object.keys(request.headers).length > 40 || Object.values(request.headers).some((value) => !opaque(value)))
866
- reject("request_headers");
867
- const pairs = [];
868
- const seen = /* @__PURE__ */ new Set();
869
- for (const raw of request.body.split("&")) {
870
- const index = raw.indexOf("=");
871
- if (index < 1)
872
- reject("form_encoding");
873
- let key, value;
874
- try {
875
- key = decodeURIComponent(raw.slice(0, index).replace(/\+/g, " "));
876
- value = decodeURIComponent(raw.slice(index + 1).replace(/\+/g, " "));
877
- }
878
- catch {
879
- reject("form_encoding");
880
- }
881
- if (!fields.has(key))
882
- reject("form_field_unknown");
883
- if (seen.has(key))
884
- reject("form_field_duplicate");
885
- if (!opaque(value))
886
- reject("form_value_bounds");
887
- seen.add(key);
888
- pairs.push({ key, value, raw });
889
- }
890
- return { pairs, values: new Map(pairs.map(({ key, value }) => [key, value])) };
891
- }
892
- function keyFor(request, values, expected) {
893
- const authorization = Object.entries(request.headers).filter(([key2]) => key2.toLowerCase() === "authorization");
894
- if (authorization.length > 1)
895
- reject("publishable_key");
896
- let headerKey;
897
- if (authorization.length) {
898
- const match = /^Bearer (pk_(?:test|live)_[A-Za-z0-9_]{1,256})$/.exec(authorization[0][1]);
899
- if (!match)
900
- reject("publishable_key");
901
- headerKey = match[1];
902
- }
903
- const key = values.get("key") || headerKey;
904
- if (!KEY.test(key) || headerKey && headerKey !== key || expected !== void 0 && expected !== key)
905
- reject("publishable_key");
906
- return key;
907
- }
908
- var billingEmail = (value) => value === null || typeof value === "string" && value.length > 0 && value.length <= 320 && opaque(value);
909
- function captureStripeCheckoutBilling(request, options) {
910
- const phase = classifyStripeCheckoutRequest(request, options);
911
- if (phase?.phase !== "tokenization" || !SYNTHETIC.test(options?.syntheticPaymentMethodId))
912
- reject("billing_binding");
913
- const email = form(request, tokenFields).values.get("billing_details[email]") ?? null;
914
- if (!billingEmail(email))
915
- reject("billing_email");
916
- return Object.freeze({
917
- session_id: phase.session_id,
918
- synthetic_payment_method_id: options.syntheticPaymentMethodId,
919
- email
920
- });
921
- }
922
- function attachStripeCheckoutBilling(request, captured, options) {
923
- const phase = classifyStripeCheckoutRequest(request, options);
924
- if (phase?.phase !== "final" || !object(captured) || Object.keys(captured).length !== 3 || !["session_id", "synthetic_payment_method_id", "email"].every((key) => Object.hasOwn(captured, key)) || captured.session_id !== phase.session_id || captured.synthetic_payment_method_id !== phase.synthetic_payment_method_id || !billingEmail(captured.email))
925
- reject("billing_binding");
926
- const field = "payment_method_data[billing_details][email]";
927
- const existing = form(request, finalFields).values.get(field);
928
- if (existing !== void 0 && existing !== captured.email)
929
- reject("billing_conflict");
930
- if (captured.email === null || existing !== void 0)
931
- return { ...request };
932
- const body = `${request.body}&${encodeURIComponent(field)}=${encodeURIComponent(captured.email)}`;
933
- const result = { ...request, body };
934
- classifyStripeCheckoutRequest(result, options);
935
- return result;
936
- }
937
- function classifyStripeCheckoutRequest(request, options = {}) {
938
- const url = endpoint(request?.url);
939
- if (!url) {
940
- if (hasStripeCheckoutMarker(request))
941
- reject("context_invalid");
942
- return null;
943
- }
944
- const final = isStripeCheckoutEndpoint(request.url);
945
- const token = url.pathname === "/v1/payment_methods";
946
- if (!final && !token) {
947
- if (hasStripeCheckoutMarker(request))
948
- reject("context_invalid");
949
- return null;
950
- }
951
- if (token && options.sessionId === void 0 && !hasStripeCheckoutMarker(request))
952
- return null;
953
- const marker = parseStripeCheckoutContext(request);
954
- const sessionId = final ? url.pathname.split("/")[3] : options.sessionId;
955
- if (!SESSION.test(sessionId) || options.sessionId !== void 0 && options.sessionId !== sessionId || marker && marker.session_id !== sessionId)
956
- reject("session_binding");
957
- const { values } = form(request, final ? finalFields : tokenFields);
958
- const key = keyFor(request, values, options.publishableKey);
959
- validateStripeCheckoutIdentity({ environment: options.environment, sessionId, publishableKey: key });
960
- const attributionSession = values.get("client_attribution_metadata[checkout_session_id]");
961
- if (attributionSession !== void 0 && attributionSession !== sessionId)
962
- reject("attribution_session");
963
- if (token) {
964
- const number = values.get("card[number]");
965
- if (values.get("type") !== "card" || typeof number !== "string" || number.length > 64 || /[^0-9 -]/.test(number) || !["4111111111111111", "4242424242424242"].includes(number.replace(/[ -]/g, "")) || !/^\d{3}$/.test(values.get("card[cvc]") ?? "") || !/^(?:0?[1-9]|1[0-2])$/.test(values.get("card[exp_month]") ?? "") || !/^(?:\d{2}|\d{4})$/.test(values.get("card[exp_year]") ?? "") || values.has("allow_redisplay") && values.get("allow_redisplay") !== "unspecified")
966
- reject("tokenization_fields");
967
- return { phase: "tokenization", session_id: sessionId, publishable_key: key };
968
- }
969
- const amount = values.get("expected_amount");
970
- const syntheticId = values.get("payment_method");
971
- const email = values.get("payment_method_data[billing_details][email]");
972
- if (email !== void 0 && !billingEmail(email))
973
- reject("billing_email");
974
- if (!/^[1-9]\d{0,11}$/.test(amount ?? "") || !Number.isSafeInteger(Number(amount)) || options.amountCents !== void 0 && options.amountCents !== Number(amount))
975
- reject("expected_amount");
976
- if (values.get("expected_payment_method_type") !== "card")
977
- reject("payment_method_type");
978
- if (!SYNTHETIC.test(syntheticId))
979
- reject("synthetic_payment_method");
980
- if (["init_checksum", "js_checksum", "version", "rv_timestamp"].some((key2) => !values.get(key2)))
981
- reject("required_final_fields");
982
- if (options.syntheticPaymentMethodId !== void 0 && options.syntheticPaymentMethodId !== syntheticId || marker && marker.synthetic_payment_method_id !== syntheticId)
983
- reject("synthetic_payment_method");
984
- return {
985
- phase: "final",
986
- session_id: sessionId,
987
- publishable_key: key,
988
- synthetic_payment_method_id: syntheticId,
989
- amount_cents: Number(amount),
990
- currency: "usd"
991
- };
992
- }
993
- function createStripeCheckoutTokenizationStub(request, options) {
994
- const phase = classifyStripeCheckoutRequest(request, options);
995
- if (phase?.phase !== "tokenization" || !SYNTHETIC.test(options?.syntheticPaymentMethodId))
996
- reject();
997
- const { values } = form(request, tokenFields);
998
- const address = Object.fromEntries(addressFields.filter((key) => values.has(`billing_details[address][${key}]`)).map((key) => [key, values.get(`billing_details[address][${key}]`)]));
999
- const billing = Object.fromEntries(["name", "email", "phone"].filter((key) => values.has(`billing_details[${key}]`)).map((key) => [key, values.get(`billing_details[${key}]`)]));
1000
- const year = values.get("card[exp_year]");
1001
- return { status: 200, headers: { "content-type": "application/json" }, body: JSON.stringify({
1002
- id: options.syntheticPaymentMethodId,
1003
- object: "payment_method",
1004
- type: "card",
1005
- livemode: validateStripeCheckoutIdentity({ environment: options.environment, sessionId: phase.session_id }).livemode,
1006
- billing_details: { ...billing, address },
1007
- card: {
1008
- brand: "visa",
1009
- last4: "4242",
1010
- exp_month: Number(values.get("card[exp_month]")),
1011
- exp_year: Number(year.length === 2 ? `20${year}` : year),
1012
- funding: "credit",
1013
- country: "US"
1014
- }
1015
- }) };
1016
- }
1017
- var terminalFields = /* @__PURE__ */ new Set([
1018
- "id",
1019
- "object",
1020
- "state",
1021
- "status",
1022
- "payment_status",
1023
- "currency",
1024
- "livemode",
1025
- "session_id",
1026
- "mode",
1027
- "ui_mode",
1028
- "success_url",
1029
- "return_url"
1030
- ]);
1031
- function parseStripeCheckoutResponse(raw, options) {
1032
- let value;
1033
- try {
1034
- value = typeof raw === "string" ? JSON.parse(raw) : raw;
1035
- validateStripeCheckoutIdentity({ environment: options?.environment, sessionId: options?.sessionId, livemode: value?.livemode });
1036
- }
1037
- catch {
1038
- return null;
1039
- }
1040
- if (!object(value) || !SESSION.test(options?.sessionId) || value.session_id !== options.sessionId || Object.keys(value).some((key) => !terminalFields.has(key)) || !["id", "object", "state", "status", "payment_status", "currency", "livemode", "session_id", "mode", "ui_mode"].every((key) => Object.hasOwn(value, key)) || typeof value.id !== "string" || !/^[A-Za-z0-9_]{1,256}(?![\s\S])/.test(value.id) || value.object !== "checkout.session" || value.state !== "succeeded" || value.status !== "complete" || value.payment_status !== "paid" || value.currency !== "usd" || value.mode !== "payment" || !isStripeCheckoutHostedMode(value.ui_mode))
1041
- return null;
1042
- for (const key of ["success_url", "return_url"]) {
1043
- if (!Object.hasOwn(value, key) || value[key] === null)
1044
- continue;
1045
- if (typeof value[key] !== "string" || value[key].length > 2048)
1046
- return null;
1047
- let url;
1048
- try {
1049
- url = new URL(value[key]);
1050
- }
1051
- catch {
1052
- return null;
1053
- }
1054
- let parameters;
1055
- try {
1056
- parameters = decodeURIComponent(url.search + url.hash);
1057
- }
1058
- catch {
1059
- return null;
1060
- }
1061
- if (url.protocol !== "https:" || url.username || url.password || options.returnOrigin !== void 0 && url.origin !== options.returnOrigin || /(?:client_secret|payment_method|secret_)/i.test(parameters))
1062
- return null;
1063
- }
1064
- return { ...value };
1065
- }
1066
- function prepareStripeCheckoutReplay(request, card, options = {}) {
1067
- const marker = parseStripeCheckoutContext(request);
1068
- if (!marker)
1069
- reject();
1070
- const phase = classifyStripeCheckoutRequest(request, {
1071
- ...options,
1072
- sessionId: marker.session_id,
1073
- syntheticPaymentMethodId: marker.synthetic_payment_method_id
1074
- });
1075
- if (phase?.phase !== "final" || options.amountCents !== void 0 && phase.amount_cents !== options.amountCents || options.sessionId !== void 0 && options.sessionId !== marker.session_id || !validateStripeCheckoutCard(card, options.environment))
1076
- reject();
1077
- const replacements = {
1078
- "payment_method_data[type]": "card",
1079
- "payment_method_data[card][number]": card.number,
1080
- "payment_method_data[card][cvc]": card.cvc,
1081
- "payment_method_data[card][exp_month]": String(card.month),
1082
- "payment_method_data[card][exp_year]": String(card.year),
1083
- ...card.name ? { "payment_method_data[billing_details][name]": card.name } : {}
1084
- };
1085
- const inline = Object.entries(replacements).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
1086
- const body = form(request, finalFields).pairs.map((pair) => pair.key === "payment_method" ? inline : pair.raw).join("&");
1087
- return {
1088
- psp: "stripe",
1089
- request: { url: request.url, method: "POST", headers: replayHeaders("stripe", request.headers), body },
1090
- where: Object.keys(replacements)
1091
- };
1092
- }
1093
- export { STRIPE_CHECKOUT_CONTEXT_HEADER, STRIPE_CHECKOUT_OPERATION, STRIPE_CHECKOUT_VALIDATION_CODES, attachStripeCheckoutBilling, captureStripeCheckoutBilling, classifyStripeCheckoutRequest, createStripeCheckoutTokenizationStub, encodeStripeCheckoutContext, hasStripeCheckoutMarker, isStripeCheckoutEndpoint, isStripeCheckoutHostedMode, parseStripeCheckoutContext, parseStripeCheckoutResponse, prepareStripeCheckoutReplay, stripeCheckoutValidationCode, validateStripeCheckoutCard, validateStripeCheckoutIdentity };