@agent-cards/sdk 0.23.22 → 0.23.24

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 (54) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/PREFLIGHT.md +2 -0
  3. package/README.md +34 -4
  4. package/dist/aci.generated.js +3 -3
  5. package/dist/agent-amount.generated.d.ts +7 -0
  6. package/dist/agent-amount.generated.js +3 -0
  7. package/dist/amadeus.d.ts +5 -2
  8. package/dist/amadeus.js +18 -3
  9. package/dist/amazon-payment-services.d.ts +25 -0
  10. package/dist/amazon-payment-services.generated.d.ts +76 -0
  11. package/dist/amazon-payment-services.generated.js +883 -0
  12. package/dist/amazon-payment-services.js +53 -0
  13. package/dist/capabilities.generated.d.ts +2 -0
  14. package/dist/capabilities.generated.js +3 -0
  15. package/dist/card-fields.generated.js +1127 -25
  16. package/dist/cdp.js +116 -15
  17. package/dist/client.d.ts +107 -8
  18. package/dist/client.js +277 -128
  19. package/dist/hosted-entry.js +2 -1
  20. package/dist/hosted-form.d.ts +5 -0
  21. package/dist/hosted-form.js +5 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +1 -0
  24. package/dist/openpay.generated.d.ts +67 -0
  25. package/dist/openpay.generated.js +446 -0
  26. package/dist/paddle-checkout-watch.d.ts +70 -0
  27. package/dist/paddle-checkout-watch.js +239 -0
  28. package/dist/paddle-tokenex.generated.d.ts +29 -1
  29. package/dist/paddle-tokenex.generated.js +175 -26
  30. package/dist/paytrail.d.ts +58 -0
  31. package/dist/paytrail.js +98 -0
  32. package/dist/pre-claim.d.ts +4 -0
  33. package/dist/pre-claim.js +46 -2
  34. package/dist/prepared-processor.d.ts +12 -4
  35. package/dist/prepared-processor.js +31 -5
  36. package/dist/primer.generated.js +1155 -53
  37. package/dist/registry.d.ts +4 -3
  38. package/dist/registry.js +12 -4
  39. package/dist/sbps.d.ts +57 -0
  40. package/dist/sbps.generated.d.ts +96 -0
  41. package/dist/sbps.generated.js +427 -0
  42. package/dist/sbps.js +90 -0
  43. package/dist/stripe-checkout.generated.js +458 -8
  44. package/dist/tap-payments.d.ts +43 -0
  45. package/dist/tap-payments.generated.d.ts +59 -0
  46. package/dist/tap-payments.generated.js +990 -0
  47. package/dist/tap-payments.js +72 -0
  48. package/dist/transbank.d.ts +33 -0
  49. package/dist/transbank.js +62 -0
  50. package/dist/url-mask-parts.d.ts +24 -0
  51. package/dist/url-mask-parts.js +22 -0
  52. package/dist/url-masks.generated.d.ts +3 -0
  53. package/dist/url-masks.generated.js +2 -0
  54. package/package.json +4 -4
package/dist/client.js CHANGED
@@ -1,13 +1,10 @@
1
1
  import { BUILTIN_REGISTRY, cardUrlPatterns as deriveCardUrlPatterns, LOOPBACK_URL_PATTERNS, findRecognizer, isRecognizerRequest, recognizerRequestMethod, recognizerRequiresPreparation, urlsOnProcessorHosts, } from './registry.js';
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
- import { ACI_CAPABILITY, ACI_PSP, aciCardLegAnswer } from './aci.js';
5
- import { PRZELEWY24_CAPABILITY, PRZELEWY24_PSP } from './przelewy24.js';
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';
4
+ import { ACI_PSP, aciCardLegAnswer } from './aci.js';
5
+ import { EPROTECT_PSP, eprotectCheckoutRefusal, eprotectScriptAnswer, isEprotectRegistration } from './eprotect.js';
9
6
  import { isHostedGetEntry } from './hosted-entry.js';
10
- import { TRANSBANK_CAPABILITY, TRANSBANK_PSP } from './transbank.js';
7
+ import { SBPS_PSP, isSbpsTokenGet, sbpsScriptAnswer } from './sbps.js';
11
8
  import { isMercadoTokenRequest, parseMercadoCheckoutContext, validateMercadoProcessorContext } from './mercado-checkout.generated.js';
12
9
  import { matchesPreparation, preparationMode, validPreparationEnvironment } from './prepared-processor.js';
13
10
  import { fiservKeyPinById, isFiservSubstitution, requiresFiservPreparation } from './fiserv.js';
@@ -16,37 +13,39 @@ import { hasOwnedShopMarker, parseOwnedShopOrder, parseOwnedShopReceipt } from '
16
13
  import { requestWithoutCard } from './card-fields.generated.js';
17
14
  import { merchantSessionCapture } from './merchant-session-probe.js';
18
15
  import { creditguardPageProcessUrl, creditguardPpDomainHost, creditguardPreparationEnvironment, creditguardProcessTxId, sanitizeCreditguardResponse, } from './creditguard.generated.js';
19
- import { CONVERGE_CAPABILITY } from './elavon-converge.js';
20
- import { SHIJI_STS_CAPABILITY, SHIJI_STS_PSP, isShijiStsServedCompanion, isShijiStsServedSubstitution } from './shiji-sts.js';
21
- import { AMADEUS_CHECKOUT_CAPABILITY, AMADEUS_CHECKOUT_PSP } from './amadeus-checkout.generated.js';
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';
24
- import { PADDLE_TOKENEX_CAPABILITY, PADDLE_TOKENEX_PSP, isPaddleTokenexServedFacts, isPaddleTokenexServedSubstitution } from './paddle-tokenex.js';
25
- import { TWOC2P_CAPABILITY, TWOC2P_PSP } from './twoc2p.js';
26
- import { WIX_PAYMENTS_CAPABILITY, WIX_PAYMENTS_PSP } from './wix-payments.generated.js';
27
- import { HIPAY_CAPABILITY, HIPAY_PSP } from './hipay.generated.js';
28
- import { TOAST_CAPABILITY, TOAST_PSP, isToastCiphertext } from './toast.generated.js';
16
+ import { SHIJI_STS_PSP, isShijiStsServedCompanion, isShijiStsServedSubstitution } from './shiji-sts.js';
17
+ import { AMADEUS_CHECKOUT_PSP } from './amadeus-checkout.generated.js';
18
+ import { PADDLE_VGS_PSP, paddleVgsCardFactsOf } from './paddle-vgs.generated.js';
19
+ import { PAYGENT_PSP, paygentAnswerOutcome } from './paygent.generated.js';
20
+ import { PADDLE_TOKENEX_PSP, isPaddleTokenexServedFacts, isPaddleTokenexServedSubstitution } from './paddle-tokenex.js';
21
+ import { TAP_PAYMENTS_PSP, isTapPaymentsServedSubstitution } from './tap-payments.js';
22
+ import { WIX_PAYMENTS_PSP } from './wix-payments.generated.js';
23
+ import { HIPAY_PSP } from './hipay.generated.js';
24
+ import { OPENPAY_PSP, openpayAnswerOutcome } from './openpay.generated.js';
25
+ import { TOAST_PSP, isToastCiphertext } from './toast.generated.js';
29
26
  import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
30
27
  import { VoiceModule } from './voice.js';
31
28
  import { BuyModule } from './buy.js';
32
29
  import { Languages } from './languages.js';
33
30
  import { RequestRegistry } from './requests.js';
34
31
  import { AgentcardError } from './voice-types.js';
35
- import { GMO_CAPABILITY, GMO_PSP, gmoTokenAnswer } from './gmo.js';
32
+ import { HYPHEN, SLASH, maskUuids } from './url-mask-parts.js';
33
+ import { URL_MASKS } from './url-masks.generated.js';
34
+ import { DECLARED_CAPABILITIES } from './capabilities.generated.js';
35
+ import { AGENT_AMOUNT } from './agent-amount.generated.js';
36
+ import { GMO_PSP, gmoTokenAnswer } from './gmo.js';
36
37
  import { admitCybersourceFlexPreparation, isCybersourceFlexTokenShape, sameCybersourceFlexPreparation } from './cybersource-flex.generated.js';
37
- import { CYBERSOURCE_FLEX_CAPABILITY, CYBERSOURCE_FLEX_PSP, validCybersourceFlexSidecar, } from './cybersource-flex.js';
38
- import { JUSPAY_CAPABILITY, JUSPAY_PSP, juspayPageAnswer } from './juspay-gate.js';
38
+ import { CYBERSOURCE_FLEX_PSP, validCybersourceFlexSidecar, } from './cybersource-flex.js';
39
+ import { JUSPAY_PSP, juspayPageAnswer } from './juspay-gate.js';
39
40
  import { CARD_ENDPOINT_MODE, CARD_ENDPOINT_PSP, CardEndpointRefusedError, cardEndpointById, cardEndpointLive, cardEndpointSessionRefusal, } from './card-endpoint.js';
40
41
  import { merchantCardEndpointFactsRefusal as sharedFactsRefusal, readMerchantCardEndpointFacts as sharedReadFacts, } from './merchant-card-endpoints.generated.js';
41
- import { DATATRANS_CAPABILITY } from './datatrans.js';
42
- import { TOKENEX_IFRAME_CAPABILITY, TOKENEX_IFRAME_PSP, tokenexIframePageAnswer, tokenexIframePlaceholderFits } from './tokenex-iframe.js';
43
- import { PELECARD_CAPABILITY, PELECARD_PSP } from './pelecard.js';
44
- import { PRIMER_CAPABILITY, PRIMER_PSP, primerTokenAnswer } from './primer.js';
45
- import { PAYRAILS_CAPABILITY, isPayrailsPaymentUrl, payrailsAnswer } from './payrails.js';
46
- import { WSPAY_CAPABILITY, WSPAY_PSP } from './wspay.js';
42
+ import { TOKENEX_IFRAME_PSP, tokenexIframePageAnswer, tokenexIframePlaceholderFits } from './tokenex-iframe.js';
43
+ import { APS_PSP, apsNavigationAnswer } from './amazon-payment-services.js';
44
+ import { PRIMER_PSP, primerTokenAnswer } from './primer.js';
45
+ import { isPayrailsPaymentUrl, payrailsAnswer } from './payrails.js';
47
46
  import { EbanxFollowUpError } from './ebanx.js';
48
- import { PAYREXX_CAPABILITY, PAYREXX_PSP, PayrexxFollowUpError } from './payrexx.js';
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';
47
+ import { PAYREXX_PSP, PayrexxFollowUpError } from './payrexx.js';
48
+ import { SECURE_ACCEPTANCE_FAST_POLL_MS, SECURE_ACCEPTANCE_PSP, SECURE_ACCEPTANCE_REPLY_WINDOW_MS, secureAcceptanceCheckoutProfile, secureAcceptanceCheckoutRefusal, secureAcceptanceFrameAnswer, } from './cybersource-secure-acceptance.js';
50
49
  /**
51
50
  * The modes this SDK can finish. Asked for on syncRegistry (the API serves
52
51
  * only recognizers in these modes, so a request this build cannot complete
@@ -206,6 +205,12 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
206
205
  * never lets the agent's browser post to (pre-claim.ts, redsys.ts). Served on the same terms as NMI's: only
207
206
  * to a build that lists it, for a company Agentcard turned Redsys on for.
208
207
  *
208
+ * `paytrail_hosted_payment_page`: Paytrail's card method, whose card-free signed Visa and Mastercard
209
+ * entry this build pauses for the cardholder's device, and whose loading page post, card page posts and
210
+ * card-number lookup on Payment Highway's host this build never lets the agent's browser send
211
+ * (pre-claim.ts, paytrail.ts). Served on the same terms as NMI's and Redsys's: only to a build that lists
212
+ * it, for a company Agentcard turned Paytrail on for.
213
+ *
209
214
  * `paddle_vgs_cards`: the card request Paddle's checkout frame sends to its VGS card
210
215
  * vault (vault.paddle.com/cards) on a checkout that names VGS, only through a prepared
211
216
  * checkout: the frame aborts the request 10 seconds after Pay and sends it once more,
@@ -322,8 +327,8 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
322
327
  * This build pauses only the form's own multipart body at that exact URL; the cardholder's
323
328
  * device sends the card through the checkout relay, and the frame gets the token answer
324
329
  * 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
330
+ * request names no amount, so the agent's pair, above zero, is the one the cardholder approves.
331
+ * Served on the same terms: only to a build that lists it, for a company
327
332
  * Agentcard turned Pay. on for.
328
333
  *
329
334
  * `paypal_card_fields`: PayPal Card Fields' confirm, the POST its v5 frame sends to
@@ -391,8 +396,8 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
391
396
  * encrypted under Clover's key. This build pauses only the frame's own body at that exact URL; the
392
397
  * cardholder's device builds the request again with the real card under the key payment-core pins
393
398
  * 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
399
+ * The request names no amount, so the agent's pair, above zero, is the one the cardholder approves.
400
+ * Served on the same terms: only to a build that lists it, for a company
396
401
  * Agentcard turned Clover on for.
397
402
  *
398
403
  * `eximbay_hosted_payment_page`: Eximbay's payment window, whose card-free signed entry
@@ -410,13 +415,86 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
410
415
  * this build writes it over the agent's placeholder and nothing else before it
411
416
  * continues the request. Served on the same terms: only to a build that lists it, for
412
417
  * a company Agentcard turned Toast on for.
418
+ *
419
+ * `xendit_card_token`: the card token request Xendit's hosted invoice checkout
420
+ * (checkout.xendit.co/web/<invoice id>, where Xendit's payment and donation links land) sends
421
+ * when the payer presses Pay Now: Xendit.js posts the card as JSON to
422
+ * https://api.xendit.co/v2/credit_card_tokens from the page itself, under the business's
423
+ * publishable key. This build pauses only the card form's single-use token body at that exact URL
424
+ * (payment-core xendit.js); a token that keeps the card on file, Xendit.js's code-only storeCVN post
425
+ * and any other body there fail in the browser before anything is created. The cardholder's device
426
+ * sends the request itself with the real card, and the page gets the token answer payment-core's
427
+ * judge rebuilt, after which the page runs 3-D Secure and charges the token without the card. The
428
+ * token names the invoice's amount and currency, so no agent pair is needed: the API reads the
429
+ * token's and holds an agent's pair to it. Served on the same terms: only to a build that lists it,
430
+ * for a company Agentcard turned Xendit on for.
431
+ *
432
+ * `omise_card_token`: the card token request every Omise card form sends, Omise.js's pay.html frame
433
+ * or a merchant's own page calling Omise.js or Omise's Node library: a JSON POST to
434
+ * https://vault.omise.co/tokens under the merchant's public key. This build pauses only the card
435
+ * form's token body at that exact URL (payment-core omise.js); a wallet's tokenization and any other
436
+ * body there fail in the browser before anything is created. The cardholder's device sends the
437
+ * request itself with the real card, and the page gets the token answer payment-core's judge
438
+ * rebuilt. The request names no amount, so the agent's pair, above zero, is the one the cardholder
439
+ * approves, and the API pays only a merchant payment-core pins for the checkout
440
+ * page the create names. Served on the same terms: only to a build that lists it, for a company
441
+ * Agentcard turned Omise on for.
442
+ *
443
+ * `sbps_token_jsonp`: SB Payment Service's card token request, the JSONP <script>
444
+ * GET its token script sends to /token/generateToken on token.sps-system.com. This
445
+ * build pauses it only as the browser writes it, aborts any other request on that
446
+ * path, and answers the paused script with the token as the API recorded it, in the
447
+ * request's own callback. The device rebuilds the card's ciphertext and sends it
448
+ * through the checkout relay. Served on the same terms: only to a build that lists
449
+ * it, for a company Agentcard turned SB Payment Service on for.
450
+ *
451
+ * `midtrans_card_token`: the card token Midtrans' Snap popup posts as one JSON request to
452
+ * api.midtrans.com/v2/token (api.sandbox.midtrans.com in test), under the business's
453
+ * publishable client key. The cardholder's device sends it and the page gets back Midtrans'
454
+ * token, which the merchant's own server charges afterwards, card free, running 3-D Secure
455
+ * there. The token names no amount, so authorize() needs the agent's pair.
456
+ * Served on the same terms, and the API holds the lane to Agentcard's own company
457
+ * until a reviewed change binds the client key to the merchant.
458
+ *
459
+ * `tap_card_sdk_token`: the token request Tap Payments' card frame sends to sdk.tap.company, carrying
460
+ * the card only as one ciphertext the frame writes under the merchant's own RSA key. This build pauses
461
+ * it and writes over the placeholder's ciphertext the one the cardholder's device wrote under the key
462
+ * payment-core pins for the request's publishable key (tap-payments.ts), into that request alone; 3-D
463
+ * Secure and the merchant's charge then run in the agent's browser. Served on the same terms: only to
464
+ * a build that lists it, for a company Agentcard turned Tap on for.
465
+ *
466
+ * `fastspring_card_payment`: the card payment FastSpring's checkout posts, on every store's own
467
+ * host, to <store>.onfastspring.com[/popup-<name>]/session/<id>/payment under the session's
468
+ * X-Session-Token. This build pauses only the checkout's own card body at that exact URL (a card
469
+ * FastSpring saved, a renewal, a gift, a tax id's second card send and a CAPTCHA answer are refused
470
+ * at the pause, and another method's payment with no card continues); the cardholder's device writes
471
+ * the real card into the paused body and sends it through the checkout relay, and the checkout gets
472
+ * FastSpring's answer as payment-core's judge rebuilt it. The request names no amount, so the agent's
473
+ * pair, above zero, is the total the checkout shows. Served on the same terms:
474
+ * only to a build that lists it, for a company Agentcard turned FastSpring on for.
475
+ *
476
+ * `openpay_tokenizer`: the token create openpay.js sends from a merchant's own page to
477
+ * api.openpay.mx/v1/<merchant id>/tokens (api.openpay.co, api.openpay.pe; sandbox-api for a test
478
+ * row), only through a prepared checkout: openpay.js abandons the request 40 seconds after Pay, so
479
+ * the cardholder approves before Pay. The token URL names the merchant, so the claim binds the
480
+ * request by its host's environment and its shape, never by the prepared URL. The cardholder's
481
+ * device sends the card to Openpay itself and the page gets back Openpay's token alone, rebuilt by
482
+ * payment-core's judge. The request names no amount, so the agent's pair, above zero
483
+ * (agent-amount-processors.json), is the one the cardholder approves. Served on the same terms: only
484
+ * to a build that lists it, for a company Agentcard turned Openpay on for.
485
+ *
486
+ * `aps_custom_merchant_page`: Amazon Payment Services' custom merchant page, whose own card form
487
+ * posts top-level to /FortAPI/paymentPage on checkout.payfort.com (sbcheckout.payfort.com on APS's
488
+ * sandbox) with service_command=TOKENIZATION. This build pauses that navigation; the cardholder's
489
+ * phone sends the form with the real card through the checkout relay, and the paused navigation
490
+ * gets APS's own answer, its page or its redirect to the form's return page, only as payment-core's
491
+ * judge reads it for that form (amazon-payment-services.ts apsNavigationAnswer). The form names no
492
+ * amount, so the agent's pair, above zero, is the one the cardholder approves.
493
+ * Served on the same terms: only to a build that lists it, for a company Agentcard turned APS on for.
494
+ *
495
+ * The list itself is capabilities.json, sorted, one per line (scripts/generate-capabilities.mjs).
413
496
  */
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,
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];
497
+ export const SUPPORTED_CAPABILITIES = DECLARED_CAPABILITIES;
420
498
  /**
421
499
  * Processors whose paused request names no amount at all, so the pair the
422
500
  * agent passes is the amount the cardholder approves: authorize() refuses one
@@ -436,12 +514,12 @@ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CY
436
514
  * carries, is the total the cardholder approves, and the card that pays can spend
437
515
  * no more than that (the API's card rule). Moneris Hosted Tokenization's
438
516
  * request.php carries the card and no price: the merchant's server charges the
439
- * token later, so the agent's pair, above zero (MONERIS_PSP below), is the one
517
+ * token later, so the agent's pair, above zero (`moneris` in the table), is the one
440
518
  * figure the approval shows. Paddle VGS's card request carries none either: Paddle
441
519
  * charges the stored card at /pay for what the checkout says, so the agent's pair,
442
520
  * which the prepared checkout carries, is the total the cardholder approves. Payfast's
443
521
  * card pay carries none either: the merchant's entry bound the total to the payment,
444
- * so the agent sends the total Payfast's page shows, above zero (PAYFAST_PSP below),
522
+ * so the agent sends the total Payfast's page shows, above zero (`payfast` in the table),
445
523
  * and the API holds it to the payment Payfast itself reports. Wix's tokenize request
446
524
  * carries none either: the merchant's page charges Wix's token for its own order, so the
447
525
  * agent's pair, which the prepared checkout carries, is the total the cardholder approves. Toast's
@@ -450,88 +528,87 @@ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CY
450
528
  * the one figure the approval shows.
451
529
  * PayMongo's card attach carries none either: the merchant bound the total, always in
452
530
  * Philippine pesos, to the checkout session when it created it, so the agent sends that
453
- * total, above zero (PAYMONGO_PSP below), and the API holds it to the session PayMongo's
531
+ * total, above zero (`paymongo` in the table), and the API holds it to the session PayMongo's
454
532
  * own page shows. GoPay's card post carries none either: the shop's server set the amount
455
533
  * 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).
534
+ * session, so the agent sends the total the gateway shows, above zero (`gopay` in the table).
457
535
  * Paygent's token request carries none either: the merchant's server charges the token later, so
458
536
  * the agent's pair, which the prepared checkout carries, is the total the cardholder approves.
459
537
  * Payrexx's card request names the page's total, but it only mints a token: the merchant's page
460
538
  * 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.
539
+ * above zero (`payrexx` in the table), and the API holds it to the frame's figure exactly.
462
540
  * 2C2P's Payment carries none either: the merchant bound the total to the
463
541
  * payment token when it created it, so the agent sends the total 2C2P's page shows, above
464
- * zero (TWOC2P_PSP), and the API holds it to the amount 2C2P's own PaymentOption answer names.
542
+ * zero (`twoc2p` in the table), and the API holds it to the amount 2C2P's own PaymentOption answer names.
465
543
  * Przelewy24's pay wall link carries none either: the merchant's server registered the amount
466
544
  * 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.
545
+ * zero (`przelewy24` in the table), and the cardholder's device holds the pay wall to it.
468
546
  * HiPay's token create carries none either: the merchant's server charges
469
547
  * HiPay's token for its own order, so the agent's pair, which the prepared checkout carries,
470
548
  * is the total the cardholder approves, above zero as Moneris's is. Revolut's card payment
471
549
  * names the order's amount in minor units but not its currency, which only the order holds,
472
550
  * 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
551
+ * zero (`revolut` in the table), and the API holds its amount to the body's and its currency to
474
552
  * one that Revolut and the API both count in hundredths.
475
553
  * 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
554
+ * its own order total, so the agent's pair, above zero (`pay_nl` in the table), is the one figure the
477
555
  * approval shows.
478
556
  * 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.
557
+ * amount when it created the order, so the agent's pair, above zero (`paypal` in
558
+ * the table), is the one figure the approval shows.
481
559
  * ComGate's gateway link carries none either: the merchant's server set
482
560
  * 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
561
+ * (`comgate` in the table), is the figure the cardholder approves, and the cardholder's device
484
562
  * holds ComGate's own figure to it before any card leaves.
485
563
  * 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.
564
+ * euros, so the agent sends the total the shop's order page shows, above zero (`makecommerce` in
565
+ * the table), and the API refuses any other currency.
488
566
  * 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).
567
+ * the nonce later, so its pair must be above zero too (`poynt` in the table).
490
568
  * The TokenEx iframe's card post carries none either: it mints a token into the merchant's TokenEx
491
569
  * 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.
570
+ * the merchant's page shows, above zero (`tokenex_iframe` in the table), and the API refuses a create without it.
493
571
  * 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),
572
+ * to the transaction when it created it, so the agent's pair, above zero (`transbank_webpay` in the table),
495
573
  * is the figure the cardholder approves, and the cardholder's device holds Webpay's own
496
574
  * transaction to it before any card leaves.
497
575
  * SumUp's card request carries none either: the merchant's server bound the
498
576
  * 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
577
+ * payment page shows, above zero (`sumup` in the table), and the API holds it to the checkout SumUp
500
578
  * itself reports.
501
579
  * 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
580
+ * later for its own order, so the agent's pair, above zero (`clover` in the table), is the one figure
503
581
  * the approval shows.
582
+ * Omise's card token request carries none either, for the same reason: the merchant's server
583
+ * charges the token afterwards for its own order (`omise` in the table).
584
+ * Paytrail's card method entry carries none either: the merchant's server fixed the amount, in
585
+ * euros, when it created the payment, so the agent sends that amount (`paytrail` in the table), and
586
+ * the cardholder's device holds Payment Highway's card page to it.
587
+ * SB Payment Service's token request carries none either: the merchant's server
588
+ * charges the token later, so its pair must be above zero too (`sb_payment_service` in the table), and
589
+ * agree with the page's order total (PAGE_AMOUNT_PROCESSORS).
590
+ * Midtrans' Snap card token carries none either: the merchant's server charges the token
591
+ * afterwards, so the agent's pair, above zero (`midtrans` in the table), is the one figure the
592
+ * approval shows.
593
+ * The table itself is agent-amount-processors.json, sorted, one processor per line
594
+ * (scripts/generate-agent-amount.mjs). A processor added there states its reason in the API
595
+ * create route's refusal, not in this comment.
504
596
  */
505
- const MONERIS_PSP = 'moneris';
506
- const PAYFAST_PSP = 'payfast';
507
- const PAYMONGO_PSP = 'paymongo';
508
597
  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];
598
+ const AGENT_AMOUNT_PROCESSORS = AGENT_AMOUNT.processors;
599
+ /** The ones a zero amount is refused for, like a missing one (authorize()). */
600
+ const ABOVE_ZERO_PROCESSORS = AGENT_AMOUNT.aboveZero;
519
601
  /** How AmountRequiredError names a processor. */
520
- const PROCESSOR_NAMES = { [GMO_PSP]: 'GMO', [PELECARD_PSP]: 'Pelecard', [WSPAY_PSP]: 'WSPay',
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' };
602
+ const PROCESSOR_NAMES = AGENT_AMOUNT.names;
527
603
  /**
528
604
  * Processors whose request and answer name no amount at all, so the total the
529
605
  * payment page shows (`pageAmount`) is the one figure that ties the agent's pair
530
606
  * to what the page charges: authorize() refuses one without it
531
607
  * (PageAmountRequiredError), and the API answers 400 page_amount_required for the
532
- * same case. Pelecard only.
608
+ * same case. Pelecard, and SB Payment Service, whose token request and answer
609
+ * name no amount either.
533
610
  */
534
- const PAGE_AMOUNT_PROCESSORS = [PELECARD_PSP];
611
+ const PAGE_AMOUNT_PROCESSORS = AGENT_AMOUNT.pageAmount;
535
612
  /** An integer in the smallest unit, or a decimal string in normal units with a point; nothing else. */
536
613
  export function validAmountInput(amount) {
537
614
  if (typeof amount === 'number')
@@ -805,8 +882,11 @@ export class AmountRequiredError extends Error {
805
882
  const processor = PROCESSOR_NAMES[psp] ?? psp;
806
883
  // A Payrexx card request names the frame's total, but the merchant's page charges its own total after the token.
807
884
  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.`);
885
+ : psp === PAYREXX_PSP ? "only mints a token the merchant's page charges later"
886
+ : psp === TAP_PAYMENTS_PSP ? 'does not decide the amount charged' : 'names no amount';
887
+ // "An Omise payment request", "A Clover payment request": the article follows the name's first letter.
888
+ const article = /^[AEIOU]/.test(processor) ? 'An' : 'A';
889
+ super(`${article} ${processor} payment request ${lacks}, so pass amount and currency: that is the amount the cardholder approves. Nothing was created and nothing was charged.`);
810
890
  this.psp = psp;
811
891
  this.name = 'AmountRequiredError';
812
892
  }
@@ -824,7 +904,7 @@ export class PageAmountRequiredError extends Error {
824
904
  psp;
825
905
  code = 'page_amount_required';
826
906
  constructor(psp) {
827
- const processor = psp === PELECARD_PSP ? 'Pelecard' : psp;
907
+ const processor = PROCESSOR_NAMES[psp] ?? psp;
828
908
  super(`A ${processor} payment request names no amount, so pass the total its payment page shows as pageAmount beside amount and currency. Nothing was created and nothing was charged.`);
829
909
  this.psp = psp;
830
910
  this.name = 'PageAmountRequiredError';
@@ -1263,23 +1343,7 @@ function parseErrorEnvelope(bodyText) {
1263
1343
  }
1264
1344
  return { code: null, details: {} };
1265
1345
  }
1266
- /**
1267
- * A slash as the masks below read one: plain, or escaped once or more in either case (%2F, %2f, %252F). A path keeps
1268
- * an escape as it was written, and the id behind it is the same id, so every slash a mask names reads in any of these
1269
- * spellings. The slashes right before an id read as a run, and a doubled slash hides nothing either.
1270
- */
1271
- const SLASH = '(?:/|%(?:25)*2f)';
1272
- /** A hyphen inside an id: plain, or escaped once or more in either case (%2D, %252d). */
1273
- const HYPHEN = '(?:-|%(?:25)*2d)';
1274
- /**
1275
- * The masks that find a uuid by its shape take it in any case, with or without its hyphens, each hyphen in any
1276
- * spelling HYPHEN reads. The pattern reads a percent escape as one unit and keeps it, so the hex digits of an escaped
1277
- * separator before the id (%2F, %252F, %3D) never start a match and leave the id's last digits showing.
1278
- */
1279
- const UUID_OR_ESCAPE = new RegExp(`%(?:25)*[0-9a-f]{2}|[0-9a-f]{8}${HYPHEN}?[0-9a-f]{4}${HYPHEN}?[0-9a-f]{4}${HYPHEN}?[0-9a-f]{4}${HYPHEN}?[0-9a-f]{12}`, 'gi');
1280
- function maskUuids(path, label) {
1281
- return path.replace(UUID_OR_ESCAPE, (token) => (token.startsWith('%') ? token : label));
1282
- }
1346
+ // SLASH, HYPHEN and maskUuids, which every mask below reads, live in url-mask-parts.ts with the UrlMask shape.
1283
1347
  const CREDITGUARD_HOST = /(?:^|\.)creditguard\.co\.il\.?$/i;
1284
1348
  const CREDITGUARD_TXN = new RegExp(`(${SLASH}txn${SLASH}+)[^/]+`, 'gi');
1285
1349
  function maskCreditguardTxId(path) {
@@ -1452,14 +1516,28 @@ function maskSumupCheckout(path) {
1452
1516
  * FastSpring's checkout posts its card payment to <store>.onfastspring.com[/popup-<name>]/session/<id>/payment
1453
1517
  * (payment-core fastspring.js), and the session id in that path is a credential: the session's own document,
1454
1518
  * 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}.
1519
+ * host under onfastspring.com the segment after /session/, in any case and in any spelling of the slashes around
1520
+ * /session/ that SLASH reads, reads {session}.
1457
1521
  */
1458
1522
  const FASTSPRING_HOST = /(?:^|\.)onfastspring\.com\.?$/i;
1459
- const FASTSPRING_SESSION = /(\/session(?:\/|%2f))[^/]*/gi;
1523
+ const FASTSPRING_SESSION = new RegExp(`(${SLASH}session${SLASH}+)[^/]*`, 'gi');
1460
1524
  function maskFastspringSession(path) {
1461
1525
  return path.replace(FASTSPRING_SESSION, '$1{session}');
1462
1526
  }
1527
+ /**
1528
+ * PayU Latam's WebCheckout sends the card to /ppp-web-gateway-payu/checkout/submit/<country>/credit_card/<key>
1529
+ * on its own host (payment-core payu-latam.js), and the key, 32 hex characters, is the checkout session's
1530
+ * only credential: the page's card-free routes answer it with no cookie, the order, the payer and the
1531
+ * merchant among them. Every route of the page carries the same key, so on every host under
1532
+ * payulatam.com any run of 32 or more hex characters, in any case or after an escaped slash, reads {key}. A hex
1533
+ * character escaped once or more (%66 or %2566 for f, in either case) counts as that character, since PayU's server
1534
+ * reads the same key, so an escape inside the key cannot split it into two runs too short to mask.
1535
+ */
1536
+ const PAYU_LATAM_HOST = /(?:^|\.)payulatam\.com\.?$/i;
1537
+ const PAYU_LATAM_KEY = /(?:[0-9a-f]|%(?:25)*(?:3[0-9]|4[1-6]|6[1-6])){32,}/gi;
1538
+ function maskPayuLatamKey(path) {
1539
+ return path.replace(PAYU_LATAM_KEY, '{key}');
1540
+ }
1463
1541
  /**
1464
1542
  * The processors whose card request carries its own credential in the URL path, with the hosts
1465
1543
  * that send it, the text that names those hosts anywhere else (a string that is not a URL, or a
@@ -1607,10 +1685,7 @@ const QUICKPAY_SESSION = /(\/|%2f)[0-9a-f]{32,}/gi;
1607
1685
  function maskQuickpaySession(path) {
1608
1686
  return path.replace(QUICKPAY_SESSION, '$1{session}');
1609
1687
  }
1610
- /**
1611
- * `mask` reads a text that names the processor's host; `pathMask`, where a processor has one, reads the path of a
1612
- * URL already parsed on that host, which `mask` reads otherwise.
1613
- */
1688
+ /** Today's masks here; each later processor's in its own src/url-masks/<psp>.ts (URL_MASKS). */
1614
1689
  const PATH_CREDENTIALS = [
1615
1690
  { host: PATH_SECRET_HOST, named: /(?:wspay\.biz|monri\.com)/i, mask: maskPathSecret },
1616
1691
  { host: CREDITGUARD_HOST, named: /creditguard\.co\.il/i, mask: maskCreditguardTxId },
@@ -1635,6 +1710,8 @@ const PATH_CREDENTIALS = [
1635
1710
  { host: TWOC2P_PAGE_HOST, named: /pgw-ui\.2c2p\.com/i, mask: maskTwoC2pToken, pathMask: maskTwoC2pPath },
1636
1711
  { host: QUICKPAY_HOST, named: /quickpay\.net/i, mask: maskQuickpaySession },
1637
1712
  { host: FASTSPRING_HOST, named: /onfastspring\.com/i, mask: maskFastspringSession },
1713
+ { host: PAYU_LATAM_HOST, named: /payulatam\.com/i, mask: maskPayuLatamKey },
1714
+ ...URL_MASKS,
1638
1715
  ];
1639
1716
  /**
1640
1717
  * A URL as it may appear in an error message: origin + path only. A paused
@@ -1674,6 +1751,9 @@ const PATH_CREDENTIALS = [
1674
1751
  * https://payment.quickpay.net/{session}/3ds_auth.
1675
1752
  * On FastSpring's store hosts the session id reads `{session}`:
1676
1753
  * https://daisydisk.onfastspring.com/session/{session}/payment.
1754
+ * On PayU Latam's hosts the checkout session's key reads `{key}`:
1755
+ * https://checkout.payulatam.com/ppp-web-gateway-payu/checkout/submit/co/credit_card/{key}.
1756
+ * Each mask in src/url-masks/ says what it reads in its own file.
1677
1757
  * A string that is not a URL keeps the part before any `?` or `#`, with the
1678
1758
  * same segments masked for every processor whose host it names.
1679
1759
  * A URL is read by its host only when the parser finds one. Text before a URL
@@ -1804,19 +1884,33 @@ const PAYGENT_PREPARATION_REFUSALS = [
1804
1884
  */
1805
1885
  const HIPAY_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'sandbox_host_on_live_row', 'merchant_profile_mismatch',
1806
1886
  'amount_required'];
1887
+ /**
1888
+ * The API's refusals of an Openpay preparation, before any preparation exists, that prepareCheckout passes on by
1889
+ * name: the company is not turned on for Openpay (`processor_unavailable`), the environment is not the caller's key
1890
+ * mode's (`live_host_on_sandbox_row`, `sandbox_host_on_live_row`), a live checkout's merchant is not the name the
1891
+ * reviewed pin for its origin carries (`merchant_profile_mismatch`), and the price is zero (`amount_required`), since
1892
+ * the token create names no amount. An origin no reviewed pin lists answers `unsupported_checkout`, which
1893
+ * PREPARATION_REFUSALS names for every checkout. The API holds the pins, not this build, so a merchant a later release
1894
+ * reviews needs no new SDK. Only for an Openpay checkout.
1895
+ */
1896
+ const OPENPAY_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'sandbox_host_on_live_row',
1897
+ 'merchant_profile_mismatch', 'amount_required'];
1807
1898
  /**
1808
1899
  * The reason the cardholder's device declines a bound prepared row with, per processor, when its own
1809
1900
  * send boundary passed before the card left it (the Vault's sendWindow, 12 seconds after it last saw
1810
1901
  * the checkout unbound): a prepared Amadeus SDK add, a prepared Secure Acceptance token create and a
1811
1902
  * prepared Wix tokenize request. It names the same known non-payment the adapters' own send window
1812
1903
  * closes on (amadeus.ts), which can close later, so authorize() ends the attempt the way that window
1813
- * does, as a CheckoutPreparationError with that reason, and never as the cardholder's "no".
1904
+ * does, as a CheckoutPreparationError with that reason, and never as the cardholder's "no". A prepared
1905
+ * HiPay token create too, and a prepared Openpay token create, whose device sends the card at most 25
1906
+ * seconds after it last saw the checkout unbound, inside the adapters' 30.
1814
1907
  */
1815
1908
  const PREPARED_SEND_WINDOW_DECLINES = Object.freeze({
1816
1909
  amadeus_checkout: 'amadeus_send_window_closed',
1817
1910
  [SECURE_ACCEPTANCE_PSP]: 'secure_acceptance_send_window_closed',
1818
1911
  [WIX_PAYMENTS_PSP]: 'wix_payments_send_window_closed',
1819
1912
  [HIPAY_PSP]: 'hipay_send_window_closed',
1913
+ [OPENPAY_PSP]: 'openpay_send_window_closed',
1820
1914
  });
1821
1915
  /**
1822
1916
  * Refusals a CyberSource Flex preparation that tokenizes before Pay answers (4xx) before any
@@ -2140,9 +2234,10 @@ export class VaultClient {
2140
2234
  * capability (syncRegistry does), so before a sync, or against an API that
2141
2235
  * withheld it, the adapters leave that processor's traffic exactly as they
2142
2236
  * found it. prepare() asks this for ACI, whose card-number request names a
2143
- * checkout id the merchant mints only at Pay time, and for a processor whose
2144
- * card request is a GET (Worldpay eProtect), since that request is recognized
2145
- * by a query the page has not written yet.
2237
+ * checkout id the merchant mints only at Pay time, for Openpay, whose token
2238
+ * URL names the merchant's own id, and for a processor whose card request is
2239
+ * a GET (Worldpay eProtect), since that request is recognized by a query the
2240
+ * page has not written yet.
2146
2241
  */
2147
2242
  recognizesProcessor(psp) {
2148
2243
  return this.registry.some((rec) => rec.psp === psp);
@@ -2385,7 +2480,8 @@ export class VaultClient {
2385
2480
  const secureAcceptanceProfileId = secureAcceptance ? secureAcceptanceCheckoutProfile({ environment: input.environment,
2386
2481
  profileId: input.secureAcceptanceProfileId, merchantOrigin: input.merchantOrigin }) : null;
2387
2482
  // 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.
2483
+ // asked to approve (PAYGENT_PREPARATION_REFUSALS); test mode needs no pin. So is a live Openpay checkout's
2484
+ // origin and merchant (OPENPAY_PREPARATION_REFUSALS); Openpay's sandbox needs no pin either.
2389
2485
  const timeoutMs = input.timeoutMs ?? 15 * 60_000;
2390
2486
  if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647)
2391
2487
  throw fail('timeout_invalid');
@@ -2507,6 +2603,7 @@ export class VaultClient {
2507
2603
  || (input.psp === WIX_PAYMENTS_PSP && WIX_PAYMENTS_PREPARATION_REFUSALS.includes(error.code))
2508
2604
  || (input.psp === PAYGENT_PSP && PAYGENT_PREPARATION_REFUSALS.includes(error.code))
2509
2605
  || (input.psp === HIPAY_PSP && HIPAY_PREPARATION_REFUSALS.includes(error.code))
2606
+ || (input.psp === OPENPAY_PSP && OPENPAY_PREPARATION_REFUSALS.includes(error.code))
2510
2607
  || (!!flexContext && CYBERSOURCE_FLEX_PREPARATION_REFUSALS.includes(error.code))))
2511
2608
  || (input.psp === 'fiserv' && error.status === 502 && error.code === 'cse_key_unavailable')))
2512
2609
  throw fail(error.code);
@@ -2761,14 +2858,15 @@ export class VaultClient {
2761
2858
  : `checkout authorization is POST-only; got ${method} for ${redactUrl(input.request.url)}. ` +
2762
2859
  'A non-POST tokenizer needs recognizer support before it can be intercepted.');
2763
2860
  }
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.
2861
+ // A GET card request is its URL alone. This build completes three kinds: Worldpay
2862
+ // eProtect's registration and SB Payment Service's token request, each exactly as
2863
+ // payment-core admits it, with no body, and a hosted page's entry link exactly as its
2864
+ // rule admits it (hosted-entry.ts), with no body, which the cardholder's device takes over.
2768
2865
  const eprotect = rec?.psp === EPROTECT_PSP;
2769
2866
  const hostedGetEntry = mode === 'hosted_form' && !!rec && isHostedGetEntry(rec.psp, input.request.url)
2770
2867
  && (input.request.body === undefined || input.request.body === null || input.request.body === '');
2771
- if (cardMethod === 'GET' && !(eprotect && isEprotectRegistration(input.request)) && !hostedGetEntry) {
2868
+ const sbps = rec?.psp === SBPS_PSP;
2869
+ if (cardMethod === 'GET' && !(eprotect && isEprotectRegistration(input.request)) && !hostedGetEntry && !(sbps && isSbpsTokenGet(input.request))) {
2772
2870
  throw new Error(`this ${rec?.psp} GET is not a card registration this version of @agent-cards/checkout can complete: ${redactUrl(input.request.url)}.`);
2773
2871
  }
2774
2872
  // A CyberSource Secure Acceptance token create (always prepared: its recognizer requires it). The
@@ -2804,20 +2902,18 @@ export class VaultClient {
2804
2902
  // like a missing one.
2805
2903
  // A ComGate payment charges the total ComGate already holds for it, the same way, and a Webpay transaction the
2806
2904
  // total the merchant's server bound to it, and a Payrexx page charges its own
2807
- // total after the frame's token.
2905
+ // total after the frame's token. The charge that follows a Tap token charges the order the merchant's session
2906
+ // holds, the same way.
2808
2907
  // A Moneris token is charged later for whatever the merchant's server decides, and a Payfast pay, a
2809
2908
  // PayMongo attach or a MakeCommerce card post charges the total its payment, checkout session or
2810
2909
  // transaction already holds, so a zero pair would show the cardholder no price at all: it is refused
2811
2910
  // like a missing one. So is a TokenEx iframe token, which the merchant's server charges later for its own order.
2812
2911
  // A Clover token is charged later for whatever the merchant's server decides, as a Moneris one is.
2813
2912
  // 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)
2913
+ // An Omise token is charged later for whatever the merchant's server decides, as a Clover one is.
2914
+ // An SB Payment Service token is charged later the same way.
2915
+ // An Openpay token is charged later for whatever the merchant's server decides, as a HiPay one is.
2916
+ if (hasAmount && rec && ABOVE_ZERO_PROCESSORS.includes(rec.psp) && Number(input.amount) <= 0)
2821
2917
  throw new AmountRequiredError(rec.psp);
2822
2918
  // Nor is the agent's pair tied to the page by anything but the total the
2823
2919
  // page shows: without it the cardholder could approve one figure while the
@@ -2883,6 +2979,12 @@ export class VaultClient {
2883
2979
  // Only the profile's id: the API takes the leg, the key and its environment from the
2884
2980
  // profile Agentcard reviewed, never from the caller.
2885
2981
  ...(verifoneProfile ? { verifone: { profile: verifoneProfile.id } } : {}),
2982
+ // Paddle TokenEx only: Paddle's checkout answers this browser recorded before the /pay paused, which the API
2983
+ // holds `amount` to (paddle-checkout-watch.ts). Any other processor's create would be refused with them.
2984
+ ...(rec?.psp === PADDLE_TOKENEX_PSP && input.paddleCheckout?.answers.length ? { paddle_checkout: {
2985
+ paused_at: input.paddleCheckout.pausedAt,
2986
+ answers: input.paddleCheckout.answers.map((answer) => ({ method: answer.method, url: answer.url, status: answer.status, completed_at: answer.completedAt, body: answer.body })),
2987
+ } } : {}),
2886
2988
  // A prepared checkout already carries the page origin as merchant_origin.
2887
2989
  ...(preparation
2888
2990
  ? { preparation_id: preparation.id, checkout_key: preparation.checkoutKey, merchant_origin: preparation.merchantOrigin, form_guidance: 'filled' }
@@ -2966,10 +3068,14 @@ export class VaultClient {
2966
3068
  // A prepared HiPay token create too, for 20 seconds: HiPay's hosted fields give it 20 seconds from Pay, so
2967
3069
  // an answer the cardholder's device records late in that span still reaches the frame before it gives up,
2968
3070
  // and the API names 500 ms as such a row's poll interval.
3071
+ // A prepared Openpay token create too, for 40 seconds: openpay.js gives it 40 seconds from Pay, so the token
3072
+ // reaches the page within half a second of the device's answer, and the API names 500 ms as such a row's poll
3073
+ // interval.
2969
3074
  const preparedFastPollUntil = preparation?.psp === 'mercado_pago' ? Date.now() + 10_000
2970
3075
  : preparation?.psp === PADDLE_VGS_PSP || preparation?.psp === HIPAY_PSP ? Date.now() + 20_000
2971
- : preparation?.psp === EPROTECT_PSP ? Date.now() + 15_000
2972
- : preparation?.psp === SECURE_ACCEPTANCE_PSP ? Date.now() + SECURE_ACCEPTANCE_FAST_POLL_MS : 0;
3076
+ : preparation?.psp === OPENPAY_PSP ? Date.now() + 40_000
3077
+ : preparation?.psp === EPROTECT_PSP ? Date.now() + 15_000
3078
+ : preparation?.psp === SECURE_ACCEPTANCE_PSP ? Date.now() + SECURE_ACCEPTANCE_FAST_POLL_MS : 0;
2973
3079
  // A preparation that tokenized before Pay: the cardholder's device already holds CyberSource's
2974
3080
  // token and approves as soon as this bind reaches it, while the page may give createToken only
2975
3081
  // seconds (Alaska Airlines' flight checkout gives it three). Poll every 100 ms for 10 seconds,
@@ -3142,11 +3248,14 @@ export class VaultClient {
3142
3248
  // Paddle TokenEx carries the two ciphertexts at tokenex_card and the five facts beside them in
3143
3249
  // `paddle_tokenex_facts` (the expiry parts are numbers, so they cannot ride `fields`).
3144
3250
  const paddleTokenex = rec?.psp === PADDLE_TOKENEX_PSP;
3251
+ // Tap carries its one ciphertext at the body root too (`at` is ''), and nothing to remove (tap-payments.ts).
3252
+ const tap = rec?.psp === TAP_PAYMENTS_PSP;
3145
3253
  const fieldsOk = shiji ? isShijiStsServedSubstitution(sub) && isShijiStsServedCompanion(s.shiji_sts_companion)
3146
3254
  : paddleTokenex ? isPaddleTokenexServedSubstitution(sub) && isPaddleTokenexServedFacts(s.paddle_tokenex_facts)
3147
- : sub && typeof sub === 'object' && sub.encoding === 'json' && typeof sub.at === 'string' && sub.at
3148
- && sub.fields && typeof sub.fields === 'object' && !Array.isArray(sub.fields)
3149
- && Object.values(sub.fields).every((v) => typeof v === 'string' && v.length > 0);
3255
+ : tap ? isTapPaymentsServedSubstitution(sub)
3256
+ : sub && typeof sub === 'object' && sub.encoding === 'json' && typeof sub.at === 'string' && sub.at
3257
+ && sub.fields && typeof sub.fields === 'object' && !Array.isArray(sub.fields)
3258
+ && Object.values(sub.fields).every((v) => typeof v === 'string' && v.length > 0);
3150
3259
  if (!fieldsOk)
3151
3260
  throw new PaymentOutcomeUnknownError(authorizationId, 'cse_substitutions_malformed');
3152
3261
  // A Fiserv approval carries exactly its envelope's four members at
@@ -3255,6 +3364,18 @@ export class VaultClient {
3255
3364
  response.headers = answer.headers;
3256
3365
  response.body = answer.body;
3257
3366
  }
3367
+ if (sbps) {
3368
+ // SB Payment Service: the paused <script> is answered the way SBPS answers it,
3369
+ // `<callback>({"tokenRes": ...})` under a script type, with the callback the paused URL named
3370
+ // and only a token payment-core's judge accepts for that URL. Anything else is not an answer to
3371
+ // hand the page, and the device may already have sent the card to SBPS.
3372
+ const answer = sbpsScriptAnswer(input.request.url, response);
3373
+ if (!answer)
3374
+ throw new PaymentOutcomeUnknownError(authorizationId, 'sbps_token_unconfirmed');
3375
+ response.status = answer.status;
3376
+ response.headers = answer.headers;
3377
+ response.body = answer.body;
3378
+ }
3258
3379
  if (secureAcceptance) {
3259
3380
  // CyberSource Secure Acceptance: the paused frame navigation is answered with CyberSource's own
3260
3381
  // reply page, byte for byte, with status 200 and its one content type, and only a page
@@ -3339,6 +3460,19 @@ export class VaultClient {
3339
3460
  response.headers = { 'content-type': 'application/json' };
3340
3461
  response.body = judged.body;
3341
3462
  }
3463
+ if (rec?.psp === OPENPAY_PSP) {
3464
+ // Openpay's answer enters the page only as payment-core rebuilds an approved token from its id, masked number,
3465
+ // brand and points flag (openpay.js openpayAnswerOutcome: HTTP 200 or 201 and a token in shape), under its
3466
+ // approved status and a JSON content type. openpay.js hands any 2xx to the page's success callback, so
3467
+ // anything else, an error object Openpay answers inside a 2xx included, is never handed over as an approval;
3468
+ // the card may have left the device, so the outcome is unknown rather than declined.
3469
+ const judged = openpayAnswerOutcome(response.status, response.body, null);
3470
+ if (judged.outcome !== 'approved' || typeof judged.body !== 'string')
3471
+ throw new PaymentOutcomeUnknownError(authorizationId, 'openpay_token_unconfirmed');
3472
+ response.status = response.status === 201 ? 201 : 200;
3473
+ response.headers = { 'content-type': 'application/json' };
3474
+ response.body = judged.body;
3475
+ }
3342
3476
  if (rec?.psp === PRIMER_PSP) {
3343
3477
  // Primer's answer enters the page only as one single-use card token rebuilt from
3344
3478
  // payment-core's allow-list, under a JSON content type (primer.ts primerTokenAnswer).
@@ -3363,6 +3497,21 @@ export class VaultClient {
3363
3497
  response.headers = answer.headers;
3364
3498
  response.body = answer.body;
3365
3499
  }
3500
+ if (rec?.psp === APS_PSP) {
3501
+ // Amazon Payment Services: the paused top-level navigation is answered with APS's own page under its HTML
3502
+ // type, or with APS's redirect to the paused form's return page, and only with a token payment-core's judge
3503
+ // reads for that form (amazon-payment-services.ts apsNavigationAnswer). Anything else is not an answer to
3504
+ // hand the browser: the card has already reached APS. No autopilot run sends an APS form (payment-core, the
3505
+ // authority and the API refuse it), so an approval the API reports as unattended is not handed over either.
3506
+ if (execution.executionMode === 'autopilot')
3507
+ throw new PaymentOutcomeUnknownError(authorizationId, 'autopilot_submission_mode_invalid');
3508
+ const answer = apsNavigationAnswer(input.request, response);
3509
+ if (!answer)
3510
+ throw new PaymentOutcomeUnknownError(authorizationId, 'aps_answer_unconfirmed');
3511
+ response.status = answer.status;
3512
+ response.headers = answer.headers;
3513
+ response.body = answer.body;
3514
+ }
3366
3515
  let processorContext;
3367
3516
  if (mercadoContext) {
3368
3517
  try {