@agent-cards/sdk 0.23.13 → 0.23.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/PREFLIGHT.md +3 -1
  3. package/README.md +54 -13
  4. package/dist/adyen-merchant-hosted.generated.js +2 -2
  5. package/dist/amadeus.d.ts +5 -2
  6. package/dist/amadeus.js +17 -3
  7. package/dist/card-fields.generated.js +924 -3
  8. package/dist/cdp.js +83 -19
  9. package/dist/client.d.ts +97 -1
  10. package/dist/client.js +235 -16
  11. package/dist/creditguard.generated.d.ts +100 -2
  12. package/dist/creditguard.generated.js +333 -3
  13. package/dist/datatrans.generated.d.ts +19 -4
  14. package/dist/datatrans.generated.js +15 -3
  15. package/dist/ebanx.d.ts +107 -0
  16. package/dist/ebanx.generated.d.ts +78 -0
  17. package/dist/ebanx.generated.js +577 -0
  18. package/dist/ebanx.js +159 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/lifecycle.d.ts +32 -5
  22. package/dist/lifecycle.js +40 -5
  23. package/dist/paddle-tokenex.d.ts +56 -0
  24. package/dist/paddle-tokenex.generated.d.ts +67 -0
  25. package/dist/paddle-tokenex.generated.js +951 -0
  26. package/dist/paddle-tokenex.js +95 -0
  27. package/dist/paypal-card-forms.d.ts +73 -0
  28. package/dist/paypal-card-forms.js +126 -0
  29. package/dist/pre-claim.d.ts +39 -1
  30. package/dist/pre-claim.js +348 -18
  31. package/dist/preparation.d.ts +13 -0
  32. package/dist/preparation.js +22 -0
  33. package/dist/prepared-processor.d.ts +5 -1
  34. package/dist/prepared-processor.js +17 -2
  35. package/dist/primer.generated.js +960 -39
  36. package/dist/redsys.d.ts +42 -0
  37. package/dist/redsys.js +68 -0
  38. package/dist/stripe-checkout.generated.js +494 -9
  39. package/dist/wix-payments.generated.d.ts +42 -0
  40. package/dist/wix-payments.generated.js +241 -0
  41. package/package.json +8 -5
package/dist/client.js CHANGED
@@ -15,6 +15,8 @@ import { CONVERGE_CAPABILITY } from './elavon-converge.js';
15
15
  import { SHIJI_STS_CAPABILITY, SHIJI_STS_PSP, isShijiStsServedCompanion, isShijiStsServedSubstitution } from './shiji-sts.js';
16
16
  import { AMADEUS_CHECKOUT_CAPABILITY, AMADEUS_CHECKOUT_PSP } from './amadeus-checkout.generated.js';
17
17
  import { PADDLE_VGS_CAPABILITY, PADDLE_VGS_PSP, paddleVgsCardFactsOf } from './paddle-vgs.generated.js';
18
+ import { PADDLE_TOKENEX_CAPABILITY, PADDLE_TOKENEX_PSP, isPaddleTokenexServedFacts, isPaddleTokenexServedSubstitution } from './paddle-tokenex.js';
19
+ import { WIX_PAYMENTS_CAPABILITY, WIX_PAYMENTS_PSP } from './wix-payments.generated.js';
18
20
  import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
19
21
  import { VoiceModule } from './voice.js';
20
22
  import { BuyModule } from './buy.js';
@@ -32,6 +34,7 @@ import { PELECARD_CAPABILITY, PELECARD_PSP } from './pelecard.js';
32
34
  import { PRIMER_CAPABILITY, PRIMER_PSP, primerTokenAnswer } from './primer.js';
33
35
  import { PAYRAILS_CAPABILITY, isPayrailsPaymentUrl, payrailsAnswer } from './payrails.js';
34
36
  import { WSPAY_CAPABILITY, WSPAY_PSP } from './wspay.js';
37
+ import { EbanxFollowUpError } from './ebanx.js';
35
38
  import { SECURE_ACCEPTANCE_CAPABILITY, SECURE_ACCEPTANCE_FAST_POLL_MS, SECURE_ACCEPTANCE_PSP, SECURE_ACCEPTANCE_REPLY_WINDOW_MS, secureAcceptanceCheckoutProfile, secureAcceptanceCheckoutRefusal, secureAcceptanceFrameAnswer, } from './cybersource-secure-acceptance.js';
36
39
  /**
37
40
  * The modes this SDK can finish. Asked for on syncRegistry (the API serves
@@ -187,6 +190,11 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
187
190
  * Moneris on for. A gated recognizer never sits in BUILTIN_REGISTRY, so it is paused
188
191
  * only after a sync.
189
192
  *
193
+ * `redsys_hosted_payment_page`: Redsys's hosted payment page (the TPV Virtual redirect), whose
194
+ * card-free signed entry this build pauses for the cardholder's device and whose card page this build
195
+ * never lets the agent's browser post to (pre-claim.ts, redsys.ts). Served on the same terms as NMI's: only
196
+ * to a build that lists it, for a company Agentcard turned Redsys on for.
197
+ *
190
198
  * `paddle_vgs_cards`: the card request Paddle's checkout frame sends to its VGS card
191
199
  * vault (vault.paddle.com/cards) on a checkout that names VGS, only through a prepared
192
200
  * checkout: the frame aborts the request 10 seconds after Pay and sends it once more,
@@ -194,8 +202,40 @@ export const SUPPORTED_REGISTRY_FEATURES = ['card_fields', 'checkout_sessions'];
194
202
  * device sends the card itself and the frame gets back the stored card's id alone.
195
203
  * Served on the same terms: only to a build that lists it, for a company Agentcard
196
204
  * turned Paddle on for.
205
+ *
206
+ * `paddle_tokenex_pay`: the /pay Paddle's checkout frame sends to its checkout service
207
+ * (checkout-service.paddle.com) on a checkout that names TokenEx. The frame encrypts the
208
+ * card in the page under the account's TokenEx key, so /pay carries the placeholder's two
209
+ * ciphertexts and facts; this build pauses it and writes over them the device's own, under
210
+ * the key payment-core pins (paddle-tokenex.ts). Served on the same terms: only to a build
211
+ * that lists it, for a company Agentcard turned Paddle on for.
212
+ *
213
+ * `lyra_krypton_payment`: the card payment Lyra's Krypton form (PayZen, Systempay,
214
+ * Sogecommerce) sends from its hidden checkout frame to ProcessPayment/2 on the brand's API
215
+ * host. This build pauses only the frame's card body under a form token that pays once; the
216
+ * cardholder's device sends the card through the checkout relay, and the frame gets Lyra's
217
+ * answer byte for byte with Lyra's payment id, so it runs any 3-D Secure step itself and its
218
+ * card-free follow-up passes untouched. Served on the same terms: only to a build that lists
219
+ * it, for a company Agentcard turned Lyra on for.
220
+ *
221
+ * `payfast_card_pay`: the card tab's pay on Payfast's hosted payment page (POST
222
+ * /eng/method/CreditCard/<uuid>/pay on payment.payfast.io, www.payfast.co.za or the sandbox),
223
+ * which this build pauses only as the card tab's own form body and answers with Payfast's own
224
+ * answer as the API recorded it: `[true]` once the payment is done, or the 3-D Secure step the
225
+ * card tab then runs in the agent's browser. The cardholder's device sends the card through the
226
+ * checkout relay. Served on the same terms: only to a build that lists it, for a company
227
+ * Agentcard turned Payfast on for.
228
+ *
229
+ * `wix_payments_tokenize`: the card request Wix Cashier's card frame sends to Wix's
230
+ * tokenizer (cashier-pci.wixapps.net), only through a prepared checkout: Wix's payment
231
+ * widget fails the payment 30 seconds after it asks the frame for a token. The
232
+ * cardholder's device sends the card through the checkout relay and the frame gets back
233
+ * Wix's token. A second card request from the frame, which Wix's frame sends after a 401
234
+ * or 403 under a fresh instance, is never answered from the same approval. Served on the
235
+ * same terms: only to a build that lists it, for a company Agentcard turned Wix on for.
197
236
  */
198
- export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CYBERSOURCE_FLEX_CAPABILITY, JUSPAY_CAPABILITY, DATATRANS_CAPABILITY, 'authorize_net_dpm_form', PELECARD_CAPABILITY, ACI_CAPABILITY, 'creditguard_pps_process', PRIMER_CAPABILITY, PAYRAILS_CAPABILITY, CONVERGE_CAPABILITY, EPROTECT_CAPABILITY, WSPAY_CAPABILITY, SHIJI_STS_CAPABILITY, AMADEUS_CHECKOUT_CAPABILITY, 'worldline_mycheckout_form', 'verifone_card_capture', 'ekashu_hosted_payment_page', SECURE_ACCEPTANCE_CAPABILITY, 'gravy_session_fields_put', 'moneris_hosted_tokenization', PADDLE_VGS_CAPABILITY];
237
+ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CYBERSOURCE_FLEX_CAPABILITY, JUSPAY_CAPABILITY, DATATRANS_CAPABILITY, 'authorize_net_dpm_form', PELECARD_CAPABILITY, ACI_CAPABILITY, 'creditguard_pps_process', PRIMER_CAPABILITY, PAYRAILS_CAPABILITY, CONVERGE_CAPABILITY, EPROTECT_CAPABILITY, WSPAY_CAPABILITY, SHIJI_STS_CAPABILITY, AMADEUS_CHECKOUT_CAPABILITY, 'worldline_mycheckout_form', 'verifone_card_capture', 'ekashu_hosted_payment_page', SECURE_ACCEPTANCE_CAPABILITY, 'gravy_session_fields_put', 'moneris_hosted_tokenization', PADDLE_VGS_CAPABILITY, PADDLE_TOKENEX_CAPABILITY,
238
+ 'redsys_hosted_payment_page', 'lyra_krypton_payment', 'payfast_card_pay', WIX_PAYMENTS_CAPABILITY];
199
239
  /**
200
240
  * Processors whose paused request names no amount at all, so the pair the
201
241
  * agent passes is the amount the cardholder approves: authorize() refuses one
@@ -218,13 +258,19 @@ export const SUPPORTED_CAPABILITIES = ['fiserv_card_capture', GMO_CAPABILITY, CY
218
258
  * token later, so the agent's pair, above zero (MONERIS_PSP below), is the one
219
259
  * figure the approval shows. Paddle VGS's card request carries none either: Paddle
220
260
  * charges the stored card at /pay for what the checkout says, so the agent's pair,
221
- * which the prepared checkout carries, is the total the cardholder approves.
261
+ * which the prepared checkout carries, is the total the cardholder approves. Payfast's
262
+ * card pay carries none either: the merchant's entry bound the total to the payment,
263
+ * so the agent sends the total Payfast's page shows, above zero (PAYFAST_PSP below),
264
+ * and the API holds it to the payment Payfast itself reports. Wix's tokenize request
265
+ * carries none either: the merchant's page charges Wix's token for its own order, so the
266
+ * agent's pair, which the prepared checkout carries, is the total the cardholder approves.
222
267
  */
223
268
  const MONERIS_PSP = 'moneris';
224
- const AGENT_AMOUNT_PROCESSORS = [GMO_PSP, PELECARD_PSP, WSPAY_PSP, SECURE_ACCEPTANCE_PSP, MONERIS_PSP, PADDLE_VGS_PSP];
269
+ const PAYFAST_PSP = 'payfast';
270
+ const AGENT_AMOUNT_PROCESSORS = [GMO_PSP, PELECARD_PSP, WSPAY_PSP, SECURE_ACCEPTANCE_PSP, MONERIS_PSP, PADDLE_VGS_PSP, PAYFAST_PSP, WIX_PAYMENTS_PSP];
225
271
  /** How AmountRequiredError names a processor. */
226
272
  const PROCESSOR_NAMES = { [GMO_PSP]: 'GMO', [PELECARD_PSP]: 'Pelecard', [WSPAY_PSP]: 'WSPay',
227
- [SECURE_ACCEPTANCE_PSP]: 'CyberSource Secure Acceptance', [MONERIS_PSP]: 'Moneris', [PADDLE_VGS_PSP]: 'Paddle' };
273
+ [SECURE_ACCEPTANCE_PSP]: 'CyberSource Secure Acceptance', [MONERIS_PSP]: 'Moneris', [PADDLE_VGS_PSP]: 'Paddle', [PAYFAST_PSP]: 'Payfast', [WIX_PAYMENTS_PSP]: 'Wix' };
228
274
  /**
229
275
  * Processors whose request and answer name no amount at all, so the total the
230
276
  * payment page shows (`pageAmount`) is the one figure that ties the agent's pair
@@ -840,6 +886,14 @@ function refusalsOf(v) {
840
886
  }
841
887
  return out;
842
888
  }
889
+ /**
890
+ * How often sendEbanxFollowUp reads a send back while the cardholder's device has it: every half second, whatever the
891
+ * client's own `pollIntervalMs`. One send is out at a time per approval, so this is 120 reads a minute, inside the
892
+ * API's 240 a minute per caller and authorization (routes/api/v2/checkout-follow-ups.ts).
893
+ */
894
+ export const EBANX_FOLLOW_UP_READ_MS = 500;
895
+ /** The longest sendEbanxFollowUp waits after reads the API refused as too many (429): 1, 2, 4, 8, then 15 seconds. */
896
+ export const EBANX_FOLLOW_UP_BACKOFF_MAX_MS = 15_000;
843
897
  export class CheckoutApiError extends Error {
844
898
  status;
845
899
  path;
@@ -1012,18 +1066,81 @@ const ACI_CHECKOUT_ID = /[0-9a-f]{32}\.[a-z0-9]{1,16}-vm-tx[0-9]{1,3}/gi;
1012
1066
  function maskAciCheckout(path) {
1013
1067
  return path.replace(ACI_CHECKOUT, '$1{checkoutId}').replace(ACI_CHECKOUT_ID, '{checkoutId}');
1014
1068
  }
1069
+ /**
1070
+ * Payfast's hosted payment page sends the card to /eng/method/CreditCard/<uuid>/pay on its own host
1071
+ * with no auth header and no CSRF token (payment-core payfast.js): the payment's uuid is the
1072
+ * request's only credential, and with it anyone can read that payment page, its amount and its
1073
+ * merchant. Every route of the page carries the same uuid, so on every host Payfast owns (payfast.io and
1074
+ * payfast.co.za and any name under them) any id of its shape, in any case or after an escaped slash,
1075
+ * reads {uuid}.
1076
+ */
1077
+ const PAYFAST_HOST = /(?:^|\.)payfast\.(?:io|co\.za)\.?$/i;
1078
+ const PAYFAST_UUID = /[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}/gi;
1079
+ function maskPayfastUuid(path) {
1080
+ return path.replace(PAYFAST_UUID, '{uuid}');
1081
+ }
1082
+ /**
1083
+ * Paddle's checkout frame pays a TokenEx checkout with its own /pay on checkout-service.paddle.com
1084
+ * (sandbox-checkout-service in test), and the checkout id in that path is the checkout's only
1085
+ * credential there, no cookies (payment-core paddle-tokenex.js): with it, and the card in the body,
1086
+ * a payment reaches that checkout. On those hosts the segment between /checkout/ or
1087
+ * /transaction-checkout/ and /pay, in any case or after an escaped slash, reads {checkout}.
1088
+ */
1089
+ const PADDLE_CHECKOUT_HOST = /^(?:sandbox-)?checkout-service\.paddle\.com\.?$/i;
1090
+ const PADDLE_PAY = /(\/(?:checkout|transaction-checkout)(?:\/|%2f))[^/]*(\/pay)/gi;
1091
+ function maskPaddleCheckout(path) {
1092
+ return path.replace(PADDLE_PAY, '$1{checkout}$2');
1093
+ }
1094
+ /**
1095
+ * Lyra's Krypton frame sends its payment to ProcessPayment/2 on the brand's API host with the form
1096
+ * token's session id as a `;jsessionid=` path parameter (payment-core lyra.js). With the form token the
1097
+ * body carries, that id continues the payment on Lyra's side, so on Lyra's hosts it reads `{session}`,
1098
+ * in any case: https://api.systempay.fr/api-payment/V4/Charge/Public/ProcessPayment/2;jsessionid={session}.
1099
+ */
1100
+ const LYRA_HOST = /^(?:api\.payzen\.eu|api\.systempay\.fr|api-sogecommerce\.societegenerale\.eu|api\.lyra\.com)\.?$/i;
1101
+ const LYRA_SESSION = /;jsessionid=[^/;?#]*/gi;
1102
+ function maskLyraSession(path) {
1103
+ return path.replace(LYRA_SESSION, ';jsessionid={session}');
1104
+ }
1015
1105
  /**
1016
1106
  * The processors whose card request carries its own credential in the URL path, with the hosts
1017
1107
  * that send it, the text that names those hosts in a string that is not a URL, and the mask. The
1018
1108
  * guard in redact-url.test.mjs fails for a payment-core recognizer that no one has sorted into
1019
1109
  * this list or out of it.
1020
1110
  */
1111
+ /**
1112
+ * Redsys's hosted payment page names its session in the path of every page and post after the entry
1113
+ * (/sis/pagoTarjeta;jsessionid=<id>), and that id is what carries a card post into the session the
1114
+ * merchant's signed entry opened. On Redsys's hosts the session id, in any case or escaped, reads
1115
+ * {session}. The entry itself carries none.
1116
+ */
1117
+ const REDSYS_HOST = /^sis(?:-t)?\.redsys\.es\.?$/i;
1118
+ const REDSYS_SESSION = /((?:;|%3b)jsessionid(?:=|%3d))[^/?#;]*/gi;
1119
+ function maskRedsysSession(path) {
1120
+ return path.replace(REDSYS_SESSION, '$1{session}');
1121
+ }
1122
+ /**
1123
+ * A PayPal order id opens PayPal's own checkout page for that order (/checkoutnow?token=<order id>) to
1124
+ * anyone who holds it, and PayPal's card confirms carry it in the path:
1125
+ * /v2/checkout/orders/<order id>/confirm-payment-source on www, api-m and cors.api. On PayPal's hosts the
1126
+ * segment after /orders/, in any case or after an escaped slash, reads {order}.
1127
+ */
1128
+ const PAYPAL_HOST = /(?:^|\.)paypal\.com\.?$/i;
1129
+ const PAYPAL_ORDER = /(\/orders(?:\/|%2f))[^/]*/gi;
1130
+ function maskPaypalOrder(path) {
1131
+ return path.replace(PAYPAL_ORDER, '$1{order}');
1132
+ }
1021
1133
  const PATH_CREDENTIALS = [
1022
1134
  { host: CREDITGUARD_HOST, named: /creditguard\.co\.il/i, mask: maskCreditguardTxId },
1023
1135
  { host: GRAVY_HOST, named: /gr4vy\.app/i, mask: maskGravySession },
1024
1136
  { host: RAPYD_HOST, named: /rapyd\.net/i, mask: maskRapydToken },
1025
1137
  { host: STRIPE_API_HOST, named: /api\.stripe\.com/i, mask: maskStripeSession },
1026
1138
  { host: ACI_HOST, named: /oppwa\.com/i, mask: maskAciCheckout },
1139
+ { host: PAYFAST_HOST, named: /payfast\.(?:io|co\.za)/i, mask: maskPayfastUuid },
1140
+ { host: PADDLE_CHECKOUT_HOST, named: /checkout-service\.paddle\.com/i, mask: maskPaddleCheckout },
1141
+ { host: REDSYS_HOST, named: /redsys\.es/i, mask: maskRedsysSession },
1142
+ { host: LYRA_HOST, named: /(?:payzen\.eu|systempay\.fr|societegenerale\.eu|lyra\.com)/i, mask: maskLyraSession },
1143
+ { host: PAYPAL_HOST, named: /paypal\.com/i, mask: maskPaypalOrder },
1027
1144
  ];
1028
1145
  /**
1029
1146
  * A URL as it may appear in an error message: origin + path only. A paused
@@ -1039,6 +1156,13 @@ const PATH_CREDENTIALS = [
1039
1156
  * On Rapyd's hosts the hosted page's id reads `{token}`: https://api.rapyd.net/v1/checkout/{token}.
1040
1157
  * On api.stripe.com a Checkout Session id reads `{session}`:
1041
1158
  * https://api.stripe.com/v1/payment_pages/{session}/confirm.
1159
+ * On Payfast's hosts the payment's uuid reads `{uuid}`:
1160
+ * https://payment.payfast.io/eng/method/CreditCard/{uuid}/pay.
1161
+ * On Redsys's hosts the session id after `;jsessionid=` reads `{session}`:
1162
+ * https://sis.redsys.es/sis/pagoTarjeta;jsessionid={session}.
1163
+ * On Lyra's API hosts the form token's session id reads `{session}`.
1164
+ * On PayPal's hosts an order id reads `{order}`:
1165
+ * https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source.
1042
1166
  * A string that is not a URL keeps the part before any `?` or `#`, with the
1043
1167
  * same segments masked.
1044
1168
  */
@@ -1127,17 +1251,26 @@ const SECURE_ACCEPTANCE_PREPARATION_REFUSALS = [
1127
1251
  const PADDLE_VGS_PREPARATION_REFUSALS = [
1128
1252
  'processor_unavailable', 'sandbox_host_on_live_row', 'live_host_on_sandbox_row', 'merchant_profile_mismatch', 'paddle_vgs_card_unsupported',
1129
1253
  ];
1254
+ /**
1255
+ * The API's refusals of a Wix Payments preparation, before any preparation exists, that prepareCheckout
1256
+ * surfaces by name: the lane is held to Agentcard's own account (`processor_unavailable`), a test client
1257
+ * asked for it (`live_host_on_sandbox_row`: Wix has no test environment), and a checkout's merchant is not
1258
+ * the name the reviewed store pin carries (`merchant_profile_mismatch`). An unreviewed store answers
1259
+ * `unsupported_checkout`, which PREPARATION_REFUSALS names for every checkout. Only for a Wix checkout.
1260
+ */
1261
+ const WIX_PAYMENTS_PREPARATION_REFUSALS = ['processor_unavailable', 'live_host_on_sandbox_row', 'merchant_profile_mismatch'];
1130
1262
  /**
1131
1263
  * The reason the cardholder's device declines a bound prepared row with, per processor, when its own
1132
1264
  * send boundary passed before the card left it (the Vault's sendWindow, 12 seconds after it last saw
1133
- * the checkout unbound): a prepared Amadeus SDK add and a prepared Secure Acceptance token create. It
1134
- * names the same known non-payment the adapters' own send window closes on (amadeus.ts), which can
1135
- * close later, so authorize() ends the attempt the way that window does, as a CheckoutPreparationError
1136
- * with that reason, and never as the cardholder's "no".
1265
+ * the checkout unbound): a prepared Amadeus SDK add, a prepared Secure Acceptance token create and a
1266
+ * prepared Wix tokenize request. It names the same known non-payment the adapters' own send window
1267
+ * closes on (amadeus.ts), which can close later, so authorize() ends the attempt the way that window
1268
+ * does, as a CheckoutPreparationError with that reason, and never as the cardholder's "no".
1137
1269
  */
1138
1270
  const PREPARED_SEND_WINDOW_DECLINES = Object.freeze({
1139
1271
  amadeus_checkout: 'amadeus_send_window_closed',
1140
1272
  [SECURE_ACCEPTANCE_PSP]: 'secure_acceptance_send_window_closed',
1273
+ [WIX_PAYMENTS_PSP]: 'wix_payments_send_window_closed',
1141
1274
  });
1142
1275
  /**
1143
1276
  * Refusals a CyberSource Flex preparation that tokenizes before Pay answers (4xx) before any
@@ -1341,7 +1474,7 @@ export class VaultClient {
1341
1474
  // wspay_confirm, shiji_sts_pinned_key, amadeus_checkout_actions.v1,
1342
1475
  // worldline_mycheckout_form, verifone_card_capture, ekashu_hosted_payment_page,
1343
1476
  // cybersource_sa_token_create, gravy_session_fields_put,
1344
- // moneris_hosted_tokenization) and
1477
+ // moneris_hosted_tokenization, paddle_vgs_cards, redsys_hosted_payment_page) and
1345
1478
  // the caller's company is one Agentcard turned that processor on for, so this
1346
1479
  // build arms Fiserv's card capture, GMO's getToken, CyberSource Flex's card
1347
1480
  // fields, Juspay's card flow, Datatrans's Secure Fields card post, the
@@ -1720,7 +1853,10 @@ export class VaultClient {
1720
1853
  ...(fiservPin ? { merchant_profile: fiservPin.id } : {}),
1721
1854
  ...(verifone ? { merchant_profile: verifone.id } : {}),
1722
1855
  ...(declaration ? { sandbox_declaration: { endpoint: declaration.endpoint, client_key: declaration.clientKey, environment: 'test' } } : {}),
1723
- ...(input.psp === 'creditguard' ? { payment_page_url: input.paymentPageUrl } : {}),
1856
+ // CreditGuard: this build answers the hosted page's own 3-D Secure calls (pre-claim.ts), so the
1857
+ // cardholder's device can run 3-D Secure before the submit; the API admits such a transaction
1858
+ // only for a build that says so.
1859
+ ...(input.psp === 'creditguard' ? { payment_page_url: input.paymentPageUrl, three_d_secure: 'device' } : {}),
1724
1860
  ...(eprotect ? { paypage_id: input.paypageId } : {}),
1725
1861
  ...(secureAcceptance ? { secure_acceptance_profile_id: secureAcceptanceProfileId } : {}),
1726
1862
  ...(flexContext ? { capture_context: flexContext.captureContext,
@@ -1816,6 +1952,7 @@ export class VaultClient {
1816
1952
  || (input.psp === 'fiserv' && FISERV_PREPARATION_REFUSALS.includes(error.code))
1817
1953
  || (input.psp === EPROTECT_PSP && EPROTECT_PREPARATION_REFUSALS.includes(error.code))
1818
1954
  || (input.psp === PADDLE_VGS_PSP && PADDLE_VGS_PREPARATION_REFUSALS.includes(error.code))
1955
+ || (input.psp === WIX_PAYMENTS_PSP && WIX_PAYMENTS_PREPARATION_REFUSALS.includes(error.code))
1819
1956
  || (!!flexContext && CYBERSOURCE_FLEX_PREPARATION_REFUSALS.includes(error.code))))
1820
1957
  || (input.psp === 'fiserv' && error.status === 502 && error.code === 'cse_key_unavailable')))
1821
1958
  throw fail(error.code);
@@ -2099,9 +2236,10 @@ export class VaultClient {
2099
2236
  // amount_required.
2100
2237
  if (!hasAmount && rec && AGENT_AMOUNT_PROCESSORS.includes(rec.psp))
2101
2238
  throw new AmountRequiredError(rec.psp);
2102
- // A Moneris token is charged later for whatever the merchant's server decides, so a zero pair
2103
- // would show the cardholder no price at all: it is refused like a missing one.
2104
- if (hasAmount && rec && rec.psp === MONERIS_PSP && Number(input.amount) <= 0)
2239
+ // A Moneris token is charged later for whatever the merchant's server decides, and a Payfast pay
2240
+ // charges the total its payment already holds, so a zero pair would show the cardholder no price
2241
+ // at all: it is refused like a missing one.
2242
+ if (hasAmount && rec && (rec.psp === MONERIS_PSP || rec.psp === PAYFAST_PSP) && Number(input.amount) <= 0)
2105
2243
  throw new AmountRequiredError(rec.psp);
2106
2244
  // Nor is the agent's pair tied to the page by anything but the total the
2107
2245
  // page shows: without it the cardholder could approve one figure while the
@@ -2420,10 +2558,14 @@ export class VaultClient {
2420
2558
  // the page's reviewed getCvvId keys (no keys, no continuation);
2421
2559
  // every other processor names a holder path, as before.
2422
2560
  const shiji = rec?.psp === SHIJI_STS_PSP;
2561
+ // Paddle TokenEx carries the two ciphertexts at tokenex_card and the five facts beside them in
2562
+ // `paddle_tokenex_facts` (the expiry parts are numbers, so they cannot ride `fields`).
2563
+ const paddleTokenex = rec?.psp === PADDLE_TOKENEX_PSP;
2423
2564
  const fieldsOk = shiji ? isShijiStsServedSubstitution(sub) && isShijiStsServedCompanion(s.shiji_sts_companion)
2424
- : sub && typeof sub === 'object' && sub.encoding === 'json' && typeof sub.at === 'string' && sub.at
2425
- && sub.fields && typeof sub.fields === 'object' && !Array.isArray(sub.fields)
2426
- && Object.values(sub.fields).every((v) => typeof v === 'string' && v.length > 0);
2565
+ : paddleTokenex ? isPaddleTokenexServedSubstitution(sub) && isPaddleTokenexServedFacts(s.paddle_tokenex_facts)
2566
+ : sub && typeof sub === 'object' && sub.encoding === 'json' && typeof sub.at === 'string' && sub.at
2567
+ && sub.fields && typeof sub.fields === 'object' && !Array.isArray(sub.fields)
2568
+ && Object.values(sub.fields).every((v) => typeof v === 'string' && v.length > 0);
2427
2569
  if (!fieldsOk)
2428
2570
  throw new PaymentOutcomeUnknownError(authorizationId, 'cse_substitutions_malformed');
2429
2571
  // A Fiserv approval carries exactly its envelope's four members at
@@ -2448,6 +2590,7 @@ export class VaultClient {
2448
2590
  ...(removeRaw ? { remove: [...removeRaw] } : {}),
2449
2591
  },
2450
2592
  ...(shiji ? { shijiStsCompanion: Object.freeze({ keySequenceSha256: Object.freeze([...s.shiji_sts_companion.key_sequence_sha256]) }) } : {}),
2593
+ ...(paddleTokenex ? { paddleTokenexFacts: Object.freeze({ ...s.paddle_tokenex_facts }) } : {}),
2451
2594
  ...amountAuthority,
2452
2595
  ...execution,
2453
2596
  };
@@ -2641,6 +2784,9 @@ export class VaultClient {
2641
2784
  chargedAmount: typeof s.charged_amount === 'number' ? s.charged_amount : null,
2642
2785
  chargedCurrency: typeof s.charged_currency === 'string' ? s.charged_currency : null,
2643
2786
  chargedKind: s.charged_kind === 'captured' || s.charged_kind === 'authorized' || s.charged_kind === 'none' ? s.charged_kind : null,
2787
+ // An EBANX token approval that covers the sends after it names until when (ebanx.ts EbanxFollowUps).
2788
+ ...(rec?.psp === 'ebanx' && typeof s.follow_ups?.until === 'string' && Number.isFinite(Date.parse(s.follow_ups.until))
2789
+ ? { followUpsUntil: s.follow_ups.until } : {}),
2644
2790
  ...amountAuthority,
2645
2791
  ...execution,
2646
2792
  };
@@ -2838,6 +2984,79 @@ export class VaultClient {
2838
2984
  }
2839
2985
  return { setupIntentId: confirmed };
2840
2986
  }
2987
+ /**
2988
+ * Send one request an approved EBANX token request covers (ebanx.ts says which: EBANX's 3-D Secure calls and the
2989
+ * hosted page's confirm, bound to that approval) from the cardholder's device, and resolve with EBANX's answer for
2990
+ * the paused request: POST /v2/checkout/authorizations/:id/follow_ups with the request as the page sent it, the
2991
+ * agent's placeholder card in it, then the send read back every half second until the device answered
2992
+ * (EBANX_FOLLOW_UP_READ_MS, whatever `pollIntervalMs` says, so the reads stay inside the API's per-approval budget).
2993
+ * A read the API refuses as too many (429) waits longer each time, doubling from a second up to
2994
+ * EBANX_FOLLOW_UP_BACKOFF_MAX_MS, inside the same deadline, and the next answered read goes back to the half second.
2995
+ * No prompt: the cardholder approved the token request, and the Vault page that sent it sends these while it stays
2996
+ * open. Rejects with an EbanxFollowUpError naming why there is no answer and whether the card may have reached
2997
+ * EBANX: the API refused the send (nothing sent), no device started it in time, the device refused it or withheld
2998
+ * an answer that carried the card, or `options.until` (the approval's window, plus the API's own clocks) passed.
2999
+ */
3000
+ async sendEbanxFollowUp(authorizationId, request, options = {}) {
3001
+ if (!/^cauth_[A-Za-z0-9_-]{1,128}$/.test(authorizationId))
3002
+ throw new Error('Invalid authorization ID.');
3003
+ const base = `/v2/checkout/authorizations/${authorizationId}/follow_ups`;
3004
+ // The API's own clocks: a minute for a device to start a send and a minute for its answer.
3005
+ const deadline = Math.min(Date.now() + 3 * 60_000, (options.until ?? Number.POSITIVE_INFINITY) + 2 * 60_000);
3006
+ const signal = options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(Math.max(1, deadline - Date.now()))]) : AbortSignal.timeout(Math.max(1, deadline - Date.now()));
3007
+ let created;
3008
+ try {
3009
+ created = await this.post(base, { request: { url: request.url, method: 'POST', headers: pickHeaders(request.headers, []), body: request.body } }, AbortSignal.any([signal, AbortSignal.timeout(15_000)]));
3010
+ }
3011
+ catch (error) {
3012
+ // A refusal (a 4xx answer) stored no send, so nothing left. Any other failure (a 5xx, a timeout, a dropped
3013
+ // connection, an unreadable 201) may have come after the API stored the send, which the device then sends:
3014
+ // the card may reach EBANX, and for a confirm that is a payment whose outcome is unknown.
3015
+ const refused = error instanceof CheckoutApiError && error.status >= 400 && error.status < 500;
3016
+ const reason = error instanceof CheckoutApiError
3017
+ ? (typeof error.details.reason === 'string' && /^[a-z_]{1,64}$/.test(error.details.reason) ? error.details.reason : error.code ?? `http_${error.status}`)
3018
+ : 'follow_up_unavailable';
3019
+ throw new EbanxFollowUpError(reason, !refused, authorizationId);
3020
+ }
3021
+ const id = created?.id;
3022
+ if (typeof id !== 'string' || !/^cfu_[0-9a-f]{24}$/.test(id) || created.authorization !== authorizationId) {
3023
+ throw new EbanxFollowUpError('follow_up_unrecognized', true, authorizationId);
3024
+ }
3025
+ // Reads the API refused in a row as too many (429): each waits twice as long as the last before the next read.
3026
+ let limited = 0;
3027
+ for (;;) {
3028
+ let read = null;
3029
+ try {
3030
+ read = await this.get(`${base}/${id}`, AbortSignal.any([signal, AbortSignal.timeout(15_000)]));
3031
+ limited = 0;
3032
+ }
3033
+ catch (error) {
3034
+ if (signal.aborted)
3035
+ throw new EbanxFollowUpError('answer_late', true, authorizationId);
3036
+ if (error instanceof CheckoutApiError && error.status === 429)
3037
+ limited += 1;
3038
+ }
3039
+ if (read?.status === 'answered') {
3040
+ const r = read.response;
3041
+ if (!r || !Number.isInteger(r.status) || r.status < 200 || r.status > 599 || typeof r.body !== 'string' || !r.headers || typeof r.headers !== 'object') {
3042
+ throw new EbanxFollowUpError('follow_up_unrecognized', true, authorizationId);
3043
+ }
3044
+ const headers = {};
3045
+ for (const [name, value] of Object.entries(r.headers))
3046
+ if (name.toLowerCase() === 'content-type' && typeof value === 'string')
3047
+ headers['content-type'] = value;
3048
+ return { status: r.status, headers: headers['content-type'] ? headers : { 'content-type': 'application/json' }, body: r.body };
3049
+ }
3050
+ if (read?.status === 'failed' || read?.status === 'expired') {
3051
+ const reason = typeof read.failure_reason === 'string' && /^[a-z_]{1,64}$/.test(read.failure_reason) ? read.failure_reason : read.status;
3052
+ throw new EbanxFollowUpError(reason, read.card_sent !== false, authorizationId);
3053
+ }
3054
+ if (signal.aborted || Date.now() >= deadline)
3055
+ throw new EbanxFollowUpError('answer_late', true, authorizationId);
3056
+ const wait = limited === 0 ? EBANX_FOLLOW_UP_READ_MS : Math.min(EBANX_FOLLOW_UP_BACKOFF_MAX_MS, 1000 * 2 ** (limited - 1));
3057
+ await interruptibleSleep(Math.max(1, Math.min(wait, deadline - Date.now())), signal);
3058
+ }
3059
+ }
2841
3060
  /**
2842
3061
  * Create a merchant-session probe, the phase-0 read of the deterministic merchant total:
2843
3062
  * `user` is the org's connected user, who opens the returned Vault link and reads the
@@ -51,11 +51,13 @@ declare function parseCreditguardTransaction(body: any, txId: any): {
51
51
  dccActive: boolean;
52
52
  recaptcha: boolean;
53
53
  jwePublicKey: string;
54
+ jwePublicKeyJson: any;
54
55
  personalIdAlwaysEnabled: boolean;
55
56
  inputs: any;
56
57
  } | null;
57
- declare function creditguardTransactionRefusal(txn: any, expectation: any): "otp" | "keep_on_file" | "dcc" | "installments" | "personal_id" | "jwe_unsupported" | "transaction_type" | "amount_mismatch" | "environment_mismatch" | "currency_mismatch" | "transaction_unreadable" | "three_d_secure" | "form_inputs" | "card_field_hidden" | "jwe_public_key_malformed" | null;
58
+ declare function creditguardTransactionRefusal(txn: any, expectation: any): "otp" | "keep_on_file" | "dcc" | "installments" | "personal_id" | "jwe_unsupported" | "transaction_type" | "amount_mismatch" | "environment_mismatch" | "currency_mismatch" | "three_d_secure" | "transaction_unreadable" | "form_inputs" | "card_field_hidden" | "jwe_public_key_malformed" | null;
58
59
  declare function creditguardSendWindowRefusal(timing: any): "timing_unknown" | "page_expired" | "recaptcha_expired" | null;
60
+ declare function creditguardBodyTransactionRefusal(body: any, txn: any): "jwe_mismatch" | null;
59
61
  declare var CREDITGUARD_REFUSAL_CODES: readonly string[];
60
62
  declare function creditguardResponseOutcome(status: any, body: any, options: any): {
61
63
  outcome: string;
@@ -76,4 +78,100 @@ declare function creditguardFacts(status: any, body: any): {
76
78
  errorCode: any;
77
79
  refusalCode: any;
78
80
  };
79
- export { CREDITGUARD_JWE_HEADER, CREDITGUARD_OPERATION, CREDITGUARD_PAGE_WINDOW_MS, CREDITGUARD_PPS_HOSTS, CREDITGUARD_RECAPTCHA_WINDOW_MS, CREDITGUARD_REFUSAL_CODES, CREDITGUARD_SANDBOX_HOST, classifyCreditguardRequest, creditguardBodyRefusal, creditguardFacts, creditguardNuveiRefusal, creditguardPageProcessUrl, creditguardPpDomainHost, creditguardPreparationEnvironment, creditguardProcessRefusal, creditguardProcessTxId, creditguardRefusalMessage, creditguardReplayRefusal, creditguardResponseOutcome, creditguardSendWindowRefusal, creditguardTransactionReadUrl, creditguardTransactionRefusal, encryptCreditguardJwe, injectCreditguardCard, injectCreditguardJweCard, isCreditguardJweCompact, isCreditguardPpsDocument, isCreditguardProcessRequest, isCreditguardProcessUrl, parseCreditguardJwePublicKey, parseCreditguardTransaction, readCreditguardJson, sanitizeCreditguardResponse };
81
+ declare var CREDITGUARD_COMPANION_PATHS: Readonly<{
82
+ card_details: "/card/details";
83
+ ddc_init: "/cc/ddc/init";
84
+ threeds_init: "/cg3ds/process/init";
85
+ threeds_outcome: "/cg3ds/process/outcome";
86
+ }>;
87
+ declare var CREDITGUARD_DEVICE_STEPS: readonly string[];
88
+ declare var CREDITGUARD_CARDINAL_HOSTS: Readonly<{
89
+ production: readonly string[];
90
+ sandbox: readonly string[];
91
+ }>;
92
+ declare var CREDITGUARD_CARDINAL_COLLECT_URLS: Readonly<{
93
+ production: "https://client.cardinaltrusted.com/centinelapi/V2/Cruise/Collect";
94
+ sandbox: "https://cas.client.cardinaltrusted.com/centinelapi/V2/Cruise/Collect";
95
+ }>;
96
+ declare function creditguardCompanionStep(url: any): {
97
+ step: string;
98
+ txId: string;
99
+ } | null;
100
+ declare function creditguardDeviceStepUrl(processUrl: any, step: any): string | null;
101
+ declare function creditguardCompanionCall(url: any, method: any, body: any): {
102
+ step: null;
103
+ txId: null;
104
+ refusal: string;
105
+ } | {
106
+ refusal: string | null;
107
+ step: string;
108
+ txId: string;
109
+ } | null;
110
+ declare function creditguardCompanionAnswer(step: any): {
111
+ status: number;
112
+ headers: {
113
+ "content-type": string;
114
+ };
115
+ body: any;
116
+ } | null;
117
+ declare function creditguardDeviceDdcRequest(processUrl: any, card: any, options?: {}): Promise<{
118
+ url: string;
119
+ method: string;
120
+ headers: {
121
+ "content-type": "application/json";
122
+ accept: "application/json";
123
+ };
124
+ body: string;
125
+ }>;
126
+ declare function creditguardDeviceThreeDSInitRequest(processUrl: any, card: any, options?: {}): {
127
+ url: string;
128
+ method: string;
129
+ headers: {
130
+ "content-type": "application/json";
131
+ accept: "application/json";
132
+ };
133
+ body: string;
134
+ };
135
+ declare function creditguardDeviceThreeDSOutcomeRequest(processUrl: any): {
136
+ url: string;
137
+ method: string;
138
+ headers: {
139
+ "content-type": "application/json";
140
+ accept: "application/json";
141
+ };
142
+ body: string;
143
+ };
144
+ declare function creditguardDeviceStepRefusal(request: any, processUrl: any): "headers" | "malformed_body" | "wallet" | "unexpected_field" | "gift_card" | "dcc" | "txid_mismatch" | "card_fields" | "not_companion_request" | null;
145
+ declare function creditguardCardinalUrl(url: any, purpose: any, environment: any): string | null;
146
+ declare function isCreditguardCardinalOrigin(origin: any): boolean;
147
+ declare function creditguardCardinalMessage(data: any): {
148
+ sessionId: any;
149
+ } | null;
150
+ declare function creditguardDdcAnswer(status: any, body: any, environment: any): {
151
+ collectUrl: any;
152
+ ddcJwt: any;
153
+ } | null;
154
+ declare function creditguardThreeDSInitAnswer(status: any, body: any, environment: any): {
155
+ outcome: string;
156
+ reason: string;
157
+ stepUpUrl?: undefined;
158
+ stepUpJwt?: undefined;
159
+ } | {
160
+ outcome: string;
161
+ reason?: undefined;
162
+ stepUpUrl?: undefined;
163
+ stepUpJwt?: undefined;
164
+ } | {
165
+ outcome: string;
166
+ stepUpUrl: string;
167
+ stepUpJwt: any;
168
+ reason?: undefined;
169
+ };
170
+ declare function creditguardThreeDSOutcomeAnswer(status: any, body: any): {
171
+ outcome: string;
172
+ reason: string;
173
+ } | {
174
+ outcome: string;
175
+ reason?: undefined;
176
+ };
177
+ export { CREDITGUARD_CARDINAL_COLLECT_URLS, CREDITGUARD_CARDINAL_HOSTS, CREDITGUARD_COMPANION_PATHS, CREDITGUARD_DEVICE_STEPS, CREDITGUARD_JWE_HEADER, CREDITGUARD_OPERATION, CREDITGUARD_PAGE_WINDOW_MS, CREDITGUARD_PPS_HOSTS, CREDITGUARD_RECAPTCHA_WINDOW_MS, CREDITGUARD_REFUSAL_CODES, CREDITGUARD_SANDBOX_HOST, classifyCreditguardRequest, creditguardBodyRefusal, creditguardBodyTransactionRefusal, creditguardCardinalMessage, creditguardCardinalUrl, creditguardCompanionAnswer, creditguardCompanionCall, creditguardCompanionStep, creditguardDdcAnswer, creditguardDeviceDdcRequest, creditguardDeviceStepRefusal, creditguardDeviceStepUrl, creditguardDeviceThreeDSInitRequest, creditguardDeviceThreeDSOutcomeRequest, creditguardFacts, creditguardNuveiRefusal, creditguardPageProcessUrl, creditguardPpDomainHost, creditguardPreparationEnvironment, creditguardProcessRefusal, creditguardProcessTxId, creditguardRefusalMessage, creditguardReplayRefusal, creditguardResponseOutcome, creditguardSendWindowRefusal, creditguardThreeDSInitAnswer, creditguardThreeDSOutcomeAnswer, creditguardTransactionReadUrl, creditguardTransactionRefusal, encryptCreditguardJwe, injectCreditguardCard, injectCreditguardJweCard, isCreditguardCardinalOrigin, isCreditguardJweCompact, isCreditguardPpsDocument, isCreditguardProcessRequest, isCreditguardProcessUrl, parseCreditguardJwePublicKey, parseCreditguardTransaction, readCreditguardJson, sanitizeCreditguardResponse };