@agent-cards/sdk 0.23.19 → 0.23.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -2
- package/PREFLIGHT.md +4 -0
- package/README.md +175 -7
- package/dist/amadeus.d.ts +4 -2
- package/dist/amadeus.js +16 -3
- package/dist/buy.d.ts +84 -2
- package/dist/buy.js +16 -1
- package/dist/card-endpoint-facts.d.ts +2 -1
- package/dist/card-endpoint-facts.js +18 -9
- package/dist/card-endpoint.d.ts +20 -4
- package/dist/card-endpoint.js +38 -4
- package/dist/card-fields.generated.js +1444 -51
- package/dist/cdp.d.ts +29 -0
- package/dist/cdp.js +444 -24
- package/dist/client.d.ts +134 -7
- package/dist/client.js +350 -25
- package/dist/comgate.d.ts +50 -0
- package/dist/comgate.js +80 -0
- package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
- package/dist/eprotect.generated.d.ts +1 -1
- package/dist/gmo.generated.js +7 -3
- package/dist/hipay.generated.d.ts +56 -0
- package/dist/hipay.generated.js +392 -0
- package/dist/hold.d.ts +180 -0
- package/dist/hold.js +270 -0
- package/dist/hosted-entry.d.ts +4 -0
- package/dist/hosted-entry.js +32 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.js +21 -0
- package/dist/merchant-card-endpoints.generated.js +102 -11
- package/dist/paddle-tokenex.generated.js +7 -3
- package/dist/paddle-vgs.generated.d.ts +3 -2
- package/dist/paddle-vgs.generated.js +9 -5
- package/dist/payrails.generated.js +7 -3
- package/dist/pre-claim.d.ts +20 -0
- package/dist/pre-claim.js +202 -22
- package/dist/preflight-catalog.json +58 -0
- package/dist/preflight-schemas.json +4 -0
- package/dist/preflight.generated.js +26 -2
- package/dist/prepared-processor.d.ts +6 -2
- package/dist/prepared-processor.js +18 -2
- package/dist/primer.generated.js +1487 -94
- package/dist/przelewy24.d.ts +47 -0
- package/dist/przelewy24.js +88 -0
- package/dist/quickpay.d.ts +44 -0
- package/dist/quickpay.js +65 -0
- package/dist/registry.d.ts +6 -5
- package/dist/registry.js +27 -7
- package/dist/shiji-sts.generated.js +7 -3
- package/dist/stripe-checkout.generated.js +744 -13
- package/dist/tokenex-iframe.d.ts +93 -0
- package/dist/tokenex-iframe.generated.d.ts +81 -0
- package/dist/tokenex-iframe.generated.js +403 -0
- package/dist/tokenex-iframe.js +195 -0
- package/dist/transbank.d.ts +45 -0
- package/dist/transbank.js +73 -0
- package/dist/twoc2p.d.ts +54 -0
- package/dist/twoc2p.generated.d.ts +33 -0
- package/dist/twoc2p.generated.js +1149 -0
- package/dist/twoc2p.js +85 -0
- package/dist/voice.d.ts +16 -13
- package/dist/voice.js +13 -10
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
package/dist/pre-claim.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// the vault can never pay, CyberSource Flex Microform's field iframes (whose
|
|
8
8
|
// capture context rides into the pause), Datatrans Secure Fields, whose
|
|
9
9
|
// card URL also carries card posts the device could never fill and card posts
|
|
10
|
-
// it already answered,
|
|
10
|
+
// it already answered, the TokenEx hosted iframe v3, whose card path carries the
|
|
11
|
+
// same two, and Pelecard's card submission URL, which also carries
|
|
11
12
|
// saved-token, wallet and keep-on-file bodies, Primer's
|
|
12
13
|
// /payment-instruments, which also carries requests the vault can never pay,
|
|
13
14
|
// Payrails, whose save needs the session key the page captured and whose
|
|
@@ -63,6 +64,7 @@ import { gmoRequestRefusal, isGmoTokenHost, isGmoTokenV1Request, recognizesGmo }
|
|
|
63
64
|
import { JUSPAY_PSP } from './juspay-gate.js';
|
|
64
65
|
import { datatransPauseDecision, isDatatransHost } from './datatrans.js';
|
|
65
66
|
import { DATATRANS_INVALID_REQUEST } from './datatrans.generated.js';
|
|
67
|
+
import { INVALID_TOKENEX_IFRAME_REQUEST, TOKENEX_IFRAME_PSP, isTokenexIframePath, servesTokenexIframe, tokenexIframePauseDecision, } from './tokenex-iframe.js';
|
|
66
68
|
import { isPelecardHost, pelecardRefusal } from './pelecard.js';
|
|
67
69
|
import { ACI_CHECKOUT_REUSED } from './aci.js';
|
|
68
70
|
import { ACI_EXPIRY_UNSTRIPPABLE } from './aci.generated.js';
|
|
@@ -79,12 +81,17 @@ import { cardEndpointStoppedPage, refusedNavigationOutcome } from './card-endpoi
|
|
|
79
81
|
import { PAYRAILS_HOLDING_REASONS, PAYRAILS_SESSION_MESSAGE, isPayrailsHost, isPayrailsPaymentUrl, isPayrailsStaticHost, payrailsKeyRefusal, payrailsPaymentRefusal, payrailsRequestKind, payrailsSaveRefusal, } from './payrails.js';
|
|
80
82
|
import { classifyVerifoneProfileRequest, verifoneTemplatedUrl } from './verifone.js';
|
|
81
83
|
import { ekashuEntryUrl, isEkashuEntry, isEkashuHost } from './ekashu.js';
|
|
84
|
+
import { isArmedTransbankUrl } from './transbank.js';
|
|
82
85
|
import { isRedsysEntry, isRedsysHost, redsysEntryUrl } from './redsys.js';
|
|
86
|
+
import { isArmedPrzelewy24Url, isPrzelewy24PayStep } from './przelewy24.js';
|
|
87
|
+
import { isQuickpayCardLookup, isQuickpayEntry, quickpayEntryUrl } from './quickpay.js';
|
|
88
|
+
import { isArmedComgateUrl } from './comgate.js';
|
|
83
89
|
import { PAYPAL_HOSTED_FIELDS_REASON, isPaypalHostedFieldsConfirm, paypalHostedFieldsBody } from './paypal-hosted-fields.js';
|
|
84
90
|
import { PAYPAL_CARD_FIELDS_REASON, PAYPAL_GUEST_CHECKOUT_REASON, isPaypalCardFieldsConfirm, isPaypalGraphql, paypalCardFieldsBody, paypalGraphqlCard, paypalGraphqlNamesCardOperation, } from './paypal-card-forms.js';
|
|
85
91
|
import { CYBERSOURCE_SECURE_ACCEPTANCE_REASON, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, cybersourceSecureAcceptanceRequest, isCybersourceSecureAcceptanceUrl, isSecureAcceptanceFramePost, } from './cybersource-secure-acceptance.js';
|
|
86
92
|
import { isWorldlineMyCheckoutEndpoint } from './worldline-mycheckout.js';
|
|
87
93
|
import { paddleVgsPayEventUrl } from './paddle-vgs-pay.js';
|
|
94
|
+
import { HIPAY_PSP, HIPAY_TOKEN_URLS, HIPAY_UPDATE_PATH, HIPAY_VAULT_HOSTS, hipayNumberOnlyRequest } from './hipay.generated.js';
|
|
88
95
|
import { EBANX_RESEND_REASON, EbanxFollowUpError, ebanxResendRefusalOf, isEbanxResendUrl, } from './ebanx.js';
|
|
89
96
|
/**
|
|
90
97
|
* The URL an event may name for a request. For an armed merchant profile's
|
|
@@ -815,10 +822,10 @@ async function juspayUnsupported(ctx, reason) {
|
|
|
815
822
|
// card-on-file policy the API served with the recognizer (VaultClient.cardOnFilePolicy):
|
|
816
823
|
// - a sound TOKENIZE card post defers, and the arbiter pauses it for approval, unless the same
|
|
817
824
|
// frame's same form was already answered with an approved token (the repeat rule) and that
|
|
818
|
-
// approval is still the one being handed off (
|
|
825
|
+
// approval is still the one being handed off (handoffRepeatApproval): then that stored answer
|
|
819
826
|
// is fulfilled here, with no claim, create or prompt. Once the answer reached the page, the
|
|
820
827
|
// application's merchant resolver must first report the order still pending
|
|
821
|
-
// (
|
|
828
|
+
// (repeatAfterHandoff);
|
|
822
829
|
// - a card post the API would refuse at create aborts with a `blocked` event that names the
|
|
823
830
|
// API's own code, `invalid_datatrans_request` or `datatrans_card_on_file_refused` (the latter
|
|
824
831
|
// only while the served policy refuses card on file), before anything is claimed, created or
|
|
@@ -846,25 +853,26 @@ const datatransPreClaim = (ctx) => {
|
|
|
846
853
|
return undefined;
|
|
847
854
|
if (decision !== 'card')
|
|
848
855
|
return datatransBlock(ctx, decision);
|
|
849
|
-
const approval =
|
|
856
|
+
const approval = handoffRepeatApproval(ctx);
|
|
850
857
|
const repeat = approval === null ? null : ctx.datatrans?.answerFor(ctx.frame, url, ctx.request.body, approval) ?? null;
|
|
851
858
|
if (!repeat)
|
|
852
859
|
return undefined;
|
|
853
|
-
return ctx.lifecycle.getState().status === 'awaiting_merchant' ?
|
|
860
|
+
return ctx.lifecycle.getState().status === 'awaiting_merchant' ? repeatAfterHandoff(ctx, repeat, 'datatrans') : answerRepeat(ctx, repeat, 'datatrans');
|
|
854
861
|
};
|
|
855
862
|
/**
|
|
856
|
-
* The approval whose stored Datatrans answer a repeat may still receive, or null. A stored token
|
|
863
|
+
* The approval whose stored Datatrans or TokenEx answer a repeat may still receive, or null. A stored token
|
|
857
864
|
* answers a re-submit only while the approval it came from is still being handed off: the lifecycle
|
|
858
865
|
* names it and reads awaiting_approval (its answer is being delivered) or awaiting_merchant (the
|
|
859
866
|
* merchant has not confirmed yet), and nothing latched, tore down or cancelled the attachment. The
|
|
860
867
|
* order completing or failing, an unknown outcome, a decline, a hold for the cardholder, a retry
|
|
861
868
|
* after a merchant failure and a new authorization all end it, and the re-submit is then a new
|
|
862
|
-
* question for the terminal arbiter, which holds or pauses it like any card post:
|
|
863
|
-
* amount, so one approval never answers for a later purchase. Once the answer reached the
|
|
864
|
-
* (awaiting_merchant), the gate also reconciles before it answers (
|
|
865
|
-
*
|
|
869
|
+
* question for the terminal arbiter, which holds or pauses it like any card post: neither card post
|
|
870
|
+
* binds an amount, so one approval never answers for a later purchase. Once the answer reached the
|
|
871
|
+
* page (awaiting_merchant), the gate also reconciles before it answers (repeatAfterHandoff). A stored
|
|
872
|
+
* Datatrans answer also ends once its form goes quiet (DatatransRepeats, DATATRANS_REPEAT_IDLE_MS), and
|
|
873
|
+
* a stored TokenEx answer once its iframe key expires (TokenexIframeRepeats).
|
|
866
874
|
*/
|
|
867
|
-
function
|
|
875
|
+
function handoffRepeatApproval(ctx) {
|
|
868
876
|
if (ctx.terminal() || ctx.interceptionAborted() || ctx.lifecycleAborted())
|
|
869
877
|
return null;
|
|
870
878
|
const { status, authorizationId } = ctx.lifecycle.getState();
|
|
@@ -881,36 +889,82 @@ function datatransRepeatApproval(ctx) {
|
|
|
881
889
|
* arbiter, and so does every re-submit on an attachment with no resolver: a later purchase never
|
|
882
890
|
* receives an earlier token without the cardholder, and the paused post never waits on a stalled read.
|
|
883
891
|
*/
|
|
884
|
-
function
|
|
892
|
+
function repeatAfterHandoff(ctx, repeat, psp) {
|
|
885
893
|
if (!ctx.lifecycle.hasMerchantResolver())
|
|
886
894
|
return undefined;
|
|
887
|
-
return
|
|
895
|
+
return pendingRepeat(ctx, repeat, psp);
|
|
888
896
|
}
|
|
889
|
-
async function
|
|
897
|
+
async function pendingRepeat(ctx, repeat, psp) {
|
|
890
898
|
if (!await ctx.lifecycle.merchantOrderPending())
|
|
891
899
|
return undefined;
|
|
892
900
|
// The resolver answered asynchronously: the checkout must still be handing off this approval.
|
|
893
|
-
if (ctx.lifecycle.getState().status !== 'awaiting_merchant' ||
|
|
901
|
+
if (ctx.lifecycle.getState().status !== 'awaiting_merchant' || handoffRepeatApproval(ctx) !== repeat.authorizationId)
|
|
894
902
|
return undefined;
|
|
895
|
-
return
|
|
903
|
+
return answerRepeat(ctx, repeat, psp);
|
|
896
904
|
}
|
|
897
905
|
async function datatransBlock(ctx, reason) {
|
|
898
906
|
ctx.onEvent?.({ type: 'blocked', detail: reason });
|
|
899
907
|
await ctx.effects.abort();
|
|
900
908
|
return { kind: 'abort' };
|
|
901
909
|
}
|
|
902
|
-
|
|
910
|
+
/** Fulfill a repeat with the stored answer (`repeat_answered`), or abort it when the browser cannot take it (`<psp>_repeat_unanswered`). */
|
|
911
|
+
async function answerRepeat(ctx, repeat, psp) {
|
|
903
912
|
try {
|
|
904
913
|
await ctx.effects.answer(repeat.response);
|
|
905
914
|
}
|
|
906
915
|
catch {
|
|
907
|
-
ctx.onEvent?.({ type: 'blocked', detail:
|
|
916
|
+
ctx.onEvent?.({ type: 'blocked', detail: `${psp}_repeat_unanswered` });
|
|
908
917
|
await ctx.effects.abort();
|
|
909
918
|
return { kind: 'abort' };
|
|
910
919
|
}
|
|
911
|
-
ctx.onEvent?.({ type: 'repeat_answered', detail: { psp
|
|
920
|
+
ctx.onEvent?.({ type: 'repeat_answered', detail: { psp, authorizationId: repeat.authorizationId } });
|
|
912
921
|
return { kind: 'answer' };
|
|
913
922
|
}
|
|
923
|
+
// The TokenEx hosted iframe v3 (tokenex-iframe.ts), JetBlue's card form. TokenEx's card-number frame posts the
|
|
924
|
+
// whole card to its own document URL, and the same path takes posts the device could never fill (a cvvOnly frame,
|
|
925
|
+
// a query this lane has not read) and, after an approval, the frame's own re-sends. The recognizer matches only the
|
|
926
|
+
// exact card URL, so once the registry serves the TokenEx iframe (servesTokenexIframe), every POST on the frame's
|
|
927
|
+
// /iframe/v3 path is judged here, before the terminal arbiter would pause it, by payment-core's own pause decision:
|
|
928
|
+
// - the sound card post defers, and the arbiter pauses it for approval, unless the same frame already sent the
|
|
929
|
+
// same bytes and had them answered with an approved token, and that approval is still the one being handed off
|
|
930
|
+
// (handoffRepeatApproval): then that stored answer is fulfilled here, with no claim, create or prompt. Once the
|
|
931
|
+
// answer reached the page, the application's merchant resolver must first report the order still pending
|
|
932
|
+
// (repeatAfterHandoff). A post with another placeholder or CVV is a new question, as for Datatrans;
|
|
933
|
+
// - any other post on that path that carries a value in Data aborts with a `blocked` event that names the API's
|
|
934
|
+
// own code, invalid_tokenex_iframe_request, before anything is claimed, created or prompted. Like Datatrans's
|
|
935
|
+
// refusal it neither holds the attachment nor changes its state;
|
|
936
|
+
// - a post with nothing in Data carries no card and defers: on the exact URL it continues untouched through the
|
|
937
|
+
// recognizer's passWithoutCard, and anywhere else it continues as an unrecognized request.
|
|
938
|
+
// Nothing is decided without the registry: for an org the API did not serve the entry to, the gate defers and every
|
|
939
|
+
// TokenEx request continues as before. The frame's document GETs, BinLookup and Forter never reach it as a card post.
|
|
940
|
+
// Synchronous until an effect, so a card post pauses with no await. A check that throws blocks the request.
|
|
941
|
+
const tokenexIframePreClaim = (ctx) => {
|
|
942
|
+
const { url, method } = ctx.request;
|
|
943
|
+
if (method.toUpperCase() !== 'POST' || !isTokenexIframePath(url) || !servesTokenexIframe(ctx.vault))
|
|
944
|
+
return undefined;
|
|
945
|
+
let decision;
|
|
946
|
+
try {
|
|
947
|
+
decision = tokenexIframePauseDecision({ url, headers: ctx.request.headers, body: ctx.request.body });
|
|
948
|
+
}
|
|
949
|
+
catch {
|
|
950
|
+
decision = INVALID_TOKENEX_IFRAME_REQUEST;
|
|
951
|
+
}
|
|
952
|
+
if (decision === null)
|
|
953
|
+
return undefined;
|
|
954
|
+
if (decision !== 'card')
|
|
955
|
+
return tokenexIframeBlock(ctx, decision);
|
|
956
|
+
const approval = handoffRepeatApproval(ctx);
|
|
957
|
+
const repeat = approval === null ? null : ctx.tokenexIframe?.answerFor(ctx.frame, url, ctx.request.body, approval) ?? null;
|
|
958
|
+
if (!repeat)
|
|
959
|
+
return undefined;
|
|
960
|
+
return ctx.lifecycle.getState().status === 'awaiting_merchant'
|
|
961
|
+
? repeatAfterHandoff(ctx, repeat, TOKENEX_IFRAME_PSP) : answerRepeat(ctx, repeat, TOKENEX_IFRAME_PSP);
|
|
962
|
+
};
|
|
963
|
+
async function tokenexIframeBlock(ctx, reason) {
|
|
964
|
+
ctx.onEvent?.({ type: 'blocked', detail: reason });
|
|
965
|
+
await ctx.effects.abort();
|
|
966
|
+
return { kind: 'abort' };
|
|
967
|
+
}
|
|
914
968
|
// Pelecard Iframe/Redirect 2.0 (pelecard.ts). The payment page's card URL, POST
|
|
915
969
|
// /PaymentGW/Transaction on gateway21 or gateway20.pelecard.biz, also carries the wallet, Bit,
|
|
916
970
|
// PIN pad and bank tabs with no card, and card-shaped bodies the device could never fill: a saved
|
|
@@ -1572,6 +1626,23 @@ async function shijiStsCompanion(ctx, body) {
|
|
|
1572
1626
|
ctx.onEvent?.({ type: 'companion_continued', detail: { url: redactUrl(url), psp: 'shiji_sts', fields: ['cvv'] } });
|
|
1573
1627
|
return { kind: 'continue' };
|
|
1574
1628
|
}
|
|
1629
|
+
// Przelewy24's pay wall (przelewy24.ts). Once this build's registry serves Przelewy24's entry (a capability the
|
|
1630
|
+
// API serves only to a company Agentcard turned Przelewy24 on for), every request to the pay wall's two hosts
|
|
1631
|
+
// pauses. The merchant's entry, a GET of /trnRequest/<token> exactly, is no step of this gate's: it defers, and the
|
|
1632
|
+
// terminal arbiter pauses it for the claim flow like any card request, so the cardholder's device pays it. Every
|
|
1633
|
+
// step toward paying on the pay wall itself aborts as an unsupported checkout: every POST there (the card method's
|
|
1634
|
+
// post, the card form's post, a form posted to the entry's link) and the GET of the Saferpay resolver, which names
|
|
1635
|
+
// the typed card's type. So Przelewy24 never sees the card the agent typed. The agent's browser only reaches the
|
|
1636
|
+
// pay wall when the entry was not paused (an attachment made too late, say), and then only the cardholder's device
|
|
1637
|
+
// can pay. The pay wall's own pages, scripts and images are not this gate's and continue. With Przelewy24 not
|
|
1638
|
+
// served, the gate defers, and every request continues as before. Synchronous until it must abort, like every gate.
|
|
1639
|
+
// Exported for the gate registry's tests.
|
|
1640
|
+
export const przelewy24PreClaim = (ctx) => {
|
|
1641
|
+
const { url, method } = ctx.request;
|
|
1642
|
+
if (!isPrzelewy24PayStep(url, method) || !isArmedPrzelewy24Url(ctx.vault, url))
|
|
1643
|
+
return undefined;
|
|
1644
|
+
return processorPageAbort(ctx);
|
|
1645
|
+
};
|
|
1575
1646
|
// NMI's hosted payment page (ekashu.ts). Once this build's registry serves NMI's entry (a
|
|
1576
1647
|
// capability the API serves only to a company Agentcard turned NMI on for), every request to
|
|
1577
1648
|
// NMI's two hosts pauses, and this gate judges the POSTs:
|
|
@@ -1595,7 +1666,8 @@ export const ekashuPreClaim = (ctx) => {
|
|
|
1595
1666
|
return undefined;
|
|
1596
1667
|
return processorPageAbort(ctx);
|
|
1597
1668
|
};
|
|
1598
|
-
// The stop for a card post into a processor's own page that only the cardholder's device may pay (NMI's, Redsys's
|
|
1669
|
+
// The stop for a card post into a processor's own page that only the cardholder's device may pay (NMI's, Redsys's,
|
|
1670
|
+
// QuickPay's).
|
|
1599
1671
|
async function processorPageAbort(ctx) {
|
|
1600
1672
|
const { url, method } = ctx.request;
|
|
1601
1673
|
ctx.preparationGate.invalidate('unsupported_checkout');
|
|
@@ -1604,6 +1676,27 @@ async function processorPageAbort(ctx) {
|
|
|
1604
1676
|
await ctx.effects.abort();
|
|
1605
1677
|
return { kind: 'abort' };
|
|
1606
1678
|
}
|
|
1679
|
+
// Transbank's Webpay Plus (transbank.ts). Once this build's registry serves Webpay's entry (a capability the API
|
|
1680
|
+
// serves only to a company Agentcard turned Webpay on for), every request to Webpay's two hosts pauses, and this gate
|
|
1681
|
+
// judges them:
|
|
1682
|
+
// - the merchant's entry, the GET of initTransaction?token_ws=<token> exactly as the redirect writes it, is a GET
|
|
1683
|
+
// and not this gate's: the terminal arbiter pauses it for the claim flow like any card request, and the
|
|
1684
|
+
// cardholder's device pays it;
|
|
1685
|
+
// - every other request there but a GET (the token's one submit to init_transaction.cgi, Webpay's card lookup and
|
|
1686
|
+
// card check, the card post to bp_control.cgi, and an entry posted as a form) aborts as an unsupported checkout,
|
|
1687
|
+
// so the token is never spent in the agent's browser and Webpay never sees the card the agent typed. The agent's
|
|
1688
|
+
// browser only reaches Webpay's pages when the entry was not paused (an attachment made too late, an entry with
|
|
1689
|
+
// another parameter), and then only the cardholder's device can pay;
|
|
1690
|
+
// - another GET there (Webpay's scripts and images, or an entry written another way, whose page then submits the
|
|
1691
|
+
// token, which this gate aborts) continues.
|
|
1692
|
+
// With Webpay not served, the gate defers and every request continues as before. Synchronous until it must abort,
|
|
1693
|
+
// like every gate. Exported for the gate registry's tests.
|
|
1694
|
+
export const transbankPreClaim = (ctx) => {
|
|
1695
|
+
const { url, method } = ctx.request;
|
|
1696
|
+
if (String(method).toUpperCase() === 'GET' || !isArmedTransbankUrl(ctx.vault, url))
|
|
1697
|
+
return undefined;
|
|
1698
|
+
return processorPageAbort(ctx);
|
|
1699
|
+
};
|
|
1607
1700
|
// Redsys's hosted payment page (redsys.ts). Once this build's registry serves Redsys's entry (a
|
|
1608
1701
|
// capability the API serves only to a company Agentcard turned Redsys on for), every request to
|
|
1609
1702
|
// Redsys's hosts pauses, and this gate judges the POSTs:
|
|
@@ -1628,6 +1721,47 @@ export const redsysPreClaim = (ctx) => {
|
|
|
1628
1721
|
return undefined;
|
|
1629
1722
|
return processorPageAbort(ctx);
|
|
1630
1723
|
};
|
|
1724
|
+
// QuickPay's hosted payment window (quickpay.ts). Once this build's registry serves QuickPay's entry (a capability
|
|
1725
|
+
// the API serves only to a company Agentcard turned QuickPay on for), every request to QuickPay's host pauses, and
|
|
1726
|
+
// this gate judges the POSTs and the window's card lookup:
|
|
1727
|
+
// - the merchant's entry (the host's root, no query) defers, and the terminal arbiter pauses it for the claim
|
|
1728
|
+
// flow like any card request: the cardholder's device pays it;
|
|
1729
|
+
// - every other POST to QuickPay (the window's card post, its fee lookup, its wallet token post, its 3-D Secure
|
|
1730
|
+
// step, and the framed twin of the entry, whose window asks for a card) aborts as an unsupported checkout,
|
|
1731
|
+
// and so does the window's GET that looks up the typed card's first digits, so QuickPay never sees the card
|
|
1732
|
+
// the agent typed. The agent's browser only reaches QuickPay's window when the entry was not paused (an
|
|
1733
|
+
// attachment made too late, say), and then only the cardholder's device can pay.
|
|
1734
|
+
// Any other GET (the window itself, its scripts and images) is not this gate's and continues. With QuickPay not
|
|
1735
|
+
// served, the entry is no card request, the gate defers, and every request continues as before. Synchronous
|
|
1736
|
+
// until it must abort, like every gate. Exported for the gate registry's tests.
|
|
1737
|
+
export const quickpayPreClaim = (ctx) => {
|
|
1738
|
+
const { url, method } = ctx.request;
|
|
1739
|
+
if (String(method).toUpperCase() !== 'POST' && !isQuickpayCardLookup(url, method))
|
|
1740
|
+
return undefined;
|
|
1741
|
+
const entry = quickpayEntryUrl(url);
|
|
1742
|
+
if (!entry || !ctx.vault.isCardRequest(entry, 'POST'))
|
|
1743
|
+
return undefined;
|
|
1744
|
+
if (isQuickpayEntry(url, method))
|
|
1745
|
+
return undefined;
|
|
1746
|
+
return processorPageAbort(ctx);
|
|
1747
|
+
};
|
|
1748
|
+
// ComGate's payment gateway (comgate.ts). Once this build's registry serves ComGate's entry (a capability the API serves
|
|
1749
|
+
// only to a company Agentcard turned ComGate on for), the gateway link pauses for the cardholder's device and every
|
|
1750
|
+
// request to ComGate's card hosts pauses too, and this gate judges those:
|
|
1751
|
+
// - every request but a GET (the gateway's card post and card check, the Web Checkout SDK's provider calls and card
|
|
1752
|
+
// check, the card frames' BIN lookup and their log) aborts as an unsupported checkout, so ComGate never sees the
|
|
1753
|
+
// card the agent typed. The agent's browser only reaches ComGate's card form when the link was not paused (an
|
|
1754
|
+
// attachment made too late, a link with another parameter, a shop on ComGate's Web Checkout SDK), and then only the
|
|
1755
|
+
// cardholder's device can pay;
|
|
1756
|
+
// - a GET there (the gateway's reads of the payment, the card frames' pages and scripts) is not this gate's and continues.
|
|
1757
|
+
// With ComGate not served, the gate defers and every request continues as before. Synchronous until it must abort, like
|
|
1758
|
+
// every gate. Exported for the gate registry's tests.
|
|
1759
|
+
export const comgatePreClaim = (ctx) => {
|
|
1760
|
+
const { url, method } = ctx.request;
|
|
1761
|
+
if (String(method).toUpperCase() === 'GET' || !isArmedComgateUrl(ctx.vault, url))
|
|
1762
|
+
return undefined;
|
|
1763
|
+
return processorPageAbort(ctx);
|
|
1764
|
+
};
|
|
1631
1765
|
// PayPal's legacy Hosted Fields (paypal-hosted-fields.ts). Its tokenization frame posts the card the
|
|
1632
1766
|
// agent typed to the Orders v2 confirm-payment-source on cors.api.paypal.com (or its sandbox), which
|
|
1633
1767
|
// no Vault mode takes over, so every company's adapters hand each POST there to this gate:
|
|
@@ -1952,6 +2086,52 @@ async function paddleVgsPayContinued(ctx, body) {
|
|
|
1952
2086
|
fields: ['card_first_six', 'card_last_four', 'expiry_month', 'expiry_year', 'card_brand'] } });
|
|
1953
2087
|
return { kind: 'continue' };
|
|
1954
2088
|
}
|
|
2089
|
+
// HiPay's hosted fields (payment-core hipay.js). The controller frame posts the card number alone as soon as the typed
|
|
2090
|
+
// number is valid, before Pay, and posts /rest/v2/token/update with a token once it holds one. Neither is this lane's
|
|
2091
|
+
// card request. The number-only post fails here as a network error, so the frame keeps the number and Pay sends one
|
|
2092
|
+
// complete create, the request a preparation binds. /update fails the same way, so the page's placeholder code and
|
|
2093
|
+
// expiry never ride under a token HiPay minted for the real card (the frame holds one only when an answer reached it
|
|
2094
|
+
// after the page gave up). Both fail before the preparation claim reads them: a ready preparation is neither spent
|
|
2095
|
+
// nor invalidated, and nothing reaches the API. Only while the synced registry serves HiPay (a build that declared
|
|
2096
|
+
// hipay_vault_token, for a company Agentcard turned HiPay on for); for anyone else HiPay's traffic is left as it was.
|
|
2097
|
+
const HIPAY_UPDATE_URLS = Object.values(HIPAY_TOKEN_URLS).map((url) => `${new URL(url).origin}${HIPAY_UPDATE_PATH}`);
|
|
2098
|
+
export const hipayPreClaim = (ctx) => {
|
|
2099
|
+
const { url, method } = ctx.request;
|
|
2100
|
+
if (typeof method !== 'string' || method.toUpperCase() !== 'POST' || ctx.vault.recognizesProcessor?.(HIPAY_PSP) !== true)
|
|
2101
|
+
return undefined;
|
|
2102
|
+
if (HIPAY_UPDATE_URLS.includes(url))
|
|
2103
|
+
return hipayRefused(ctx, 'hipay_update_refused');
|
|
2104
|
+
let body;
|
|
2105
|
+
// A reader that throws (Playwright's postData() can) proves nothing: the request is judged later, as any other.
|
|
2106
|
+
try {
|
|
2107
|
+
body = ctx.request.body;
|
|
2108
|
+
}
|
|
2109
|
+
catch {
|
|
2110
|
+
return undefined;
|
|
2111
|
+
}
|
|
2112
|
+
return hipayNumberOnlyRequest(url, method, body) ? hipayRefused(ctx, 'hipay_number_only') : undefined;
|
|
2113
|
+
};
|
|
2114
|
+
/**
|
|
2115
|
+
* Whether attachToPlaywright routes `url` to the gates for HiPay: any request to HiPay's two vault hosts, once the
|
|
2116
|
+
* synced registry serves HiPay, as attachToCdp arms both hosts from the recognizer. Playwright's route matcher sees
|
|
2117
|
+
* only the URL and the recognizer takes the token create alone, so without this /update would never reach
|
|
2118
|
+
* hipayPreClaim there. Any other request to those hosts goes on untouched.
|
|
2119
|
+
*/
|
|
2120
|
+
export function routesHipayVault(vault, url) {
|
|
2121
|
+
let host;
|
|
2122
|
+
try {
|
|
2123
|
+
host = new URL(url).hostname;
|
|
2124
|
+
}
|
|
2125
|
+
catch {
|
|
2126
|
+
return false;
|
|
2127
|
+
}
|
|
2128
|
+
return Object.keys(HIPAY_VAULT_HOSTS).includes(host) && vault?.recognizesProcessor?.(HIPAY_PSP) === true;
|
|
2129
|
+
}
|
|
2130
|
+
async function hipayRefused(ctx, reason) {
|
|
2131
|
+
ctx.onEvent?.({ type: 'blocked', detail: reason });
|
|
2132
|
+
await ctx.effects.abort();
|
|
2133
|
+
return { kind: 'abort' };
|
|
2134
|
+
}
|
|
1955
2135
|
// EBANX after its card token (ebanx.ts, payment-core ebanx.js). Once the cardholder's device has sent the card to
|
|
1956
2136
|
// /ws/token, EBANX's hosted payment page and EBANX.js send the card the page holds again: its 3-D Secure calls post
|
|
1957
2137
|
// the card, and the hosted page's confirm posts its code beside EBANX's token. On a page this SDK is attached to that
|
|
@@ -2147,8 +2327,8 @@ async function unsupportedContinue(ctx) {
|
|
|
2147
2327
|
* arbiter runs after all of them and is not part of this list. A gate appended
|
|
2148
2328
|
* after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or
|
|
2149
2329
|
* pauses each one itself. */
|
|
2150
|
-
export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, redsysPreClaim, paypalHostedFieldsPreClaim,
|
|
2151
|
-
paypalCardFormsPreClaim, cybersourceSecureAcceptancePreClaim, worldlineMyCheckoutPreClaim, paddleVgsPayPreClaim, ebanxPreClaim];
|
|
2330
|
+
export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, tokenexIframePreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, redsysPreClaim, quickpayPreClaim, comgatePreClaim, przelewy24PreClaim, transbankPreClaim, paypalHostedFieldsPreClaim,
|
|
2331
|
+
paypalCardFormsPreClaim, cybersourceSecureAcceptancePreClaim, worldlineMyCheckoutPreClaim, paddleVgsPayPreClaim, ebanxPreClaim, hipayPreClaim];
|
|
2152
2332
|
/** Run each gate in order; the first to return a decision wins. If none does,
|
|
2153
2333
|
* the terminal arbiter decides between continue, abort (unsupported) and pause.
|
|
2154
2334
|
* Returns synchronously while every gate defers synchronously (an ordinary card
|
|
@@ -2174,6 +2174,64 @@
|
|
|
2174
2174
|
"hostname_pattern": "^assets\\.braintreegateway\\.com$",
|
|
2175
2175
|
"pathname_pattern": "^\\/web\\/[0-9]+\\.[0-9]+\\.[0-9]+-payments-sdk(?:-[a-z]+)?\\/html\\/hosted-fields-(?:input-frame|payments-sdk-tokenization-frame)(?:\\.min)?\\.html$"
|
|
2176
2176
|
},
|
|
2177
|
+
{
|
|
2178
|
+
"id": "paypal-card-fields-v5",
|
|
2179
|
+
"psp": "paypal",
|
|
2180
|
+
"kinds": [
|
|
2181
|
+
"frame",
|
|
2182
|
+
"iframe"
|
|
2183
|
+
],
|
|
2184
|
+
"origin": "https://www.paypal.com",
|
|
2185
|
+
"path_pattern": "/smart/card-field",
|
|
2186
|
+
"sources": [
|
|
2187
|
+
{
|
|
2188
|
+
"kind": "primary_source",
|
|
2189
|
+
"reference": "https://www.paypal.com/sdk/js?components=card-fields",
|
|
2190
|
+
"reviewed_on": "2026-10-15"
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
"kind": "primary_source",
|
|
2194
|
+
"reference": "https://developer.paypal.com/docs/checkout/advanced/integrate/",
|
|
2195
|
+
"reviewed_on": "2026-10-15"
|
|
2196
|
+
},
|
|
2197
|
+
{
|
|
2198
|
+
"kind": "saved_observation",
|
|
2199
|
+
"reference": "lake-antoine-park-2026-10-15:paypal-card-field-frames",
|
|
2200
|
+
"reviewed_on": "2026-10-15"
|
|
2201
|
+
}
|
|
2202
|
+
],
|
|
2203
|
+
"hostname_pattern": "^www\\.paypal\\.com$",
|
|
2204
|
+
"pathname_pattern": "^\\/smart\\/card-field$"
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
"id": "paypal-card-fields-v6",
|
|
2208
|
+
"psp": "paypal",
|
|
2209
|
+
"kinds": [
|
|
2210
|
+
"frame",
|
|
2211
|
+
"iframe"
|
|
2212
|
+
],
|
|
2213
|
+
"origin": "https://www.paypal.com",
|
|
2214
|
+
"path_pattern": "/web-sdk/v6/card-fields-content",
|
|
2215
|
+
"sources": [
|
|
2216
|
+
{
|
|
2217
|
+
"kind": "primary_source",
|
|
2218
|
+
"reference": "https://www.paypal.com/sdk/js?components=card-fields",
|
|
2219
|
+
"reviewed_on": "2026-10-15"
|
|
2220
|
+
},
|
|
2221
|
+
{
|
|
2222
|
+
"kind": "primary_source",
|
|
2223
|
+
"reference": "https://developer.paypal.com/docs/checkout/advanced/integrate/",
|
|
2224
|
+
"reviewed_on": "2026-10-15"
|
|
2225
|
+
},
|
|
2226
|
+
{
|
|
2227
|
+
"kind": "saved_observation",
|
|
2228
|
+
"reference": "lake-antoine-park-2026-10-15:paypal-card-field-frames",
|
|
2229
|
+
"reviewed_on": "2026-10-15"
|
|
2230
|
+
}
|
|
2231
|
+
],
|
|
2232
|
+
"hostname_pattern": "^www\\.paypal\\.com$",
|
|
2233
|
+
"pathname_pattern": "^\\/web-sdk\\/v6\\/card-fields-content$"
|
|
2234
|
+
},
|
|
2177
2235
|
{
|
|
2178
2236
|
"id": "cybersource-secure-acceptance-silent-order-post",
|
|
2179
2237
|
"psp": "cybersource_secure_acceptance",
|
|
@@ -467,6 +467,8 @@
|
|
|
467
467
|
"gravy-secure-fields-script",
|
|
468
468
|
"gravy-secure-fields-script-sandbox",
|
|
469
469
|
"paypal-hosted-fields-payments-sdk",
|
|
470
|
+
"paypal-card-fields-v5",
|
|
471
|
+
"paypal-card-fields-v6",
|
|
470
472
|
"cybersource-secure-acceptance-silent-order-post",
|
|
471
473
|
"fiserv-commercehub-payment-fields",
|
|
472
474
|
"fiserv-commercehub-checkout-script",
|
|
@@ -1101,6 +1103,8 @@
|
|
|
1101
1103
|
"gravy-secure-fields-script",
|
|
1102
1104
|
"gravy-secure-fields-script-sandbox",
|
|
1103
1105
|
"paypal-hosted-fields-payments-sdk",
|
|
1106
|
+
"paypal-card-fields-v5",
|
|
1107
|
+
"paypal-card-fields-v6",
|
|
1104
1108
|
"cybersource-secure-acceptance-silent-order-post",
|
|
1105
1109
|
"fiserv-commercehub-payment-fields",
|
|
1106
1110
|
"fiserv-commercehub-checkout-script",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
2
|
-
// source-sha256:
|
|
2
|
+
// source-sha256: 639ffa689e8dfcf093eeba7e34b86c570e53ab17970976ae462a41fd2120d753
|
|
3
3
|
import { CHECKOUT_PREFLIGHT_CAPABILITIES } from './preflight-capabilities.generated.js';
|
|
4
4
|
/** Advisory page discovery. These rules never admit a payment destination. */
|
|
5
5
|
export const CHECKOUT_PREFLIGHT_VERSION = '1';
|
|
@@ -87,6 +87,20 @@ RULES.push({ id: 'paypal-hosted-fields-payments-sdk', psp: 'paypal', kinds: ['fr
|
|
|
87
87
|
paypalHostedFieldsSource('https://www.paypal.com/sdk/js?components=hosted-fields'),
|
|
88
88
|
{ kind: 'saved_observation', reference: 'planet-golf-uk-2026-10-06:paypal-hosted-fields-payments-sdk-frames', reviewed_on: '2026-10-07' }],
|
|
89
89
|
variant_unverified: 'PayPal legacy Hosted Fields frames send the card to the Orders v2 confirm-payment-source on cors.api.paypal.com; no Vault mode takes that request over.' });
|
|
90
|
+
// PayPal Card Fields (its JS SDK's components=card-fields, v5, and web-sdk v6), reviewed 2026-10-15 from
|
|
91
|
+
// WooCommerce PayPal Payments 4.1.3 checkouts (PayPal JS SDK 5.0.582, card-field client 1.270.0) and PayPal's
|
|
92
|
+
// public web-sdk v6 6.68.0 bundles, with no card typed. v5 renders each card field as a frame on
|
|
93
|
+
// www.paypal.com/smart/card-field (?type=number, expiry, cvv or name), and v6 as
|
|
94
|
+
// www.paypal.com/web-sdk/v6/card-fields-content. Each sends the card to the Orders v2 confirm-payment-source,
|
|
95
|
+
// which payment-core's paypal entry takes while it ships dark, so the frames name paypal and the result
|
|
96
|
+
// stays unknown until the lane is served to every client.
|
|
97
|
+
const paypalCardFieldsSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-10-15' });
|
|
98
|
+
const PAYPAL_CARD_FIELDS_SOURCES = [paypalCardFieldsSource('https://www.paypal.com/sdk/js?components=card-fields'),
|
|
99
|
+
paypalCardFieldsSource('https://developer.paypal.com/docs/checkout/advanced/integrate/'),
|
|
100
|
+
{ kind: 'saved_observation', reference: 'lake-antoine-park-2026-10-15:paypal-card-field-frames', reviewed_on: '2026-10-15' }];
|
|
101
|
+
RULES.push({ id: 'paypal-card-fields-v5', psp: 'paypal', kinds: ['frame', 'iframe'], hostname: /^www\.paypal\.com$/, pathname: /^\/smart\/card-field$/,
|
|
102
|
+
origin: 'https://www.paypal.com', path_pattern: '/smart/card-field', sources: PAYPAL_CARD_FIELDS_SOURCES }, { id: 'paypal-card-fields-v6', psp: 'paypal', kinds: ['frame', 'iframe'], hostname: /^www\.paypal\.com$/, pathname: /^\/web-sdk\/v6\/card-fields-content$/,
|
|
103
|
+
origin: 'https://www.paypal.com', path_pattern: '/web-sdk/v6/card-fields-content', sources: PAYPAL_CARD_FIELDS_SOURCES });
|
|
90
104
|
// CyberSource Secure Acceptance's Silent Order POST (the Secure Acceptance Checkout API), reviewed 2026-10-08 from
|
|
91
105
|
// CyberSource's endpoint table and Arrow Electronics' public bundles (no card was sent). The merchant's own page
|
|
92
106
|
// posts the card fields, unsigned, beside the fields its server signed, to a /silent/ path on CyberSource's Secure
|
|
@@ -308,7 +322,7 @@ RULES.push({ id: 'worldline-mycheckout-page', psp: 'worldline_mycheckout', kinds
|
|
|
308
322
|
origin: 'https://payment.pay1.checkout.worldline-solutions.com', path_pattern: '/checkout/{payment-product-details/}:hosted_checkout', sources: worldlineSources }, { id: 'worldline-mycheckout-card-form', psp: 'worldline_mycheckout', kinds: ['form'], hostname: /^payment\.pay1\.checkout\.worldline-solutions\.com$/, pathname: /^\/checkout$/,
|
|
309
323
|
origin: 'https://payment.pay1.checkout.worldline-solutions.com', path_pattern: '/checkout', sources: worldlineSources });
|
|
310
324
|
// A processor that ships dark (its definition declares a capability) is left out of the bundled
|
|
311
|
-
// catalog. Datatrans's discovery rules wait with it: they join RULES only once KNOWN_PSPS, the
|
|
325
|
+
// catalog. Datatrans's and the TokenEx iframe's discovery rules wait with it: they join RULES only once KNOWN_PSPS, the
|
|
312
326
|
// bundled catalog's processors, lists its psp. Until then its assets are not evidence, so a page
|
|
313
327
|
// that loads them, alone or beside another processor's SDK, reads exactly as it did before the
|
|
314
328
|
// processor existed. (The dark GMO, CyberSource Flex, Juspay, Pelecard, ACI, CreditGuard,
|
|
@@ -326,6 +340,16 @@ for (const [suffix, hostname, origin] of [['', /^pay\.datatrans\.com$/, 'https:/
|
|
|
326
340
|
sources: [datatransSource('https://pay.datatrans.com/upp/payment/js/secure-fields-2.0.0.js'), datatransSource('https://docs.pci-proxy.com/docs/secure-fields-quick-start')] }, { id: `datatrans-secure-fields-frame${suffix}`, psp: 'datatrans', kinds: ['document', 'frame', 'iframe'], hostname, pathname: /^\/upp\/payment\/SecureFields\/paymentField$/, origin, path_pattern: '/upp/payment/SecureFields/paymentField',
|
|
327
341
|
sources: [datatransSource('https://pay.datatrans.com/upp/payment/SecureFields/field.js'), datatransSource('https://docs.datatrans.ch/docs/secure-fields')] });
|
|
328
342
|
}
|
|
343
|
+
// TokenEx hosted iframe v3, read 2026-10-18 from JetBlue's checkout: the parent library
|
|
344
|
+
// (htp.tokenex.com/Iframe/Iframe-v3.min.js) and the iframe document it loads (/iframe/v3?<query>), on the
|
|
345
|
+
// live host and its test twin. Dark behind tokenex_iframe_tokenize, so it waits with Datatrans.
|
|
346
|
+
const tokenexSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-10-18' });
|
|
347
|
+
for (const [suffix, hostname, origin] of [['', /^htp\.tokenex\.com$/, 'https://htp.tokenex.com'],
|
|
348
|
+
['-test', /^test-htp\.tokenex\.com$/, 'https://test-htp.tokenex.com']]) {
|
|
349
|
+
SERVED_ONLY_RULES.push({ id: `tokenex-iframe-v3-script${suffix}`, psp: 'tokenex_iframe', kinds: ['script'], hostname, pathname: /^\/Iframe\/Iframe-v3(?:\.min)?\.js$/, origin, path_pattern: '/Iframe/Iframe-v3{,.min}.js',
|
|
350
|
+
sources: [tokenexSource('https://htp.tokenex.com/Iframe/Iframe-v3.min.js')] }, { id: `tokenex-iframe-v3-frame${suffix}`, psp: 'tokenex_iframe', kinds: ['document', 'frame', 'iframe'], hostname, pathname: /^\/iframe\/v3$/, origin, path_pattern: '/iframe/v3',
|
|
351
|
+
sources: [tokenexSource('https://htp.tokenex.com/Iframe/v3.min.js?version=latest')] });
|
|
352
|
+
}
|
|
329
353
|
RULES.push(...SERVED_ONLY_RULES.filter(rule => KNOWN_PSPS.includes(rule.psp)));
|
|
330
354
|
// Keep sandbox evidence truthful instead of labelling it with a production origin.
|
|
331
355
|
for (const [baseId, suffix, hostname, origin] of [
|
|
@@ -24,8 +24,12 @@
|
|
|
24
24
|
* wix_payments: prepared only. Wix's payment widget fails the payment 30 seconds after it asks Wix
|
|
25
25
|
* Cashier's card frame for a token, too soon for an approval after Pay. Its one environment is
|
|
26
26
|
* production: Wix's tokenizer has no test host.
|
|
27
|
+
*
|
|
28
|
+
* hipay: prepared only. The merchant's page gives HiPay's hosted fields 20 seconds after Pay, too soon for an
|
|
29
|
+
* approval after Pay. Its environments are its two vault hosts: secure2-vault (production) and
|
|
30
|
+
* stage-secure2-vault (sandbox).
|
|
27
31
|
*/
|
|
28
|
-
export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com' | 'paysafe' | 'fiserv' | 'cybersource_flex' | 'aci' | 'creditguard' | 'worldpay_eprotect' | 'amadeus_checkout' | 'verifone' | 'cybersource_secure_acceptance' | 'paddle_vgs' | 'wix_payments';
|
|
32
|
+
export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com' | 'paysafe' | 'fiserv' | 'cybersource_flex' | 'aci' | 'creditguard' | 'worldpay_eprotect' | 'amadeus_checkout' | 'verifone' | 'cybersource_secure_acceptance' | 'paddle_vgs' | 'wix_payments' | 'hipay';
|
|
29
33
|
export type PreparationEnvironment = 'production' | 'sandbox' | 'shared';
|
|
30
34
|
/**
|
|
31
35
|
* How the approved card reaches the processor: the device's own request (token), or
|
|
@@ -62,7 +66,7 @@ export declare function preparedApprovalAnswersRetry(psp: string): boolean;
|
|
|
62
66
|
* its own signing keys. A Verifone preparation pays in the environment of the key its reviewed
|
|
63
67
|
* profile pins. CyberSource Secure Acceptance's are separate hosts too: secureacceptance is
|
|
64
68
|
* live, testsecureacceptance is test. So are Paddle VGS's two card URLs: vault is live,
|
|
65
|
-
* sandbox-vault is test.
|
|
69
|
+
* sandbox-vault is test. So are HiPay's two vault hosts: secure2-vault is live, stage-secure2-vault is test.
|
|
66
70
|
*/
|
|
67
71
|
export declare function validPreparationEnvironment(psp: string, environment: string): boolean;
|
|
68
72
|
/**
|
|
@@ -15,6 +15,7 @@ import { isPreparedVerifoneRequest } from './verifone.js';
|
|
|
15
15
|
import { isPreparedSecureAcceptanceRequest, secureAcceptanceEnvironment, secureAcceptancePreparationUrl, secureAcceptanceRequestRefusal, } from './cybersource-secure-acceptance.generated.js';
|
|
16
16
|
import { PADDLE_VGS_CARDS_URLS, isPreparedPaddleVgsRequest } from './paddle-vgs.generated.js';
|
|
17
17
|
import { WIX_PAYMENTS_TOKENIZE_URL, isPreparedWixPaymentsRequest } from './wix-payments.generated.js';
|
|
18
|
+
import { HIPAY_TOKEN_URLS, hipayRequestRefusal, hipayTokenEnvironment, isPreparedHipayRequest } from './hipay.generated.js';
|
|
18
19
|
export function preparationMode(psp) {
|
|
19
20
|
return psp === 'adyen' || psp === 'fiserv' || psp === 'verifone' ? 'cse' : 'token';
|
|
20
21
|
}
|
|
@@ -49,7 +50,7 @@ export function preparedApprovalAnswersRetry(psp) {
|
|
|
49
50
|
* its own signing keys. A Verifone preparation pays in the environment of the key its reviewed
|
|
50
51
|
* profile pins. CyberSource Secure Acceptance's are separate hosts too: secureacceptance is
|
|
51
52
|
* live, testsecureacceptance is test. So are Paddle VGS's two card URLs: vault is live,
|
|
52
|
-
* sandbox-vault is test.
|
|
53
|
+
* sandbox-vault is test. So are HiPay's two vault hosts: secure2-vault is live, stage-secure2-vault is test.
|
|
53
54
|
*/
|
|
54
55
|
export function validPreparationEnvironment(psp, environment) {
|
|
55
56
|
if (psp === 'bambora' || psp === 'mercado_pago' || psp === 'recurly' || psp === 'spreedly')
|
|
@@ -58,7 +59,7 @@ export function validPreparationEnvironment(psp, environment) {
|
|
|
58
59
|
if (psp === 'wix_payments')
|
|
59
60
|
return environment === 'production';
|
|
60
61
|
return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe', 'fiserv', 'cybersource_flex', 'aci', 'creditguard', 'worldpay_eprotect', 'amadeus_checkout', 'verifone',
|
|
61
|
-
'cybersource_secure_acceptance', 'paddle_vgs'].includes(psp) && ['production', 'sandbox'].includes(environment);
|
|
62
|
+
'cybersource_secure_acceptance', 'paddle_vgs', 'hipay'].includes(psp) && ['production', 'sandbox'].includes(environment);
|
|
62
63
|
}
|
|
63
64
|
/**
|
|
64
65
|
* ACI's prepared endpoint, the literal the API names (backend checkout-prepared-processor.ts): its
|
|
@@ -106,6 +107,9 @@ export function preparationEndpoint(psp, environment) {
|
|
|
106
107
|
// Wix Payments: the tokenize URL, the only URL the claim takes for it.
|
|
107
108
|
if (psp === 'wix_payments')
|
|
108
109
|
return WIX_PAYMENTS_TOKENIZE_URL;
|
|
110
|
+
// HiPay: the token create on the environment's vault, the only URL the claim takes for it.
|
|
111
|
+
if (psp === 'hipay')
|
|
112
|
+
return HIPAY_TOKEN_URLS[environment];
|
|
109
113
|
// A Verifone endpoint is its reviewed profile's leg, never one per environment (verifone.ts).
|
|
110
114
|
if (psp === 'verifone')
|
|
111
115
|
throw new Error('unsupported_preparation_processor');
|
|
@@ -263,6 +267,11 @@ export function matchesPreparedRequest(psp, environment, requestUrl, method, bod
|
|
|
263
267
|
// site's app instance and the card frame's body), the rule the API and the Vault bind with too.
|
|
264
268
|
if (psp === 'wix_payments')
|
|
265
269
|
return isPreparedWixPaymentsRequest(requestUrl, method.toUpperCase(), body ?? null, environment, headers ?? {});
|
|
270
|
+
// HiPay: the environment's own vault and payment-core's whole request rule (a JSON content type, a public username of
|
|
271
|
+
// that environment and a complete create, never the hosted fields' number-only post), the rule the API and the Vault
|
|
272
|
+
// bind with too.
|
|
273
|
+
if (psp === 'hipay')
|
|
274
|
+
return isPreparedHipayRequest(requestUrl, method.toUpperCase(), body ?? null, environment, headers ?? {});
|
|
266
275
|
if (psp === 'creditguard')
|
|
267
276
|
return matchesPreparedCreditguard(environment, requestUrl, body, headers, binding);
|
|
268
277
|
// A Verifone request is judged only through the profile its preparation names (matchesPreparation).
|
|
@@ -423,6 +432,13 @@ export function preparedRequestRefusal(psp, environment, requestUrl, body, heade
|
|
|
423
432
|
const reason = secureAcceptanceClaimRefusal(requestUrl, body, headers, merchantOrigin, currency);
|
|
424
433
|
return reason === null ? 'checkout_changed' : `secure_acceptance_${reason}`;
|
|
425
434
|
}
|
|
435
|
+
// A HiPay token create on the prepared environment's vault names what payment-core's request rule refused
|
|
436
|
+
// (hipay_keeps_card, hipay_headers_refused, ...); another vault or another request is `checkout_changed`.
|
|
437
|
+
if (psp === 'hipay') {
|
|
438
|
+
if (hipayTokenEnvironment(requestUrl) !== environment)
|
|
439
|
+
return 'checkout_changed';
|
|
440
|
+
return hipayRequestRefusal({ url: requestUrl, method: 'POST', headers, body: body ?? null }) ?? 'checkout_changed';
|
|
441
|
+
}
|
|
426
442
|
if (psp !== 'amadeus_checkout' || amadeusEnvironment(requestUrl) !== environment)
|
|
427
443
|
return 'checkout_changed';
|
|
428
444
|
const merchantHost = merchantHostOf(merchantOrigin);
|