@agent-cards/sdk 0.23.11 → 0.23.13

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.
@@ -0,0 +1,408 @@
1
+ // @ts-nocheck
2
+ // Generated from @agent-cards/payment-core. Do not edit.
3
+ // artifact-sha256: f3437a15e11b72b9958179cc9610fdafb411cc5db76be536af62c08360a45575
4
+ // src/paddle-vgs.js
5
+ var PADDLE_VGS_PSP = "paddle_vgs";
6
+ var PADDLE_VGS_CAPABILITY = "paddle_vgs_cards";
7
+ var PADDLE_VGS_OPERATION = "paddle_vgs.card_request";
8
+ var PADDLE_VGS_CONTENT_TYPE = "application/vnd.api+json";
9
+ var PADDLE_VGS_CARDS_URLS = /* @__PURE__ */ Object.freeze({
10
+ production: "https://vault.paddle.com/cards",
11
+ sandbox: "https://sandbox-vault.paddle.com/cards"
12
+ });
13
+ var PADDLE_VGS_REFUSAL_CODES = /* @__PURE__ */ Object.freeze(["paddle_vgs_url_refused", "paddle_vgs_headers_refused", "paddle_vgs_body_unsupported"]);
14
+ var BODY_LIMIT = 4096;
15
+ var ROOT_KEYS = /* @__PURE__ */ Object.freeze(["data"]);
16
+ var DATA_KEYS = /* @__PURE__ */ Object.freeze(["type", "attributes"]);
17
+ var ATTRIBUTE_KEYS = /* @__PURE__ */ Object.freeze(["pan", "cvc", "exp_month", "exp_year", "cardholder"]);
18
+ var WHERE = /* @__PURE__ */ Object.freeze(["data.attributes.pan", "data.attributes.cvc", "data.attributes.exp_month", "data.attributes.exp_year"]);
19
+ var CARD_NUMBER = /^[0-9]{12,19}$/;
20
+ var SECURITY_CODE = /^[0-9]{3,4}$/;
21
+ var NAME_LIMIT = 256;
22
+ var CONTROL = /[\u0000-\u001f\u007f]/;
23
+ var PAN_LIKE = /(?:[0-9][ _.:-]*){12,}/;
24
+ var cardLikeName = (name) => (name.match(/[0-9]/g) ?? []).length >= 12;
25
+ var BEARER = /^Bearer [A-Za-z0-9_-]{8,512}\.([A-Za-z0-9_-]{8,4096})\.[A-Za-z0-9_-]{8,1024}$/;
26
+ var BEARER_LIFETIME_SECONDS = 1200;
27
+ var BASE64URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
28
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
29
+ var sameKeys = (value, keys) => {
30
+ const own = Object.keys(value);
31
+ return own.length === keys.length && own.every((key, at) => key === keys[at]);
32
+ };
33
+ var refuse = (code) => Object.freeze({ verdict: "refuse", code });
34
+ var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
35
+ function base64UrlText(segment) {
36
+ let bits = 0;
37
+ let value = 0;
38
+ let escaped = "";
39
+ for (const character of segment) {
40
+ const sextet = BASE64URL.indexOf(character);
41
+ if (sextet < 0)
42
+ return null;
43
+ value = value << 6 | sextet;
44
+ bits += 6;
45
+ if (bits >= 8) {
46
+ bits -= 8;
47
+ escaped += "%" + (value >> bits & 255).toString(16).padStart(2, "0");
48
+ value &= (1 << bits) - 1;
49
+ }
50
+ }
51
+ try {
52
+ return decodeURIComponent(escaped);
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ }
58
+ function bearerClaims(authorization) {
59
+ const match = typeof authorization === "string" ? BEARER.exec(authorization) : null;
60
+ const text = match ? base64UrlText(match[1]) : null;
61
+ if (text === null)
62
+ return null;
63
+ try {
64
+ const claims = JSON.parse(text);
65
+ return record(claims) ? claims : null;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ function bearerRunning(authorization, nowSeconds = Math.floor(Date.now() / 1e3)) {
72
+ const claims = bearerClaims(authorization);
73
+ if (!claims || !Number.isSafeInteger(claims.iat) || !Number.isSafeInteger(claims.exp))
74
+ return false;
75
+ return claims.exp > claims.iat && claims.exp - claims.iat <= BEARER_LIFETIME_SECONDS && claims.exp > nowSeconds;
76
+ }
77
+ function paddleVgsEnvironment(url) {
78
+ if (url === PADDLE_VGS_CARDS_URLS.production)
79
+ return "production";
80
+ if (url === PADDLE_VGS_CARDS_URLS.sandbox)
81
+ return "sandbox";
82
+ return null;
83
+ }
84
+ function isPaddleVgsCardsUrl(url) {
85
+ return paddleVgsEnvironment(url) !== null;
86
+ }
87
+ function readBody(body) {
88
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
89
+ return null;
90
+ let value;
91
+ try {
92
+ value = JSON.parse(body);
93
+ if (!record(value) || JSON.stringify(value) !== body)
94
+ return null;
95
+ }
96
+ catch {
97
+ return null;
98
+ }
99
+ return value;
100
+ }
101
+ function paddleVgsBodyVerdict(body) {
102
+ const value = readBody(body);
103
+ if (value === null || !sameKeys(value, ROOT_KEYS) || !record(value.data) || !sameKeys(value.data, DATA_KEYS) || value.data.type !== "cards" || !record(value.data.attributes) || !sameKeys(value.data.attributes, ATTRIBUTE_KEYS)) {
104
+ return refuse("paddle_vgs_body_unsupported");
105
+ }
106
+ const card = value.data.attributes;
107
+ const holder = card.cardholder;
108
+ if (typeof card.pan !== "string" || !CARD_NUMBER.test(card.pan) || typeof card.cvc !== "string" || !SECURITY_CODE.test(card.cvc) || !Number.isInteger(card.exp_month) || card.exp_month < 1 || card.exp_month > 12 || !Number.isInteger(card.exp_year) || card.exp_year < 0 || card.exp_year > 99 || !record(holder) || !sameKeys(holder, ["name"]) || typeof holder.name !== "string" || holder.name.length > NAME_LIMIT || CONTROL.test(holder.name) || cardLikeName(holder.name)) {
109
+ return refuse("paddle_vgs_body_unsupported");
110
+ }
111
+ return CARD;
112
+ }
113
+ function headerValue(headers, name) {
114
+ const found = Object.keys(headers).filter((key) => key.toLowerCase() === name);
115
+ return found.length === 1 && typeof headers[found[0]] === "string" ? headers[found[0]] : null;
116
+ }
117
+ function paddleVgsRequestRefusal(request) {
118
+ if (!record(request) || typeof request.method !== "string" || request.method.toUpperCase() !== "POST")
119
+ return "paddle_vgs_url_refused";
120
+ if (!isPaddleVgsCardsUrl(request.url))
121
+ return "paddle_vgs_url_refused";
122
+ const headers = record(request.headers) ? request.headers : {};
123
+ const contentType = headerValue(headers, "content-type");
124
+ const authorization = headerValue(headers, "authorization");
125
+ if (contentType === null || contentType.trim().toLowerCase() !== PADDLE_VGS_CONTENT_TYPE || authorization === null || !bearerRunning(authorization))
126
+ return "paddle_vgs_headers_refused";
127
+ const verdict = paddleVgsBodyVerdict(request.body);
128
+ return verdict.verdict === "card" ? null : verdict.code;
129
+ }
130
+ function isPreparedPaddleVgsRequest(url, method, body, environment, headers) {
131
+ return (environment === "production" || environment === "sandbox") && paddleVgsEnvironment(url) === environment && paddleVgsRequestRefusal({ url, method, headers, body }) === null;
132
+ }
133
+ var pin = (fields) => Object.freeze({
134
+ ...fields,
135
+ origins: Object.freeze([...fields.origins]),
136
+ sellerClaim: fields.sellerClaim ? Object.freeze({ ...fields.sellerClaim }) : null
137
+ });
138
+ var PADDLE_VGS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
139
+ pin({ id: "rectangle_pro", merchant: "Rectangle Pro", origins: ["https://rectangleapp.com"], sellerClaim: null })
140
+ ]);
141
+ function paddleVgsMerchantPin(merchantOrigin, pins = PADDLE_VGS_MERCHANT_PINS) {
142
+ if (typeof merchantOrigin !== "string" || !Array.isArray(pins))
143
+ return null;
144
+ return pins.find((entry) => !!entry && Array.isArray(entry.origins) && entry.origins.includes(merchantOrigin)) ?? null;
145
+ }
146
+ function paddleVgsMerchantRefusal(environment, merchantOrigin, authorization, pins = PADDLE_VGS_MERCHANT_PINS) {
147
+ if (environment === "sandbox")
148
+ return null;
149
+ if (environment !== "production")
150
+ return "unsupported_environment";
151
+ const found = paddleVgsMerchantPin(merchantOrigin, pins);
152
+ if (!found)
153
+ return "paddle_vgs_merchant_unreviewed";
154
+ if (found.sellerClaim && authorization !== void 0) {
155
+ const claims = bearerClaims(authorization);
156
+ if (!claims || claims[found.sellerClaim.name] !== found.sellerClaim.value)
157
+ return "paddle_vgs_seller_unreviewed";
158
+ }
159
+ return null;
160
+ }
161
+ function paddleVgsSellerClaimsPinned(pins = PADDLE_VGS_MERCHANT_PINS) {
162
+ return Array.isArray(pins) && pins.length > 0 && pins.every((entry) => !!entry && !!entry.sellerClaim && typeof entry.sellerClaim.name === "string" && entry.sellerClaim.name !== "" && typeof entry.sellerClaim.value === "string" && entry.sellerClaim.value !== "");
163
+ }
164
+ var REFUSAL_MESSAGES = /* @__PURE__ */ Object.freeze({
165
+ paddle_vgs_url_refused: "Refusing to pay: this Paddle card request does not go to Paddle's card vault exactly as Paddle's checkout sends it. Nothing was sent.",
166
+ paddle_vgs_headers_refused: "Refusing to pay: this Paddle card request does not carry the checkout's own headers. Nothing was sent.",
167
+ paddle_vgs_body_unsupported: "Refusing to pay: this is not the card request of Paddle's checkout. Nothing was sent."
168
+ });
169
+ function paddleVgsRefusalMessage(code) {
170
+ return Object.prototype.hasOwnProperty.call(REFUSAL_MESSAGES, code) ? REFUSAL_MESSAGES[code] : REFUSAL_MESSAGES.paddle_vgs_body_unsupported;
171
+ }
172
+ var CARD_MISMATCH_REFUSAL = "Refusing to pay: this Paddle payment names a card expiry or network that is not your card's. Nothing was sent.";
173
+ var PADDLE_VGS_CARD_NETWORKS = /* @__PURE__ */ Object.freeze(["visa", "mastercard", "amex"]);
174
+ function cardNetwork(number) {
175
+ if (typeof number !== "string" || !CARD_NUMBER.test(number))
176
+ return null;
177
+ const two = Number(number.slice(0, 2));
178
+ const four = Number(number.slice(0, 4));
179
+ if (number[0] === "4")
180
+ return "visa";
181
+ if (two >= 51 && two <= 55 || four >= 2221 && four <= 2720)
182
+ return "mastercard";
183
+ if (two === 34 || two === 37)
184
+ return "amex";
185
+ return null;
186
+ }
187
+ function paddleVgsCardRefusal(body, card) {
188
+ const verdict = paddleVgsBodyVerdict(body);
189
+ if (verdict.verdict !== "card")
190
+ return verdict.code;
191
+ const attributes = JSON.parse(body).data.attributes;
192
+ const number = card ? String(card.number) : "";
193
+ const month = card ? Number(card.month) : NaN;
194
+ const year = card ? Number(card.year) % 100 : NaN;
195
+ const network = cardNetwork(number);
196
+ if (attributes.exp_month !== month || attributes.exp_year !== year || network === null || cardNetwork(attributes.pan) !== network) {
197
+ return "paddle_vgs_card_mismatch";
198
+ }
199
+ return null;
200
+ }
201
+ var storedBrand = (brand) => typeof brand === "string" && brand !== "" ? brand.toLowerCase() : null;
202
+ function paddleVgsStoredCardRefusal(brand) {
203
+ const network = storedBrand(brand);
204
+ return network === null || PADDLE_VGS_CARD_NETWORKS.includes(network) ? null : "paddle_vgs_card_unsupported";
205
+ }
206
+ function paddleVgsTypedCardRefusal(body, stored) {
207
+ const verdict = paddleVgsBodyVerdict(body);
208
+ if (verdict.verdict !== "card")
209
+ return verdict.code;
210
+ const attributes = JSON.parse(body).data.attributes;
211
+ const card = stored && typeof stored === "object" ? stored : {};
212
+ if (Number.isInteger(card.expiryMonth) && attributes.exp_month !== card.expiryMonth)
213
+ return "paddle_vgs_card_mismatch";
214
+ if (Number.isInteger(card.expiryYear) && attributes.exp_year !== card.expiryYear % 100)
215
+ return "paddle_vgs_card_mismatch";
216
+ const brand = storedBrand(card.brand);
217
+ if (brand !== null && cardNetwork(attributes.pan) !== brand)
218
+ return "paddle_vgs_card_mismatch";
219
+ return null;
220
+ }
221
+ function injectPaddleVgsCard(body, card) {
222
+ const verdict = paddleVgsBodyVerdict(body);
223
+ if (verdict.verdict !== "card")
224
+ throw new Error(paddleVgsRefusalMessage(verdict.code));
225
+ const number = card ? String(card.number) : "";
226
+ const cvc = card ? String(card.cvc) : "";
227
+ const month = card ? Number(card.month) : NaN;
228
+ const year = card ? Number(card.year) % 100 : NaN;
229
+ if (!CARD_NUMBER.test(number) || !SECURITY_CODE.test(cvc) || !Number.isInteger(month) || month < 1 || month > 12 || !Number.isInteger(year) || year < 0) {
230
+ throw new Error("Could not fill the card fields for Paddle. Nothing was sent.");
231
+ }
232
+ if (paddleVgsCardRefusal(body, { number, month, year }) !== null)
233
+ throw new Error(CARD_MISMATCH_REFUSAL);
234
+ const value = JSON.parse(body);
235
+ const attributes = value.data.attributes;
236
+ attributes.pan = number;
237
+ attributes.cvc = cvc;
238
+ attributes.exp_month = month;
239
+ attributes.exp_year = year;
240
+ return { body: JSON.stringify(value), where: [...WHERE] };
241
+ }
242
+ var ANSWER_LIMIT = 65536;
243
+ var STORED_CARD_ID = /^[A-Za-z0-9_-]{1,128}$/;
244
+ var REFUSAL_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
245
+ var MAX_CODE_DIGITS = 11;
246
+ function readAnswer(text) {
247
+ if (typeof text !== "string" || text === "" || text.length > ANSWER_LIMIT)
248
+ return null;
249
+ try {
250
+ const value = JSON.parse(text);
251
+ return record(value) ? value : null;
252
+ }
253
+ catch {
254
+ return null;
255
+ }
256
+ }
257
+ var isStoredCardId = (id) => typeof id === "string" && STORED_CARD_ID.test(id) && /[A-Za-z]/.test(id) && !PAN_LIKE.test(id);
258
+ function storedCardId(answer) {
259
+ const data = answer && answer.data;
260
+ if (!record(data) || data.type !== "cards")
261
+ return null;
262
+ return isStoredCardId(data.id) ? data.id : null;
263
+ }
264
+ var boundedCode = (value) => typeof value === "string" && REFUSAL_CODE.test(value) && (value.match(/[0-9]/g) ?? []).length <= MAX_CODE_DIGITS ? value : null;
265
+ function paddleVgsRefusalCode(text) {
266
+ const answer = readAnswer(text);
267
+ if (!answer)
268
+ return null;
269
+ const first = Array.isArray(answer.errors) && record(answer.errors[0]) ? answer.errors[0] : null;
270
+ return boundedCode(first && first.error_code) ?? boundedCode(first && first.code) ?? boundedCode(answer.error_code) ?? boundedCode(answer.code);
271
+ }
272
+ function paddleVgsAnswerOutcome(status, text, card) {
273
+ if (!Number.isInteger(status))
274
+ return { outcome: "unknown", reason: "paddle_vgs_unconfirmed" };
275
+ if (status >= 500)
276
+ return { outcome: "unknown", reason: "server_error" };
277
+ if (status >= 400 && status < 500)
278
+ return { outcome: "refused", code: paddleVgsRefusalCode(text) ?? `http_${status}` };
279
+ if (status !== 200 && status !== 201)
280
+ return { outcome: "unknown", reason: "paddle_vgs_unconfirmed" };
281
+ const id = storedCardId(readAnswer(text));
282
+ if (id === null)
283
+ return { outcome: "unknown", reason: "paddle_vgs_unsafe_response" };
284
+ const number = card ? String(card.number).replace(/[^0-9]/g, "") : "";
285
+ if (number && id.replace(/[^0-9]/g, "").includes(number))
286
+ return { outcome: "unknown", reason: "paddle_vgs_unsafe_response" };
287
+ return { outcome: "approved", body: JSON.stringify({ data: { type: "cards", id } }) };
288
+ }
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,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 66c54c571ca1294220b070eaca1ca3888feebcd7f2562b761d74ded25a453c57
3
+ // artifact-sha256: 38065a5bd834d5233979a0528fde4345378914e8e8b1a63e329bff0994572da5
4
4
  // src/json-path.js
5
5
  function roundTrips(parsed, body) {
6
6
  try {
@@ -404,14 +404,8 @@ function payrailsCardOnFileRefusal(body) {
404
404
  }
405
405
  var AUTHORIZE_LIMIT = 32768;
406
406
  var AUTHORIZE_KEYS = Object.freeze(["amount", "paymentComposition", "returnInfo", "meta"]);
407
- var COMPOSITION_KEYS = Object.freeze([
408
- "paymentMethodCode",
409
- "integrationType",
410
- "amount",
411
- "storeInstrument",
412
- "paymentInstrumentData",
413
- "enrollInstrumentToNetworkOffers"
414
- ]);
407
+ var COMPOSITION_KEYS = Object.freeze(["paymentMethodCode", "integrationType", "amount", "storeInstrument", "paymentInstrumentData"]);
408
+ var ENROLLMENT_COMPOSITION_KEYS = Object.freeze([...COMPOSITION_KEYS, "enrollInstrumentToNetworkOffers"]);
415
409
  var RETURN_KEYS = Object.freeze(["success", "cancel", "error", "pending"]);
416
410
  var AMOUNT_VALUE = /^[0-9]{1,15}(?:\.[0-9]{1,12})?$/;
417
411
  var CURRENCY = /^[A-Z]{3}$/;
@@ -476,12 +470,14 @@ function readAuthorizeBody(body) {
476
470
  const amount = readAmount(parsed.amount);
477
471
  const parts = parsed.paymentComposition;
478
472
  const part = Array.isArray(parts) && parts.length === 1 && record(parts[0]) ? parts[0] : null;
479
- if (!amount || !part || !sameKeys(part, COMPOSITION_KEYS) || part.paymentMethodCode !== "card" || part.integrationType !== "api")
473
+ const flagged = !!part && owns(part, "enrollInstrumentToNetworkOffers");
474
+ if (!amount || !part || !sameKeys(part, flagged ? ENROLLMENT_COMPOSITION_KEYS : COMPOSITION_KEYS) || part.paymentMethodCode !== "card" || part.integrationType !== "api")
480
475
  return malformed;
481
476
  const partAmount = readAmount(part.amount);
482
477
  if (!partAmount || partAmount.value !== amount.value || partAmount.currency !== amount.currency)
483
478
  return malformed;
484
- if (typeof part.storeInstrument !== "boolean" || typeof part.enrollInstrumentToNetworkOffers !== "boolean")
479
+ const enrollInstrumentToNetworkOffers = flagged ? part.enrollInstrumentToNetworkOffers : false;
480
+ if (typeof part.storeInstrument !== "boolean" || typeof enrollInstrumentToNetworkOffers !== "boolean")
485
481
  return malformed;
486
482
  const data = part.paymentInstrumentData;
487
483
  const keys = record(data) ? Object.keys(data) : [];
@@ -502,7 +498,7 @@ function readAuthorizeBody(body) {
502
498
  value: Object.freeze({
503
499
  amount: Object.freeze({ value: amount.value, currency: amount.currency }),
504
500
  storeInstrument: part.storeInstrument,
505
- enrollInstrumentToNetworkOffers: part.enrollInstrumentToNetworkOffers,
501
+ enrollInstrumentToNetworkOffers,
506
502
  encryptedData: data.encryptedData,
507
503
  vaultProviderConfigId: data.vaultProviderConfigId,
508
504
  wrappedKeyBytes: jwe.wrappedKey.length
@@ -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
  }
@@ -13,6 +13,7 @@ import { type CardEndpointProfile } from './card-endpoint.js';
13
13
  import { type PayrailsSessionCapture, type PayrailsSidecar } from './payrails.js';
14
14
  import { type VerifoneProfile } from './verifone.js';
15
15
  import { type WorldlineMyCheckoutProducts } from './worldline-mycheckout.js';
16
+ import { type PaddleVgsPayGate } from './paddle-vgs-pay.js';
16
17
  /** The native Stripe step the gate claimed, or null when none was: exactly what StripeCheckoutGate.claim returns. */
17
18
  type StripeStep = ReturnType<StripeCheckoutGate['claim']>;
18
19
  /** A synthetic response a gate fulfills locally. The effect applies CORS and encoding. */
@@ -189,6 +190,12 @@ export interface PreClaimContext {
189
190
  * Absent, no MyCheckout card submit is held to a product here.
190
191
  */
191
192
  worldlineMyCheckout?: WorldlineMyCheckoutProducts;
193
+ /**
194
+ * The attachment's binding of Paddle's /pay to the stored card's id it handed a frame (paddle-vgs-pay.ts),
195
+ * judged by the request's frame (`frame`). Absent, or for an org the API did not serve Paddle VGS to, Paddle's
196
+ * checkout services are not this gate's.
197
+ */
198
+ paddleVgsPay?: PaddleVgsPayGate;
192
199
  }
193
200
  /**
194
201
  * The URL an event may name for a request. For an armed merchant profile's
@@ -211,6 +218,7 @@ export declare const ekashuPreClaim: PreClaimGate;
211
218
  export declare const paypalHostedFieldsPreClaim: PreClaimGate;
212
219
  export declare const cybersourceSecureAcceptancePreClaim: PreClaimGate;
213
220
  export declare const worldlineMyCheckoutPreClaim: PreClaimGate;
221
+ export declare const paddleVgsPayPreClaim: PreClaimGate;
214
222
  /** The processor gates, in order. A new family adds one entry here; the terminal
215
223
  * arbiter runs after all of them and is not part of this list. A gate appended
216
224
  * after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or