@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.
- package/CHANGELOG.md +34 -0
- package/PREFLIGHT.md +2 -0
- package/README.md +34 -4
- package/dist/aci.generated.js +3 -3
- package/dist/agent-amount.generated.d.ts +7 -0
- package/dist/agent-amount.generated.js +3 -0
- package/dist/amadeus.d.ts +5 -2
- package/dist/amadeus.js +18 -3
- package/dist/amazon-payment-services.d.ts +25 -0
- package/dist/amazon-payment-services.generated.d.ts +76 -0
- package/dist/amazon-payment-services.generated.js +883 -0
- package/dist/amazon-payment-services.js +53 -0
- package/dist/capabilities.generated.d.ts +2 -0
- package/dist/capabilities.generated.js +3 -0
- package/dist/card-fields.generated.js +1127 -25
- package/dist/cdp.js +116 -15
- package/dist/client.d.ts +107 -8
- package/dist/client.js +277 -128
- package/dist/hosted-entry.js +2 -1
- package/dist/hosted-form.d.ts +5 -0
- package/dist/hosted-form.js +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/openpay.generated.d.ts +67 -0
- package/dist/openpay.generated.js +446 -0
- package/dist/paddle-checkout-watch.d.ts +70 -0
- package/dist/paddle-checkout-watch.js +239 -0
- package/dist/paddle-tokenex.generated.d.ts +29 -1
- package/dist/paddle-tokenex.generated.js +175 -26
- package/dist/paytrail.d.ts +58 -0
- package/dist/paytrail.js +98 -0
- package/dist/pre-claim.d.ts +4 -0
- package/dist/pre-claim.js +46 -2
- package/dist/prepared-processor.d.ts +12 -4
- package/dist/prepared-processor.js +31 -5
- package/dist/primer.generated.js +1155 -53
- package/dist/registry.d.ts +4 -3
- package/dist/registry.js +12 -4
- package/dist/sbps.d.ts +57 -0
- package/dist/sbps.generated.d.ts +96 -0
- package/dist/sbps.generated.js +427 -0
- package/dist/sbps.js +90 -0
- package/dist/stripe-checkout.generated.js +458 -8
- package/dist/tap-payments.d.ts +43 -0
- package/dist/tap-payments.generated.d.ts +59 -0
- package/dist/tap-payments.generated.js +990 -0
- package/dist/tap-payments.js +72 -0
- package/dist/transbank.d.ts +33 -0
- package/dist/transbank.js +62 -0
- package/dist/url-mask-parts.d.ts +24 -0
- package/dist/url-mask-parts.js +22 -0
- package/dist/url-masks.generated.d.ts +3 -0
- package/dist/url-masks.generated.js +2 -0
- 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 {
|
|
5
|
-
import {
|
|
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 {
|
|
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 {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
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 {
|
|
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 {
|
|
38
|
-
import {
|
|
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 {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
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 {
|
|
49
|
-
import {
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
480
|
-
*
|
|
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
|
-
* (
|
|
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 (
|
|
487
|
-
*
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
510
|
-
|
|
511
|
-
const
|
|
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 =
|
|
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
|
|
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 =
|
|
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"
|
|
809
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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,
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
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
|
|
2765
|
-
// eProtect's registration
|
|
2766
|
-
// hosted page's entry link exactly as its
|
|
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
|
-
|
|
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
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
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 ===
|
|
2972
|
-
: preparation?.psp ===
|
|
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
|
-
:
|
|
3148
|
-
&& sub.
|
|
3149
|
-
|
|
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 {
|