@agent-cards/sdk 0.23.28 → 0.24.1
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.
- package/CHANGELOG.md +84 -0
- package/PREFLIGHT.md +7 -1
- package/README.md +197 -102
- package/dist/aci.d.ts +52 -22
- package/dist/aci.js +79 -15
- package/dist/adyen-merchant-hosted.generated.d.ts +2 -2
- package/dist/adyen-merchant-hosted.generated.js +586 -55
- package/dist/agent-amount.generated.js +1 -1
- package/dist/agentcard.d.ts +4 -5
- package/dist/agentcard.js +4 -5
- package/dist/amadeus.d.ts +9 -3
- package/dist/amadeus.js +33 -4
- package/dist/api-module.d.ts +1 -7
- package/dist/api-module.js +3 -5
- package/dist/{voice-retry.js → api-retry.js} +1 -1
- package/dist/api-types.d.ts +53 -0
- package/dist/{voice-types.js → api-types.js} +7 -2
- package/dist/approvals.d.ts +166 -0
- package/dist/approvals.js +213 -0
- package/dist/aurus.d.ts +33 -0
- package/dist/aurus.js +75 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/buy.d.ts +20 -101
- package/dist/buy.js +19 -74
- package/dist/capabilities.generated.js +1 -1
- package/dist/card-fields.generated.js +2864 -249
- package/dist/cardlink-vpos.d.ts +43 -0
- package/dist/cardlink-vpos.js +90 -0
- package/dist/cdp.d.ts +3 -13
- package/dist/cdp.js +244 -175
- package/dist/checkout-com.generated.js +5 -1
- package/dist/client.d.ts +170 -22
- package/dist/client.js +281 -40
- package/dist/creditguard.generated.d.ts +4 -4
- package/dist/cybersource-flex.generated.d.ts +39 -1
- package/dist/cybersource-flex.generated.js +100 -11
- package/dist/datatrans.generated.js +22 -1
- package/dist/eghl.d.ts +40 -0
- package/dist/eghl.js +72 -0
- package/dist/elavon-converge.generated.js +5 -3
- package/dist/gmo.generated.js +7 -2
- package/dist/hold.d.ts +8 -0
- package/dist/hold.js +43 -2
- package/dist/hosted-entry.js +4 -0
- package/dist/index.d.ts +9 -15
- package/dist/index.js +6 -10
- package/dist/lifecycle.d.ts +9 -61
- package/dist/lifecycle.js +24 -10
- package/dist/merchant-card-endpoints.generated.d.ts +6 -1
- package/dist/merchant-card-endpoints.generated.js +344 -19
- package/dist/merchant-hosted.d.ts +8 -1
- package/dist/nike-order.d.ts +51 -0
- package/dist/nike-order.js +81 -0
- package/dist/oceanpayment.d.ts +38 -0
- package/dist/oceanpayment.js +70 -0
- package/dist/paddle-tokenex.generated.js +111 -11
- package/dist/payplug.d.ts +41 -0
- package/dist/payplug.js +71 -0
- package/dist/payrails.generated.js +33 -8
- package/dist/pre-claim.d.ts +11 -0
- package/dist/pre-claim.js +130 -7
- package/dist/preflight-capabilities.generated.d.ts +0 -4
- package/dist/preflight-capabilities.generated.js +0 -5
- package/dist/preflight-catalog.json +200 -6
- package/dist/preflight-playwright.d.ts +2 -2
- package/dist/preflight-playwright.js +63 -14
- package/dist/preflight-schemas.json +18 -0
- package/dist/preflight.generated.d.ts +5 -4
- package/dist/preflight.generated.js +63 -3
- package/dist/preparation.d.ts +7 -0
- package/dist/preparation.js +21 -0
- package/dist/prepared-processor.d.ts +18 -5
- package/dist/prepared-processor.js +48 -8
- package/dist/primer.generated.js +2889 -268
- package/dist/processout.generated.d.ts +61 -0
- package/dist/processout.generated.js +577 -0
- package/dist/requests.js +1 -1
- package/dist/shiji-sts.generated.js +111 -11
- package/dist/stripe-checkout.generated.js +2761 -319
- package/dist/substitutions.generated.js +553 -26
- package/dist/tap-payments.generated.js +111 -11
- package/dist/twoc2p.generated.js +111 -11
- package/dist/url-masks/bluesnap.d.ts +2 -0
- package/dist/url-masks/bluesnap.js +15 -0
- package/dist/url-masks/oceanpayment.d.ts +2 -0
- package/dist/url-masks/oceanpayment.js +13 -0
- package/dist/url-masks/payplug.d.ts +2 -0
- package/dist/url-masks/payplug.js +13 -0
- package/dist/url-masks/vtex_payments.d.ts +2 -0
- package/dist/url-masks/vtex_payments.js +12 -0
- package/dist/url-masks.generated.d.ts +1 -1
- package/dist/url-masks.generated.js +6 -2
- package/dist/verifone.generated.js +42 -2
- package/dist/vtex-payments.generated.d.ts +127 -0
- package/dist/vtex-payments.generated.js +544 -0
- package/dist/webhooks.d.ts +2 -2
- package/dist/webhooks.js +2 -2
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +6 -8
- package/dist/languages.d.ts +0 -23
- package/dist/languages.js +0 -76
- package/dist/voice-pkce.d.ts +0 -49
- package/dist/voice-pkce.js +0 -147
- package/dist/voice-stream.generated.d.ts +0 -118
- package/dist/voice-stream.generated.js +0 -170
- package/dist/voice-types.d.ts +0 -735
- package/dist/voice.d.ts +0 -373
- package/dist/voice.js +0 -815
- package/readme.test.mjs +0 -53
- /package/dist/{voice-retry.d.ts → api-retry.d.ts} +0 -0
package/dist/client.js
CHANGED
|
@@ -22,13 +22,14 @@ import { TAP_PAYMENTS_PSP, isTapPaymentsServedSubstitution } from './tap-payment
|
|
|
22
22
|
import { WIX_PAYMENTS_PSP } from './wix-payments.generated.js';
|
|
23
23
|
import { HIPAY_PSP } from './hipay.generated.js';
|
|
24
24
|
import { OPENPAY_PSP, openpayAnswerOutcome } from './openpay.generated.js';
|
|
25
|
+
import { VTEX_PAYMENTS_PSP } from './vtex-payments.generated.js';
|
|
26
|
+
import { PROCESSOUT_PSP, processoutAnswerOutcome } from './processout.generated.js';
|
|
25
27
|
import { TOAST_PSP, isToastCiphertext } from './toast.generated.js';
|
|
26
28
|
import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
|
|
27
|
-
import { VoiceModule } from './voice.js';
|
|
28
29
|
import { BuyModule } from './buy.js';
|
|
29
|
-
import {
|
|
30
|
+
import { ApprovalsModule } from './approvals.js';
|
|
30
31
|
import { RequestRegistry } from './requests.js';
|
|
31
|
-
import { AgentcardError } from './
|
|
32
|
+
import { AgentcardError } from './api-types.js';
|
|
32
33
|
import { HYPHEN, SLASH, maskUuids } from './url-mask-parts.js';
|
|
33
34
|
import { URL_MASKS } from './url-masks.generated.js';
|
|
34
35
|
import { DECLARED_CAPABILITIES } from './capabilities.generated.js';
|
|
@@ -114,6 +115,15 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
|
|
|
114
115
|
* recognizer is never in BUILTIN_REGISTRY, so a build reaches it only through a
|
|
115
116
|
* sync that declares its capability, and whether an org may pay through it
|
|
116
117
|
* stays the API's decision.
|
|
118
|
+
* `bluesnap_hosted_payment_fields`: BlueSnap's Hosted Payment Fields, whose card
|
|
119
|
+
* number, expiry and code frames each PUT their own field as JSON to the
|
|
120
|
+
* merchant's payment-fields token. This build pauses the card number PUT, holds
|
|
121
|
+
* the expiry and code PUTs and answers them with its answer (aci.ts, the JSON
|
|
122
|
+
* split card), and the cardholder's device sends the whole card in that one PUT
|
|
123
|
+
* (payment-core processors/bluesnap.js). The request names no amount, so the
|
|
124
|
+
* agent's pair, above zero, is the one the cardholder approves. Served on the
|
|
125
|
+
* same terms: only to a build that lists it, for a company Agentcard turned
|
|
126
|
+
* BlueSnap on for.
|
|
117
127
|
* `creditguard_pps_process`: CreditGuard's hosted payment page (Hyp PPS), whose
|
|
118
128
|
* card submit this build pays only under a prepared checkout that names the
|
|
119
129
|
* framed page (prepare({ psp: 'creditguard', environment, paymentPageUrl })). The
|
|
@@ -452,6 +462,18 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
|
|
|
452
462
|
* page the create names. Served on the same terms: only to a build that lists it, for a company
|
|
453
463
|
* Agentcard turned Pin Payments on for.
|
|
454
464
|
*
|
|
465
|
+
* `paymentwall_card_token`: the card request Paymentwall's card form (Brick) sends, from its number
|
|
466
|
+
* frame on pwgateway.com inside Paymentwall's hosted widget or from a merchant's own page: a form POST
|
|
467
|
+
* to https://pwgateway.com/api/token (https://api.paymentwall.com/api/brick/token for a test key)
|
|
468
|
+
* under the merchant's public key. This build pauses only the card form's own body at those exact
|
|
469
|
+
* URLs (payment-core processors/paymentwall.js); a stored card's code, a wallet's token, a brand from
|
|
470
|
+
* the page's select or any other body there fails in the browser before anything is created. The
|
|
471
|
+
* cardholder's device sends the request itself with the real card, and the page gets the token answer
|
|
472
|
+
* payment-core's judge rebuilt. The request names no amount, so the agent's pair, above zero, is the
|
|
473
|
+
* one the cardholder approves, and the API pays only a Paymentwall project payment-core pins for the
|
|
474
|
+
* checkout page the create names. Served on the same terms: only to a build that lists it, for a
|
|
475
|
+
* company Agentcard turned Paymentwall on for.
|
|
476
|
+
*
|
|
455
477
|
* `sbps_token_jsonp`: SB Payment Service's card token request, the JSONP <script>
|
|
456
478
|
* GET its token script sends to /token/generateToken on token.sps-system.com. This
|
|
457
479
|
* build pauses it only as the browser writes it, aborts any other request on that
|
|
@@ -468,6 +490,56 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
|
|
|
468
490
|
* Served on the same terms, and the API holds the lane to Agentcard's own company
|
|
469
491
|
* until a reviewed change binds the client key to the merchant.
|
|
470
492
|
*
|
|
493
|
+
* `iyzico_checkout_form`: the card payment iyzico's Checkout Form posts as one JSON request to
|
|
494
|
+
* api.iyzipay.com/payment/iyzipos/checkoutform/auth/ecom, or initialize3ds/ecom for 3-D Secure
|
|
495
|
+
* (sandbox-api.iyzipay.com in test), under the form's X-IYZI-TOKEN. This build pauses only the
|
|
496
|
+
* form's own payment with a new card at those exact URLs (payment-core processors/iyzico.js); a saved
|
|
497
|
+
* card, a card kept on file, installments and a currency conversion fail in the browser before
|
|
498
|
+
* anything is created. The cardholder's device sends the payment itself, and the page gets iyzico's
|
|
499
|
+
* answer as payment-core's judge rebuilt it: the payment done, or the bank's 3-D Secure page, which
|
|
500
|
+
* the form then runs in the agent's browser. The payment names its price but not its currency, so
|
|
501
|
+
* authorize() needs the agent's pair, and the API holds its amount to the price. Served on the same
|
|
502
|
+
* terms, and the API holds the lane to Agentcard's own company.
|
|
503
|
+
*
|
|
504
|
+
* `aurus_one_time_token`: the card request Aurus Pay's ecom card frame (O'Reilly Auto Parts) sends to
|
|
505
|
+
* pecst<NN>.aurusepay.com/storeservices/ecom/oneTimeToken, the card sealed in the frame under a public key its own
|
|
506
|
+
* script carries. This build pauses it only when Aurus's card frame sent it (aurus.ts aurusPreClaim); the same
|
|
507
|
+
* request from the store's page or another frame fails in the browser before anything is created. The request
|
|
508
|
+
* names no amount, so authorize() needs the agent's pair, above zero. The cardholder's device does not seal for
|
|
509
|
+
* Aurus yet (payment-core processors/aurus.js), so a paused request opens no approval that pays.
|
|
510
|
+
*
|
|
511
|
+
* `viva_smart_checkout_chargetoken`: the card request Viva Wallet's Smart Checkout page sends as one
|
|
512
|
+
* JSON request to www.vivapayments.com/web2/checkout/v2/chargetokens (demo.vivapayments.com in test),
|
|
513
|
+
* on Pay, after the page's own reCAPTCHA run. This build pauses only the page's own card body at that
|
|
514
|
+
* exact URL (payment-core processors/viva_wallet.js); a body that keeps the card, pays with a stored
|
|
515
|
+
* card, converts the currency or is the page's second try after a decline fails in the browser before
|
|
516
|
+
* anything is created. The cardholder's device sends the card through Agentcard's checkout relay, since
|
|
517
|
+
* the page's request answers no other origin, and the page gets Viva's single-use charge token as
|
|
518
|
+
* payment-core's judge rebuilt it; the page then charges that token card free. The body names its
|
|
519
|
+
* amount, which the API holds to the agent's pair, and the API pays a live token only for an order
|
|
520
|
+
* payment-core pins. Served on the same terms, and the API holds the lane to Agentcard's own company.
|
|
521
|
+
*
|
|
522
|
+
* `paystack_pinned_key`: the charge Paystack's hosted checkout sends as one form POST to
|
|
523
|
+
* standard.paystack.co/charge/, the card only as `clientdata`, RSA ciphertext under the one key
|
|
524
|
+
* Paystack's page carries. This build pauses only the card page's own charge at that exact URL
|
|
525
|
+
* (payment-core processors/paystack.js); a charge that keeps the card, starts a plan or carries a PIN
|
|
526
|
+
* fails in the browser before anything is created. The cardholder's device encrypts the real card under
|
|
527
|
+
* the same pinned key and sends the charge itself, and the page gets Paystack's answer: paid, declined,
|
|
528
|
+
* or the issuer's 3-D Secure step, which the page then runs in the agent's browser. The charge names
|
|
529
|
+
* no amount, so authorize() needs the agent's pair, above zero. Served on the same terms, and the API
|
|
530
|
+
* holds the lane to Agentcard's own company.
|
|
531
|
+
*
|
|
532
|
+
* `usaepay_payment_key`: the card request USAePay's Pay.js card frame sends as one JSON request to
|
|
533
|
+
* www.usaepay.com/api/v2/pub/payment_keys (secure.usaepay.com alike; sandbox.usaepay.com in test), under
|
|
534
|
+
* the merchant's public key in its Authorization. This build pauses only the frame's own card body at
|
|
535
|
+
* that exact URL (payment-core processors/usaepay.js); a Google Pay or Apple Pay token and a bank
|
|
536
|
+
* account on the same URL fail in the browser before anything is created. The cardholder's device sends
|
|
537
|
+
* the card through Agentcard's checkout relay, since USAePay answers no browser origin, and the frame
|
|
538
|
+
* gets USAePay's single-use payment key as payment-core's judge rebuilt it; the merchant's server
|
|
539
|
+
* charges that key afterwards. The request names no amount, so authorize() needs the agent's pair, and
|
|
540
|
+
* the API pays a live key only to a merchant payment-core pins for the checkout page. Served on the same
|
|
541
|
+
* terms, and the API holds the lane to Agentcard's own company.
|
|
542
|
+
*
|
|
471
543
|
* `tap_card_sdk_token`: the token request Tap Payments' card frame sends to sdk.tap.company, carrying
|
|
472
544
|
* the card only as one ciphertext the frame writes under the merchant's own RSA key. This build pauses
|
|
473
545
|
* it and writes over the placeholder's ciphertext the one the cardholder's device wrote under the key
|
|
@@ -495,6 +567,41 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
|
|
|
495
567
|
* (agent-amount-processors.json), is the one the cardholder approves. Served on the same terms: only
|
|
496
568
|
* to a build that lists it, for a company Agentcard turned Openpay on for.
|
|
497
569
|
*
|
|
570
|
+
* `processout_card_token`: the card request ProcessOut.js's card frame sends from js.processout.com to
|
|
571
|
+
* api.processout.com/cards under the store's project id, only through a prepared checkout: the frame
|
|
572
|
+
* gives the page 20 seconds from the shop's continue press, so the cardholder approves before it. The
|
|
573
|
+
* card URL names the project in its query, so the claim binds the request by its project's environment
|
|
574
|
+
* and its shape, never by the prepared URL. The cardholder's device sends the card to ProcessOut itself
|
|
575
|
+
* and the frame gets back ProcessOut's token alone, rebuilt by payment-core's judge. The request names
|
|
576
|
+
* no amount, so the agent's pair, above zero (agent-amount-processors.json), is the one the cardholder
|
|
577
|
+
* approves. Served on the same terms: only to a build that lists it, for a company Agentcard turned
|
|
578
|
+
* ProcessOut on for.
|
|
579
|
+
*
|
|
580
|
+
* `monei_card_token`: the card request MONEI's card frame sends from MONEI's payment page
|
|
581
|
+
* (secure.monei.com/payments/<id>) to api.monei.com/v1/tokens, with the payment's id and the card. This
|
|
582
|
+
* build pauses only the frame's own body for a payment at that exact URL (payment-core
|
|
583
|
+
* processors/monei.js); MONEI's account flow, Click to Pay and a wallet on the same URL fail in the
|
|
584
|
+
* browser before anything is created. The request names neither the merchant nor the amount, so the API
|
|
585
|
+
* reads MONEI's own record of the payment and holds the agent's pair, above zero
|
|
586
|
+
* (agent-amount-processors.json), to MONEI's amount exactly. The cardholder's device sends the card to
|
|
587
|
+
* MONEI itself and the frame gets back MONEI's payment token alone, rebuilt by payment-core's judge.
|
|
588
|
+
* Served on the same terms: only to a build that lists it, for a company Agentcard turned MONEI on for.
|
|
589
|
+
*
|
|
590
|
+
* `frisbii_card_token`: the card request Frisbii Checkout's hosted payment page (checkout.reepay.com or
|
|
591
|
+
* checkout.frisbii.com, as an overlay, an embedded frame or a full window) sends to
|
|
592
|
+
* card.reepay.com or card.frisbii.com /v1/token/?session=<id>, with the merchant's public key and the
|
|
593
|
+
* card. This build pauses only the page's own body at that exact URL (payment-core
|
|
594
|
+
* processors/frisbii.js); any other body on it, one that keeps the card on file included, fails in the
|
|
595
|
+
* browser before anything is created. The page's two other card paths, a stored card's token
|
|
596
|
+
* (/v1/token/cof/) and Click to Pay (/v1/token/create_click_to_pay), are held and refused whatever
|
|
597
|
+
* their body, so neither leaves the browser either. The request names the amount only when
|
|
598
|
+
* the session carries 3-D Secure data, so the agent's pair is required, above zero
|
|
599
|
+
* (agent-amount-processors.json), and the API refuses it when it differs from the page's own. The
|
|
600
|
+
* cardholder's device sends the card to Frisbii
|
|
601
|
+
* itself and the page gets back Frisbii's card token or 3-D Secure step alone, rebuilt by payment-core's
|
|
602
|
+
* judge. Served on the same terms: only to a build that lists it, for a company Agentcard turned Frisbii
|
|
603
|
+
* on for.
|
|
604
|
+
*
|
|
498
605
|
* `aps_custom_merchant_page`: Amazon Payment Services' custom merchant page, whose own card form
|
|
499
606
|
* posts top-level to /FortAPI/paymentPage on checkout.payfort.com (sbcheckout.payfort.com on APS's
|
|
500
607
|
* sandbox) with service_command=TOKENIZATION. This build pauses that navigation; the cardholder's
|
|
@@ -515,6 +622,14 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
|
|
|
515
622
|
* the cardholder approves, and the API holds its amount to the body's. Served on the same terms: only to a build
|
|
516
623
|
* that lists it, for a company Agentcard turned Nexi Checkout on for.
|
|
517
624
|
*
|
|
625
|
+
* `oceanpayment_hosted_payment_page`: the card post of Oceanpayment's hosted payment page (secure.oceanpayment.com, where
|
|
626
|
+
* Oceanpayment's Shopify redirect app sends the buyer), a native form post to /gateway/sendPay;jsessionid=<session> with
|
|
627
|
+
* the card in clear (test-secure.oceanpayment.com for a test row). This build pauses only that post, exactly as the page's
|
|
628
|
+
* form action writes it, and aborts every other post to Oceanpayment's two hosts (oceanpayment.ts), so the agent's
|
|
629
|
+
* placeholder never reaches Oceanpayment; the cardholder's device writes the real card into the paused post and sends it
|
|
630
|
+
* through the checkout relay. The post names no amount, so the agent's pair, above zero, is the one the cardholder
|
|
631
|
+
* approves. Served on the same terms: only to a build that lists it, for a company Agentcard turned Oceanpayment on for.
|
|
632
|
+
*
|
|
518
633
|
* `barion_hosted_payment_page`: Barion's Smart Gateway, whose card-free link
|
|
519
634
|
* (secure.barion.com/Pay?Id=<payment id>, a plain GET the merchant's redirect or script sends
|
|
520
635
|
* the buyer to) this build pauses for the cardholder's device by its exact link (hosted-entry.ts),
|
|
@@ -522,6 +637,27 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
|
|
|
522
637
|
* send (pre-claim.ts, barion.ts). Served on the same terms as Przelewy24's: only to a build that
|
|
523
638
|
* lists it, for a company Agentcard turned Barion on for.
|
|
524
639
|
*
|
|
640
|
+
* `payplug_hosted_payment_page`: PayPlug's payment page, whose card-free link
|
|
641
|
+
* (secure.payplug.com/pay/<payment id>, or /pay/test/<payment id>, a plain GET the merchant's
|
|
642
|
+
* redirect or script sends the buyer to) this build pauses for the cardholder's device by its exact
|
|
643
|
+
* link (hosted-entry.ts), and whose card post this build never lets the agent's browser send
|
|
644
|
+
* (pre-claim.ts, payplug.ts). The link names no amount, so authorize() asks for the agent's, above
|
|
645
|
+
* zero. Served only to a build that lists it, and the API turns PayPlug on for Agentcard's own
|
|
646
|
+
* company alone.
|
|
647
|
+
*
|
|
648
|
+
* `cardlink_vpos_hosted_payment_page`: Cardlink's VPOS, the payment page of Eurobank, Cardlink and Nexi
|
|
649
|
+
* Greece, whose card-free signed entry (a form post to /vpos/shophandlermpi on one of the three hosts) this build
|
|
650
|
+
* pauses for the cardholder's device, and whose card page's post and card check on the same URL this build never
|
|
651
|
+
* lets the agent's browser send (pre-claim.ts, cardlink-vpos.ts). Served only to a build that lists it, and the API
|
|
652
|
+
* turns the lane on for Agentcard's own company alone.
|
|
653
|
+
*
|
|
654
|
+
* `eghl_hosted_payment_page`: eGHL's payment page (ADAPTIS, formerly eGHL, on securepay.e-ghl.com), whose card-free
|
|
655
|
+
* entry (a GET of /IPG/payment.aspx with the payment in its query, which the merchant's checkout or eGHL's Shopify app
|
|
656
|
+
* sends the buyer to) this build pauses for the cardholder's device by its exact URL (hosted-entry.ts), and whose card
|
|
657
|
+
* page's post to the same address this build never lets the agent's browser send (pre-claim.ts, eghl.ts). The entry
|
|
658
|
+
* names its amount, and the API holds the agent's pair to it exactly. Served only to a build that lists it, and the API
|
|
659
|
+
* turns the lane on for Agentcard's own company alone.
|
|
660
|
+
*
|
|
525
661
|
* `paymob_checkout_pay`: the card payment Paymob's own checkout sends, from Unified Checkout
|
|
526
662
|
* (<country>.checkout.paymob.com) or from the Pixel card frame a merchant's page embeds, as JSON to
|
|
527
663
|
* https://<accept|uae|ksa|oman>.paymob.com/api/acceptance/payments/pay. This build pauses only the
|
|
@@ -651,6 +787,8 @@ export const SUPPORTED_CAPABILITIES = DECLARED_CAPABILITIES;
|
|
|
651
787
|
const REVOLUT_PSP = 'revolut';
|
|
652
788
|
/** Nexi Checkout's card payment names the payment's amount in minor units but not its currency, as Revolut's does. */
|
|
653
789
|
const NEXI_CHECKOUT_PSP = 'nexi_checkout';
|
|
790
|
+
/** iyzico's Checkout Form payment names its price but not its currency, which the form's token holds. */
|
|
791
|
+
const IYZICO_PSP = 'iyzico';
|
|
654
792
|
const AGENT_AMOUNT_PROCESSORS = AGENT_AMOUNT.processors;
|
|
655
793
|
/** The ones a zero amount is refused for, like a missing one (authorize()). */
|
|
656
794
|
const ABOVE_ZERO_PROCESSORS = AGENT_AMOUNT.aboveZero;
|
|
@@ -937,11 +1075,12 @@ export class AmountRequiredError extends Error {
|
|
|
937
1075
|
constructor(psp) {
|
|
938
1076
|
const processor = PROCESSOR_NAMES[psp] ?? psp;
|
|
939
1077
|
// A Payrexx card request names the frame's total, but the merchant's page charges its own total after the token.
|
|
940
|
-
const lacks = psp === REVOLUT_PSP || psp === NEXI_CHECKOUT_PSP ? 'names its amount but not its currency'
|
|
1078
|
+
const lacks = psp === REVOLUT_PSP || psp === NEXI_CHECKOUT_PSP || psp === IYZICO_PSP ? 'names its amount but not its currency'
|
|
941
1079
|
: psp === PAYREXX_PSP ? "only mints a token the merchant's page charges later"
|
|
942
1080
|
: psp === TAP_PAYMENTS_PSP ? 'does not decide the amount charged' : 'names no amount';
|
|
943
|
-
// "An Omise payment request", "A Clover payment request": the article follows the
|
|
944
|
-
|
|
1081
|
+
// "An Omise payment request", "A Clover payment request", "An iyzico payment request": the article follows the
|
|
1082
|
+
// name's first letter, in either case, and "A USAePay payment request" its first sound, a consonant's.
|
|
1083
|
+
const article = /^(?:[AEIO]|U(?!SA))/i.test(processor) ? 'An' : 'A';
|
|
945
1084
|
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.`);
|
|
946
1085
|
this.psp = psp;
|
|
947
1086
|
this.name = 'AmountRequiredError';
|
|
@@ -1365,11 +1504,11 @@ export class CheckoutAuthError extends Error {
|
|
|
1365
1504
|
}
|
|
1366
1505
|
}
|
|
1367
1506
|
/**
|
|
1368
|
-
* A failure as the
|
|
1369
|
-
* API's code
|
|
1507
|
+
* A failure as the modules report it: an `AgentcardError` carrying the
|
|
1508
|
+
* API's code or `oauth_failed`;
|
|
1370
1509
|
* a failure to reach Agentcard at all passes through as it came.
|
|
1371
1510
|
*/
|
|
1372
|
-
function
|
|
1511
|
+
function moduleError(err) {
|
|
1373
1512
|
if (err instanceof CheckoutAuthError)
|
|
1374
1513
|
return new AgentcardError('oauth_failed', `Could not get a token for the client (${err.status}${err.code ? ', ' + err.code : ''}).`, err.status);
|
|
1375
1514
|
if (err instanceof CheckoutApiError) {
|
|
@@ -1951,6 +2090,27 @@ const HIPAY_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandb
|
|
|
1951
2090
|
*/
|
|
1952
2091
|
const OPENPAY_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'sandbox_host_on_live_row',
|
|
1953
2092
|
'merchant_profile_mismatch', 'amount_required'];
|
|
2093
|
+
/**
|
|
2094
|
+
* The API's refusals of a VTEX Payments preparation, before any preparation exists, that prepareCheckout passes on by
|
|
2095
|
+
* name: the company is not turned on for VTEX (`processor_unavailable`), the client is in test mode, which VTEX has no
|
|
2096
|
+
* host for (`live_host_on_sandbox_row`), the merchant is not the name the reviewed pin for the checkout's page carries
|
|
2097
|
+
* (`merchant_profile_mismatch`), the currency is not the store's (`unsupported_currency`), and the price is zero
|
|
2098
|
+
* (`amount_required`). A page no reviewed pin lists answers `unsupported_checkout`, which PREPARATION_REFUSALS names for
|
|
2099
|
+
* every checkout. The API holds the pins, so a store a later release reviews needs no new SDK. Only for a VTEX checkout.
|
|
2100
|
+
*/
|
|
2101
|
+
const VTEX_PAYMENTS_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'merchant_profile_mismatch',
|
|
2102
|
+
'unsupported_currency', 'amount_required'];
|
|
2103
|
+
/**
|
|
2104
|
+
* The API's refusals of a ProcessOut preparation, before any preparation exists, that prepareCheckout passes on by
|
|
2105
|
+
* name: the company is not turned on for ProcessOut (`processor_unavailable`), the environment is not the caller's key
|
|
2106
|
+
* mode's (`processout_environment_mismatch`, since a live and a sandbox project share one host), a live checkout's
|
|
2107
|
+
* merchant is not the name the reviewed pin for its origin carries (`merchant_profile_mismatch`), and the price is zero
|
|
2108
|
+
* (`amount_required`), since the card request names no amount. An origin no reviewed pin lists answers
|
|
2109
|
+
* `unsupported_checkout`, which PREPARATION_REFUSALS names for every checkout. The API holds the pins, not this build,
|
|
2110
|
+
* so a store a later release reviews needs no new SDK. Only for a ProcessOut checkout.
|
|
2111
|
+
*/
|
|
2112
|
+
const PROCESSOUT_PREPARATION_REFUSALS = ['processor_unavailable', 'processout_environment_mismatch', 'merchant_profile_mismatch',
|
|
2113
|
+
'amount_required'];
|
|
1954
2114
|
/**
|
|
1955
2115
|
* The reason the cardholder's device declines a bound prepared row with, per processor, when its own
|
|
1956
2116
|
* send boundary passed before the card left it (the Vault's sendWindow, 12 seconds after it last saw
|
|
@@ -1959,7 +2119,9 @@ const OPENPAY_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_san
|
|
|
1959
2119
|
* closes on (amadeus.ts), which can close later, so authorize() ends the attempt the way that window
|
|
1960
2120
|
* does, as a CheckoutPreparationError with that reason, and never as the cardholder's "no". A prepared
|
|
1961
2121
|
* HiPay token create too, and a prepared Openpay token create, whose device sends the card at most 25
|
|
1962
|
-
* seconds after it last saw the checkout unbound, inside the adapters' 30.
|
|
2122
|
+
* seconds after it last saw the checkout unbound, inside the adapters' 30. And a prepared VTEX card post,
|
|
2123
|
+
* whose device sends the card at most 6 seconds after it last saw the checkout unbound, inside the adapters' 8, and a
|
|
2124
|
+
* prepared ProcessOut card request, whose device keeps HiPay's 10 seconds, inside the adapters' 12.
|
|
1963
2125
|
*/
|
|
1964
2126
|
const PREPARED_SEND_WINDOW_DECLINES = Object.freeze({
|
|
1965
2127
|
amadeus_checkout: 'amadeus_send_window_closed',
|
|
@@ -1967,7 +2129,17 @@ const PREPARED_SEND_WINDOW_DECLINES = Object.freeze({
|
|
|
1967
2129
|
[WIX_PAYMENTS_PSP]: 'wix_payments_send_window_closed',
|
|
1968
2130
|
[HIPAY_PSP]: 'hipay_send_window_closed',
|
|
1969
2131
|
[OPENPAY_PSP]: 'openpay_send_window_closed',
|
|
2132
|
+
[VTEX_PAYMENTS_PSP]: 'vtex_payments_send_window_closed',
|
|
2133
|
+
[PROCESSOUT_PSP]: 'processout_send_window_closed',
|
|
1970
2134
|
});
|
|
2135
|
+
/**
|
|
2136
|
+
* The reason the API declines a bound VTEX card post with when its send-by passed before the card left the cardholder's
|
|
2137
|
+
* device (backend services/commerce/checkout-vtex-payments.ts): Ollie's checkout gives the post 12 seconds, and the API
|
|
2138
|
+
* counts 8 of them from the pause the create named. A known non-payment, as a closed send window is.
|
|
2139
|
+
*/
|
|
2140
|
+
const VTEX_PAYMENTS_POST_TIMED_OUT = 'vtex_payments_post_timed_out';
|
|
2141
|
+
/** A pause the create can count `paused_ms` from: a whole, non-negative epoch millisecond on this machine's clock. */
|
|
2142
|
+
const validPausedAt = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
1971
2143
|
/**
|
|
1972
2144
|
* Refusals a CyberSource Flex preparation that tokenizes before Pay answers (4xx) before any
|
|
1973
2145
|
* preparation exists, surfaced as the CheckoutPreparationError reason: the processor is off for
|
|
@@ -2050,22 +2222,22 @@ export class VaultClient {
|
|
|
2050
2222
|
* false: a live client.
|
|
2051
2223
|
*/
|
|
2052
2224
|
get sandbox() { return this.sandboxNamed || this.sandboxLearned === true; }
|
|
2053
|
-
/** What every module shares: the token, the HTTP path
|
|
2225
|
+
/** What every module shares: the token, the HTTP path and the poll settings. */
|
|
2054
2226
|
transport;
|
|
2055
|
-
/**
|
|
2056
|
-
* Approve purchases by voice inside your own product: connect a person once,
|
|
2057
|
-
* create the purchase, ask for the key word, read the result. Same
|
|
2058
|
-
* credentials, same token as the checkout calls. Approves on Agentcard's
|
|
2059
|
-
* page unless `voice: { approval: 'in_app' }` was named at construction.
|
|
2060
|
-
*/
|
|
2061
|
-
voice;
|
|
2062
2227
|
/**
|
|
2063
2228
|
* The Purchase API from the same client: ask for a cart on behalf of a
|
|
2064
2229
|
* person you connected, confirm it by its hash, and name an approval the
|
|
2065
|
-
* person already gave
|
|
2066
|
-
* nothing a second time. Same credentials, same token as the
|
|
2230
|
+
* person already gave so the API places against it and asks for
|
|
2231
|
+
* nothing a second time. Same credentials, same token as the checkout calls.
|
|
2067
2232
|
*/
|
|
2068
2233
|
buy;
|
|
2234
|
+
/**
|
|
2235
|
+
* Purchases your server creates for a person, approved on Agentcard's page:
|
|
2236
|
+
* create one (`request: 'sandbox'` under a sandbox client), send the person
|
|
2237
|
+
* its `approval_url`, then read the decision or wait for it. Same
|
|
2238
|
+
* credentials, same token as the checkout calls.
|
|
2239
|
+
*/
|
|
2240
|
+
approvals;
|
|
2069
2241
|
/**
|
|
2070
2242
|
* The request builders: the processor requests a purchase pauses on, the
|
|
2071
2243
|
* shipped ones and yours (`requests.register`, `requests.build`).
|
|
@@ -2090,7 +2262,7 @@ export class VaultClient {
|
|
|
2090
2262
|
});
|
|
2091
2263
|
}
|
|
2092
2264
|
catch (err) {
|
|
2093
|
-
throw
|
|
2265
|
+
throw moduleError(err);
|
|
2094
2266
|
}
|
|
2095
2267
|
};
|
|
2096
2268
|
this.transport = {
|
|
@@ -2099,18 +2271,14 @@ export class VaultClient {
|
|
|
2099
2271
|
return await this.accessToken();
|
|
2100
2272
|
}
|
|
2101
2273
|
catch (err) {
|
|
2102
|
-
throw
|
|
2274
|
+
throw moduleError(err);
|
|
2103
2275
|
}
|
|
2104
2276
|
},
|
|
2105
2277
|
request,
|
|
2106
2278
|
pollIntervalMs: this.pollIntervalMs,
|
|
2107
|
-
pollTimeoutMs: opts.voice?.pollTimeoutMs,
|
|
2108
|
-
backoffMs: opts.voice?.backoffMs,
|
|
2109
|
-
wait: opts.voice?.wait,
|
|
2110
|
-
languages: new Languages(request),
|
|
2111
2279
|
};
|
|
2112
|
-
this.voice = new VoiceModule(this.transport, opts.voice, () => this.sandbox);
|
|
2113
2280
|
this.buy = new BuyModule(this.transport);
|
|
2281
|
+
this.approvals = new ApprovalsModule(this.transport, () => this.sandbox);
|
|
2114
2282
|
const declared = (opts.sandboxMerchants ?? []).map(declareSandboxMerchant);
|
|
2115
2283
|
if (new Set(declared.map((profile) => profile.id)).size !== declared.length) {
|
|
2116
2284
|
throw new TypeError('sandboxMerchants: declare each profile at most once.');
|
|
@@ -2660,6 +2828,8 @@ export class VaultClient {
|
|
|
2660
2828
|
|| (input.psp === PAYGENT_PSP && PAYGENT_PREPARATION_REFUSALS.includes(error.code))
|
|
2661
2829
|
|| (input.psp === HIPAY_PSP && HIPAY_PREPARATION_REFUSALS.includes(error.code))
|
|
2662
2830
|
|| (input.psp === OPENPAY_PSP && OPENPAY_PREPARATION_REFUSALS.includes(error.code))
|
|
2831
|
+
|| (input.psp === VTEX_PAYMENTS_PSP && VTEX_PAYMENTS_PREPARATION_REFUSALS.includes(error.code))
|
|
2832
|
+
|| (input.psp === PROCESSOUT_PSP && PROCESSOUT_PREPARATION_REFUSALS.includes(error.code))
|
|
2663
2833
|
|| (!!flexContext && CYBERSOURCE_FLEX_PREPARATION_REFUSALS.includes(error.code))))
|
|
2664
2834
|
|| (input.psp === 'fiserv' && error.status === 502 && error.code === 'cse_key_unavailable')))
|
|
2665
2835
|
throw fail(error.code);
|
|
@@ -2778,6 +2948,11 @@ export class VaultClient {
|
|
|
2778
2948
|
if (preparation) {
|
|
2779
2949
|
if (!this.preparations.has(preparation) || this.usedPreparations.has(preparation))
|
|
2780
2950
|
throw new CheckoutPreparationError(preparation.id ?? null, 'already_used_or_foreign');
|
|
2951
|
+
// VTEX Payments: the API counts the row's send-by from the pause the create names (`paused_ms`) and refuses a create
|
|
2952
|
+
// that names none, so a caller without `pausedAt` is refused here, before the preparation is spent. The adapters fill it.
|
|
2953
|
+
if (preparation.psp === VTEX_PAYMENTS_PSP && !validPausedAt(input.pausedAt)) {
|
|
2954
|
+
throw new Error('A VTEX Payments card post needs pausedAt: when the card request paused, in epoch milliseconds. Nothing was created.');
|
|
2955
|
+
}
|
|
2781
2956
|
// Consume locally before any await, including OAuth, and never recycle it.
|
|
2782
2957
|
this.usedPreparations.add(preparation);
|
|
2783
2958
|
if (Date.parse(preparation.expiresAt) <= Date.now())
|
|
@@ -3065,8 +3240,14 @@ export class VaultClient {
|
|
|
3065
3240
|
...(payrailsPayment ? { holder_reference: sidecar.holder_reference } : {}) } } : {}),
|
|
3066
3241
|
},
|
|
3067
3242
|
};
|
|
3243
|
+
// VTEX Payments: how long ago the post paused, on this machine's clock as each create request leaves, after any wait
|
|
3244
|
+
// for a token or a refresh after a 401, so the API counts the row's send-by from Ollie's own 12 seconds on its own
|
|
3245
|
+
// clock, whatever the offset between the two machines.
|
|
3246
|
+
const pausedAt = input.pausedAt;
|
|
3247
|
+
const atSend = rec?.psp === VTEX_PAYMENTS_PSP && validPausedAt(pausedAt)
|
|
3248
|
+
? () => ({ paused_ms: Math.max(0, Date.now() - pausedAt) }) : undefined;
|
|
3068
3249
|
try {
|
|
3069
|
-
created = await this.createAuthorization(payload, input.currency, AbortSignal.any([operationSignal, AbortSignal.timeout(30_000)]), input.merchantSignal);
|
|
3250
|
+
created = await this.createAuthorization(payload, input.currency, AbortSignal.any([operationSignal, AbortSignal.timeout(30_000)]), input.merchantSignal, atSend);
|
|
3070
3251
|
}
|
|
3071
3252
|
catch (error) {
|
|
3072
3253
|
if (error instanceof PaymentOutcomeUnknownError) {
|
|
@@ -3095,6 +3276,14 @@ export class VaultClient {
|
|
|
3095
3276
|
await this.cancelPreparation(preparation.id).catch(() => { });
|
|
3096
3277
|
throw new CheckoutPreparationError(preparation?.id ?? null, error.code);
|
|
3097
3278
|
}
|
|
3279
|
+
// A VTEX create the API refused past its send-by (409 vtex_payments_post_timed_out), before any row, bind or
|
|
3280
|
+
// notification: the card never left the cardholder's device, and Ollie stops waiting for the post before one could.
|
|
3281
|
+
// The same known non-payment as the polled decline below. The preparation, which no later post of this attempt can
|
|
3282
|
+
// use, is retired so the cardholder's page stops waiting for it.
|
|
3283
|
+
if (preparation?.psp === VTEX_PAYMENTS_PSP && error instanceof CheckoutApiError && error.status === 409 && error.code === VTEX_PAYMENTS_POST_TIMED_OUT) {
|
|
3284
|
+
await this.cancelPreparation(preparation.id).catch(() => { });
|
|
3285
|
+
throw new CheckoutPreparationError(preparation.id, error.code);
|
|
3286
|
+
}
|
|
3098
3287
|
// A missing answer or generic 5xx can hide a committed row and a delivered approval link.
|
|
3099
3288
|
// Only the documented pre-create read-back errors prove it is safe to retry.
|
|
3100
3289
|
const safeReadFailure = error instanceof CheckoutApiError
|
|
@@ -3127,11 +3316,17 @@ export class VaultClient {
|
|
|
3127
3316
|
// A prepared Openpay token create too, for 40 seconds: openpay.js gives it 40 seconds from Pay, so the token
|
|
3128
3317
|
// reaches the page within half a second of the device's answer, and the API names 500 ms as such a row's poll
|
|
3129
3318
|
// interval.
|
|
3319
|
+
// A prepared VTEX card post too, for 12 seconds: Ollie's container aborts the post 12 seconds after it starts it,
|
|
3320
|
+
// so VTEX's answer reaches the container within half a second of the device's report, and the API names 500 ms as
|
|
3321
|
+
// such a row's poll interval.
|
|
3322
|
+
// A prepared ProcessOut card request too, for 20 seconds: ProcessOut.js gives its card frame 20 seconds from the
|
|
3323
|
+
// shop's press, and the API names 500 ms as such a row's poll interval.
|
|
3130
3324
|
const preparedFastPollUntil = preparation?.psp === 'mercado_pago' ? Date.now() + 10_000
|
|
3131
|
-
: preparation?.psp === PADDLE_VGS_PSP || preparation?.psp === HIPAY_PSP ? Date.now() + 20_000
|
|
3325
|
+
: preparation?.psp === PADDLE_VGS_PSP || preparation?.psp === HIPAY_PSP || preparation?.psp === PROCESSOUT_PSP ? Date.now() + 20_000
|
|
3132
3326
|
: preparation?.psp === OPENPAY_PSP ? Date.now() + 40_000
|
|
3133
|
-
: preparation?.psp ===
|
|
3134
|
-
: preparation?.psp ===
|
|
3327
|
+
: preparation?.psp === VTEX_PAYMENTS_PSP ? Date.now() + 12_000
|
|
3328
|
+
: preparation?.psp === EPROTECT_PSP ? Date.now() + 15_000
|
|
3329
|
+
: preparation?.psp === SECURE_ACCEPTANCE_PSP ? Date.now() + SECURE_ACCEPTANCE_FAST_POLL_MS : 0;
|
|
3135
3330
|
// A preparation that tokenized before Pay: the cardholder's device already holds CyberSource's
|
|
3136
3331
|
// token and approves as soon as this bind reaches it, while the page may give createToken only
|
|
3137
3332
|
// seconds (Alaska Airlines' flight checkout gives it three). Poll every 100 ms for 10 seconds,
|
|
@@ -3529,6 +3724,19 @@ export class VaultClient {
|
|
|
3529
3724
|
response.headers = { 'content-type': 'application/json' };
|
|
3530
3725
|
response.body = judged.body;
|
|
3531
3726
|
}
|
|
3727
|
+
if (rec?.psp === PROCESSOUT_PSP) {
|
|
3728
|
+
// ProcessOut's answer enters the card frame only as payment-core rebuilds an approved token from the card facts
|
|
3729
|
+
// the page reads (processout.js processoutAnswerOutcome: HTTP 200 or 201, success and a card token in shape),
|
|
3730
|
+
// under its approved status and a JSON content type. The frame reads the body of any answer, so anything else, a
|
|
3731
|
+
// refusal ProcessOut answers inside a 2xx or a pending answer included, is never handed over as an approval;
|
|
3732
|
+
// the card may have left the device, so the outcome is unknown rather than declined.
|
|
3733
|
+
const judged = processoutAnswerOutcome(response.status, response.body, null, null);
|
|
3734
|
+
if (judged.outcome !== 'approved' || typeof judged.body !== 'string')
|
|
3735
|
+
throw new PaymentOutcomeUnknownError(authorizationId, 'processout_token_unconfirmed');
|
|
3736
|
+
response.status = response.status === 201 ? 201 : 200;
|
|
3737
|
+
response.headers = { 'content-type': 'application/json' };
|
|
3738
|
+
response.body = judged.body;
|
|
3739
|
+
}
|
|
3532
3740
|
if (rec?.psp === PRIMER_PSP) {
|
|
3533
3741
|
// Primer's answer enters the page only as one single-use card token rebuilt from
|
|
3534
3742
|
// payment-core's allow-list, under a JSON content type (primer.ts primerTokenAnswer).
|
|
@@ -3629,6 +3837,10 @@ export class VaultClient {
|
|
|
3629
3837
|
if (preparation && typeof s.reason === 'string' && PREPARED_SEND_WINDOW_DECLINES[preparation.psp] === s.reason) {
|
|
3630
3838
|
throw new CheckoutPreparationError(preparation.id, s.reason);
|
|
3631
3839
|
}
|
|
3840
|
+
// The API declined a bound VTEX post past its send-by, counted from the pause this create named: the card
|
|
3841
|
+
// never left the cardholder's device, so the checkout failed unsent the same way.
|
|
3842
|
+
if (preparation?.psp === VTEX_PAYMENTS_PSP && s.reason === VTEX_PAYMENTS_POST_TIMED_OUT)
|
|
3843
|
+
throw new CheckoutPreparationError(preparation.id, s.reason);
|
|
3632
3844
|
throw new ApprovalDeclinedError(s.reason ?? 'no reason given');
|
|
3633
3845
|
}
|
|
3634
3846
|
if (s.status === 'cancelled') {
|
|
@@ -3766,9 +3978,9 @@ export class VaultClient {
|
|
|
3766
3978
|
* exactly the approved payment method (the page's own, or written by the
|
|
3767
3979
|
* adapters in place of a card the confirm carries again).
|
|
3768
3980
|
* The API allows a card save for any company, for an approval given on the
|
|
3769
|
-
* Vault page,
|
|
3770
|
-
*
|
|
3771
|
-
*
|
|
3981
|
+
* Vault page, with no amount, and a SetupIntent Stripe reports still
|
|
3982
|
+
* confirmable on the same Stripe account; any other answer rejects with a
|
|
3983
|
+
* CheckoutApiError whose code says why (for example `amount_mismatch`, or
|
|
3772
3984
|
* `continuation_invalid` for a confirm that is not a card-free SetupIntent
|
|
3773
3985
|
* confirm). Nothing
|
|
3774
3986
|
* is charged: the adapters continue the page's request once this resolves.
|
|
@@ -3973,13 +4185,17 @@ export class VaultClient {
|
|
|
3973
4185
|
* (Stripe did not answer the read-back) is retried on a short backoff
|
|
3974
4186
|
* instead of being left to the page's own retry loop, and a 409
|
|
3975
4187
|
* `amount_mismatch` becomes an AmountMismatchError at stage 'create' so the
|
|
3976
|
-
* adapters treat it as an answered request, not a dead page.
|
|
4188
|
+
* adapters treat it as an answered request, not a dead page. `atSend` adds
|
|
4189
|
+
* fields counted as each request leaves, after any token wait, 401 refresh or
|
|
4190
|
+
* backoff, so a field that measures time (VTEX's paused_ms) measures it then.
|
|
3977
4191
|
*/
|
|
3978
|
-
async createAuthorization(payload, currency, signal, stopRetries) {
|
|
4192
|
+
async createAuthorization(payload, currency, signal, stopRetries, atSend) {
|
|
3979
4193
|
for (let attempt = 0;; attempt++) {
|
|
3980
4194
|
if (stopRetries?.aborted)
|
|
3981
4195
|
throw new PaymentOutcomeUnknownError(null, 'merchant_request_aborted');
|
|
3982
4196
|
try {
|
|
4197
|
+
if (atSend)
|
|
4198
|
+
return await this.postAtSend('/v2/checkout/authorizations', () => ({ ...payload, ...atSend() }), signal, stopRetries);
|
|
3983
4199
|
return await this.post('/v2/checkout/authorizations', payload, signal, stopRetries);
|
|
3984
4200
|
}
|
|
3985
4201
|
catch (err) {
|
|
@@ -4065,8 +4281,10 @@ export class VaultClient {
|
|
|
4065
4281
|
* Only fetch and a successful response's body read belong to that retry:
|
|
4066
4282
|
* OAuth, HTTP errors, invalid JSON and every mutation remain outside it.
|
|
4067
4283
|
* The existing one-time 401 refresh carries the remaining read budget.
|
|
4284
|
+
* `bodyAt`, when given, builds the body as each request leaves, after the
|
|
4285
|
+
* token and on the 401 refresh's resend, in place of `init.body`.
|
|
4068
4286
|
*/
|
|
4069
|
-
async call(path, init = {}, retried = false, stopNewRequests, preparationReadAttempt) {
|
|
4287
|
+
async call(path, init = {}, retried = false, stopNewRequests, preparationReadAttempt, bodyAt) {
|
|
4070
4288
|
const signal = init.signal ?? AbortSignal.timeout(30_000);
|
|
4071
4289
|
const token = await withSignal(this.accessToken(), signal);
|
|
4072
4290
|
// Auth can outlive the merchant request. Stop a create that has not left
|
|
@@ -4081,6 +4299,7 @@ export class VaultClient {
|
|
|
4081
4299
|
try {
|
|
4082
4300
|
r = await withSignal(this.fetch(`${this.baseUrl}${path}`, {
|
|
4083
4301
|
...init,
|
|
4302
|
+
...(bodyAt ? { body: bodyAt() } : {}),
|
|
4084
4303
|
signal,
|
|
4085
4304
|
headers: { ...(init.headers ?? {}), authorization: `Bearer ${token}`, 'content-type': 'application/json' },
|
|
4086
4305
|
}), signal);
|
|
@@ -4100,7 +4319,7 @@ export class VaultClient {
|
|
|
4100
4319
|
if (stopNewRequests?.aborted)
|
|
4101
4320
|
throw new PaymentOutcomeUnknownError(null, 'merchant_request_aborted');
|
|
4102
4321
|
await withSignal(this.accessToken(true), signal);
|
|
4103
|
-
return this.call(path, { ...init, signal }, true, stopNewRequests, preparationReadAttempt);
|
|
4322
|
+
return this.call(path, { ...init, signal }, true, stopNewRequests, preparationReadAttempt, bodyAt);
|
|
4104
4323
|
}
|
|
4105
4324
|
throw new CheckoutApiError(r.status, path, await withSignal(r.text(), signal));
|
|
4106
4325
|
}
|
|
@@ -4108,6 +4327,10 @@ export class VaultClient {
|
|
|
4108
4327
|
post(path, body, signal, stopNewRequests) {
|
|
4109
4328
|
return this.call(path, { method: 'POST', body: JSON.stringify(body), signal }, false, stopNewRequests);
|
|
4110
4329
|
}
|
|
4330
|
+
/** A POST whose body is built as each request leaves (call's `bodyAt`), for a body that counts time. */
|
|
4331
|
+
postAtSend(path, body, signal, stopNewRequests) {
|
|
4332
|
+
return this.call(path, { method: 'POST', signal }, false, stopNewRequests, undefined, () => JSON.stringify(body()));
|
|
4333
|
+
}
|
|
4111
4334
|
get(path, signal) {
|
|
4112
4335
|
return this.call(path, { signal });
|
|
4113
4336
|
}
|
|
@@ -4209,7 +4432,9 @@ function cardEndpointReplay(s, authorizationId, profile, metadata) {
|
|
|
4209
4432
|
const answer = named.answer;
|
|
4210
4433
|
if (!isRecord(answer) || !Number.isInteger(answer.status) || answer.status < 100 || answer.status > 599)
|
|
4211
4434
|
throw unknown('merchant_answer_malformed');
|
|
4212
|
-
const
|
|
4435
|
+
const approved = Number.isSafeInteger(s.amount) && s.amount > 0 && typeof s.currency === 'string' && /^[a-z]{3}$/i.test(s.currency)
|
|
4436
|
+
? { approved: { amount: s.amount, currency: s.currency.toLowerCase() } } : {};
|
|
4437
|
+
const base = { mode: CARD_ENDPOINT_MODE, authorizationId, profile, status: answer.status, ...approved, ...metadata };
|
|
4213
4438
|
if (answer.withheld !== undefined) {
|
|
4214
4439
|
if (answer.withheld !== 'card_in_response' && answer.withheld !== 'unreadable_in_response' && answer.withheld !== 'refused_step_in_response') {
|
|
4215
4440
|
throw unknown('merchant_answer_malformed');
|
|
@@ -4341,6 +4566,22 @@ function jsonPathOf(value) {
|
|
|
4341
4566
|
|| (typeof step === 'number' && Number.isSafeInteger(step) && step >= 0));
|
|
4342
4567
|
return ok ? [...value] : null;
|
|
4343
4568
|
}
|
|
4569
|
+
/** Whether a served step is the step into JSON text, exactly `{text: 'json'}`. */
|
|
4570
|
+
const isTextStep = (step) => isRecord(step) && Object.keys(step).length === 1 && step.text === 'json';
|
|
4571
|
+
/**
|
|
4572
|
+
* A served holder path: a JSON path that may also step into a string holding JSON text, at most twice, never first
|
|
4573
|
+
* or last and never twice in a row (payment-core json-path.js isProfilePath); null for anything else. The substitution
|
|
4574
|
+
* itself checks it against this build's own profile.
|
|
4575
|
+
*/
|
|
4576
|
+
function holderPathOf(value) {
|
|
4577
|
+
if (!Array.isArray(value) || value.length > 16)
|
|
4578
|
+
return null;
|
|
4579
|
+
const texts = value.map((step, i) => (isTextStep(step) ? i : -1)).filter((i) => i !== -1);
|
|
4580
|
+
if (texts.length > 2 || texts.some((i) => i === 0 || i === value.length - 1 || texts.includes(i - 1)))
|
|
4581
|
+
return null;
|
|
4582
|
+
const plain = jsonPathOf(value.filter((step) => !isTextStep(step)));
|
|
4583
|
+
return plain ? value.map((step) => (isTextStep(step) ? { text: 'json' } : step)) : null;
|
|
4584
|
+
}
|
|
4344
4585
|
/** Served card facts: a list of {path, value}; null when anything in it is not one. */
|
|
4345
4586
|
function factsOf(value) {
|
|
4346
4587
|
if (!Array.isArray(value))
|
|
@@ -4386,7 +4627,7 @@ function merchantHostedApproval(s, authorizationId, profile) {
|
|
|
4386
4627
|
if (!isRecord(served) || served.profile !== profile.id || typeof served.body_sha256 !== 'string'
|
|
4387
4628
|
|| !/^[0-9a-f]{64}$/.test(served.body_sha256))
|
|
4388
4629
|
throw malformed();
|
|
4389
|
-
const path =
|
|
4630
|
+
const path = holderPathOf(sub.path);
|
|
4390
4631
|
const fields = isRecord(sub.fields) && Object.keys(sub.fields).length > 0
|
|
4391
4632
|
&& Object.values(sub.fields).every((value) => typeof value === 'string' && value.length > 0) ? { ...sub.fields } : null;
|
|
4392
4633
|
const remove = sub.remove === undefined ? [] : Array.isArray(sub.remove) && sub.remove.every((key) => typeof key === 'string' && key.length > 0)
|
|
@@ -14,11 +14,11 @@ declare function isCreditguardPpsDocument(url: any): boolean;
|
|
|
14
14
|
declare function creditguardNuveiRefusal(requestUrl: any, initiatorUrl: any): "creditguard_three_d_secure" | null;
|
|
15
15
|
declare function isCreditguardJweCompact(value: any): boolean;
|
|
16
16
|
declare function creditguardPpDomainHost(ppDomain: any): string | null;
|
|
17
|
-
declare function creditguardBodyRefusal(body: any, txId: any, options: any): "
|
|
17
|
+
declare function creditguardBodyRefusal(body: any, txId: any, options: any): "wallet" | "pp_domain_unbound" | "malformed_body" | "saved_card" | "otp" | "unexpected_field" | "keep_on_file" | "gift_card" | "dcc" | "installments" | "personal_id" | "txid_mismatch" | "pp_domain" | "jwe_fallback" | "jwe_unsupported" | "card_fields" | null;
|
|
18
18
|
declare function classifyCreditguardRequest(url: any, method: any, body: any, options: any): "card" | "unsupported" | null;
|
|
19
|
-
declare function creditguardProcessRefusal(request: any, options: any): "not_process_request" | "headers" | "pp_domain_unbound" | "malformed_body" | "saved_card" | "otp" | "
|
|
19
|
+
declare function creditguardProcessRefusal(request: any, options: any): "wallet" | "not_process_request" | "headers" | "pp_domain_unbound" | "malformed_body" | "saved_card" | "otp" | "unexpected_field" | "keep_on_file" | "gift_card" | "dcc" | "installments" | "personal_id" | "txid_mismatch" | "pp_domain" | "jwe_fallback" | "jwe_unsupported" | "card_fields" | null;
|
|
20
20
|
declare function isCreditguardProcessRequest(request: any, options: any): boolean;
|
|
21
|
-
declare function creditguardReplayRefusal(request: any, options: any): "not_process_request" | "headers" | "pp_domain_unbound" | "malformed_body" | "saved_card" | "otp" | "
|
|
21
|
+
declare function creditguardReplayRefusal(request: any, options: any): "wallet" | "not_process_request" | "headers" | "pp_domain_unbound" | "malformed_body" | "saved_card" | "otp" | "unexpected_field" | "keep_on_file" | "gift_card" | "dcc" | "installments" | "personal_id" | "txid_mismatch" | "pp_domain" | "jwe_fallback" | "jwe_unsupported" | "card_fields" | null;
|
|
22
22
|
declare function creditguardRefusalMessage(reason: any): string;
|
|
23
23
|
declare function injectCreditguardCard(body: any, card: any, options: any): {
|
|
24
24
|
body: string;
|
|
@@ -141,7 +141,7 @@ declare function creditguardDeviceThreeDSOutcomeRequest(processUrl: any): {
|
|
|
141
141
|
};
|
|
142
142
|
body: string;
|
|
143
143
|
};
|
|
144
|
-
declare function creditguardDeviceStepRefusal(request: any, processUrl: any): "
|
|
144
|
+
declare function creditguardDeviceStepRefusal(request: any, processUrl: any): "wallet" | "headers" | "malformed_body" | "unexpected_field" | "gift_card" | "dcc" | "txid_mismatch" | "card_fields" | "not_companion_request" | null;
|
|
145
145
|
declare function creditguardCardinalUrl(url: any, purpose: any, environment: any): string | null;
|
|
146
146
|
declare function isCreditguardCardinalOrigin(origin: any): boolean;
|
|
147
147
|
declare function creditguardCardinalMessage(data: any): {
|