@agent-cards/sdk 0.23.19 → 0.23.21

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 (66) hide show
  1. package/CHANGELOG.md +61 -2
  2. package/PREFLIGHT.md +4 -0
  3. package/README.md +175 -7
  4. package/dist/amadeus.d.ts +4 -2
  5. package/dist/amadeus.js +16 -3
  6. package/dist/buy.d.ts +84 -2
  7. package/dist/buy.js +16 -1
  8. package/dist/card-endpoint-facts.d.ts +2 -1
  9. package/dist/card-endpoint-facts.js +18 -9
  10. package/dist/card-endpoint.d.ts +20 -4
  11. package/dist/card-endpoint.js +38 -4
  12. package/dist/card-fields.generated.js +1444 -51
  13. package/dist/cdp.d.ts +29 -0
  14. package/dist/cdp.js +444 -24
  15. package/dist/client.d.ts +134 -7
  16. package/dist/client.js +350 -25
  17. package/dist/comgate.d.ts +50 -0
  18. package/dist/comgate.js +80 -0
  19. package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
  20. package/dist/eprotect.generated.d.ts +1 -1
  21. package/dist/gmo.generated.js +7 -3
  22. package/dist/hipay.generated.d.ts +56 -0
  23. package/dist/hipay.generated.js +392 -0
  24. package/dist/hold.d.ts +180 -0
  25. package/dist/hold.js +270 -0
  26. package/dist/hosted-entry.d.ts +4 -0
  27. package/dist/hosted-entry.js +32 -0
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.js +1 -0
  30. package/dist/lifecycle.d.ts +10 -0
  31. package/dist/lifecycle.js +21 -0
  32. package/dist/merchant-card-endpoints.generated.js +102 -11
  33. package/dist/paddle-tokenex.generated.js +7 -3
  34. package/dist/paddle-vgs.generated.d.ts +3 -2
  35. package/dist/paddle-vgs.generated.js +9 -5
  36. package/dist/payrails.generated.js +7 -3
  37. package/dist/pre-claim.d.ts +20 -0
  38. package/dist/pre-claim.js +202 -22
  39. package/dist/preflight-catalog.json +58 -0
  40. package/dist/preflight-schemas.json +4 -0
  41. package/dist/preflight.generated.js +26 -2
  42. package/dist/prepared-processor.d.ts +6 -2
  43. package/dist/prepared-processor.js +18 -2
  44. package/dist/primer.generated.js +1487 -94
  45. package/dist/przelewy24.d.ts +47 -0
  46. package/dist/przelewy24.js +88 -0
  47. package/dist/quickpay.d.ts +44 -0
  48. package/dist/quickpay.js +65 -0
  49. package/dist/registry.d.ts +6 -5
  50. package/dist/registry.js +27 -7
  51. package/dist/shiji-sts.generated.js +7 -3
  52. package/dist/stripe-checkout.generated.js +744 -13
  53. package/dist/tokenex-iframe.d.ts +93 -0
  54. package/dist/tokenex-iframe.generated.d.ts +81 -0
  55. package/dist/tokenex-iframe.generated.js +403 -0
  56. package/dist/tokenex-iframe.js +195 -0
  57. package/dist/transbank.d.ts +45 -0
  58. package/dist/transbank.js +73 -0
  59. package/dist/twoc2p.d.ts +54 -0
  60. package/dist/twoc2p.generated.d.ts +33 -0
  61. package/dist/twoc2p.generated.js +1149 -0
  62. package/dist/twoc2p.js +85 -0
  63. package/dist/voice.d.ts +16 -13
  64. package/dist/voice.js +13 -10
  65. package/examples/preflight/kernel-native/inventory.json +1 -1
  66. package/package.json +3 -3
package/dist/client.js CHANGED
@@ -2,7 +2,11 @@ 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 { isHostedGetEntry } from './hosted-entry.js';
9
+ import { TRANSBANK_CAPABILITY, TRANSBANK_PSP } from './transbank.js';
6
10
  import { isMercadoTokenRequest, parseMercadoCheckoutContext, validateMercadoProcessorContext } from './mercado-checkout.generated.js';
7
11
  import { matchesPreparation, preparationMode, validPreparationEnvironment } from './prepared-processor.js';
8
12
  import { fiservKeyPinById, isFiservSubstitution, requiresFiservPreparation } from './fiserv.js';
@@ -16,7 +20,9 @@ import { SHIJI_STS_CAPABILITY, SHIJI_STS_PSP, isShijiStsServedCompanion, isShiji
16
20
  import { AMADEUS_CHECKOUT_CAPABILITY, AMADEUS_CHECKOUT_PSP } from './amadeus-checkout.generated.js';
17
21
  import { PADDLE_VGS_CAPABILITY, PADDLE_VGS_PSP, paddleVgsCardFactsOf } from './paddle-vgs.generated.js';
18
22
  import { PADDLE_TOKENEX_CAPABILITY, PADDLE_TOKENEX_PSP, isPaddleTokenexServedFacts, isPaddleTokenexServedSubstitution } from './paddle-tokenex.js';
23
+ import { TWOC2P_CAPABILITY, TWOC2P_PSP } from './twoc2p.js';
19
24
  import { WIX_PAYMENTS_CAPABILITY, WIX_PAYMENTS_PSP } from './wix-payments.generated.js';
25
+ import { HIPAY_CAPABILITY, HIPAY_PSP } from './hipay.generated.js';
20
26
  import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
21
27
  import { VoiceModule } from './voice.js';
22
28
  import { BuyModule } from './buy.js';
@@ -30,6 +36,7 @@ import { JUSPAY_CAPABILITY, JUSPAY_PSP, juspayPageAnswer } from './juspay-gate.j
30
36
  import { CARD_ENDPOINT_MODE, CARD_ENDPOINT_PSP, CardEndpointRefusedError, cardEndpointById, cardEndpointLive, cardEndpointSessionRefusal, } from './card-endpoint.js';
31
37
  import { merchantCardEndpointFactsRefusal as sharedFactsRefusal, readMerchantCardEndpointFacts as sharedReadFacts, } from './merchant-card-endpoints.generated.js';
32
38
  import { DATATRANS_CAPABILITY } from './datatrans.js';
39
+ import { TOKENEX_IFRAME_CAPABILITY, TOKENEX_IFRAME_PSP, tokenexIframePageAnswer, tokenexIframePlaceholderFits } from './tokenex-iframe.js';
33
40
  import { PELECARD_CAPABILITY, PELECARD_PSP } from './pelecard.js';
34
41
  import { PRIMER_CAPABILITY, PRIMER_PSP, primerTokenAnswer } from './primer.js';
35
42
  import { PAYRAILS_CAPABILITY, isPayrailsPaymentUrl, payrailsAnswer } from './payrails.js';
@@ -226,6 +233,13 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
226
233
  * checkout relay. Served on the same terms: only to a build that lists it, for a company
227
234
  * Agentcard turned Payfast on for.
228
235
  *
236
+ * `przelewy24_hosted_payment_page`: Przelewy24's pay wall, whose card-free link
237
+ * (secure.przelewy24.pl/trnRequest/<token>, a plain GET the merchant's redirect or script sends
238
+ * the buyer to) this build pauses for the cardholder's device by its exact link (hosted-entry.ts),
239
+ * and whose card method post, card form post and Saferpay resolver this build never lets the
240
+ * agent's browser send (pre-claim.ts, przelewy24.ts). Served on the same terms as NMI's and
241
+ * Redsys's: only to a build that lists it, for a company Agentcard turned Przelewy24 on for.
242
+ *
229
243
  * `wix_payments_tokenize`: the card request Wix Cashier's card frame sends to Wix's
230
244
  * tokenizer (cashier-pci.wixapps.net), only through a prepared checkout: Wix's payment
231
245
  * widget fails the payment 30 seconds after it asks the frame for a token. The
@@ -241,9 +255,120 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
241
255
  * that the page then runs in the agent's browser. The cardholder's device sends the card through
242
256
  * the checkout relay. Served on the same terms: only to a build that lists it, for a company
243
257
  * Agentcard turned PayMongo on for.
258
+ *
259
+ * `gopay_card_form`: the card post GoPay's card frame sends to its own URL
260
+ * (secure.gopay.com/gp-card-comm/h/<token>) inside the GoPay gateway a shop opens. This build pauses
261
+ * only the frame's own card post and answers it with GoPay's answer as the API recorded it, the
262
+ * empty JSON object the frame reads; the gateway then reads the payment's end from GoPay and runs
263
+ * 3-D Secure in the agent's browser when the bank asks for it. The cardholder's device sends the card
264
+ * through the checkout relay. Served only to a build that lists it, and only for Agentcard's proof
265
+ * company until a reviewed change binds GoPay's payee and amount to the approval: the post names
266
+ * neither. The API takes it only at a shop Agentcard reviewed.
267
+ *
268
+ * `hipay_vault_token`: the token create HiPay's hosted fields send to HiPay's Secure Vault
269
+ * (secure2-vault.hipay-tpp.com, stage-secure2-vault for a test row), only through a prepared
270
+ * checkout: the merchant's page gives the frame 20 seconds after Pay. The cardholder's device
271
+ * sends the card to HiPay directly and the frame gets back HiPay's token. The number-only post
272
+ * the frame sends while the number is typed, and its /update once it holds a token, fail in the
273
+ * browser (pre-claim.ts hipayPreClaim). Served on the same terms: only to a build that lists it,
274
+ * for a company Agentcard turned HiPay on for.
275
+ *
276
+ * `twoc2p_secure_pay`: the Payment 2C2P's hosted payment page posts to its payment API
277
+ * (pgwv4.2c2p.com), carrying the card only as one field the page encrypts under 2C2P's static
278
+ * key. This build pauses it and writes over the placeholder's field the one the cardholder's
279
+ * device wrote under the key payment-core pins (twoc2p.ts), into that Payment alone; 3-D Secure
280
+ * then runs in the agent's browser. Served only to a build that lists it, and only for Agentcard's
281
+ * proof company, whatever the API's processor lists name: the field is the card under 2C2P's one
282
+ * static key, which any 2C2P merchant could charge.
283
+ *
284
+ * `quickpay_hosted_payment_page`: QuickPay's hosted payment window, whose card-free signed entry
285
+ * this build pauses for the cardholder's device and whose card post, fee lookup, wallet post, 3-D
286
+ * Secure step and card lookup this build never lets the agent's browser send (pre-claim.ts,
287
+ * quickpay.ts). Served on the same terms as NMI's and Redsys's: only to a build that lists it, for a
288
+ * company Agentcard turned QuickPay on for.
289
+ *
290
+ * `revolut_card_field`: the card payment Revolut Business's card field or card popup sends from
291
+ * its frame to /api/public/orders/<order token>/payments on merchant.revolut.com, on a merchant's
292
+ * own page or Revolut's hosted checkout. This build pauses only the frame's card payment and
293
+ * answers it with Revolut's answer as the API recorded it: the payment Revolut created, which the
294
+ * frame then follows, through any 3-D Secure step, in the agent's browser. A payment by Apple Pay,
295
+ * Google Pay or Revolut Pay on the same URL carries no card and continues untouched. Revolut's
296
+ * host answers the Vault's origin, so the cardholder's device sends the card itself, with no relay.
297
+ * Served on the same terms: only to a build that lists it, for a company Agentcard turned Revolut
298
+ * on for.
299
+ *
300
+ * `pay_nl_card_token`: the card token request a shop's card form sends to Pay. (pay.nl),
301
+ * Weeztix's sandboxed frame posting its FormData to payment.pay.nl/v1/Payment/getCardToken/json.
302
+ * This build pauses only the form's own multipart body at that exact URL; the cardholder's
303
+ * device sends the card through the checkout relay, and the frame gets the token answer
304
+ * payment-core's judge rebuilt, with the CORS answer for the frame's own origin, null. The
305
+ * request names no amount, so the agent's pair, above zero, is the one the cardholder approves
306
+ * (PAY_NL_PSP below). Served on the same terms: only to a build that lists it, for a company
307
+ * Agentcard turned Pay. on for.
308
+ *
309
+ * `paypal_card_fields`: PayPal Card Fields' confirm, the POST its v5 frame sends to
310
+ * www.paypal.com and its v6 frame to api-m.paypal.com. This build pauses it only in
311
+ * the frames' own shape, refuses one that asks PayPal to keep the card or forces
312
+ * 3-D Secure in the browser with a `blocked` event, and answers the frame with the
313
+ * order's id and status as the API recorded them. The device sends a v5 confirm
314
+ * through the checkout relay and a v6 one directly. Served on the same terms: only
315
+ * to a build that lists it, for a company Agentcard turned PayPal Card Fields on
316
+ * for. Every other company keeps the checkout guard's refusal (paypal-card-forms.ts).
317
+ *
318
+ * `comgate_hosted_payment_page`: ComGate's payment gateway, whose card-free gateway link
319
+ * (payN.comgate.cz/init?id=<transaction id>, a plain GET) this build pauses for the
320
+ * cardholder's device by its exact link (hosted-entry.ts), in the page, in a frame of it or
321
+ * in a window it opened, and whose card hosts this build never lets the agent's browser post
322
+ * to (pre-claim.ts, comgate.ts). Served on the same terms as NMI's and Redsys's: only to a
323
+ * build that lists it, for a company Agentcard turned ComGate on for.
324
+ *
325
+ * `makecommerce_card_frame`: the card post MakeCommerce's card frame sends to its own card endpoint
326
+ * (cc.maksekeskus.ee/card-payment) under the shop's publishable key. This build pauses only the
327
+ * frame's own one-time card payment and answers it with MakeCommerce's answer as the API recorded
328
+ * it: the URL of MakeCommerce's 3-D Secure page, which the frame then opens in the agent's browser.
329
+ * The cardholder's device sends the card through the checkout relay. Served on the same terms: only
330
+ * to a build that lists it, for a company Agentcard turned MakeCommerce on for.
331
+ *
332
+ * `poynt_collect_nonce`: GoDaddy Payments' card request, the POST Poynt Collect's
333
+ * card frame sends to a store's generate-nonce on services.poynt.net. This build
334
+ * pauses it only as the frame writes it, refuses a body that asks Poynt to keep the
335
+ * card in the browser with a `blocked` event, and answers the frame with the nonce as
336
+ * the API recorded it. The device sends the card through the checkout relay. Served
337
+ * on the same terms: only to a build that lists it, for a company Agentcard turned
338
+ * GoDaddy Payments on for.
339
+ *
340
+ * `tokenex_iframe_tokenize`: the card post TokenEx's hosted iframe v3 sends from its card-number frame
341
+ * to its own document URL on htp.tokenex.com (JetBlue's card form). This build pauses only the PCI frame's
342
+ * own card post with its CVV frame, refuses in the browser every other post on that path that carries a
343
+ * card (tokenex-iframe.ts), answers it with TokenEx's token as the API recorded it, rebuilt from
344
+ * payment-core's allow-list, and answers the same frame's byte-identical repeat from that answer while the
345
+ * approval is being handed off. Served on the same terms: only to a build that lists it, for a company
346
+ * Agentcard turned the TokenEx iframe on for.
347
+ *
348
+ * `transbank_webpay_hosted_payment_page`: Transbank's Webpay Plus, whose card-free entry
349
+ * (webpay3g.transbank.cl/webpayserver/initTransaction?token_ws=<token>, the merchant's
350
+ * redirect, a plain GET) this build pauses for the cardholder's device by its exact link
351
+ * (hosted-entry.ts) in the page or a frame of it, and with attachToCdp also in a window the
352
+ * page opened, and whose hosts this build never lets the agent's browser post to (pre-claim.ts,
353
+ * transbank.ts), so the token is never spent there. attachToPlaywright covers the page it is
354
+ * attached to and that page's frames only, and pauses only a direct or scripted navigation to
355
+ * the entry: its page.route sees no HTTP redirect hop, so a merchant's redirect needs attachToCdp.
356
+ * Served on the same terms as NMI's and Redsys's: only to a build that lists it, for a company
357
+ * Agentcard turned Webpay on for.
358
+ *
359
+ * `sumup_checkout_process`: the card request SumUp's Payment Widget sends from its hidden form frame
360
+ * on gateway.sumup.com, a PUT to https://api.sumup.com/v0.2/checkouts/<checkout id>. This build pauses
361
+ * that PUT as it pauses Gravy's, so only the CDP and Playwright adapters see it (the form frame is
362
+ * cross-origin to the page), and answers the frame with SumUp's own answer as the API recorded it: the
363
+ * paid checkout, or the 3-D Secure step the widget then runs in the agent's browser. The cardholder's
364
+ * device sends the card itself, and the API holds the payment to a merchant Agentcard reviewed, at the
365
+ * amount SumUp holds for the checkout. Served on the same terms: only to a build that lists it, for a
366
+ * company Agentcard turned SumUp on for.
244
367
  */
245
368
  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,
246
- 'redsys_hosted_payment_page', 'lyra_krypton_payment', 'payfast_card_pay', WIX_PAYMENTS_CAPABILITY, 'paymongo_checkout_pay'];
369
+ 'redsys_hosted_payment_page', 'lyra_krypton_payment', 'payfast_card_pay', WIX_PAYMENTS_CAPABILITY, 'paymongo_checkout_pay', 'pay_nl_card_token', HIPAY_CAPABILITY, TWOC2P_CAPABILITY, 'quickpay_hosted_payment_page', 'revolut_card_field', 'paypal_card_fields', 'gopay_card_form', COMGATE_CAPABILITY, PRZELEWY24_CAPABILITY, 'makecommerce_card_frame', 'poynt_collect_nonce',
370
+ TOKENEX_IFRAME_CAPABILITY, TRANSBANK_CAPABILITY,
371
+ 'sumup_checkout_process'];
247
372
  /**
248
373
  * Processors whose paused request names no amount at all, so the pair the
249
374
  * agent passes is the amount the cardholder approves: authorize() refuses one
@@ -275,15 +400,68 @@ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CY
275
400
  * PayMongo's card attach carries none either: the merchant bound the total, always in
276
401
  * Philippine pesos, to the checkout session when it created it, so the agent sends that
277
402
  * total, above zero (PAYMONGO_PSP below), and the API holds it to the session PayMongo's
278
- * own page shows.
403
+ * own page shows. GoPay's card post carries none either: the shop's server set the amount
404
+ * when it created the payment at GoPay, and GoPay's gateway shows it only inside its own
405
+ * session, so the agent sends the total the gateway shows, above zero (GOPAY_PSP below).
406
+ * 2C2P's Payment carries none either: the merchant bound the total to the
407
+ * payment token when it created it, so the agent sends the total 2C2P's page shows, above
408
+ * zero (TWOC2P_PSP), and the API holds it to the amount 2C2P's own PaymentOption answer names.
409
+ * Przelewy24's pay wall link carries none either: the merchant's server registered the amount
410
+ * with Przelewy24 under the link's token, so the agent sends the amount the pay wall shows, above
411
+ * zero (PRZELEWY24_PSP below), and the cardholder's device holds the pay wall to it.
412
+ * HiPay's token create carries none either: the merchant's server charges
413
+ * HiPay's token for its own order, so the agent's pair, which the prepared checkout carries,
414
+ * is the total the cardholder approves, above zero as Moneris's is. Revolut's card payment
415
+ * names the order's amount in minor units but not its currency, which only the order holds,
416
+ * so the number alone is no price the cardholder can approve: the agent sends the pair, above
417
+ * zero (REVOLUT_PSP below), and the API holds its amount to the body's and its currency to
418
+ * one that Revolut and the API both count in hundredths.
419
+ * Pay.'s card token request carries none either: the shop's server charges the token later for
420
+ * its own order total, so the agent's pair, above zero (PAY_NL_PSP below), is the one figure the
421
+ * approval shows.
422
+ * PayPal Card Fields' confirm carries none either: the merchant's server fixed the
423
+ * amount when it created the order, so the agent's pair, above zero (PAYPAL_PSP
424
+ * below), is the one figure the approval shows.
425
+ * ComGate's gateway link carries none either: the merchant's server set
426
+ * the amount when it created the payment at ComGate, so the agent's pair, above zero
427
+ * (COMGATE_PSP below), is the figure the cardholder approves, and the cardholder's device
428
+ * holds ComGate's own figure to it before any card leaves.
429
+ * MakeCommerce's card post carries none either: MakeCommerce's transaction holds the total, in
430
+ * euros, so the agent sends the total the shop's order page shows, above zero (MAKECOMMERCE_PSP
431
+ * below), and the API refuses any other currency.
432
+ * GoDaddy Payments' generate-nonce carries none either: GoDaddy's server charges
433
+ * the nonce later, so its pair must be above zero too (POYNT_PSP below).
434
+ * The TokenEx iframe's card post carries none either: it mints a token into the merchant's TokenEx
435
+ * vault, which the merchant's server charges later for its own order, so the agent sends the total
436
+ * the merchant's page shows, above zero (TOKENEX_IFRAME_PSP), and the API refuses a create without it.
437
+ * Webpay's entry carries none either: the merchant's server bound the total, in Chilean pesos,
438
+ * to the transaction when it created it, so the agent's pair, above zero (TRANSBANK_PSP below),
439
+ * is the figure the cardholder approves, and the cardholder's device holds Webpay's own
440
+ * transaction to it before any card leaves.
441
+ * SumUp's card request carries none either: the merchant's server bound the
442
+ * total and its currency to the checkout when it created it, so the agent sends the total the
443
+ * payment page shows, above zero (SUMUP_PSP below), and the API holds it to the checkout SumUp
444
+ * itself reports.
279
445
  */
280
446
  const MONERIS_PSP = 'moneris';
281
447
  const PAYFAST_PSP = 'payfast';
282
448
  const PAYMONGO_PSP = 'paymongo';
283
- 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];
449
+ const REVOLUT_PSP = 'revolut';
450
+ const PAY_NL_PSP = 'pay_nl';
451
+ const PAYPAL_PSP = 'paypal';
452
+ const GOPAY_PSP = 'gopay';
453
+ const MAKECOMMERCE_PSP = 'makecommerce';
454
+ const POYNT_PSP = 'poynt';
455
+ const SUMUP_PSP = 'sumup';
456
+ 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, PAY_NL_PSP, HIPAY_PSP, TWOC2P_PSP, REVOLUT_PSP, PAYPAL_PSP, GOPAY_PSP, COMGATE_PSP,
457
+ PRZELEWY24_PSP, MAKECOMMERCE_PSP, POYNT_PSP, TOKENEX_IFRAME_PSP, TRANSBANK_PSP, SUMUP_PSP];
284
458
  /** How AmountRequiredError names a processor. */
285
459
  const PROCESSOR_NAMES = { [GMO_PSP]: 'GMO', [PELECARD_PSP]: 'Pelecard', [WSPAY_PSP]: 'WSPay',
286
- [SECURE_ACCEPTANCE_PSP]: 'CyberSource Secure Acceptance', [MONERIS_PSP]: 'Moneris', [PADDLE_VGS_PSP]: 'Paddle', [PAYFAST_PSP]: 'Payfast', [WIX_PAYMENTS_PSP]: 'Wix', [PAYMONGO_PSP]: 'PayMongo' };
460
+ [SECURE_ACCEPTANCE_PSP]: 'CyberSource Secure Acceptance', [MONERIS_PSP]: 'Moneris', [PADDLE_VGS_PSP]: 'Paddle', [PAYFAST_PSP]: 'Payfast', [WIX_PAYMENTS_PSP]: 'Wix', [PAYMONGO_PSP]: 'PayMongo', [PAY_NL_PSP]: 'Pay.', [HIPAY_PSP]: 'HiPay', [TWOC2P_PSP]: '2C2P',
461
+ [REVOLUT_PSP]: 'Revolut', [PAYPAL_PSP]: 'PayPal', [GOPAY_PSP]: 'GoPay',
462
+ [COMGATE_PSP]: 'ComGate', [PRZELEWY24_PSP]: 'Przelewy24',
463
+ [MAKECOMMERCE_PSP]: 'MakeCommerce', [POYNT_PSP]: 'GoDaddy Payments', [TOKENEX_IFRAME_PSP]: 'TokenEx', [TRANSBANK_PSP]: 'Webpay',
464
+ [SUMUP_PSP]: 'SumUp' };
287
465
  /**
288
466
  * Processors whose request and answer name no amount at all, so the total the
289
467
  * payment page shows (`pageAmount`) is the one figure that ties the agent's pair
@@ -514,13 +692,18 @@ const PROCESSOR_PLACEHOLDER_NUMBERS = Object.freeze({});
514
692
  /**
515
693
  * The placeholder number for a network (lowercase, as GET /api/v2/vault_cards names it) on a
516
694
  * processor's page: the processor's own number for that network when `overrides` names one, else
517
- * the network's default, else undefined for a network with no placeholder.
695
+ * the network's default, else undefined for a network with no placeholder. On a TokenEx iframe
696
+ * (`tokenex_iframe`) the number must also be one TokenEx's frame reads as that network, since the frame
697
+ * names the brand to the page from the typed digits and the cardholder's device refuses a mismatch: the
698
+ * frame's table takes Visa, Mastercard and American Express, so every other network gets undefined.
518
699
  */
519
700
  export function placeholderNumber(brand, psp, overrides = PROCESSOR_PLACEHOLDER_NUMBERS) {
520
701
  const own = psp !== undefined && Object.prototype.hasOwnProperty.call(overrides, psp) ? overrides[psp] : undefined;
521
- if (own && Object.prototype.hasOwnProperty.call(own, brand))
522
- return own[brand];
523
- return Object.prototype.hasOwnProperty.call(PLACEHOLDER_NUMBERS, brand) ? PLACEHOLDER_NUMBERS[brand] : undefined;
702
+ const number = own && Object.prototype.hasOwnProperty.call(own, brand) ? own[brand]
703
+ : Object.prototype.hasOwnProperty.call(PLACEHOLDER_NUMBERS, brand) ? PLACEHOLDER_NUMBERS[brand] : undefined;
704
+ if (number !== undefined && psp === TOKENEX_IFRAME_PSP && !tokenexIframePlaceholderFits(brand, number))
705
+ return undefined;
706
+ return number;
524
707
  }
525
708
  /**
526
709
  * The recognizer says this processor is preparation-required, and no preparation
@@ -546,7 +729,9 @@ export class PreparationRequiredError extends Error {
546
729
  * when the merchant's server created the payment), so the `amount` and
547
730
  * `currency` you pass are what the cardholder approves, and for WSPay the
548
731
  * figure the API compares with WSPay's answer when that answer names one (a
549
- * 3-D Secure step names none). Thrown locally before any create or
732
+ * 3-D Secure step names none). A Revolut card payment names the order's
733
+ * amount but not its currency, so it takes the same pair, above zero.
734
+ * Thrown locally before any create or
550
735
  * prompt, and terminal: the same request without an amount fails the same
551
736
  * way. The API answers 400 amount_required for the same case. A page total
552
737
  * (`pageAmount`) does not count.
@@ -556,7 +741,8 @@ export class AmountRequiredError extends Error {
556
741
  code = 'amount_required';
557
742
  constructor(psp) {
558
743
  const processor = PROCESSOR_NAMES[psp] ?? psp;
559
- 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.`);
744
+ const lacks = psp === REVOLUT_PSP ? 'names its amount but not its currency' : 'names no amount';
745
+ 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.`);
560
746
  this.psp = psp;
561
747
  this.name = 'AmountRequiredError';
562
748
  }
@@ -1154,12 +1340,19 @@ function maskPaymongoSession(path) {
1154
1340
  * https://secure.gopay.com/gp-card-comm/h/<hex token>?lang=..&ver=..&SG=<64 hex> (payment-core gopay.js). The
1155
1341
  * token names that payment's card form, and with the SG, which redactUrl drops with the query, anyone who holds
1156
1342
  * 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
1157
- * case or after an escaped slash, reads {token}: https://secure.gopay.com/gp-card-comm/h/{token}.
1343
+ * case or after an escaped slash, reads {token}: https://secure.gopay.com/gp-card-comm/h/{token}. Then every run of
1344
+ * 16 or more hex digits left reads {token} too, so a token after a path the frame never writes (an escaped h, a
1345
+ * path parameter) is masked as well; the token is 16 hex digits or more, and no other path the card frame uses holds
1346
+ * such a run. The pattern reads a percent escape as one unit: an escaped hex digit (%61, %2561) counts toward a run,
1347
+ * so escaping some of a token's digits never splits it into runs that show, and any other escape (%2F, %252F, %68)
1348
+ * is kept whole, so its own hex digits never start a run and leave part of a token showing.
1158
1349
  */
1159
1350
  const GOPAY_CARD_FORM_HOST = /^secure(?:\.sandbox)?\.gopay\.com\.?$/i;
1160
1351
  const GOPAY_CARD_FORM_TOKEN = new RegExp(`(${SLASH}h${SLASH}+)[^/?#]*`, 'gi');
1352
+ const GOPAY_TOKEN_RUN = /(?:[0-9a-f]|%(?:25)*(?:3[0-9]|[46][1-6])){16,}|%(?:25)*[0-9a-f]{2}/gi;
1353
+ const GOPAY_LONE_ESCAPE = /^%(?:25)*[0-9a-f]{2}$/i;
1161
1354
  function maskGopayToken(path) {
1162
- return path.replace(GOPAY_CARD_FORM_TOKEN, '$1{token}');
1355
+ return path.replace(GOPAY_CARD_FORM_TOKEN, '$1{token}').replace(GOPAY_TOKEN_RUN, (run) => (GOPAY_LONE_ESCAPE.test(run) ? run : '{token}'));
1163
1356
  }
1164
1357
  /**
1165
1358
  * KOMOJU's Hosted Fields frame pays a session with POST /api/v1/sessions/<id>/pay on komoju.com under
@@ -1244,6 +1437,18 @@ const REVOLUT_HOST = /^(?:sandbox-)?(?:merchant|checkout|merchant-mgmt)\.revolut
1244
1437
  function maskRevolutToken(path) {
1245
1438
  return maskUuids(path, '{token}');
1246
1439
  }
1440
+ /**
1441
+ * ComGate's gateway pages name the payment by its transaction id in their paths (pay1.comgate.cz/<id>,
1442
+ * /specify/card2/<id>, payment-core processors/comgate.js), and the id alone opens that payment's gateway page, its
1443
+ * amount and its merchant. Its entry link names the id in the query, which redactUrl drops. On every host ComGate
1444
+ * owns, any id of its shape, in any case and with each hyphen in any spelling HYPHEN reads, reads {payment}. The
1445
+ * pattern reads a percent escape as one unit and keeps it, as maskUuids does.
1446
+ */
1447
+ const COMGATE_HOST = /(?:^|\.)comgate\.cz\.?$/i;
1448
+ 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');
1449
+ function maskComgateId(path) {
1450
+ return path.replace(COMGATE_ID, (token) => (token.startsWith('%') ? token : '{payment}'));
1451
+ }
1247
1452
  /**
1248
1453
  * Paytrail names its payment in the path of every route of its card method
1249
1454
  * (/payments/<transaction>/click-to-pay/loading-and-redirect, and its success and cancel routes), and the same id
@@ -1268,6 +1473,68 @@ function maskPaytrailTransaction(path) {
1268
1473
  function maskPaymentHighwaySession(path) {
1269
1474
  return maskPaytrailId(path, '{session}');
1270
1475
  }
1476
+ /**
1477
+ * 2C2P's hosted payment page names its payment token in its hash routes (pgw-ui.2c2p.com/payment/<build>/#/token/
1478
+ * <token>, #/info/<token>, #/mobile/process/<token>), and the token is all a browser needs to open that payment's page
1479
+ * (payment-core twoc2p.js). redactUrl drops a fragment, but a URL whose `#` was escaped carries the route in its path,
1480
+ * and then in any spelling: escaped or doubled separators, escaped letters, parameters. Nothing past the build's folder
1481
+ * on 2C2P's page hosts is worth a log line, so everything after `/payment/<build>/`, its separators in any spelling
1482
+ * SLASH reads, reads {token}, and on any other path everything after its first separator. A string that is not a URL
1483
+ * is cut the same way after a whole page-host authority it names (at the start, or after a space, a quote, a bracket,
1484
+ * a colon such as a `see:` before it, a comma, a semicolon, an equals sign or `//`, and followed by a path, a port or
1485
+ * the end), and only up to the end of that URL, so the message's own words,
1486
+ * another site's path naming the host and a lookalike host stay; a text naming several such URLs has each one cut.
1487
+ * Anchored matches and cuts in one pass: no input makes it backtrack.
1488
+ */
1489
+ const TWOC2P_PAGE_HOST = /^(?:sandbox-)?pgw-ui\.2c2p\.com\.?$/i;
1490
+ const TWOC2P_PAGE_FOLDER = new RegExp(`^${SLASH}payment${SLASH}4\\.[0-9]{1,2}${SLASH}`, 'i');
1491
+ const TWOC2P_FIRST_SEPARATOR = new RegExp(`^${SLASH}`, 'i');
1492
+ const TWOC2P_PAGE_AUTHORITY = `(?:^|[\\s"'(<\\[:,;=]|//)(?:sandbox-)?pgw-ui\\.2c2p\\.com\\.?(?::[0-9]{1,5})?(?=${SLASH}|$)`;
1493
+ /** One page path, from its first separator: the build's folder stays and the rest reads {token}. */
1494
+ function maskTwoC2pPath(path) {
1495
+ const kept = TWOC2P_PAGE_FOLDER.exec(path) ?? TWOC2P_FIRST_SEPARATOR.exec(path);
1496
+ const end = kept ? kept[0].length : 0;
1497
+ return path.length > end ? `${path.slice(0, end)}{token}` : path;
1498
+ }
1499
+ /**
1500
+ * Text that is not a URL: every whole page-host authority it names, each URL cut only to its own end (the first
1501
+ * space, quote or bracket), so the words between and after them stay. A text that starts with a slash is read the
1502
+ * same way, so another site's protocol-relative URL that names the host keeps its own host and path.
1503
+ */
1504
+ function maskTwoC2pToken(path) {
1505
+ const authority = new RegExp(TWOC2P_PAGE_AUTHORITY, 'gi');
1506
+ // A closing bracket or parenthesis ends the URL only when no path or token character follows it, so a URL
1507
+ // a message wraps in [] or () keeps the words after it, and a bracket inside the path still cannot end the cut early.
1508
+ const urlEnd = /[\s"'<>]|[\])](?![A-Za-z0-9+/=%\\])/g;
1509
+ let out = '';
1510
+ let done = 0;
1511
+ for (let host = authority.exec(path); host !== null; host = authority.exec(path)) {
1512
+ const start = host.index + host[0].length;
1513
+ urlEnd.lastIndex = start;
1514
+ const stop = urlEnd.exec(path);
1515
+ const end = stop ? stop.index : path.length;
1516
+ out += path.slice(done, start) + maskTwoC2pPath(path.slice(start, end));
1517
+ done = end;
1518
+ authority.lastIndex = end;
1519
+ }
1520
+ return out + path.slice(done);
1521
+ }
1522
+ /**
1523
+ * QuickPay's window names its session as the first segment of its own path (/<32 hex>, then /3ds_auth or
1524
+ * /poll-response), and that id is what carries a card post into the payment the merchant's signed entry
1525
+ * opened; a payment link's id (/payments/<64 hex>) opens one the same way. On QuickPay's host a run of 32 hex
1526
+ * digits or more after a slash, in any case or escaped, reads {session} whatever follows it: a URL parser drops a
1527
+ * newline after the URL, so words after it can run on into the id. The entry, the host's root, carries none.
1528
+ */
1529
+ const QUICKPAY_HOST = /^payment\.quickpay\.net\.?$/i;
1530
+ const QUICKPAY_SESSION = /(\/|%2f)[0-9a-f]{32,}/gi;
1531
+ function maskQuickpaySession(path) {
1532
+ return path.replace(QUICKPAY_SESSION, '$1{session}');
1533
+ }
1534
+ /**
1535
+ * `mask` reads a text that names the processor's host; `pathMask`, where a processor has one, reads the path of a
1536
+ * URL already parsed on that host, which `mask` reads otherwise.
1537
+ */
1271
1538
  const PATH_CREDENTIALS = [
1272
1539
  { host: PATH_SECRET_HOST, named: /(?:wspay\.biz|monri\.com)/i, mask: maskPathSecret },
1273
1540
  { host: CREDITGUARD_HOST, named: /creditguard\.co\.il/i, mask: maskCreditguardTxId },
@@ -1286,8 +1553,11 @@ const PATH_CREDENTIALS = [
1286
1553
  { host: PRZELEWY24_HOST, named: /przelewy24\.pl/i, mask: maskPrzelewy24Token },
1287
1554
  { host: REVOLUT_HOST, named: /(?:merchant|checkout|merchant-mgmt)\.revolut\.com/i, mask: maskRevolutToken },
1288
1555
  { host: SUMUP_API_HOST, named: /api\.sumup\.com/i, mask: maskSumupCheckout },
1556
+ { host: COMGATE_HOST, named: /comgate\.cz/i, mask: maskComgateId },
1289
1557
  { host: PAYTRAIL_HOST, named: /paytrail\.com/i, mask: maskPaytrailTransaction },
1290
1558
  { host: PAYMENT_HIGHWAY_HOST, named: /(?:paymenthighway\.io|sph-test-solinor\.com)/i, mask: maskPaymentHighwaySession },
1559
+ { host: TWOC2P_PAGE_HOST, named: /pgw-ui\.2c2p\.com/i, mask: maskTwoC2pToken, pathMask: maskTwoC2pPath },
1560
+ { host: QUICKPAY_HOST, named: /quickpay\.net/i, mask: maskQuickpaySession },
1291
1561
  ];
1292
1562
  /**
1293
1563
  * A URL as it may appear in an error message: origin + path only. A paused
@@ -1321,6 +1591,10 @@ const PATH_CREDENTIALS = [
1321
1591
  * On Paytrail's hosts a payment's id reads `{transaction}`:
1322
1592
  * https://services.paytrail.com/payments/{transaction}/click-to-pay/loading-and-redirect, and on Payment
1323
1593
  * Highway's a card page's session reads `{session}`.
1594
+ * On 2C2P's page hosts everything after the build's folder reads `{token}`:
1595
+ * https://pgw-ui.2c2p.com/payment/4.1/{token}.
1596
+ * On QuickPay's host the window's session reads `{session}`:
1597
+ * https://payment.quickpay.net/{session}/3ds_auth.
1324
1598
  * A string that is not a URL keeps the part before any `?` or `#`, with the
1325
1599
  * same segments masked for every processor whose host it names.
1326
1600
  * A URL is read by its host only when the parser finds one. Text before a URL
@@ -1345,7 +1619,7 @@ export function redactUrl(raw) {
1345
1619
  let path = url.pathname;
1346
1620
  for (const rule of PATH_CREDENTIALS)
1347
1621
  if (rule.host.test(url.hostname))
1348
- path = rule.mask(path);
1622
+ path = (rule.pathMask ?? rule.mask)(path);
1349
1623
  return maskNamed(`${url.origin}${path}`, url.pathname);
1350
1624
  }
1351
1625
  /**
@@ -1429,6 +1703,16 @@ const PADDLE_VGS_PREPARATION_REFUSALS = [
1429
1703
  * `unsupported_checkout`, which PREPARATION_REFUSALS names for every checkout. Only for a Wix checkout.
1430
1704
  */
1431
1705
  const WIX_PAYMENTS_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'merchant_profile_mismatch'];
1706
+ /**
1707
+ * The API's refusals of a HiPay preparation, before any preparation exists, that prepareCheckout surfaces by name:
1708
+ * the company is not turned on for HiPay (`processor_unavailable`), the environment is not the caller's key mode's
1709
+ * (`live_host_on_sandbox_row`, `sandbox_host_on_live_row`), a checkout's merchant is not the name the reviewed pin
1710
+ * carries (`merchant_profile_mismatch`), and the price is zero (`amount_required`), since every create after it would be
1711
+ * refused. An unreviewed origin answers `unsupported_checkout`, which PREPARATION_REFUSALS names for every checkout.
1712
+ * Only for a HiPay checkout.
1713
+ */
1714
+ const HIPAY_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'sandbox_host_on_live_row', 'merchant_profile_mismatch',
1715
+ 'amount_required'];
1432
1716
  /**
1433
1717
  * The reason the cardholder's device declines a bound prepared row with, per processor, when its own
1434
1718
  * send boundary passed before the card left it (the Vault's sendWindow, 12 seconds after it last saw
@@ -1441,6 +1725,7 @@ const PREPARED_SEND_WINDOW_DECLINES = Object.freeze({
1441
1725
  amadeus_checkout: 'amadeus_send_window_closed',
1442
1726
  [SECURE_ACCEPTANCE_PSP]: 'secure_acceptance_send_window_closed',
1443
1727
  [WIX_PAYMENTS_PSP]: 'wix_payments_send_window_closed',
1728
+ [HIPAY_PSP]: 'hipay_send_window_closed',
1444
1729
  });
1445
1730
  /**
1446
1731
  * Refusals a CyberSource Flex preparation that tokenizes before Pay answers (4xx) before any
@@ -1529,8 +1814,8 @@ export class VaultClient {
1529
1814
  /**
1530
1815
  * Approve purchases by voice inside your own product: connect a person once,
1531
1816
  * create the purchase, ask for the key word, read the result. Same
1532
- * credentials, same token as the checkout calls. Needs `voice: { approval }`
1533
- * at construction; without it every verb throws `missing_approval_posture`.
1817
+ * credentials, same token as the checkout calls. Approves on Agentcard's
1818
+ * page unless `voice: { approval: 'in_app' }` was named at construction.
1534
1819
  */
1535
1820
  voice;
1536
1821
  /**
@@ -1644,7 +1929,9 @@ export class VaultClient {
1644
1929
  // wspay_confirm, shiji_sts_pinned_key, amadeus_checkout_actions.v1,
1645
1930
  // worldline_mycheckout_form, verifone_card_capture, ekashu_hosted_payment_page,
1646
1931
  // cybersource_sa_token_create, gravy_session_fields_put,
1647
- // moneris_hosted_tokenization, paddle_vgs_cards, redsys_hosted_payment_page) and
1932
+ // moneris_hosted_tokenization, paddle_vgs_cards, paddle_tokenex_pay,
1933
+ // redsys_hosted_payment_page, lyra_krypton_payment, payfast_card_pay,
1934
+ // quickpay_hosted_payment_page, tokenex_iframe_tokenize, among others) and
1648
1935
  // the caller's company is one Agentcard turned that processor on for, so this
1649
1936
  // build arms Fiserv's card capture, GMO's getToken, CyberSource Flex's card
1650
1937
  // fields, Juspay's card flow, Datatrans's Secure Fields card post, the
@@ -1838,7 +2125,9 @@ export class VaultClient {
1838
2125
  * The facts hold only while the cardholder pays with that card. `psp` names the processor
1839
2126
  * whose card form the agent types into (the recognizer's name, `worldline_mycheckout`): a
1840
2127
  * processor that needs its own number for the card's network gets it, and every other gets the
1841
- * network's default. Throws PlaceholderCardError when there is no such card, its network has no
2128
+ * network's default. On a TokenEx iframe (`tokenex_iframe`, JetBlue) the number is one TokenEx's frame
2129
+ * reads as the card's own network, Visa, Mastercard or American Express, and any other network has no
2130
+ * placeholder there. Throws PlaceholderCardError when there is no such card, its network has no
1842
2131
  * placeholder, or it carries no expiry.
1843
2132
  */
1844
2133
  async placeholderCard(input) {
@@ -2123,6 +2412,7 @@ export class VaultClient {
2123
2412
  || (input.psp === EPROTECT_PSP && EPROTECT_PREPARATION_REFUSALS.includes(error.code))
2124
2413
  || (input.psp === PADDLE_VGS_PSP && PADDLE_VGS_PREPARATION_REFUSALS.includes(error.code))
2125
2414
  || (input.psp === WIX_PAYMENTS_PSP && WIX_PAYMENTS_PREPARATION_REFUSALS.includes(error.code))
2415
+ || (input.psp === HIPAY_PSP && HIPAY_PREPARATION_REFUSALS.includes(error.code))
2126
2416
  || (!!flexContext && CYBERSOURCE_FLEX_PREPARATION_REFUSALS.includes(error.code))))
2127
2417
  || (input.psp === 'fiserv' && error.status === 502 && error.code === 'cse_key_unavailable')))
2128
2418
  throw fail(error.code);
@@ -2377,10 +2667,14 @@ export class VaultClient {
2377
2667
  : `checkout authorization is POST-only; got ${method} for ${redactUrl(input.request.url)}. ` +
2378
2668
  'A non-POST tokenizer needs recognizer support before it can be intercepted.');
2379
2669
  }
2380
- // A GET card request is its URL alone. This build completes one: Worldpay
2381
- // eProtect's registration exactly as payment-core admits it, with no body.
2670
+ // A GET card request is its URL alone. This build completes two kinds: Worldpay
2671
+ // eProtect's registration exactly as payment-core admits it, with no body, and a
2672
+ // hosted page's entry link exactly as its rule admits it (hosted-entry.ts), with no
2673
+ // body, which the cardholder's device takes over.
2382
2674
  const eprotect = rec?.psp === EPROTECT_PSP;
2383
- if (cardMethod === 'GET' && !(eprotect && isEprotectRegistration(input.request))) {
2675
+ const hostedGetEntry = mode === 'hosted_form' && !!rec && isHostedGetEntry(rec.psp, input.request.url)
2676
+ && (input.request.body === undefined || input.request.body === null || input.request.body === '');
2677
+ if (cardMethod === 'GET' && !(eprotect && isEprotectRegistration(input.request)) && !hostedGetEntry) {
2384
2678
  throw new Error(`this ${rec?.psp} GET is not a card registration this version of @agent-cards/checkout can complete: ${redactUrl(input.request.url)}.`);
2385
2679
  }
2386
2680
  // A CyberSource Secure Acceptance token create (always prepared: its recognizer requires it). The
@@ -2406,10 +2700,26 @@ export class VaultClient {
2406
2700
  // amount_required.
2407
2701
  if (!hasAmount && rec && AGENT_AMOUNT_PROCESSORS.includes(rec.psp))
2408
2702
  throw new AmountRequiredError(rec.psp);
2409
- // A Moneris token is charged later for whatever the merchant's server decides, and a Payfast pay or a
2410
- // PayMongo attach charges the total its payment or checkout session already holds, so a zero pair
2411
- // would show the cardholder no price at all: it is refused like a missing one.
2412
- if (hasAmount && rec && (rec.psp === MONERIS_PSP || rec.psp === PAYFAST_PSP || rec.psp === PAYMONGO_PSP) && Number(input.amount) <= 0)
2703
+ // A Przelewy24 pay wall charges the amount the merchant's server registered with Przelewy24 under the link's
2704
+ // token, so a zero pair would show the cardholder no price at all: it is refused like a missing one.
2705
+ // A Moneris, a HiPay or a Pay. token, or a GoDaddy Payments nonce, is charged later for whatever the merchant's
2706
+ // server decides, a Payfast pay, a PayMongo attach, a 2C2P Payment, a Revolut card payment or a SumUp card request
2707
+ // charges the total its payment, checkout session, payment token, order or checkout already holds, and a PayPal
2708
+ // order's amount was fixed by the merchant's server and never shown in the confirm, and a GoPay card post charges
2709
+ // the total GoPay's payment already holds, so a zero pair would show the cardholder no price at all: it is refused
2710
+ // like a missing one.
2711
+ // A ComGate payment charges the total ComGate already holds for it, the same way, and a Webpay transaction the
2712
+ // total the merchant's server bound to it.
2713
+ // A Moneris token is charged later for whatever the merchant's server decides, and a Payfast pay, a
2714
+ // PayMongo attach or a MakeCommerce card post charges the total its payment, checkout session or
2715
+ // transaction already holds, so a zero pair would show the cardholder no price at all: it is refused
2716
+ // like a missing one. So is a TokenEx iframe token, which the merchant's server charges later for its own order.
2717
+ if (hasAmount && rec && rec.psp === PRZELEWY24_PSP && Number(input.amount) <= 0)
2718
+ throw new AmountRequiredError(rec.psp);
2719
+ if (hasAmount && rec && (rec.psp === MONERIS_PSP || rec.psp === PAYFAST_PSP || rec.psp === PAYMONGO_PSP || rec.psp === HIPAY_PSP || rec.psp === TWOC2P_PSP
2720
+ || rec.psp === REVOLUT_PSP || rec.psp === PAY_NL_PSP || rec.psp === PAYPAL_PSP
2721
+ || rec.psp === GOPAY_PSP || rec.psp === COMGATE_PSP || rec.psp === MAKECOMMERCE_PSP
2722
+ || rec.psp === POYNT_PSP || rec.psp === TOKENEX_IFRAME_PSP || rec.psp === TRANSBANK_PSP || rec.psp === SUMUP_PSP) && Number(input.amount) <= 0)
2413
2723
  throw new AmountRequiredError(rec.psp);
2414
2724
  // Nor is the agent's pair tied to the page by anything but the total the
2415
2725
  // page shows: without it the cardholder could approve one figure while the
@@ -2555,8 +2865,11 @@ export class VaultClient {
2555
2865
  // and the API names 500 ms as such a row's poll interval.
2556
2866
  // A prepared Paddle VGS card request too, for 20 seconds: Paddle's frame aborts it 10 seconds after
2557
2867
  // Pay and sends it once more, and the API names 500 ms as such a row's poll interval.
2868
+ // A prepared HiPay token create too, for 20 seconds: HiPay's hosted fields give it 20 seconds from Pay, so
2869
+ // an answer the cardholder's device records late in that span still reaches the frame before it gives up,
2870
+ // and the API names 500 ms as such a row's poll interval.
2558
2871
  const preparedFastPollUntil = preparation?.psp === 'mercado_pago' ? Date.now() + 10_000
2559
- : preparation?.psp === PADDLE_VGS_PSP ? Date.now() + 20_000
2872
+ : preparation?.psp === PADDLE_VGS_PSP || preparation?.psp === HIPAY_PSP ? Date.now() + 20_000
2560
2873
  : preparation?.psp === EPROTECT_PSP ? Date.now() + 15_000
2561
2874
  : preparation?.psp === SECURE_ACCEPTANCE_PSP ? Date.now() + SECURE_ACCEPTANCE_FAST_POLL_MS : 0;
2562
2875
  // A preparation that tokenized before Pay: the cardholder's device already holds CyberSource's
@@ -2924,6 +3237,18 @@ export class VaultClient {
2924
3237
  response.headers = answer.headers;
2925
3238
  response.body = answer.body;
2926
3239
  }
3240
+ if (rec?.psp === TOKENEX_IFRAME_PSP) {
3241
+ // TokenEx's answer enters the frame only as TokenEx's 200 with a sound token, rebuilt from payment-core's
3242
+ // allow-list under a JSON content type (tokenex-iframe.ts tokenexIframePageAnswer); the frame adds its own
3243
+ // cardType. Anything else is never handed over: the device may already have sent the card, so the
3244
+ // outcome is unknown rather than declined.
3245
+ const answer = tokenexIframePageAnswer(response.status, response.body);
3246
+ if (!answer)
3247
+ throw new PaymentOutcomeUnknownError(authorizationId, 'tokenex_iframe_token_unconfirmed');
3248
+ response.status = answer.status;
3249
+ response.headers = answer.headers;
3250
+ response.body = answer.body;
3251
+ }
2927
3252
  let processorContext;
2928
3253
  if (mercadoContext) {
2929
3254
  try {
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ComGate's payment gateway (pay1 to pay4.comgate.cz, where most Czech and Slovak e-shops send the buyer), as this
3
+ * SDK reads it.
4
+ *
5
+ * The merchant's server creates the payment at ComGate and sends the buyer's browser to the gateway link ComGate
6
+ * answered with, https://payN.comgate.cz/init?id=<transaction id>, by a redirect or by setting the page's or a
7
+ * frame's location. That GET carries no card and nothing but the payment's id, and it is the one request a build
8
+ * that declares ComGate's capability pauses for ComGate (hosted-entry.ts): the cardholder's device then makes the
9
+ * gateway's own calls for that payment over the end-to-end TLS relay (payment-core processors/comgate.js). Only the
10
+ * link exactly as ComGate writes it is the entry; any other parameter would be handed on to the gateway's own
11
+ * route, which these rules never see.
12
+ *
13
+ * COMGATE_GATEWAY_HOSTS is payment-core's COMGATE_GATEWAY_HOSTS, and isComgateEntryUrl is payment-core's rule of
14
+ * the same name; hosted-entry.test.mjs pins both to payment-core on every case payment-core's own tests list.
15
+ */
16
+ export declare const COMGATE_PSP = "comgate";
17
+ /** The dark-launch capability the API serves ComGate's recognizer behind (payment-core definitions.js). */
18
+ export declare const COMGATE_CAPABILITY = "comgate_hosted_payment_page";
19
+ /** The four gateway hosts ComGate's create answer names, each a host of its own entry. */
20
+ export declare const COMGATE_GATEWAY_HOSTS: readonly string[];
21
+ /**
22
+ * The hosts that take a card, or part of one, for ComGate: the gateway's API (payments.comgate.cz behind pay1 and pay2,
23
+ * payments-gcp.comgate.cz behind pay3 and pay4), where the gateway posts the card (/client/instructions/card-process,
24
+ * check-card) and ComGate's Web Checkout SDK posts its own (/checkout/provider/*, /checkout/card/check-card), and the
25
+ * card frames' hosts (sec.comgate.cz, sec-gcp.comgate.cz), which post the card's first digits to their /bin-checker.
26
+ * The card is already encrypted in ComGate's frame by then, so no body rule could see it: once ComGate is served, this
27
+ * build fails every request to these hosts but a GET, so the card the agent typed never reaches ComGate.
28
+ */
29
+ export declare const COMGATE_CARD_HOSTS: readonly string[];
30
+ /** The Fetch.enable globs attachToCdp arms for the card hosts, only while ComGate is served (cdp.ts). */
31
+ export declare const COMGATE_CARD_PATTERNS: readonly string[];
32
+ /** Whether this URL is on one of ComGate's card hosts (https, no credentials, any port, a trailing dot or not). */
33
+ export declare function isComgateCardHost(url: string): boolean;
34
+ /** Whether this build's registry serves ComGate's entry: a company Agentcard turned ComGate on for, on a build that declares it. */
35
+ export declare function servesComgate(vault: {
36
+ isCardRequest(url: string, method?: string): boolean;
37
+ } | undefined): boolean;
38
+ /**
39
+ * Whether an adapter must hand this request to the pre-claim gates: it is on one of ComGate's card hosts and this
40
+ * build's registry serves ComGate. attachToPlaywright's route predicate asks this, as attachToCdp arms the hosts.
41
+ */
42
+ export declare function isArmedComgateUrl(vault: {
43
+ isCardRequest(url: string, method?: string): boolean;
44
+ } | undefined, url: string): boolean;
45
+ /**
46
+ * Whether a URL is ComGate's gateway link exactly as ComGate's create answer writes it: https, one of the four
47
+ * gateway hosts in lowercase, the default port, no credentials or fragment, the path /init, and a query of the one
48
+ * `id` parameter holding a transaction id. A string that parses to another spelling is not it.
49
+ */
50
+ export declare function isComgateEntryUrl(raw: unknown): boolean;