@agent-cards/sdk 0.23.12 → 0.23.14

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 (42) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/LICENSE +38 -0
  3. package/PREFLIGHT.md +1 -1
  4. package/README.md +53 -14
  5. package/dist/adyen-merchant-hosted.generated.js +2 -2
  6. package/dist/builtin-registry.generated.js +1 -1
  7. package/dist/card-fields.generated.js +843 -3
  8. package/dist/cdp.js +101 -13
  9. package/dist/client.d.ts +91 -2
  10. package/dist/client.js +205 -13
  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 +35 -3
  22. package/dist/lifecycle.js +38 -3
  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/paddle-vgs-pay.d.ts +55 -0
  28. package/dist/paddle-vgs-pay.js +115 -0
  29. package/dist/paddle-vgs.generated.d.ts +29 -1
  30. package/dist/paddle-vgs.generated.js +124 -5
  31. package/dist/paysafe.generated.d.ts +5 -7
  32. package/dist/paysafe.generated.js +50 -18
  33. package/dist/pre-claim.d.ts +46 -1
  34. package/dist/pre-claim.js +338 -17
  35. package/dist/preparation.d.ts +13 -0
  36. package/dist/preparation.js +22 -0
  37. package/dist/prepared-processor.js +6 -2
  38. package/dist/primer.generated.js +878 -38
  39. package/dist/redsys.d.ts +42 -0
  40. package/dist/redsys.js +68 -0
  41. package/dist/stripe-checkout.generated.js +479 -11
  42. package/package.json +9 -6
@@ -0,0 +1,95 @@
1
+ // Paddle TokenEx, the card leg of a Paddle checkout whose frame names the tokenization provider "tokenex",
2
+ // as the SDK sees it (payment-core paddle-tokenex.js has the request's shape, the device encryption and the
3
+ // substitution). Paddle's frame RSA-encrypts the card number and the CVV in the page itself, each on its
4
+ // own, under the account-level TokenEx public key the checkout serves, and carries both inside one request:
5
+ // POST https://checkout-service.paddle.com/checkout/<id>/pay (Paddle Classic)
6
+ // POST https://checkout-service.paddle.com/transaction-checkout/<id>/pay (Paddle Billing)
7
+ // (sandbox-checkout-service.paddle.com in test). The agent's page encrypts a placeholder card, so the /pay
8
+ // names the placeholder's ciphertexts and the placeholder's first six, last four, expiry and brand. This
9
+ // build holds that /pay and writes over them the approved card's own: the two ciphertexts the cardholder's
10
+ // device produced under the key payment-core pins (never a key from a row, a request or a page) and the
11
+ // five metadata facts the device read from the real card. Every other byte goes as the frame wrote it
12
+ // (mode cse), so the full card number never reaches the agent's runtime.
13
+ //
14
+ // The generic Adyen swap writes only the named ciphertext path, not the five metadata facts a sibling of
15
+ // tokenex_card, so this lane is dispatched by psp to substitutePaddleTokenex instead, exactly as Shiji STS
16
+ // is dispatched for its own body root. A production checkout-service refuses the Vault's cross-origin /pay
17
+ // (403 at Cloudflare), so the device cannot send /pay itself and the lane rides in the agent's page.
18
+ //
19
+ // The registry entry ships dark (capability paddle_tokenex_pay). This build declares the capability on
20
+ // syncRegistry, and the API serves the recognizer only to an org Agentcard enabled for paddle_tokenex, so
21
+ // for every other org nothing here fires: isCardRequest never recognizes a TokenEx /pay and Paddle's
22
+ // requests go through untouched. Autopilot never pays this processor: the API, the authority and the
23
+ // enclave refuse it.
24
+ import { PADDLE_TOKENEX_FIELDS, PADDLE_TOKENEX_FACT_KEYS, paddleTokenexPayTarget, substitutePaddleTokenex } from './paddle-tokenex.generated.js';
25
+ /** The PSP id the API and the recognizer name this lane, re-exported so the client dispatches by it. */
26
+ export { PADDLE_TOKENEX_PSP } from './paddle-tokenex.generated.js';
27
+ /** The dark-launch capability a build declares when it fills a Paddle TokenEx /pay with the device's card. */
28
+ export const PADDLE_TOKENEX_CAPABILITY = 'paddle_tokenex_pay';
29
+ /** The holder the two ciphertexts are served at (the frame's tokenex_card), named once so both checks share it. */
30
+ const SERVED_AT = 'data.payment_method_data.card.tokenex_card';
31
+ const CIPHERTEXT_FIELDS = PADDLE_TOKENEX_FIELDS;
32
+ const FACT_KEYS = PADDLE_TOKENEX_FACT_KEYS;
33
+ const STRING_FACTS = ['card_first_six', 'card_last_four', 'card_brand'];
34
+ const NUMBER_FACTS = ['expiry_month', 'expiry_year'];
35
+ /**
36
+ * Whether an approved Paddle TokenEx authorization's served substitution is one this SDK can continue: JSON
37
+ * at the frame's tokenex_card holder, no sibling to remove, and exactly the two ciphertext fields, each a
38
+ * non-empty string and distinct from the other. substitutePaddleTokenex checks each against the pinned key
39
+ * for the /pay's environment; this is the shape gate the client runs before it acts.
40
+ */
41
+ export function isPaddleTokenexServedSubstitution(sub) {
42
+ if (!sub || typeof sub !== 'object' || Array.isArray(sub))
43
+ return false;
44
+ const { encoding, at, fields, remove } = sub;
45
+ if (encoding !== 'json' || at !== SERVED_AT || !fields || typeof fields !== 'object' || Array.isArray(fields))
46
+ return false;
47
+ if (remove !== undefined && !(Array.isArray(remove) && remove.length === 0))
48
+ return false;
49
+ const named = fields;
50
+ const names = Object.keys(named);
51
+ if (names.length !== CIPHERTEXT_FIELDS.length || !CIPHERTEXT_FIELDS.every((name) => typeof named[name] === 'string' && named[name].length > 0))
52
+ return false;
53
+ return named.encrypted_card_number !== named.encrypted_cvv;
54
+ }
55
+ /**
56
+ * Whether the API's `paddle_tokenex_facts` beside an approved set is the five facts this SDK can write over
57
+ * the placeholder's: exactly the five keys, the three strings as strings and the two expiry parts as
58
+ * numbers. paddleTokenexSubstitutionOf checks the digits, range and network at the swap; this is the shape
59
+ * gate the client runs before it acts, so a malformed approval is refused before anything is written.
60
+ */
61
+ export function isPaddleTokenexServedFacts(value) {
62
+ if (!value || typeof value !== 'object' || Array.isArray(value))
63
+ return false;
64
+ const named = value;
65
+ const names = Object.keys(named);
66
+ if (names.length !== FACT_KEYS.length || !FACT_KEYS.every((name) => name in named))
67
+ return false;
68
+ return STRING_FACTS.every((name) => typeof named[name] === 'string' && named[name].length > 0)
69
+ && NUMBER_FACTS.every((name) => typeof named[name] === 'number' && Number.isInteger(named[name]));
70
+ }
71
+ /**
72
+ * The continuation of an approved Paddle TokenEx /pay: the paused body with the device's two ciphertexts
73
+ * written over the placeholder's at tokenex_card and the five facts over the placeholder's at metadata
74
+ * (payment-core substitutePaddleTokenex, which refuses a ciphertext that is not one under the pinned key
75
+ * for the /pay's environment, or one already in the paused body).
76
+ *
77
+ * Only into `approved`, the paused /pay the approval was created from: `url` and `body` must equal it byte
78
+ * for byte. The account-level TokenEx key is the same for every checkout of that seller, so the device's
79
+ * ciphertext could be decrypted in any later /pay of the same shape; binding it to the one request keeps
80
+ * this lane from ever submitting one checkout's ciphertext to another.
81
+ *
82
+ * Null when neither URL is a TokenEx /pay, so every other processor keeps the generic swap. Throws for any
83
+ * other body or URL, and for a substitution or facts the shape gates refuse, in each case before anything
84
+ * continues.
85
+ */
86
+ export function paddleTokenexTokenContinuation(url, body, sub, facts, approved) {
87
+ if (paddleTokenexPayTarget(url) === null && paddleTokenexPayTarget(approved.url) === null)
88
+ return null;
89
+ if (url !== approved.url || body !== approved.body)
90
+ throw new Error('Refusing to substitute: the paused request is not the Paddle TokenEx /pay the cardholder approved.');
91
+ if (!isPaddleTokenexServedSubstitution(sub) || !isPaddleTokenexServedFacts(facts))
92
+ throw new Error('Refusing to substitute: the Paddle TokenEx approval is not two ciphertexts and five card facts.');
93
+ const substitution = { encrypted_card_number: sub.fields.encrypted_card_number, encrypted_cvv: sub.fields.encrypted_cvv, ...facts };
94
+ return { body: substitutePaddleTokenex(url, body, substitution) };
95
+ }
@@ -0,0 +1,55 @@
1
+ import type { ReplayResponse } from './client.js';
2
+ /**
3
+ * Why a /pay naming a stored card was aborted, reported as the `reason` of the `unsupported_checkout` event:
4
+ * paddle_vgs_pay_unbound this attachment handed no stored card's id to the frame that sent it
5
+ * paddle_vgs_pay_expired it came more than a minute after the id reached the frame
6
+ * paddle_vgs_pay_used the one /pay for that id already went
7
+ * paddle_vgs_pay_card_mismatch it names another stored card, or the other environment's checkout service
8
+ * paddle_vgs_pay_unsupported anything else payment-core's rule refuses: another shape, a card saved at
9
+ * Paddle for later, a network the frame chose from the typed card
10
+ */
11
+ export type PaddleVgsPayRefusal = 'paddle_vgs_pay_unbound' | 'paddle_vgs_pay_expired' | 'paddle_vgs_pay_used' | 'paddle_vgs_pay_card_mismatch' | 'paddle_vgs_pay_unsupported';
12
+ /** The patterns attachToCdp holds Paddle's two checkout services under, only for an attachment the API served Paddle VGS to. */
13
+ export declare const PADDLE_VGS_PAY_PATTERNS: readonly string[];
14
+ /** Whether `url` is on one of Paddle's two checkout services: what attachToPlaywright routes, once served. */
15
+ export declare function isPaddleCheckoutService(url: string): boolean;
16
+ /** Whether the synced registry serves Paddle VGS's card request: an org Agentcard turned Paddle on for. */
17
+ export declare function recognizesPaddleVgs(vault: {
18
+ isCardRequest?: (url: string, method?: string) => boolean;
19
+ } | null | undefined): boolean;
20
+ /** The URL an event names for a /pay: its origin and path, with the checkout's id as {checkout}. */
21
+ export declare function paddleVgsPayEventUrl(url: string): string;
22
+ /**
23
+ * One attachment's /pay binding: at most one, to the stored card's id it last handed a Paddle frame. `arm` runs
24
+ * right before the id is handed over, `release` when the attachment is cancelled, and `judge` for every request
25
+ * to Paddle's checkout services. `served` says whether the synced registry serves Paddle VGS; it is asked only
26
+ * about a POST to /pay, so no other request reads the registry here.
27
+ */
28
+ export declare class PaddleVgsPayGate {
29
+ private readonly served;
30
+ private readonly now;
31
+ private bound;
32
+ constructor(served: () => boolean, now?: () => number);
33
+ /** Whether the gate judges this request: a POST to /pay, on an attachment the API served Paddle VGS to. */
34
+ holds(url: string, method: string): boolean;
35
+ /**
36
+ * Bind the next /pay of `frame`, the frame about to receive `replay` for its card request to `cardsUrl`, to the
37
+ * stored card's id in the replay and the approved card's facts the API served beside it. Does nothing for a
38
+ * request to anything but Paddle's card vault. Throws when the frame is unknown, or the replay carries no id or
39
+ * no facts payment-core reads: the id must then not reach the frame, since its /pay could only carry the
40
+ * placeholder's facts.
41
+ */
42
+ arm(frame: unknown, cardsUrl: string, replay: ReplayResponse): void;
43
+ /** Drop the binding: no /pay continues with these facts after this. */
44
+ release(): void;
45
+ /**
46
+ * What becomes of a request to Paddle's checkout services: null when it is not a /pay naming a stored card (it
47
+ * continues untouched), the body to continue it with (payment-core paddleVgsPayVerdict), or why it is aborted.
48
+ * The binding answers one /pay: the first one from its frame continues or is refused, and either way ends it.
49
+ */
50
+ judge(frame: unknown, url: string, method: string, body: string | null): {
51
+ body: string;
52
+ } | {
53
+ reason: PaddleVgsPayRefusal;
54
+ } | null;
55
+ }
@@ -0,0 +1,115 @@
1
+ // Paddle's /pay, the request that charges the card the cardholder's phone stored at VGS, as the SDK sees it
2
+ // (payment-core paddle-vgs.js has the request's shape and the rule).
3
+ //
4
+ // Once the SDK hands the stored card's id to Paddle's checkout frame, the frame posts /pay to its checkout
5
+ // service with that id beside card facts it read from the number typed into its own form: on this lane the
6
+ // agent's placeholder, so the placeholder's first six, last four and brand. This build holds that /pay and
7
+ // writes over those five values the approved card's own, which the phone read from the card it sent to VGS and
8
+ // the API served with the id (payment-core paddleVgsPayVerdict). Every other byte goes as the frame wrote it.
9
+ //
10
+ // The binding: the frame that received the stored card's id, the id, the vault's environment and the approved
11
+ // card's facts, for PADDLE_VGS_CARD_ID_WINDOW_MS after the hand-over, for one /pay. A /pay naming a stored card
12
+ // is aborted, so Paddle charges nothing, when this attachment handed no id to that frame
13
+ // (paddle_vgs_pay_unbound), after the window (paddle_vgs_pay_expired), after the one /pay already went
14
+ // (paddle_vgs_pay_used), or when payment-core's rule refuses it: another stored card or environment
15
+ // (paddle_vgs_pay_card_mismatch), or any other shape (paddle_vgs_pay_unsupported). A /pay naming no stored card
16
+ // (PayPal, a wallet) carries none of the cardholder's cards and continues untouched.
17
+ //
18
+ // Nothing is decided without the registry: an attachment the API did not serve Paddle VGS to never holds
19
+ // Paddle's checkout service, so its /pay continues untouched, exactly as before.
20
+ import { PADDLE_VGS_CARD_ID_WINDOW_MS, PADDLE_VGS_CARDS_URLS, paddleVgsCardFactsOf, paddleVgsEnvironment, paddleVgsPayCarriesStoredCard, paddleVgsPayTarget, paddleVgsPayVerdict, } from './paddle-vgs.generated.js';
21
+ const CHECKOUT_SERVICES = ['https://checkout-service.paddle.com', 'https://sandbox-checkout-service.paddle.com'];
22
+ /** The patterns attachToCdp holds Paddle's two checkout services under, only for an attachment the API served Paddle VGS to. */
23
+ export const PADDLE_VGS_PAY_PATTERNS = CHECKOUT_SERVICES.map((origin) => `${origin}/*`);
24
+ /** Whether `url` is on one of Paddle's two checkout services: what attachToPlaywright routes, once served. */
25
+ export function isPaddleCheckoutService(url) {
26
+ try {
27
+ return CHECKOUT_SERVICES.includes(new URL(url).origin);
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /** Whether the synced registry serves Paddle VGS's card request: an org Agentcard turned Paddle on for. */
34
+ export function recognizesPaddleVgs(vault) {
35
+ if (!vault || typeof vault.isCardRequest !== 'function')
36
+ return false;
37
+ try {
38
+ return vault.isCardRequest(PADDLE_VGS_CARDS_URLS.production, 'POST') || vault.isCardRequest(PADDLE_VGS_CARDS_URLS.sandbox, 'POST');
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /** The URL an event names for a /pay: its origin and path, with the checkout's id as {checkout}. */
45
+ export function paddleVgsPayEventUrl(url) {
46
+ return url.replace(/^(https:\/\/[^/]+\/(?:checkout|transaction-checkout)\/)[^/]+(\/pay)$/, '$1{checkout}$2');
47
+ }
48
+ const known = (frame) => frame !== undefined && frame !== null && frame !== '';
49
+ /**
50
+ * One attachment's /pay binding: at most one, to the stored card's id it last handed a Paddle frame. `arm` runs
51
+ * right before the id is handed over, `release` when the attachment is cancelled, and `judge` for every request
52
+ * to Paddle's checkout services. `served` says whether the synced registry serves Paddle VGS; it is asked only
53
+ * about a POST to /pay, so no other request reads the registry here.
54
+ */
55
+ export class PaddleVgsPayGate {
56
+ served;
57
+ now;
58
+ bound = null;
59
+ constructor(served, now = Date.now) {
60
+ this.served = served;
61
+ this.now = now;
62
+ }
63
+ /** Whether the gate judges this request: a POST to /pay, on an attachment the API served Paddle VGS to. */
64
+ holds(url, method) {
65
+ return String(method).toUpperCase() === 'POST' && paddleVgsPayTarget(url) !== null && this.served();
66
+ }
67
+ /**
68
+ * Bind the next /pay of `frame`, the frame about to receive `replay` for its card request to `cardsUrl`, to the
69
+ * stored card's id in the replay and the approved card's facts the API served beside it. Does nothing for a
70
+ * request to anything but Paddle's card vault. Throws when the frame is unknown, or the replay carries no id or
71
+ * no facts payment-core reads: the id must then not reach the frame, since its /pay could only carry the
72
+ * placeholder's facts.
73
+ */
74
+ arm(frame, cardsUrl, replay) {
75
+ const environment = paddleVgsEnvironment(cardsUrl);
76
+ if (environment === null)
77
+ return;
78
+ this.release();
79
+ const token = replay.mode === undefined || replay.mode === 'token' ? replay : null;
80
+ const facts = token ? paddleVgsCardFactsOf(token.processorContext) : null;
81
+ let cardId = null;
82
+ try {
83
+ cardId = token ? JSON.parse(token.body)?.data?.id : null;
84
+ }
85
+ catch {
86
+ cardId = null;
87
+ }
88
+ if (!known(frame) || !facts || typeof cardId !== 'string')
89
+ throw new Error('paddle_vgs_card_facts_unavailable');
90
+ this.bound = { frame, environment, cardId, facts, until: this.now() + PADDLE_VGS_CARD_ID_WINDOW_MS, used: false };
91
+ }
92
+ /** Drop the binding: no /pay continues with these facts after this. */
93
+ release() {
94
+ this.bound = null;
95
+ }
96
+ /**
97
+ * What becomes of a request to Paddle's checkout services: null when it is not a /pay naming a stored card (it
98
+ * continues untouched), the body to continue it with (payment-core paddleVgsPayVerdict), or why it is aborted.
99
+ * The binding answers one /pay: the first one from its frame continues or is refused, and either way ends it.
100
+ */
101
+ judge(frame, url, method, body) {
102
+ if (!this.holds(url, method) || !paddleVgsPayCarriesStoredCard(body))
103
+ return null;
104
+ const bound = this.bound;
105
+ if (!bound || !known(frame) || frame !== bound.frame)
106
+ return { reason: 'paddle_vgs_pay_unbound' };
107
+ if (bound.used)
108
+ return { reason: 'paddle_vgs_pay_used' };
109
+ bound.used = true;
110
+ if (this.now() > bound.until)
111
+ return { reason: 'paddle_vgs_pay_expired' };
112
+ const verdict = paddleVgsPayVerdict({ url, method, body }, { environment: bound.environment, cardId: bound.cardId, facts: bound.facts });
113
+ return verdict.verdict === 'continue' ? { body: verdict.body } : { reason: verdict.code };
114
+ }
115
+ }
@@ -47,4 +47,32 @@ declare function paddleVgsAnswerOutcome(status: any, text: any, card: any): {
47
47
  reason?: undefined;
48
48
  code?: undefined;
49
49
  };
50
- export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
50
+ declare var PADDLE_VGS_CARD_ID_WINDOW_MS: number;
51
+ declare function paddleVgsPayTarget(url: any): Readonly<{
52
+ environment: "production" | "sandbox";
53
+ line: "classic" | "billing";
54
+ }> | null;
55
+ declare function paddleVgsPayCarriesStoredCard(body: any): boolean;
56
+ declare function paddleVgsCardFacts(card: any): Readonly<{
57
+ card_first_six: string;
58
+ card_last_four: string;
59
+ expiry_month: number;
60
+ expiry_year: number;
61
+ card_brand: "visa" | "master" | "american_express";
62
+ }>;
63
+ declare function paddleVgsCardFactsOf(value: any): Readonly<{
64
+ card_first_six: any;
65
+ card_last_four: string;
66
+ expiry_month: any;
67
+ expiry_year: any;
68
+ card_brand: any;
69
+ }> | null;
70
+ declare function paddleVgsCardFactsAgree(facts: any, stored: any): boolean;
71
+ declare function paddleVgsPayVerdict(request: any, approved: any): Readonly<{
72
+ verdict: "refuse";
73
+ code: any;
74
+ }> | Readonly<{
75
+ verdict: "continue";
76
+ body: string;
77
+ }>;
78
+ export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_ID_WINDOW_MS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardFacts, paddleVgsCardFactsAgree, paddleVgsCardFactsOf, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsPayCarriesStoredCard, paddleVgsPayTarget, paddleVgsPayVerdict, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: b33914c3bb882b868a28a7d7abb91d6cca450c0145dca0aa338474400b6f923e
3
+ // artifact-sha256: f3437a15e11b72b9958179cc9610fdafb411cc5db76be536af62c08360a45575
4
4
  // src/paddle-vgs.js
5
5
  var PADDLE_VGS_PSP = "paddle_vgs";
6
6
  var PADDLE_VGS_CAPABILITY = "paddle_vgs_cards";
@@ -254,12 +254,12 @@ function readAnswer(text) {
254
254
  return null;
255
255
  }
256
256
  }
257
+ var isStoredCardId = (id) => typeof id === "string" && STORED_CARD_ID.test(id) && /[A-Za-z]/.test(id) && !PAN_LIKE.test(id);
257
258
  function storedCardId(answer) {
258
259
  const data = answer && answer.data;
259
- if (!record(data) || data.type !== "cards" || typeof data.id !== "string")
260
+ if (!record(data) || data.type !== "cards")
260
261
  return null;
261
- const id = data.id;
262
- return STORED_CARD_ID.test(id) && /[A-Za-z]/.test(id) && !PAN_LIKE.test(id) ? id : null;
262
+ return isStoredCardId(data.id) ? data.id : null;
263
263
  }
264
264
  var boundedCode = (value) => typeof value === "string" && REFUSAL_CODE.test(value) && (value.match(/[0-9]/g) ?? []).length <= MAX_CODE_DIGITS ? value : null;
265
265
  function paddleVgsRefusalCode(text) {
@@ -286,4 +286,123 @@ function paddleVgsAnswerOutcome(status, text, card) {
286
286
  return { outcome: "unknown", reason: "paddle_vgs_unsafe_response" };
287
287
  return { outcome: "approved", body: JSON.stringify({ data: { type: "cards", id } }) };
288
288
  }
289
- export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
289
+ var PADDLE_VGS_CARD_ID_WINDOW_MS = 6e4;
290
+ var PAY_URL = /^https:\/\/(sandbox-)?checkout-service\.paddle\.com\/(checkout|transaction-checkout)\/[A-Za-z0-9_-]{1,64}\/pay$/;
291
+ function paddleVgsPayTarget(url) {
292
+ const match = typeof url === "string" ? PAY_URL.exec(url) : null;
293
+ return match ? Object.freeze({ environment: match[1] ? "sandbox" : "production", line: match[2] === "checkout" ? "classic" : "billing" }) : null;
294
+ }
295
+ function paddleVgsPayCarriesStoredCard(body) {
296
+ if (typeof body !== "string")
297
+ return true;
298
+ try {
299
+ return JSON.stringify(JSON.parse(body)).includes('"vgs_card"');
300
+ }
301
+ catch {
302
+ return true;
303
+ }
304
+ }
305
+ var FRAME_BRANDS = /* @__PURE__ */ Object.freeze(["visa", "master", "american_express", "discover", "diners_club", "jcb", "maestro", "dankort", "unionpay", "unknown"]);
306
+ var PAY_BRANDS = /* @__PURE__ */ Object.freeze({ visa: "visa", mastercard: "master", amex: "american_express" });
307
+ var FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
308
+ var FIRST_SIX = /^[0-9]{6}$/;
309
+ var LAST_FOUR = /^[0-9]{4}$/;
310
+ var fullYear = (year) => {
311
+ const value = Number(year);
312
+ return Number.isInteger(value) && value >= 0 && value <= 99 ? 2e3 + value : value;
313
+ };
314
+ var expiryMonth = (month) => Number.isInteger(month) && month >= 1 && month <= 12;
315
+ var expiryYear = (year) => Number.isInteger(year) && year >= 2e3 && year <= 2099;
316
+ function paddleVgsCardFacts(card) {
317
+ const number = card ? String(card.number) : "";
318
+ const network = cardNetwork(number);
319
+ const month = card ? Number(card.month) : NaN;
320
+ const year = card ? fullYear(card.year) : NaN;
321
+ if (network === null || !expiryMonth(month) || !expiryYear(year))
322
+ throw new Error("Could not read this card's details for Paddle. Nothing was sent.");
323
+ return Object.freeze({ card_first_six: number.slice(0, 6), card_last_four: number.slice(-4), expiry_month: month, expiry_year: year, card_brand: PAY_BRANDS[network] });
324
+ }
325
+ function paddleVgsCardFactsOf(value) {
326
+ if (!record(value))
327
+ return null;
328
+ const keys = Object.keys(value);
329
+ if (keys.length !== FACT_KEYS.length || !FACT_KEYS.every((key) => keys.includes(key)))
330
+ return null;
331
+ const { card_first_six: firstSix, card_last_four: lastFour, expiry_month: month, expiry_year: year, card_brand: brand } = value;
332
+ const network = typeof firstSix === "string" && FIRST_SIX.test(firstSix) ? cardNetwork(`${firstSix}000000`) : null;
333
+ if (network === null || PAY_BRANDS[network] !== brand || typeof lastFour !== "string" || !LAST_FOUR.test(lastFour) || !expiryMonth(month) || !expiryYear(year))
334
+ return null;
335
+ return Object.freeze({ card_first_six: firstSix, card_last_four: lastFour, expiry_month: month, expiry_year: year, card_brand: brand });
336
+ }
337
+ function paddleVgsCardFactsAgree(facts, stored) {
338
+ const read = paddleVgsCardFactsOf(facts);
339
+ if (!read)
340
+ return false;
341
+ const card = record(stored) ? stored : {};
342
+ const network = Object.keys(PAY_BRANDS).find((name) => PAY_BRANDS[name] === read.card_brand);
343
+ const brand = storedBrand(card.brand);
344
+ return !(typeof card.last4 === "string" && LAST_FOUR.test(card.last4) && card.last4 !== read.card_last_four) && (brand === null || brand === network) && !(Number.isInteger(card.expiryMonth) && card.expiryMonth !== read.expiry_month) && !(Number.isInteger(card.expiryYear) && card.expiryYear % 100 !== read.expiry_year % 100);
345
+ }
346
+ var PAY_BODY_LIMIT = 16384;
347
+ var PAY_DATA_KEYS = /* @__PURE__ */ Object.freeze(["payment_method_type", "payment_method_data"]);
348
+ var PAY_CARD_KEYS = /* @__PURE__ */ Object.freeze(["metadata", "three_d_s", "vgs_card"]);
349
+ var METADATA_KEYS = /* @__PURE__ */ Object.freeze(["cardholder_name", ...FACT_KEYS]);
350
+ var BROWSER_INFO = /^[A-Za-z0-9+/]{1,4096}={0,2}$/;
351
+ var RADAR_TOKEN = /^[\x21-\x7e]{1,512}$/;
352
+ var COUNTRY_CODE = /^[A-Za-z]{2}$/;
353
+ var FRONTEND_HOST = /^https:\/\/[\x21-\x7e]{1,1024}$/;
354
+ var radarKeys = (value) => sameKeys(value, ["token", "country_code"]) || sameKeys(value, ["country_code", "token"]);
355
+ function readPay(body, line) {
356
+ if (typeof body !== "string" || body === "" || body.length > PAY_BODY_LIMIT)
357
+ return null;
358
+ let value;
359
+ try {
360
+ value = JSON.parse(body);
361
+ if (!record(value) || JSON.stringify(value) !== body)
362
+ return null;
363
+ }
364
+ catch {
365
+ return null;
366
+ }
367
+ const data = value.data;
368
+ if (!sameKeys(value, ["data"]) || !record(data) || !(sameKeys(data, PAY_DATA_KEYS) || sameKeys(data, [...PAY_DATA_KEYS, "checkout"])) || data.payment_method_type !== (line === "classic" ? "CARD" : "card"))
369
+ return null;
370
+ if (data.checkout !== void 0 && !(record(data.checkout) && sameKeys(data.checkout, ["frontend_host"]) && typeof data.checkout.frontend_host === "string" && FRONTEND_HOST.test(data.checkout.frontend_host)))
371
+ return null;
372
+ const method = data.payment_method_data;
373
+ const card = record(method) && sameKeys(method, ["card"]) ? method.card : null;
374
+ if (!record(card) || !(sameKeys(card, PAY_CARD_KEYS) || sameKeys(card, [...PAY_CARD_KEYS, "session_tracking"])))
375
+ return null;
376
+ const metadata = card.metadata;
377
+ if (!record(metadata) || !sameKeys(metadata, METADATA_KEYS) || typeof metadata.cardholder_name !== "string" || metadata.cardholder_name.length > NAME_LIMIT || CONTROL.test(metadata.cardholder_name) || cardLikeName(metadata.cardholder_name) || typeof metadata.card_first_six !== "string" || !FIRST_SIX.test(metadata.card_first_six) || typeof metadata.card_last_four !== "string" || !LAST_FOUR.test(metadata.card_last_four) || !expiryMonth(metadata.expiry_month) || !expiryYear(metadata.expiry_year) || !FRAME_BRANDS.includes(metadata.card_brand))
378
+ return null;
379
+ const details = card.three_d_s;
380
+ if (!record(details) || !sameKeys(details, ["browser_info"]) || typeof details.browser_info !== "string" || !BROWSER_INFO.test(details.browser_info))
381
+ return null;
382
+ if (!record(card.vgs_card) || !sameKeys(card.vgs_card, ["card_id"]) || !isStoredCardId(card.vgs_card.card_id))
383
+ return null;
384
+ if (card.session_tracking !== void 0) {
385
+ const tracking = card.session_tracking;
386
+ const radar = record(tracking) && sameKeys(tracking, ["stripe_radar"]) ? tracking.stripe_radar : null;
387
+ if (!record(radar) || !radarKeys(radar) || typeof radar.token !== "string" || !RADAR_TOKEN.test(radar.token) || typeof radar.country_code !== "string" || !COUNTRY_CODE.test(radar.country_code))
388
+ return null;
389
+ }
390
+ return value;
391
+ }
392
+ function paddleVgsPayVerdict(request, approved) {
393
+ const facts = record(approved) ? paddleVgsCardFactsOf(approved.facts) : null;
394
+ if (!facts || !isStoredCardId(approved.cardId) || approved.environment !== "production" && approved.environment !== "sandbox") {
395
+ return refuse("paddle_vgs_pay_unbound");
396
+ }
397
+ const target = record(request) && typeof request.method === "string" && request.method.toUpperCase() === "POST" ? paddleVgsPayTarget(request.url) : null;
398
+ const value = target ? readPay(request.body, target.line) : null;
399
+ if (!value)
400
+ return refuse("paddle_vgs_pay_unsupported");
401
+ const card = value.data.payment_method_data.card;
402
+ if (target.environment !== approved.environment || card.vgs_card.card_id !== approved.cardId)
403
+ return refuse("paddle_vgs_pay_card_mismatch");
404
+ for (const key of FACT_KEYS)
405
+ card.metadata[key] = facts[key];
406
+ return Object.freeze({ verdict: "continue", body: JSON.stringify(value) });
407
+ }
408
+ export { PADDLE_VGS_CAPABILITY, PADDLE_VGS_CARDS_URLS, PADDLE_VGS_CARD_ID_WINDOW_MS, PADDLE_VGS_CARD_NETWORKS, PADDLE_VGS_CONTENT_TYPE, PADDLE_VGS_MERCHANT_PINS, PADDLE_VGS_OPERATION, PADDLE_VGS_PSP, PADDLE_VGS_REFUSAL_CODES, injectPaddleVgsCard, isPaddleVgsCardsUrl, isPreparedPaddleVgsRequest, paddleVgsAnswerOutcome, paddleVgsBodyVerdict, paddleVgsCardFacts, paddleVgsCardFactsAgree, paddleVgsCardFactsOf, paddleVgsCardRefusal, paddleVgsEnvironment, paddleVgsMerchantPin, paddleVgsMerchantRefusal, paddleVgsPayCarriesStoredCard, paddleVgsPayTarget, paddleVgsPayVerdict, paddleVgsRefusalCode, paddleVgsRefusalMessage, paddleVgsRequestRefusal, paddleVgsSellerClaimsPinned, paddleVgsStoredCardRefusal, paddleVgsTypedCardRefusal };
@@ -1,12 +1,10 @@
1
1
  export type PaysafePreparationEnvironment = 'production' | 'sandbox';
2
2
  export declare function paysafePreparationUrl(environment: string): string;
3
- /** Exact strings refuse credentials, queries, fragments and URL normalization. */
3
+ /** The environment of a reviewed build's card URL. Exact strings refuse credentials, queries, fragments and URL normalization. */
4
4
  export declare function paysafeEnvironment(url: string): PaysafePreparationEnvironment | undefined;
5
- /** Checkout 1.8.0 starts a 40-second XHR when Pay is pressed. Prior approval may
6
- * consume only its new-card request, including native clientInfo and headers.
7
- * Saved cards, alternate APIs and 3DS-only requests require separate review.
8
- * Source: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
9
- * e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c.
10
- * The guard checks original bytes and never changes the request.
5
+ /** A reviewed Checkout build (BUILDS) starts a 40-second XHR when Pay is pressed. Prior approval may
6
+ * consume only its new-card request, including native clientInfo and headers, on the card URL that
7
+ * build sends to in the approved environment. Saved cards, alternate APIs and 3DS-only requests
8
+ * require separate review. The guard checks original bytes and never changes the request.
11
9
  */
12
10
  export declare function isPreparedPaysafeRequest(url: string, method: string, body: string | null, environment: string, headers?: Record<string, string>): boolean;
@@ -1,10 +1,34 @@
1
1
  // Generated from @agent-cards/payment-core. Do not edit.
2
- // source-sha256: 24c7940ae8974c0d24e5a365144c1b07ccc46863ae3094eb92b3c54c8307e837
2
+ // source-sha256: afa00cd57246e51361474c66c5ab4282bd5b39f0297fdf68616e164e19ed1741
3
3
  import { readTokenizationJson } from './braintree.js';
4
+ /** The card URL a preparation names: Checkout 1.8.0's. The bind takes the exact URL of the build the request names (BUILDS). */
4
5
  const ENDPOINTS = {
5
6
  production: 'https://hosted.paysafe.com/checkout/api/v1/tokenize',
6
7
  sandbox: 'https://hosted.test.paysafe.com/checkout/api/v1/tokenize',
7
8
  };
9
+ /**
10
+ * The Paysafe Checkout builds whose new-card request a prior approval may consume, each read from
11
+ * Paysafe's public bundle. A request binds only to the build its own clientInfo.version names, on
12
+ * that build's URL and headers, so a build Paysafe ships later is refused, and nothing is sent,
13
+ * until it is read and listed here. The request moved inside 1.x: 1.8.0 to 1.12.0 post to
14
+ * hosted.paysafe.com with a correlationId header, 1.16.1 to api.paysafe.com with three other
15
+ * headers, so a version range would hold one build's request to another build's rules.
16
+ *
17
+ * 1.8.0: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
18
+ * e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c; LIVE/TEST select hosted[.test].
19
+ * 1.16.1: the checkout/v1/latest loader (SHA-256 5fac65c4f7a74b45388ad3ce7c02282f05e832226792bebc417cd9ff191be4f0)
20
+ * names it and frames checkout/v1/latest/index.html. Its main.js (SHA-256
21
+ * 328c6443da930f3648826248afa3c342cdd07d04e49a78b0878dda187772bf80) posts to environment +
22
+ * "checkout/api/v1/tokenize", and its vendor.js (SHA-256 7348275bd07256e295f4b83b292c7fe5115dd7ac35d1b1fe3d26eda3c37d2835)
23
+ * maps LIVE/TEST to api[.test].paysafe.com and sets X-Internal-Correlation-Id, X-Paysafe-Credentials,
24
+ * X-App-Version (the clientInfo version) and X-TransactionSource (CheckoutV1 for paysafe.checkout).
25
+ * The TEST host serves the same bytes (read 2026-10-15).
26
+ */
27
+ const BUILDS = [
28
+ { version: '1.8.0', endpoints: ENDPOINTS, correlation: 'correlationid', fixed: {} },
29
+ { version: '1.16.1', correlation: 'x-internal-correlation-id', fixed: { 'x-app-version': '1.16.1', 'x-transactionsource': 'CheckoutV1' },
30
+ endpoints: { production: 'https://api.paysafe.com/checkout/api/v1/tokenize', sandbox: 'https://api.test.paysafe.com/checkout/api/v1/tokenize' } },
31
+ ];
8
32
  const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
9
33
  const keys = (value, allowed) => record(value) && Object.keys(value).every(key => allowed.includes(key));
10
34
  const exact = (pattern, value) => typeof value === 'string' && pattern.exec(value)?.[0] === value;
@@ -16,27 +40,31 @@ export function paysafePreparationUrl(environment) {
16
40
  throw new Error('unsupported_preparation_environment');
17
41
  return ENDPOINTS[environment];
18
42
  }
19
- /** Exact strings refuse credentials, queries, fragments and URL normalization. */
43
+ /** The environment of a reviewed build's card URL. Exact strings refuse credentials, queries, fragments and URL normalization. */
20
44
  export function paysafeEnvironment(url) {
21
- return url === ENDPOINTS.production ? 'production' : url === ENDPOINTS.sandbox ? 'sandbox' : undefined;
45
+ const build = BUILDS.find(candidate => url === candidate.endpoints.production || url === candidate.endpoints.sandbox);
46
+ return !build ? undefined : url === build.endpoints.production ? 'production' : 'sandbox';
22
47
  }
23
48
  const ADDRESS_FIELDS = ['city', 'country', 'zip', 'street', 'street2', 'state'];
24
49
  function billing(value) {
25
50
  return keys(value, ADDRESS_FIELDS) && Object.values(value).every(value => text(value))
26
51
  && (!own(value, 'country') || exact(/^[A-Z]{2}$/, value.country));
27
52
  }
28
- function freshCard(value) {
53
+ /** The reviewed build whose new-card body this is: never a saved card, a 3-D Secure request or another app's body. */
54
+ function freshCardBuild(value) {
29
55
  if (!keys(value, ['card', 'clientInfo', 'billingAddress']) || !keys(value.card, ['cardNum', 'cardExpiry', 'cvv', 'holderName']))
30
- return false;
56
+ return undefined;
31
57
  const card = value.card;
32
58
  if (!exact(/^[0-9]{12,19}$/, card.cardNum) || !exact(/^[0-9]{3,4}$/, card.cvv)
33
59
  || !keys(card.cardExpiry, ['month', 'year']) || !exact(/^(?:0[1-9]|1[0-2])$/, card.cardExpiry.month)
34
60
  || !exact(/^20[0-9]{2}$/, card.cardExpiry.year) || own(card, 'holderName') && !text(card.holderName)
35
61
  || own(value, 'billingAddress') && !billing(value.billingAddress))
36
- return false;
62
+ return undefined;
37
63
  const info = value.clientInfo;
38
- return keys(info, ['correlationId', 'invocationId', 'version', 'appName'])
39
- && uuid(info.correlationId) && uuid(info.invocationId) && info.version === '1.8.0' && info.appName === 'paysafe.checkout';
64
+ if (!keys(info, ['correlationId', 'invocationId', 'version', 'appName'])
65
+ || !uuid(info.correlationId) || !uuid(info.invocationId) || info.appName !== 'paysafe.checkout')
66
+ return undefined;
67
+ return BUILDS.find(build => build.version === info.version);
40
68
  }
41
69
  const BROWSER_HEADERS = new Set(['accept', 'accept-language', 'accept-encoding', 'content-length', 'origin', 'referer',
42
70
  'user-agent', 'host', 'connection', 'cache-control', 'pragma', 'priority', 'dnt',
@@ -53,9 +81,11 @@ function singleUseCredential(value) {
53
81
  return false;
54
82
  }
55
83
  }
56
- function nativeHeaders(headers, correlationId) {
84
+ /** The build's own headers, each once with its native value, and only headers a browser adds beside them. */
85
+ function nativeHeaders(headers, correlationId, build) {
57
86
  if (!record(headers))
58
87
  return false;
88
+ const native = ['content-type', 'x-paysafe-credentials', build.correlation, ...Object.keys(build.fixed)];
59
89
  const parsed = new Map();
60
90
  let bytes = 0;
61
91
  for (const [name, value] of Object.entries(headers)) {
@@ -63,25 +93,27 @@ function nativeHeaders(headers, correlationId) {
63
93
  if (parsed.size >= 64 || parsed.has(lower) || !exact(/^[A-Za-z0-9-]+$/, name) || !text(value, 8192)
64
94
  || (bytes += name.length + value.length) > 32768)
65
95
  return false;
66
- if (!['content-type', 'correlationid', 'x-paysafe-credentials'].includes(lower) && !BROWSER_HEADERS.has(lower)
96
+ if (!native.includes(lower) && !BROWSER_HEADERS.has(lower)
67
97
  && !exact(/^sec-ch-ua(?:-[a-z0-9-]+)?$/, lower))
68
98
  return false;
69
99
  parsed.set(lower, value);
70
100
  }
71
101
  return exact(/^application\/json(?:;\s*charset=utf-8)?$/i, parsed.get('content-type'))
72
- && parsed.get('correlationid') === correlationId && singleUseCredential(parsed.get('x-paysafe-credentials'));
102
+ && parsed.get(build.correlation) === correlationId && singleUseCredential(parsed.get('x-paysafe-credentials'))
103
+ && Object.entries(build.fixed).every(([name, value]) => parsed.get(name) === value);
73
104
  }
74
- /** Checkout 1.8.0 starts a 40-second XHR when Pay is pressed. Prior approval may
75
- * consume only its new-card request, including native clientInfo and headers.
76
- * Saved cards, alternate APIs and 3DS-only requests require separate review.
77
- * Source: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
78
- * e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c.
79
- * The guard checks original bytes and never changes the request.
105
+ /** A reviewed Checkout build (BUILDS) starts a 40-second XHR when Pay is pressed. Prior approval may
106
+ * consume only its new-card request, including native clientInfo and headers, on the card URL that
107
+ * build sends to in the approved environment. Saved cards, alternate APIs and 3DS-only requests
108
+ * require separate review. The guard checks original bytes and never changes the request.
80
109
  */
81
110
  export function isPreparedPaysafeRequest(url, method, body, environment, headers) {
82
111
  if (method !== 'POST' || (environment !== 'production' && environment !== 'sandbox')
83
112
  || paysafeEnvironment(url) !== environment)
84
113
  return false;
85
114
  const value = readTokenizationJson(body);
86
- return freshCard(value) && record(value.clientInfo) && nativeHeaders(headers, value.clientInfo.correlationId);
115
+ if (!value || !record(value.clientInfo))
116
+ return false;
117
+ const build = freshCardBuild(value);
118
+ return !!build && url === build.endpoints[environment] && nativeHeaders(headers, value.clientInfo.correlationId, build);
87
119
  }