@agent-cards/sdk 0.23.20 → 0.23.22

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 (64) hide show
  1. package/CHANGELOG.md +55 -2
  2. package/PREFLIGHT.md +6 -0
  3. package/README.md +169 -5
  4. package/dist/amadeus-checkout.generated.d.ts +6 -1
  5. package/dist/amadeus-checkout.generated.js +26 -2
  6. package/dist/card-fields.generated.js +2341 -127
  7. package/dist/cdp.js +199 -28
  8. package/dist/client.d.ts +177 -9
  9. package/dist/client.js +387 -37
  10. package/dist/comgate.d.ts +50 -0
  11. package/dist/comgate.js +80 -0
  12. package/dist/creditguard.generated.js +4 -2
  13. package/dist/cse-body.js +11 -0
  14. package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
  15. package/dist/ebanx.d.ts +1 -1
  16. package/dist/ebanx.js +1 -1
  17. package/dist/eprotect.generated.d.ts +1 -1
  18. package/dist/eximbay.d.ts +46 -0
  19. package/dist/eximbay.js +69 -0
  20. package/dist/gmo.generated.js +7 -3
  21. package/dist/hipay.generated.d.ts +5 -5
  22. package/dist/hipay.generated.js +5 -2
  23. package/dist/hold.d.ts +1 -1
  24. package/dist/hold.js +10 -4
  25. package/dist/hosted-entry.d.ts +4 -0
  26. package/dist/hosted-entry.js +32 -0
  27. package/dist/index.d.ts +1 -0
  28. package/dist/lifecycle.d.ts +9 -4
  29. package/dist/lifecycle.js +16 -4
  30. package/dist/paddle-tokenex.generated.js +7 -3
  31. package/dist/paygent.generated.d.ts +60 -0
  32. package/dist/paygent.generated.js +425 -0
  33. package/dist/payrails.generated.js +7 -3
  34. package/dist/payrexx.d.ts +91 -0
  35. package/dist/payrexx.generated.d.ts +91 -0
  36. package/dist/payrexx.generated.js +536 -0
  37. package/dist/payrexx.js +141 -0
  38. package/dist/pre-claim.d.ts +30 -1
  39. package/dist/pre-claim.js +257 -39
  40. package/dist/preflight-catalog.json +58 -0
  41. package/dist/preflight-schemas.json +4 -0
  42. package/dist/preflight.generated.js +26 -2
  43. package/dist/prepared-processor.d.ts +7 -2
  44. package/dist/prepared-processor.js +16 -2
  45. package/dist/primer.generated.js +2469 -255
  46. package/dist/przelewy24.d.ts +47 -0
  47. package/dist/przelewy24.js +88 -0
  48. package/dist/registry.d.ts +6 -5
  49. package/dist/registry.js +27 -7
  50. package/dist/shiji-sts.generated.js +7 -3
  51. package/dist/stripe-checkout.generated.js +896 -19
  52. package/dist/toast-seal.generated.d.ts +37 -0
  53. package/dist/toast-seal.generated.js +625 -0
  54. package/dist/toast.generated.d.ts +41 -0
  55. package/dist/toast.generated.js +113 -0
  56. package/dist/tokenex-iframe.d.ts +93 -0
  57. package/dist/tokenex-iframe.generated.d.ts +81 -0
  58. package/dist/tokenex-iframe.generated.js +403 -0
  59. package/dist/tokenex-iframe.js +195 -0
  60. package/dist/transbank.d.ts +45 -0
  61. package/dist/transbank.js +73 -0
  62. package/dist/twoc2p.generated.js +7 -3
  63. package/examples/preflight/kernel-native/inventory.json +1 -1
  64. package/package.json +3 -3
package/dist/client.js CHANGED
@@ -2,7 +2,12 @@ import { BUILTIN_REGISTRY, cardUrlPatterns as deriveCardUrlPatterns, LOOPBACK_UR
2
2
  import { armServedProfiles, classifyProfileRequest, declareSandboxMerchant, merchantProfileEnvironment, merchantProfileFor, merchantProfileUrlPatterns, } from './merchant-hosted.js';
3
3
  import { merchantAmountExchanges, merchantAmountFromResponses, merchantProfilePricedBySource, merchantTotalWire, MerchantTotalWait, } from './merchant-total.js';
4
4
  import { ACI_CAPABILITY, ACI_PSP, aciCardLegAnswer } from './aci.js';
5
+ import { PRZELEWY24_CAPABILITY, PRZELEWY24_PSP } from './przelewy24.js';
5
6
  import { EPROTECT_CAPABILITY, EPROTECT_PSP, eprotectCheckoutRefusal, eprotectScriptAnswer, isEprotectRegistration } from './eprotect.js';
7
+ import { COMGATE_CAPABILITY, COMGATE_PSP } from './comgate.js';
8
+ import { EXIMBAY_CAPABILITY } from './eximbay.js';
9
+ import { isHostedGetEntry } from './hosted-entry.js';
10
+ import { TRANSBANK_CAPABILITY, TRANSBANK_PSP } from './transbank.js';
6
11
  import { isMercadoTokenRequest, parseMercadoCheckoutContext, validateMercadoProcessorContext } from './mercado-checkout.generated.js';
7
12
  import { matchesPreparation, preparationMode, validPreparationEnvironment } from './prepared-processor.js';
8
13
  import { fiservKeyPinById, isFiservSubstitution, requiresFiservPreparation } from './fiserv.js';
@@ -15,10 +20,12 @@ import { CONVERGE_CAPABILITY } from './elavon-converge.js';
15
20
  import { SHIJI_STS_CAPABILITY, SHIJI_STS_PSP, isShijiStsServedCompanion, isShijiStsServedSubstitution } from './shiji-sts.js';
16
21
  import { AMADEUS_CHECKOUT_CAPABILITY, AMADEUS_CHECKOUT_PSP } from './amadeus-checkout.generated.js';
17
22
  import { PADDLE_VGS_CAPABILITY, PADDLE_VGS_PSP, paddleVgsCardFactsOf } from './paddle-vgs.generated.js';
23
+ import { PAYGENT_CAPABILITY, PAYGENT_PSP, paygentAnswerOutcome } from './paygent.generated.js';
18
24
  import { PADDLE_TOKENEX_CAPABILITY, PADDLE_TOKENEX_PSP, isPaddleTokenexServedFacts, isPaddleTokenexServedSubstitution } from './paddle-tokenex.js';
19
25
  import { TWOC2P_CAPABILITY, TWOC2P_PSP } from './twoc2p.js';
20
26
  import { WIX_PAYMENTS_CAPABILITY, WIX_PAYMENTS_PSP } from './wix-payments.generated.js';
21
27
  import { HIPAY_CAPABILITY, HIPAY_PSP } from './hipay.generated.js';
28
+ import { TOAST_CAPABILITY, TOAST_PSP, isToastCiphertext } from './toast.generated.js';
22
29
  import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
23
30
  import { VoiceModule } from './voice.js';
24
31
  import { BuyModule } from './buy.js';
@@ -32,11 +39,13 @@ import { JUSPAY_CAPABILITY, JUSPAY_PSP, juspayPageAnswer } from './juspay-gate.j
32
39
  import { CARD_ENDPOINT_MODE, CARD_ENDPOINT_PSP, CardEndpointRefusedError, cardEndpointById, cardEndpointLive, cardEndpointSessionRefusal, } from './card-endpoint.js';
33
40
  import { merchantCardEndpointFactsRefusal as sharedFactsRefusal, readMerchantCardEndpointFacts as sharedReadFacts, } from './merchant-card-endpoints.generated.js';
34
41
  import { DATATRANS_CAPABILITY } from './datatrans.js';
42
+ import { TOKENEX_IFRAME_CAPABILITY, TOKENEX_IFRAME_PSP, tokenexIframePageAnswer, tokenexIframePlaceholderFits } from './tokenex-iframe.js';
35
43
  import { PELECARD_CAPABILITY, PELECARD_PSP } from './pelecard.js';
36
44
  import { PRIMER_CAPABILITY, PRIMER_PSP, primerTokenAnswer } from './primer.js';
37
45
  import { PAYRAILS_CAPABILITY, isPayrailsPaymentUrl, payrailsAnswer } from './payrails.js';
38
46
  import { WSPAY_CAPABILITY, WSPAY_PSP } from './wspay.js';
39
47
  import { EbanxFollowUpError } from './ebanx.js';
48
+ import { PAYREXX_CAPABILITY, PAYREXX_PSP, PayrexxFollowUpError } from './payrexx.js';
40
49
  import { SECURE_ACCEPTANCE_CAPABILITY, SECURE_ACCEPTANCE_FAST_POLL_MS, SECURE_ACCEPTANCE_PSP, SECURE_ACCEPTANCE_REPLY_WINDOW_MS, secureAcceptanceCheckoutProfile, secureAcceptanceCheckoutRefusal, secureAcceptanceFrameAnswer, } from './cybersource-secure-acceptance.js';
41
50
  /**
42
51
  * The modes this SDK can finish. Asked for on syncRegistry (the API serves
@@ -228,6 +237,13 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
228
237
  * checkout relay. Served on the same terms: only to a build that lists it, for a company
229
238
  * Agentcard turned Payfast on for.
230
239
  *
240
+ * `przelewy24_hosted_payment_page`: Przelewy24's pay wall, whose card-free link
241
+ * (secure.przelewy24.pl/trnRequest/<token>, a plain GET the merchant's redirect or script sends
242
+ * the buyer to) this build pauses for the cardholder's device by its exact link (hosted-entry.ts),
243
+ * and whose card method post, card form post and Saferpay resolver this build never lets the
244
+ * agent's browser send (pre-claim.ts, przelewy24.ts). Served on the same terms as NMI's and
245
+ * Redsys's: only to a build that lists it, for a company Agentcard turned Przelewy24 on for.
246
+ *
231
247
  * `wix_payments_tokenize`: the card request Wix Cashier's card frame sends to Wix's
232
248
  * tokenizer (cashier-pci.wixapps.net), only through a prepared checkout: Wix's payment
233
249
  * widget fails the payment 30 seconds after it asks the frame for a token. The
@@ -244,6 +260,31 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
244
260
  * the checkout relay. Served on the same terms: only to a build that lists it, for a company
245
261
  * Agentcard turned PayMongo on for.
246
262
  *
263
+ * `gopay_card_form`: the card post GoPay's card frame sends to its own URL
264
+ * (secure.gopay.com/gp-card-comm/h/<token>) inside the GoPay gateway a shop opens. This build pauses
265
+ * only the frame's own card post and answers it with GoPay's answer as the API recorded it, the
266
+ * empty JSON object the frame reads; the gateway then reads the payment's end from GoPay and runs
267
+ * 3-D Secure in the agent's browser when the bank asks for it. The cardholder's device sends the card
268
+ * through the checkout relay. Served only to a build that lists it, and only for Agentcard's proof
269
+ * company until a reviewed change binds GoPay's payee and amount to the approval: the post names
270
+ * neither. The API takes it only at a shop Agentcard reviewed.
271
+ *
272
+ * `paygent_token`: the token request Paygent's own PaygentToken.js sends from a merchant's
273
+ * page (token.paygent.co.jp/n/token/request), only through a prepared checkout: the script
274
+ * abandons the request 35 seconds after the press that sends it, so the cardholder approves
275
+ * before the press. The cardholder's device sends the card itself and the page gets back
276
+ * Paygent's single-use token alone; the SDK refuses a second token request on the same approval.
277
+ * Served on the same terms: only to a build that lists it, for a company Agentcard turned
278
+ * Paygent on for.
279
+ *
280
+ * `payrexx_card_frame`: the card request Payrexx Pay's card frame sends to its own URL
281
+ * (checkout.payrexx.com/?pspId=44&lang=<xx>) on a Payrexx merchant's page. This build pauses only
282
+ * the frame's own one-time card request and answers it with Payrexx's answer as the API recorded
283
+ * it: the token the merchant's page then charges, or the start of 3-D Secure, whose two later sends
284
+ * of the card this build hands to the cardholder's device under the same approval (payrexx.ts,
285
+ * pre-claim.ts payrexxPreClaim). The cardholder's device sends the card through the checkout relay.
286
+ * Served only to a build that lists it, for a company Agentcard turned Payrexx on for.
287
+ *
247
288
  * `hipay_vault_token`: the token create HiPay's hosted fields send to HiPay's Secure Vault
248
289
  * (secure2-vault.hipay-tpp.com, stage-secure2-vault for a test row), only through a prepared
249
290
  * checkout: the merchant's page gives the frame 20 seconds after Pay. The cardholder's device
@@ -265,9 +306,117 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
265
306
  * Secure step and card lookup this build never lets the agent's browser send (pre-claim.ts,
266
307
  * quickpay.ts). Served on the same terms as NMI's and Redsys's: only to a build that lists it, for a
267
308
  * company Agentcard turned QuickPay on for.
309
+ *
310
+ * `revolut_card_field`: the card payment Revolut Business's card field or card popup sends from
311
+ * its frame to /api/public/orders/<order token>/payments on merchant.revolut.com, on a merchant's
312
+ * own page or Revolut's hosted checkout. This build pauses only the frame's card payment and
313
+ * answers it with Revolut's answer as the API recorded it: the payment Revolut created, which the
314
+ * frame then follows, through any 3-D Secure step, in the agent's browser. A payment by Apple Pay,
315
+ * Google Pay or Revolut Pay on the same URL carries no card and continues untouched. Revolut's
316
+ * host answers the Vault's origin, so the cardholder's device sends the card itself, with no relay.
317
+ * Served on the same terms: only to a build that lists it, for a company Agentcard turned Revolut
318
+ * on for.
319
+ *
320
+ * `pay_nl_card_token`: the card token request a shop's card form sends to Pay. (pay.nl),
321
+ * Weeztix's sandboxed frame posting its FormData to payment.pay.nl/v1/Payment/getCardToken/json.
322
+ * This build pauses only the form's own multipart body at that exact URL; the cardholder's
323
+ * device sends the card through the checkout relay, and the frame gets the token answer
324
+ * payment-core's judge rebuilt, with the CORS answer for the frame's own origin, null. The
325
+ * request names no amount, so the agent's pair, above zero, is the one the cardholder approves
326
+ * (PAY_NL_PSP below). Served on the same terms: only to a build that lists it, for a company
327
+ * Agentcard turned Pay. on for.
328
+ *
329
+ * `paypal_card_fields`: PayPal Card Fields' confirm, the POST its v5 frame sends to
330
+ * www.paypal.com and its v6 frame to api-m.paypal.com. This build pauses it only in
331
+ * the frames' own shape, refuses one that asks PayPal to keep the card or forces
332
+ * 3-D Secure in the browser with a `blocked` event, and answers the frame with the
333
+ * order's id and status as the API recorded them. The device sends a v5 confirm
334
+ * through the checkout relay and a v6 one directly. Served on the same terms: only
335
+ * to a build that lists it, for a company Agentcard turned PayPal Card Fields on
336
+ * for. Every other company keeps the checkout guard's refusal (paypal-card-forms.ts).
337
+ *
338
+ * `comgate_hosted_payment_page`: ComGate's payment gateway, whose card-free gateway link
339
+ * (payN.comgate.cz/init?id=<transaction id>, a plain GET) this build pauses for the
340
+ * cardholder's device by its exact link (hosted-entry.ts), in the page, in a frame of it or
341
+ * in a window it opened, and whose card hosts this build never lets the agent's browser post
342
+ * to (pre-claim.ts, comgate.ts). Served on the same terms as NMI's and Redsys's: only to a
343
+ * build that lists it, for a company Agentcard turned ComGate on for.
344
+ *
345
+ * `makecommerce_card_frame`: the card post MakeCommerce's card frame sends to its own card endpoint
346
+ * (cc.maksekeskus.ee/card-payment) under the shop's publishable key. This build pauses only the
347
+ * frame's own one-time card payment and answers it with MakeCommerce's answer as the API recorded
348
+ * it: the URL of MakeCommerce's 3-D Secure page, which the frame then opens in the agent's browser.
349
+ * The cardholder's device sends the card through the checkout relay. Served on the same terms: only
350
+ * to a build that lists it, for a company Agentcard turned MakeCommerce on for.
351
+ *
352
+ * `poynt_collect_nonce`: GoDaddy Payments' card request, the POST Poynt Collect's
353
+ * card frame sends to a store's generate-nonce on services.poynt.net. This build
354
+ * pauses it only as the frame writes it, refuses a body that asks Poynt to keep the
355
+ * card in the browser with a `blocked` event, and answers the frame with the nonce as
356
+ * the API recorded it. The device sends the card through the checkout relay. Served
357
+ * on the same terms: only to a build that lists it, for a company Agentcard turned
358
+ * GoDaddy Payments on for.
359
+ *
360
+ * `tokenex_iframe_tokenize`: the card post TokenEx's hosted iframe v3 sends from its card-number frame
361
+ * to its own document URL on htp.tokenex.com (JetBlue's card form). This build pauses only the PCI frame's
362
+ * own card post with its CVV frame, refuses in the browser every other post on that path that carries a
363
+ * card (tokenex-iframe.ts), answers it with TokenEx's token as the API recorded it, rebuilt from
364
+ * payment-core's allow-list, and answers the same frame's byte-identical repeat from that answer while the
365
+ * approval is being handed off. Served on the same terms: only to a build that lists it, for a company
366
+ * Agentcard turned the TokenEx iframe on for.
367
+ *
368
+ * `transbank_webpay_hosted_payment_page`: Transbank's Webpay Plus, whose card-free entry
369
+ * (webpay3g.transbank.cl/webpayserver/initTransaction?token_ws=<token>, the merchant's
370
+ * redirect, a plain GET) this build pauses for the cardholder's device by its exact link
371
+ * (hosted-entry.ts) in the page or a frame of it, and with attachToCdp also in a window the
372
+ * page opened, and whose hosts this build never lets the agent's browser post to (pre-claim.ts,
373
+ * transbank.ts), so the token is never spent there. attachToPlaywright covers the page it is
374
+ * attached to and that page's frames only, and pauses only a direct or scripted navigation to
375
+ * the entry: its page.route sees no HTTP redirect hop, so a merchant's redirect needs attachToCdp.
376
+ * Served on the same terms as NMI's and Redsys's: only to a build that lists it, for a company
377
+ * Agentcard turned Webpay on for.
378
+ *
379
+ * `sumup_checkout_process`: the card request SumUp's Payment Widget sends from its hidden form frame
380
+ * on gateway.sumup.com, a PUT to https://api.sumup.com/v0.2/checkouts/<checkout id>. This build pauses
381
+ * that PUT as it pauses Gravy's, so only the CDP and Playwright adapters see it (the form frame is
382
+ * cross-origin to the page), and answers the frame with SumUp's own answer as the API recorded it: the
383
+ * paid checkout, or the 3-D Secure step the widget then runs in the agent's browser. The cardholder's
384
+ * device sends the card itself, and the API holds the payment to a merchant Agentcard reviewed, at the
385
+ * amount SumUp holds for the checkout. Served on the same terms: only to a build that lists it, for a
386
+ * company Agentcard turned SumUp on for.
387
+ *
388
+ * `clover_card_token`: the card token request Clover's card frame (the hidden frame of
389
+ * checkout.clover.com that Clover's iframe mounts on a merchant's page) sends to
390
+ * token.clover.com/v1/tokens (token-sandbox.dev.clover.com for a test row), with the number
391
+ * encrypted under Clover's key. This build pauses only the frame's own body at that exact URL; the
392
+ * cardholder's device builds the request again with the real card under the key payment-core pins
393
+ * and sends it to Clover itself, and the frame gets the token answer payment-core's judge rebuilt.
394
+ * The request names no amount, so the agent's pair, above zero, is the one the cardholder approves
395
+ * (CLOVER_PSP below). Served on the same terms: only to a build that lists it, for a company
396
+ * Agentcard turned Clover on for.
397
+ *
398
+ * `eximbay_hosted_payment_page`: Eximbay's payment window, whose card-free signed entry
399
+ * (a form post to /Gateway/BasicProcessor.krp on secureapi.eximbay.com, or its test host) this
400
+ * build pauses for the cardholder's device, in the page or in a window it opened once attachToCdp
401
+ * has armed that window (eximbay.ts names the entry that can leave first), and whose
402
+ * step, amount query, card lookup and card post this build never lets the agent's browser send
403
+ * (pre-claim.ts, eximbay.ts). Served on the same terms as NMI's and Redsys's: only to a build
404
+ * that lists it, for a company Agentcard turned Eximbay on for.
405
+ *
406
+ * `toast_pinned_key`: the card request Toast's payments frame sends to
407
+ * payments.toasttab.com/v1/payment-methods on a Toast online-ordering checkout, with
408
+ * the card sealed in card.cardData under one key every Toast restaurant shares. The
409
+ * cardholder's device seals the real card under that key, which payment-core pins, and
410
+ * this build writes it over the agent's placeholder and nothing else before it
411
+ * continues the request. Served on the same terms: only to a build that lists it, for
412
+ * a company Agentcard turned Toast on for.
268
413
  */
269
414
  export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CYBERSOURCE_FLEX_CAPABILITY, JUSPAY_CAPABILITY, DATATRANS_CAPABILITY, 'authorize_net_dpm_form', PELECARD_CAPABILITY, ACI_CAPABILITY, 'creditguard_pps_process', PRIMER_CAPABILITY, PAYRAILS_CAPABILITY, CONVERGE_CAPABILITY, EPROTECT_CAPABILITY, WSPAY_CAPABILITY, SHIJI_STS_CAPABILITY, AMADEUS_CHECKOUT_CAPABILITY, 'worldline_mycheckout_form', 'verifone_card_capture', 'ekashu_hosted_payment_page', SECURE_ACCEPTANCE_CAPABILITY, 'gravy_session_fields_put', 'moneris_hosted_tokenization', PADDLE_VGS_CAPABILITY, PADDLE_TOKENEX_CAPABILITY,
270
- 'redsys_hosted_payment_page', 'lyra_krypton_payment', 'payfast_card_pay', WIX_PAYMENTS_CAPABILITY, 'paymongo_checkout_pay', HIPAY_CAPABILITY, TWOC2P_CAPABILITY, 'quickpay_hosted_payment_page'];
415
+ 'redsys_hosted_payment_page', 'lyra_krypton_payment', 'payfast_card_pay', WIX_PAYMENTS_CAPABILITY, 'paymongo_checkout_pay', PAYGENT_CAPABILITY, 'pay_nl_card_token', HIPAY_CAPABILITY, TWOC2P_CAPABILITY, 'quickpay_hosted_payment_page', 'revolut_card_field', 'paypal_card_fields', 'gopay_card_form', PAYREXX_CAPABILITY, COMGATE_CAPABILITY, PRZELEWY24_CAPABILITY, 'makecommerce_card_frame', 'poynt_collect_nonce',
416
+ TOKENEX_IFRAME_CAPABILITY, TRANSBANK_CAPABILITY,
417
+ 'sumup_checkout_process',
418
+ 'clover_card_token',
419
+ EXIMBAY_CAPABILITY, TOAST_CAPABILITY];
271
420
  /**
272
421
  * Processors whose paused request names no amount at all, so the pair the
273
422
  * agent passes is the amount the cardholder approves: authorize() refuses one
@@ -295,24 +444,86 @@ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CY
295
444
  * so the agent sends the total Payfast's page shows, above zero (PAYFAST_PSP below),
296
445
  * and the API holds it to the payment Payfast itself reports. Wix's tokenize request
297
446
  * carries none either: the merchant's page charges Wix's token for its own order, so the
298
- * agent's pair, which the prepared checkout carries, is the total the cardholder approves.
447
+ * agent's pair, which the prepared checkout carries, is the total the cardholder approves. Toast's
448
+ * card request carries none either: the page's card-free confirm of its payment intent
449
+ * charges whatever the intent says, so the agent's pair, above zero (as Moneris's), is
450
+ * the one figure the approval shows.
299
451
  * PayMongo's card attach carries none either: the merchant bound the total, always in
300
452
  * Philippine pesos, to the checkout session when it created it, so the agent sends that
301
453
  * total, above zero (PAYMONGO_PSP below), and the API holds it to the session PayMongo's
302
- * own page shows. 2C2P's Payment carries none either: the merchant bound the total to the
454
+ * own page shows. GoPay's card post carries none either: the shop's server set the amount
455
+ * when it created the payment at GoPay, and GoPay's gateway shows it only inside its own
456
+ * session, so the agent sends the total the gateway shows, above zero (GOPAY_PSP below).
457
+ * Paygent's token request carries none either: the merchant's server charges the token later, so
458
+ * the agent's pair, which the prepared checkout carries, is the total the cardholder approves.
459
+ * Payrexx's card request names the page's total, but it only mints a token: the merchant's page
460
+ * charges its own total later, at its validateForm, so the agent sends the total that page shows,
461
+ * above zero (PAYREXX_PSP), and the API holds it to the frame's figure exactly.
462
+ * 2C2P's Payment carries none either: the merchant bound the total to the
303
463
  * payment token when it created it, so the agent sends the total 2C2P's page shows, above
304
464
  * zero (TWOC2P_PSP), and the API holds it to the amount 2C2P's own PaymentOption answer names.
465
+ * Przelewy24's pay wall link carries none either: the merchant's server registered the amount
466
+ * with Przelewy24 under the link's token, so the agent sends the amount the pay wall shows, above
467
+ * zero (PRZELEWY24_PSP below), and the cardholder's device holds the pay wall to it.
305
468
  * HiPay's token create carries none either: the merchant's server charges
306
469
  * HiPay's token for its own order, so the agent's pair, which the prepared checkout carries,
307
- * is the total the cardholder approves, above zero as Moneris's is.
470
+ * is the total the cardholder approves, above zero as Moneris's is. Revolut's card payment
471
+ * names the order's amount in minor units but not its currency, which only the order holds,
472
+ * so the number alone is no price the cardholder can approve: the agent sends the pair, above
473
+ * zero (REVOLUT_PSP below), and the API holds its amount to the body's and its currency to
474
+ * one that Revolut and the API both count in hundredths.
475
+ * Pay.'s card token request carries none either: the shop's server charges the token later for
476
+ * its own order total, so the agent's pair, above zero (PAY_NL_PSP below), is the one figure the
477
+ * approval shows.
478
+ * PayPal Card Fields' confirm carries none either: the merchant's server fixed the
479
+ * amount when it created the order, so the agent's pair, above zero (PAYPAL_PSP
480
+ * below), is the one figure the approval shows.
481
+ * ComGate's gateway link carries none either: the merchant's server set
482
+ * the amount when it created the payment at ComGate, so the agent's pair, above zero
483
+ * (COMGATE_PSP below), is the figure the cardholder approves, and the cardholder's device
484
+ * holds ComGate's own figure to it before any card leaves.
485
+ * MakeCommerce's card post carries none either: MakeCommerce's transaction holds the total, in
486
+ * euros, so the agent sends the total the shop's order page shows, above zero (MAKECOMMERCE_PSP
487
+ * below), and the API refuses any other currency.
488
+ * GoDaddy Payments' generate-nonce carries none either: GoDaddy's server charges
489
+ * the nonce later, so its pair must be above zero too (POYNT_PSP below).
490
+ * The TokenEx iframe's card post carries none either: it mints a token into the merchant's TokenEx
491
+ * vault, which the merchant's server charges later for its own order, so the agent sends the total
492
+ * the merchant's page shows, above zero (TOKENEX_IFRAME_PSP), and the API refuses a create without it.
493
+ * Webpay's entry carries none either: the merchant's server bound the total, in Chilean pesos,
494
+ * to the transaction when it created it, so the agent's pair, above zero (TRANSBANK_PSP below),
495
+ * is the figure the cardholder approves, and the cardholder's device holds Webpay's own
496
+ * transaction to it before any card leaves.
497
+ * SumUp's card request carries none either: the merchant's server bound the
498
+ * total and its currency to the checkout when it created it, so the agent sends the total the
499
+ * payment page shows, above zero (SUMUP_PSP below), and the API holds it to the checkout SumUp
500
+ * itself reports.
501
+ * Clover's card token request carries none either: the merchant's server charges the token
502
+ * later for its own order, so the agent's pair, above zero (CLOVER_PSP below), is the one figure
503
+ * the approval shows.
308
504
  */
309
505
  const MONERIS_PSP = 'moneris';
310
506
  const PAYFAST_PSP = 'payfast';
311
507
  const PAYMONGO_PSP = 'paymongo';
312
- const AGENT_AMOUNT_PROCESSORS = [GMO_PSP, PELECARD_PSP, WSPAY_PSP, SECURE_ACCEPTANCE_PSP, MONERIS_PSP, PADDLE_VGS_PSP, PAYFAST_PSP, WIX_PAYMENTS_PSP, PAYMONGO_PSP, HIPAY_PSP, TWOC2P_PSP];
508
+ const REVOLUT_PSP = 'revolut';
509
+ const PAY_NL_PSP = 'pay_nl';
510
+ const PAYPAL_PSP = 'paypal';
511
+ const GOPAY_PSP = 'gopay';
512
+ const MAKECOMMERCE_PSP = 'makecommerce';
513
+ const POYNT_PSP = 'poynt';
514
+ const SUMUP_PSP = 'sumup';
515
+ const CLOVER_PSP = 'clover';
516
+ const AGENT_AMOUNT_PROCESSORS = [GMO_PSP, PELECARD_PSP, WSPAY_PSP, SECURE_ACCEPTANCE_PSP, MONERIS_PSP, PADDLE_VGS_PSP, PAYFAST_PSP, WIX_PAYMENTS_PSP, PAYMONGO_PSP, PAYGENT_PSP, PAY_NL_PSP, HIPAY_PSP, TWOC2P_PSP, REVOLUT_PSP, PAYPAL_PSP, GOPAY_PSP, PAYREXX_PSP, COMGATE_PSP,
517
+ PRZELEWY24_PSP, MAKECOMMERCE_PSP, POYNT_PSP, TOKENEX_IFRAME_PSP, TRANSBANK_PSP, SUMUP_PSP,
518
+ CLOVER_PSP, TOAST_PSP];
313
519
  /** How AmountRequiredError names a processor. */
314
520
  const PROCESSOR_NAMES = { [GMO_PSP]: 'GMO', [PELECARD_PSP]: 'Pelecard', [WSPAY_PSP]: 'WSPay',
315
- [SECURE_ACCEPTANCE_PSP]: 'CyberSource Secure Acceptance', [MONERIS_PSP]: 'Moneris', [PADDLE_VGS_PSP]: 'Paddle', [PAYFAST_PSP]: 'Payfast', [WIX_PAYMENTS_PSP]: 'Wix', [PAYMONGO_PSP]: 'PayMongo', [HIPAY_PSP]: 'HiPay', [TWOC2P_PSP]: '2C2P' };
521
+ [SECURE_ACCEPTANCE_PSP]: 'CyberSource Secure Acceptance', [MONERIS_PSP]: 'Moneris', [PADDLE_VGS_PSP]: 'Paddle', [PAYFAST_PSP]: 'Payfast', [WIX_PAYMENTS_PSP]: 'Wix', [PAYMONGO_PSP]: 'PayMongo', [PAYGENT_PSP]: 'Paygent', [PAY_NL_PSP]: 'Pay.', [HIPAY_PSP]: 'HiPay', [TWOC2P_PSP]: '2C2P',
522
+ [REVOLUT_PSP]: 'Revolut', [PAYPAL_PSP]: 'PayPal', [GOPAY_PSP]: 'GoPay', [PAYREXX_PSP]: 'Payrexx',
523
+ [COMGATE_PSP]: 'ComGate', [PRZELEWY24_PSP]: 'Przelewy24',
524
+ [MAKECOMMERCE_PSP]: 'MakeCommerce', [POYNT_PSP]: 'GoDaddy Payments', [TOKENEX_IFRAME_PSP]: 'TokenEx', [TRANSBANK_PSP]: 'Webpay',
525
+ [SUMUP_PSP]: 'SumUp',
526
+ [CLOVER_PSP]: 'Clover', [TOAST_PSP]: 'Toast' };
316
527
  /**
317
528
  * Processors whose request and answer name no amount at all, so the total the
318
529
  * payment page shows (`pageAmount`) is the one figure that ties the agent's pair
@@ -543,13 +754,18 @@ const PROCESSOR_PLACEHOLDER_NUMBERS = Object.freeze({});
543
754
  /**
544
755
  * The placeholder number for a network (lowercase, as GET /api/v2/vault_cards names it) on a
545
756
  * processor's page: the processor's own number for that network when `overrides` names one, else
546
- * the network's default, else undefined for a network with no placeholder.
757
+ * the network's default, else undefined for a network with no placeholder. On a TokenEx iframe
758
+ * (`tokenex_iframe`) the number must also be one TokenEx's frame reads as that network, since the frame
759
+ * names the brand to the page from the typed digits and the cardholder's device refuses a mismatch: the
760
+ * frame's table takes Visa, Mastercard and American Express, so every other network gets undefined.
547
761
  */
548
762
  export function placeholderNumber(brand, psp, overrides = PROCESSOR_PLACEHOLDER_NUMBERS) {
549
763
  const own = psp !== undefined && Object.prototype.hasOwnProperty.call(overrides, psp) ? overrides[psp] : undefined;
550
- if (own && Object.prototype.hasOwnProperty.call(own, brand))
551
- return own[brand];
552
- return Object.prototype.hasOwnProperty.call(PLACEHOLDER_NUMBERS, brand) ? PLACEHOLDER_NUMBERS[brand] : undefined;
764
+ const number = own && Object.prototype.hasOwnProperty.call(own, brand) ? own[brand]
765
+ : Object.prototype.hasOwnProperty.call(PLACEHOLDER_NUMBERS, brand) ? PLACEHOLDER_NUMBERS[brand] : undefined;
766
+ if (number !== undefined && psp === TOKENEX_IFRAME_PSP && !tokenexIframePlaceholderFits(brand, number))
767
+ return undefined;
768
+ return number;
553
769
  }
554
770
  /**
555
771
  * The recognizer says this processor is preparation-required, and no preparation
@@ -575,7 +791,9 @@ export class PreparationRequiredError extends Error {
575
791
  * when the merchant's server created the payment), so the `amount` and
576
792
  * `currency` you pass are what the cardholder approves, and for WSPay the
577
793
  * figure the API compares with WSPay's answer when that answer names one (a
578
- * 3-D Secure step names none). Thrown locally before any create or
794
+ * 3-D Secure step names none). A Revolut card payment names the order's
795
+ * amount but not its currency, so it takes the same pair, above zero.
796
+ * Thrown locally before any create or
579
797
  * prompt, and terminal: the same request without an amount fails the same
580
798
  * way. The API answers 400 amount_required for the same case. A page total
581
799
  * (`pageAmount`) does not count.
@@ -585,7 +803,10 @@ export class AmountRequiredError extends Error {
585
803
  code = 'amount_required';
586
804
  constructor(psp) {
587
805
  const processor = PROCESSOR_NAMES[psp] ?? psp;
588
- super(`A ${processor} payment request names no amount, so pass amount and currency: that is the amount the cardholder approves. Nothing was created and nothing was charged.`);
806
+ // A Payrexx card request names the frame's total, but the merchant's page charges its own total after the token.
807
+ const lacks = psp === REVOLUT_PSP ? 'names its amount but not its currency'
808
+ : psp === PAYREXX_PSP ? "only mints a token the merchant's page charges later" : 'names no amount';
809
+ super(`A ${processor} payment request ${lacks}, so pass amount and currency: that is the amount the cardholder approves. Nothing was created and nothing was charged.`);
589
810
  this.psp = psp;
590
811
  this.name = 'AmountRequiredError';
591
812
  }
@@ -929,12 +1150,12 @@ function refusalsOf(v) {
929
1150
  return out;
930
1151
  }
931
1152
  /**
932
- * How often sendEbanxFollowUp reads a send back while the cardholder's device has it: every half second, whatever the
933
- * client's own `pollIntervalMs`. One send is out at a time per approval, so this is 120 reads a minute, inside the
934
- * API's 240 a minute per caller and authorization (routes/api/v2/checkout-follow-ups.ts).
1153
+ * How often sendEbanxFollowUp and sendPayrexxFollowUp read a send back while the cardholder's device has it: every
1154
+ * half second, whatever the client's own `pollIntervalMs`. One send is out at a time per approval, so this is 120
1155
+ * reads a minute, inside the API's 240 a minute per caller and authorization (routes/api/v2/checkout-follow-ups.ts).
935
1156
  */
936
1157
  export const EBANX_FOLLOW_UP_READ_MS = 500;
937
- /** The longest sendEbanxFollowUp waits after reads the API refused as too many (429): 1, 2, 4, 8, then 15 seconds. */
1158
+ /** The longest a follow-up send waits after reads the API refused as too many (429): 1, 2, 4, 8, then 15 seconds. */
938
1159
  export const EBANX_FOLLOW_UP_BACKOFF_MAX_MS = 15_000;
939
1160
  export class CheckoutApiError extends Error {
940
1161
  status;
@@ -1183,12 +1404,19 @@ function maskPaymongoSession(path) {
1183
1404
  * https://secure.gopay.com/gp-card-comm/h/<hex token>?lang=..&ver=..&SG=<64 hex> (payment-core gopay.js). The
1184
1405
  * token names that payment's card form, and with the SG, which redactUrl drops with the query, anyone who holds
1185
1406
  * the URL can open the form and post a card to it. On GoPay's two card frame hosts the segment after /h/, in any
1186
- * case or after an escaped slash, reads {token}: https://secure.gopay.com/gp-card-comm/h/{token}.
1407
+ * case or after an escaped slash, reads {token}: https://secure.gopay.com/gp-card-comm/h/{token}. Then every run of
1408
+ * 16 or more hex digits left reads {token} too, so a token after a path the frame never writes (an escaped h, a
1409
+ * path parameter) is masked as well; the token is 16 hex digits or more, and no other path the card frame uses holds
1410
+ * such a run. The pattern reads a percent escape as one unit: an escaped hex digit (%61, %2561) counts toward a run,
1411
+ * so escaping some of a token's digits never splits it into runs that show, and any other escape (%2F, %252F, %68)
1412
+ * is kept whole, so its own hex digits never start a run and leave part of a token showing.
1187
1413
  */
1188
1414
  const GOPAY_CARD_FORM_HOST = /^secure(?:\.sandbox)?\.gopay\.com\.?$/i;
1189
1415
  const GOPAY_CARD_FORM_TOKEN = new RegExp(`(${SLASH}h${SLASH}+)[^/?#]*`, 'gi');
1416
+ const GOPAY_TOKEN_RUN = /(?:[0-9a-f]|%(?:25)*(?:3[0-9]|[46][1-6])){16,}|%(?:25)*[0-9a-f]{2}/gi;
1417
+ const GOPAY_LONE_ESCAPE = /^%(?:25)*[0-9a-f]{2}$/i;
1190
1418
  function maskGopayToken(path) {
1191
- return path.replace(GOPAY_CARD_FORM_TOKEN, '$1{token}');
1419
+ return path.replace(GOPAY_CARD_FORM_TOKEN, '$1{token}').replace(GOPAY_TOKEN_RUN, (run) => (GOPAY_LONE_ESCAPE.test(run) ? run : '{token}'));
1192
1420
  }
1193
1421
  /**
1194
1422
  * KOMOJU's Hosted Fields frame pays a session with POST /api/v1/sessions/<id>/pay on komoju.com under
@@ -1220,6 +1448,18 @@ const SUMUP_CHECKOUT = new RegExp(`(${SLASH}checkouts${SLASH}+)[^/]*`, 'gi');
1220
1448
  function maskSumupCheckout(path) {
1221
1449
  return maskUuids(path.replace(SUMUP_CHECKOUT, '$1{checkout}'), '{checkout}');
1222
1450
  }
1451
+ /**
1452
+ * FastSpring's checkout posts its card payment to <store>.onfastspring.com[/popup-<name>]/session/<id>/payment
1453
+ * (payment-core fastspring.js), and the session id in that path is a credential: the session's own document,
1454
+ * which anyone holding the id can read with no cookie, carries the order and the session's token. On every
1455
+ * host under onfastspring.com the segment after /session/, in any case or after an escaped slash, reads
1456
+ * {session}.
1457
+ */
1458
+ const FASTSPRING_HOST = /(?:^|\.)onfastspring\.com\.?$/i;
1459
+ const FASTSPRING_SESSION = /(\/session(?:\/|%2f))[^/]*/gi;
1460
+ function maskFastspringSession(path) {
1461
+ return path.replace(FASTSPRING_SESSION, '$1{session}');
1462
+ }
1223
1463
  /**
1224
1464
  * The processors whose card request carries its own credential in the URL path, with the hosts
1225
1465
  * that send it, the text that names those hosts anywhere else (a string that is not a URL, or a
@@ -1273,6 +1513,18 @@ const REVOLUT_HOST = /^(?:sandbox-)?(?:merchant|checkout|merchant-mgmt)\.revolut
1273
1513
  function maskRevolutToken(path) {
1274
1514
  return maskUuids(path, '{token}');
1275
1515
  }
1516
+ /**
1517
+ * ComGate's gateway pages name the payment by its transaction id in their paths (pay1.comgate.cz/<id>,
1518
+ * /specify/card2/<id>, payment-core processors/comgate.js), and the id alone opens that payment's gateway page, its
1519
+ * amount and its merchant. Its entry link names the id in the query, which redactUrl drops. On every host ComGate
1520
+ * owns, any id of its shape, in any case and with each hyphen in any spelling HYPHEN reads, reads {payment}. The
1521
+ * pattern reads a percent escape as one unit and keeps it, as maskUuids does.
1522
+ */
1523
+ const COMGATE_HOST = /(?:^|\.)comgate\.cz\.?$/i;
1524
+ const COMGATE_ID = new RegExp(`%(?:25)*[0-9a-f]{2}|[a-z0-9]{4}${HYPHEN}[a-z0-9]{4}${HYPHEN}[a-z0-9]{4}`, 'gi');
1525
+ function maskComgateId(path) {
1526
+ return path.replace(COMGATE_ID, (token) => (token.startsWith('%') ? token : '{payment}'));
1527
+ }
1276
1528
  /**
1277
1529
  * Paytrail names its payment in the path of every route of its card method
1278
1530
  * (/payments/<transaction>/click-to-pay/loading-and-redirect, and its success and cancel routes), and the same id
@@ -1377,10 +1629,12 @@ const PATH_CREDENTIALS = [
1377
1629
  { host: PRZELEWY24_HOST, named: /przelewy24\.pl/i, mask: maskPrzelewy24Token },
1378
1630
  { host: REVOLUT_HOST, named: /(?:merchant|checkout|merchant-mgmt)\.revolut\.com/i, mask: maskRevolutToken },
1379
1631
  { host: SUMUP_API_HOST, named: /api\.sumup\.com/i, mask: maskSumupCheckout },
1632
+ { host: COMGATE_HOST, named: /comgate\.cz/i, mask: maskComgateId },
1380
1633
  { host: PAYTRAIL_HOST, named: /paytrail\.com/i, mask: maskPaytrailTransaction },
1381
1634
  { host: PAYMENT_HIGHWAY_HOST, named: /(?:paymenthighway\.io|sph-test-solinor\.com)/i, mask: maskPaymentHighwaySession },
1382
1635
  { host: TWOC2P_PAGE_HOST, named: /pgw-ui\.2c2p\.com/i, mask: maskTwoC2pToken, pathMask: maskTwoC2pPath },
1383
1636
  { host: QUICKPAY_HOST, named: /quickpay\.net/i, mask: maskQuickpaySession },
1637
+ { host: FASTSPRING_HOST, named: /onfastspring\.com/i, mask: maskFastspringSession },
1384
1638
  ];
1385
1639
  /**
1386
1640
  * A URL as it may appear in an error message: origin + path only. A paused
@@ -1418,6 +1672,8 @@ const PATH_CREDENTIALS = [
1418
1672
  * https://pgw-ui.2c2p.com/payment/4.1/{token}.
1419
1673
  * On QuickPay's host the window's session reads `{session}`:
1420
1674
  * https://payment.quickpay.net/{session}/3ds_auth.
1675
+ * On FastSpring's store hosts the session id reads `{session}`:
1676
+ * https://daisydisk.onfastspring.com/session/{session}/payment.
1421
1677
  * A string that is not a URL keeps the part before any `?` or `#`, with the
1422
1678
  * same segments masked for every processor whose host it names.
1423
1679
  * A URL is read by its host only when the parser finds one. Text before a URL
@@ -1526,6 +1782,18 @@ const PADDLE_VGS_PREPARATION_REFUSALS = [
1526
1782
  * `unsupported_checkout`, which PREPARATION_REFUSALS names for every checkout. Only for a Wix checkout.
1527
1783
  */
1528
1784
  const WIX_PAYMENTS_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'merchant_profile_mismatch'];
1785
+ /**
1786
+ * The API's refusals of a Paygent preparation, before any preparation exists, that prepareCheckout passes on
1787
+ * by name: Paygent is off for the company (`processor_unavailable`), the environment is not the client's test
1788
+ * or live mode, a live checkout's currency is not the one the reviewed merchant charges in
1789
+ * (`unsupported_currency`), and the price is zero (`amount_required`): Paygent's token request names no
1790
+ * amount, so the price the agent sends is the one the cardholder approves. A page and name no reviewed pin
1791
+ * lists answers `unsupported_checkout`, which PREPARATION_REFUSALS names for every checkout. The API holds the
1792
+ * pins, not this build, so a merchant a later release reviews needs no new SDK. Only for a Paygent checkout.
1793
+ */
1794
+ const PAYGENT_PREPARATION_REFUSALS = [
1795
+ 'processor_unavailable', 'sandbox_host_on_live_row', 'live_host_on_sandbox_row', 'unsupported_currency', 'amount_required',
1796
+ ];
1529
1797
  /**
1530
1798
  * The API's refusals of a HiPay preparation, before any preparation exists, that prepareCheckout surfaces by name:
1531
1799
  * the company is not turned on for HiPay (`processor_unavailable`), the environment is not the caller's key mode's
@@ -1754,7 +2022,7 @@ export class VaultClient {
1754
2022
  // cybersource_sa_token_create, gravy_session_fields_put,
1755
2023
  // moneris_hosted_tokenization, paddle_vgs_cards, paddle_tokenex_pay,
1756
2024
  // redsys_hosted_payment_page, lyra_krypton_payment, payfast_card_pay,
1757
- // quickpay_hosted_payment_page) and
2025
+ // quickpay_hosted_payment_page, tokenex_iframe_tokenize, among others) and
1758
2026
  // the caller's company is one Agentcard turned that processor on for, so this
1759
2027
  // build arms Fiserv's card capture, GMO's getToken, CyberSource Flex's card
1760
2028
  // fields, Juspay's card flow, Datatrans's Secure Fields card post, the
@@ -1948,7 +2216,9 @@ export class VaultClient {
1948
2216
  * The facts hold only while the cardholder pays with that card. `psp` names the processor
1949
2217
  * whose card form the agent types into (the recognizer's name, `worldline_mycheckout`): a
1950
2218
  * processor that needs its own number for the card's network gets it, and every other gets the
1951
- * network's default. Throws PlaceholderCardError when there is no such card, its network has no
2219
+ * network's default. On a TokenEx iframe (`tokenex_iframe`, JetBlue) the number is one TokenEx's frame
2220
+ * reads as the card's own network, Visa, Mastercard or American Express, and any other network has no
2221
+ * placeholder there. Throws PlaceholderCardError when there is no such card, its network has no
1952
2222
  * placeholder, or it carries no expiry.
1953
2223
  */
1954
2224
  async placeholderCard(input) {
@@ -2114,6 +2384,8 @@ export class VaultClient {
2114
2384
  throw fail(secureAcceptanceRefusal);
2115
2385
  const secureAcceptanceProfileId = secureAcceptance ? secureAcceptanceCheckoutProfile({ environment: input.environment,
2116
2386
  profileId: input.secureAcceptanceProfileId, merchantOrigin: input.merchantOrigin }) : null;
2387
+ // A live Paygent checkout's merchant is the API's to judge against the reviewed pins it holds, before anyone is
2388
+ // asked to approve (PAYGENT_PREPARATION_REFUSALS); test mode needs no pin.
2117
2389
  const timeoutMs = input.timeoutMs ?? 15 * 60_000;
2118
2390
  if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647)
2119
2391
  throw fail('timeout_invalid');
@@ -2233,6 +2505,7 @@ export class VaultClient {
2233
2505
  || (input.psp === EPROTECT_PSP && EPROTECT_PREPARATION_REFUSALS.includes(error.code))
2234
2506
  || (input.psp === PADDLE_VGS_PSP && PADDLE_VGS_PREPARATION_REFUSALS.includes(error.code))
2235
2507
  || (input.psp === WIX_PAYMENTS_PSP && WIX_PAYMENTS_PREPARATION_REFUSALS.includes(error.code))
2508
+ || (input.psp === PAYGENT_PSP && PAYGENT_PREPARATION_REFUSALS.includes(error.code))
2236
2509
  || (input.psp === HIPAY_PSP && HIPAY_PREPARATION_REFUSALS.includes(error.code))
2237
2510
  || (!!flexContext && CYBERSOURCE_FLEX_PREPARATION_REFUSALS.includes(error.code))))
2238
2511
  || (input.psp === 'fiserv' && error.status === 502 && error.code === 'cse_key_unavailable')))
@@ -2488,10 +2761,14 @@ export class VaultClient {
2488
2761
  : `checkout authorization is POST-only; got ${method} for ${redactUrl(input.request.url)}. ` +
2489
2762
  'A non-POST tokenizer needs recognizer support before it can be intercepted.');
2490
2763
  }
2491
- // A GET card request is its URL alone. This build completes one: Worldpay
2492
- // eProtect's registration exactly as payment-core admits it, with no body.
2764
+ // A GET card request is its URL alone. This build completes two kinds: Worldpay
2765
+ // eProtect's registration exactly as payment-core admits it, with no body, and a
2766
+ // hosted page's entry link exactly as its rule admits it (hosted-entry.ts), with no
2767
+ // body, which the cardholder's device takes over.
2493
2768
  const eprotect = rec?.psp === EPROTECT_PSP;
2494
- if (cardMethod === 'GET' && !(eprotect && isEprotectRegistration(input.request))) {
2769
+ const hostedGetEntry = mode === 'hosted_form' && !!rec && isHostedGetEntry(rec.psp, input.request.url)
2770
+ && (input.request.body === undefined || input.request.body === null || input.request.body === '');
2771
+ if (cardMethod === 'GET' && !(eprotect && isEprotectRegistration(input.request)) && !hostedGetEntry) {
2495
2772
  throw new Error(`this ${rec?.psp} GET is not a card registration this version of @agent-cards/checkout can complete: ${redactUrl(input.request.url)}.`);
2496
2773
  }
2497
2774
  // A CyberSource Secure Acceptance token create (always prepared: its recognizer requires it). The
@@ -2517,11 +2794,30 @@ export class VaultClient {
2517
2794
  // amount_required.
2518
2795
  if (!hasAmount && rec && AGENT_AMOUNT_PROCESSORS.includes(rec.psp))
2519
2796
  throw new AmountRequiredError(rec.psp);
2520
- // A Moneris or a HiPay token is charged later for whatever the merchant's server decides, and a Payfast pay, a
2521
- // PayMongo attach or a 2C2P Payment charges the total its payment, checkout session or payment token
2522
- // already holds, so a zero pair would show the cardholder no price at all: it is refused like a missing one.
2523
- if (hasAmount && rec && (rec.psp === MONERIS_PSP || rec.psp === PAYFAST_PSP || rec.psp === PAYMONGO_PSP || rec.psp === HIPAY_PSP || rec.psp === TWOC2P_PSP)
2524
- && Number(input.amount) <= 0)
2797
+ // A Przelewy24 pay wall charges the amount the merchant's server registered with Przelewy24 under the link's
2798
+ // token, so a zero pair would show the cardholder no price at all: it is refused like a missing one.
2799
+ // A Moneris, a HiPay or a Pay. token, or a GoDaddy Payments nonce, is charged later for whatever the merchant's
2800
+ // server decides, a Payfast pay, a PayMongo attach, a 2C2P Payment, a Revolut card payment or a SumUp card request
2801
+ // charges the total its payment, checkout session, payment token, order or checkout already holds, and a PayPal
2802
+ // order's amount was fixed by the merchant's server and never shown in the confirm, and a GoPay card post charges
2803
+ // the total GoPay's payment already holds, so a zero pair would show the cardholder no price at all: it is refused
2804
+ // like a missing one.
2805
+ // A ComGate payment charges the total ComGate already holds for it, the same way, and a Webpay transaction the
2806
+ // total the merchant's server bound to it, and a Payrexx page charges its own
2807
+ // total after the frame's token.
2808
+ // A Moneris token is charged later for whatever the merchant's server decides, and a Payfast pay, a
2809
+ // PayMongo attach or a MakeCommerce card post charges the total its payment, checkout session or
2810
+ // transaction already holds, so a zero pair would show the cardholder no price at all: it is refused
2811
+ // like a missing one. So is a TokenEx iframe token, which the merchant's server charges later for its own order.
2812
+ // A Clover token is charged later for whatever the merchant's server decides, as a Moneris one is.
2813
+ // A Toast payment method is charged by the page's own confirm for whatever its intent says, the same way.
2814
+ if (hasAmount && rec && rec.psp === PRZELEWY24_PSP && Number(input.amount) <= 0)
2815
+ throw new AmountRequiredError(rec.psp);
2816
+ if (hasAmount && rec && (rec.psp === MONERIS_PSP || rec.psp === PAYFAST_PSP || rec.psp === PAYMONGO_PSP || rec.psp === HIPAY_PSP || rec.psp === TWOC2P_PSP
2817
+ || rec.psp === REVOLUT_PSP || rec.psp === PAY_NL_PSP || rec.psp === PAYPAL_PSP
2818
+ || rec.psp === GOPAY_PSP || rec.psp === COMGATE_PSP || rec.psp === MAKECOMMERCE_PSP
2819
+ || rec.psp === POYNT_PSP || rec.psp === TOKENEX_IFRAME_PSP || rec.psp === TRANSBANK_PSP || rec.psp === SUMUP_PSP
2820
+ || rec.psp === CLOVER_PSP || rec.psp === PAYREXX_PSP || rec.psp === TOAST_PSP) && Number(input.amount) <= 0)
2525
2821
  throw new AmountRequiredError(rec.psp);
2526
2822
  // Nor is the agent's pair tied to the page by anything but the total the
2527
2823
  // page shows: without it the cardholder could approve one figure while the
@@ -2857,6 +3153,10 @@ export class VaultClient {
2857
3153
  // source.encryptionData and removes nothing (fiserv.ts); any other shape is not one to act on.
2858
3154
  if (rec.psp === 'fiserv' && !isFiservSubstitution(sub))
2859
3155
  throw new PaymentOutcomeUnknownError(authorizationId, 'cse_substitutions_malformed');
3156
+ // A Toast approval carries exactly one sealed card, cardData at `card`, and removes nothing
3157
+ // (payment-core toast-seal.js substituteToastCardData checks the rest at the swap).
3158
+ if (rec.psp === TOAST_PSP && !isToastSubstitution(sub))
3159
+ throw new PaymentOutcomeUnknownError(authorizationId, 'cse_substitutions_malformed');
2860
3160
  // `remove`: sibling keys the API says to drop with the swap (Adyen's
2861
3161
  // `brand`, stamped by adyen-web from the agent's dummy digits). Absent
2862
3162
  // on an older API; anything but a list of names is refused, since a
@@ -3027,6 +3327,18 @@ export class VaultClient {
3027
3327
  response.headers = answer.headers;
3028
3328
  response.body = answer.body;
3029
3329
  }
3330
+ if (rec?.psp === PAYGENT_PSP) {
3331
+ // Paygent's answer enters the page only as payment-core rebuilds an approved token from its members
3332
+ // (paygent.js paygentAnswerOutcome: HTTP 200, result 0000 and a token in shape), under a JSON content
3333
+ // type. Anything else, a refusal Paygent answers inside HTTP 200 included, is never handed over as an
3334
+ // approval; the card may have left the device, so the outcome is unknown rather than declined.
3335
+ const judged = paygentAnswerOutcome(response.status, response.body, null);
3336
+ if (judged.outcome !== 'approved' || typeof judged.body !== 'string')
3337
+ throw new PaymentOutcomeUnknownError(authorizationId, 'paygent_token_unconfirmed');
3338
+ response.status = 200;
3339
+ response.headers = { 'content-type': 'application/json' };
3340
+ response.body = judged.body;
3341
+ }
3030
3342
  if (rec?.psp === PRIMER_PSP) {
3031
3343
  // Primer's answer enters the page only as one single-use card token rebuilt from
3032
3344
  // payment-core's allow-list, under a JSON content type (primer.ts primerTokenAnswer).
@@ -3039,6 +3351,18 @@ export class VaultClient {
3039
3351
  response.headers = answer.headers;
3040
3352
  response.body = answer.body;
3041
3353
  }
3354
+ if (rec?.psp === TOKENEX_IFRAME_PSP) {
3355
+ // TokenEx's answer enters the frame only as TokenEx's 200 with a sound token, rebuilt from payment-core's
3356
+ // allow-list under a JSON content type (tokenex-iframe.ts tokenexIframePageAnswer); the frame adds its own
3357
+ // cardType. Anything else is never handed over: the device may already have sent the card, so the
3358
+ // outcome is unknown rather than declined.
3359
+ const answer = tokenexIframePageAnswer(response.status, response.body);
3360
+ if (!answer)
3361
+ throw new PaymentOutcomeUnknownError(authorizationId, 'tokenex_iframe_token_unconfirmed');
3362
+ response.status = answer.status;
3363
+ response.headers = answer.headers;
3364
+ response.body = answer.body;
3365
+ }
3042
3366
  let processorContext;
3043
3367
  if (mercadoContext) {
3044
3368
  try {
@@ -3069,8 +3393,9 @@ export class VaultClient {
3069
3393
  chargedAmount: typeof s.charged_amount === 'number' ? s.charged_amount : null,
3070
3394
  chargedCurrency: typeof s.charged_currency === 'string' ? s.charged_currency : null,
3071
3395
  chargedKind: s.charged_kind === 'captured' || s.charged_kind === 'authorized' || s.charged_kind === 'none' ? s.charged_kind : null,
3072
- // An EBANX token approval that covers the sends after it names until when (ebanx.ts EbanxFollowUps).
3073
- ...(rec?.psp === 'ebanx' && typeof s.follow_ups?.until === 'string' && Number.isFinite(Date.parse(s.follow_ups.until))
3396
+ // An EBANX token approval, or a Payrexx card request whose answer started 3-D Secure, that covers the sends
3397
+ // after it names until when (ebanx.ts EbanxFollowUps, payrexx.ts PayrexxFollowUps).
3398
+ ...((rec?.psp === 'ebanx' || rec?.psp === PAYREXX_PSP) && typeof s.follow_ups?.until === 'string' && Number.isFinite(Date.parse(s.follow_ups.until))
3074
3399
  ? { followUpsUntil: s.follow_ups.until } : {}),
3075
3400
  ...amountAuthority,
3076
3401
  ...execution,
@@ -3283,6 +3608,20 @@ export class VaultClient {
3283
3608
  * an answer that carried the card, or `options.until` (the approval's window, plus the API's own clocks) passed.
3284
3609
  */
3285
3610
  async sendEbanxFollowUp(authorizationId, request, options = {}) {
3611
+ return this.sendFollowUp(authorizationId, request, options, (reason, cardSent) => new EbanxFollowUpError(reason, cardSent, authorizationId));
3612
+ }
3613
+ /**
3614
+ * Send one of Payrexx's 3-D Secure sends an approved Payrexx card request covers (payrexx.ts says which: the card
3615
+ * frame's authentication after the ACS method and its post-authentication after the bank's challenge, bound to that
3616
+ * approval) from the cardholder's device, and resolve with Payrexx's answer for the paused request, exactly as
3617
+ * sendEbanxFollowUp does for EBANX: the same create, reads and clocks, and no prompt. Rejects with a
3618
+ * PayrexxFollowUpError naming why there is no answer and whether the card may have reached Payrexx.
3619
+ */
3620
+ async sendPayrexxFollowUp(authorizationId, request, options = {}) {
3621
+ return this.sendFollowUp(authorizationId, request, options, (reason, cardSent) => new PayrexxFollowUpError(reason, cardSent, authorizationId));
3622
+ }
3623
+ /** The send behind sendEbanxFollowUp and sendPayrexxFollowUp; `fail` builds the processor's error for a send with no answer. */
3624
+ async sendFollowUp(authorizationId, request, options, fail) {
3286
3625
  if (!/^cauth_[A-Za-z0-9_-]{1,128}$/.test(authorizationId))
3287
3626
  throw new Error('Invalid authorization ID.');
3288
3627
  const base = `/v2/checkout/authorizations/${authorizationId}/follow_ups`;
@@ -3296,16 +3635,16 @@ export class VaultClient {
3296
3635
  catch (error) {
3297
3636
  // A refusal (a 4xx answer) stored no send, so nothing left. Any other failure (a 5xx, a timeout, a dropped
3298
3637
  // connection, an unreadable 201) may have come after the API stored the send, which the device then sends:
3299
- // the card may reach EBANX, and for a confirm that is a payment whose outcome is unknown.
3638
+ // the card may reach the processor, and for an EBANX confirm that is a payment whose outcome is unknown.
3300
3639
  const refused = error instanceof CheckoutApiError && error.status >= 400 && error.status < 500;
3301
3640
  const reason = error instanceof CheckoutApiError
3302
3641
  ? (typeof error.details.reason === 'string' && /^[a-z_]{1,64}$/.test(error.details.reason) ? error.details.reason : error.code ?? `http_${error.status}`)
3303
3642
  : 'follow_up_unavailable';
3304
- throw new EbanxFollowUpError(reason, !refused, authorizationId);
3643
+ throw fail(reason, !refused);
3305
3644
  }
3306
3645
  const id = created?.id;
3307
3646
  if (typeof id !== 'string' || !/^cfu_[0-9a-f]{24}$/.test(id) || created.authorization !== authorizationId) {
3308
- throw new EbanxFollowUpError('follow_up_unrecognized', true, authorizationId);
3647
+ throw fail('follow_up_unrecognized', true);
3309
3648
  }
3310
3649
  // Reads the API refused in a row as too many (429): each waits twice as long as the last before the next read.
3311
3650
  let limited = 0;
@@ -3317,14 +3656,14 @@ export class VaultClient {
3317
3656
  }
3318
3657
  catch (error) {
3319
3658
  if (signal.aborted)
3320
- throw new EbanxFollowUpError('answer_late', true, authorizationId);
3659
+ throw fail('answer_late', true);
3321
3660
  if (error instanceof CheckoutApiError && error.status === 429)
3322
3661
  limited += 1;
3323
3662
  }
3324
3663
  if (read?.status === 'answered') {
3325
3664
  const r = read.response;
3326
3665
  if (!r || !Number.isInteger(r.status) || r.status < 200 || r.status > 599 || typeof r.body !== 'string' || !r.headers || typeof r.headers !== 'object') {
3327
- throw new EbanxFollowUpError('follow_up_unrecognized', true, authorizationId);
3666
+ throw fail('follow_up_unrecognized', true);
3328
3667
  }
3329
3668
  const headers = {};
3330
3669
  for (const [name, value] of Object.entries(r.headers))
@@ -3334,10 +3673,10 @@ export class VaultClient {
3334
3673
  }
3335
3674
  if (read?.status === 'failed' || read?.status === 'expired') {
3336
3675
  const reason = typeof read.failure_reason === 'string' && /^[a-z_]{1,64}$/.test(read.failure_reason) ? read.failure_reason : read.status;
3337
- throw new EbanxFollowUpError(reason, read.card_sent !== false, authorizationId);
3676
+ throw fail(reason, read.card_sent !== false);
3338
3677
  }
3339
3678
  if (signal.aborted || Date.now() >= deadline)
3340
- throw new EbanxFollowUpError('answer_late', true, authorizationId);
3679
+ throw fail('answer_late', true);
3341
3680
  const wait = limited === 0 ? EBANX_FOLLOW_UP_READ_MS : Math.min(EBANX_FOLLOW_UP_BACKOFF_MAX_MS, 1000 * 2 ** (limited - 1));
3342
3681
  await interruptibleSleep(Math.max(1, Math.min(wait, deadline - Date.now())), signal);
3343
3682
  }
@@ -3898,3 +4237,14 @@ function interruptibleSleep(ms, signal) {
3898
4237
  signal?.addEventListener('abort', done, { once: true });
3899
4238
  });
3900
4239
  }
4240
+ /** A served Toast set: exactly { cardData } at `card`, a 256-byte base64 value, with nothing to remove. */
4241
+ function isToastSubstitution(sub) {
4242
+ if (!sub || typeof sub !== 'object')
4243
+ return false;
4244
+ const { at, fields, remove } = sub;
4245
+ if (at !== 'card' || !fields || typeof fields !== 'object' || Array.isArray(fields))
4246
+ return false;
4247
+ const keys = Object.keys(fields);
4248
+ return keys.length === 1 && keys[0] === 'cardData' && isToastCiphertext(fields.cardData)
4249
+ && (remove === undefined || (Array.isArray(remove) && remove.length === 0));
4250
+ }