@agent-cards/checkout 0.18.0 → 0.21.0

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 (88) hide show
  1. package/README.md +6 -669
  2. package/cdp.d.ts +1 -0
  3. package/cdp.js +2 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +2 -0
  6. package/package.json +33 -33
  7. package/playwright.d.ts +1 -0
  8. package/playwright.js +2 -0
  9. package/preflight.d.ts +1 -0
  10. package/preflight.js +2 -0
  11. package/CHANGELOG.md +0 -124
  12. package/PREFLIGHT.md +0 -308
  13. package/dist/adyen.generated.d.ts +0 -24
  14. package/dist/adyen.generated.js +0 -64
  15. package/dist/attachment.d.ts +0 -11
  16. package/dist/attachment.js +0 -50
  17. package/dist/braintree.d.ts +0 -2
  18. package/dist/braintree.generated.d.ts +0 -10
  19. package/dist/braintree.generated.js +0 -302
  20. package/dist/braintree.js +0 -2
  21. package/dist/builtin-registry.generated.d.ts +0 -2
  22. package/dist/builtin-registry.generated.js +0 -1
  23. package/dist/card-fields.generated.d.ts +0 -3
  24. package/dist/card-fields.generated.js +0 -46
  25. package/dist/cdp.d.ts +0 -189
  26. package/dist/cdp.js +0 -2194
  27. package/dist/checkout-com.generated.d.ts +0 -4
  28. package/dist/checkout-com.generated.js +0 -183
  29. package/dist/client.d.ts +0 -618
  30. package/dist/client.js +0 -1251
  31. package/dist/hosted-form.d.ts +0 -44
  32. package/dist/hosted-form.js +0 -78
  33. package/dist/index.d.ts +0 -13
  34. package/dist/index.js +0 -6
  35. package/dist/lifecycle.d.ts +0 -165
  36. package/dist/lifecycle.js +0 -370
  37. package/dist/mercado-checkout.d.ts +0 -20
  38. package/dist/mercado-checkout.generated.d.ts +0 -52
  39. package/dist/mercado-checkout.generated.js +0 -198
  40. package/dist/mercado-checkout.js +0 -108
  41. package/dist/owned-shop.generated.d.ts +0 -24
  42. package/dist/owned-shop.generated.js +0 -108
  43. package/dist/paysafe.generated.d.ts +0 -12
  44. package/dist/paysafe.generated.js +0 -87
  45. package/dist/playwright.d.ts +0 -3
  46. package/dist/playwright.js +0 -3
  47. package/dist/preflight-capabilities.generated.d.ts +0 -1253
  48. package/dist/preflight-capabilities.generated.js +0 -1929
  49. package/dist/preflight-catalog.json +0 -4595
  50. package/dist/preflight-playwright.d.ts +0 -34
  51. package/dist/preflight-playwright.js +0 -355
  52. package/dist/preflight-schemas.json +0 -1110
  53. package/dist/preflight.d.ts +0 -1
  54. package/dist/preflight.generated.d.ts +0 -1965
  55. package/dist/preflight.generated.js +0 -556
  56. package/dist/preflight.js +0 -2
  57. package/dist/preparation.d.ts +0 -31
  58. package/dist/preparation.js +0 -164
  59. package/dist/prepared-processor.d.ts +0 -10
  60. package/dist/prepared-processor.js +0 -122
  61. package/dist/recurly.generated.d.ts +0 -1
  62. package/dist/recurly.generated.js +0 -87
  63. package/dist/registry.d.ts +0 -76
  64. package/dist/registry.js +0 -296
  65. package/dist/spreedly.generated.d.ts +0 -10
  66. package/dist/spreedly.generated.js +0 -332
  67. package/dist/stripe-checkout.d.ts +0 -81
  68. package/dist/stripe-checkout.generated.d.ts +0 -82
  69. package/dist/stripe-checkout.generated.js +0 -1004
  70. package/dist/stripe-checkout.js +0 -140
  71. package/dist/substitute.d.ts +0 -38
  72. package/dist/substitute.js +0 -23
  73. package/dist/substitutions.generated.d.ts +0 -10
  74. package/dist/substitutions.generated.js +0 -66
  75. package/examples/existing-browser.mjs +0 -63
  76. package/examples/preflight/classify-direct.mjs +0 -21
  77. package/examples/preflight/classify-kernel.mjs +0 -30
  78. package/examples/preflight/inspect-browser.mjs +0 -44
  79. package/examples/preflight/kernel-native/README.md +0 -112
  80. package/examples/preflight/kernel-native/documented-adapters.json +0 -113
  81. package/examples/preflight/kernel-native/inventory.json +0 -233
  82. package/examples/preflight/kernel-native/qualification.mjs +0 -182
  83. package/examples/preflight/kernel-profile.empty.json +0 -11
  84. package/examples/preflight/mollie-hosted.observations.json +0 -23
  85. package/examples/preflight/mollie-hosted.result.json +0 -103
  86. package/examples/preflight/stripe-script.direct.result.json +0 -92
  87. package/examples/preflight/stripe-script.observations.json +0 -16
  88. package/examples/preflight/stripe-script.result.json +0 -87
package/dist/registry.js DELETED
@@ -1,296 +0,0 @@
1
- /**
2
- * Which outbound requests carry a raw card, and where the card sits inside them.
3
- *
4
- * This is the only merchant-specific knowledge in the system, and it is keyed by
5
- * PSP rather than by merchant: every Shopify store shares one entry, every Stripe
6
- * site shares another. The list is served from the Agentcard API at runtime so
7
- * integrators pick up new processors without shipping a release.
8
- */
9
- // Pinned fallback generated from the single shared processor catalog.
10
- import { BUILTIN_REGISTRY } from './builtin-registry.generated.js';
11
- export { BUILTIN_REGISTRY };
12
- export function findRecognizer(url, registry = BUILTIN_REGISTRY) {
13
- // Match the way the SERVER does, on the PARSED url — never as a substring of
14
- // the raw string. `match` is unanchored, so a raw-string test classifies any
15
- // URL that merely CONTAINS a tokenizer endpoint as a card request:
16
- // https://evil.com/?next=https://api.stripe.com/v1/tokens
17
- // The server refuses that (its hosts[] are anchored against URL.hostname), so
18
- // the request could not be intercepted anyway — but the SDK had already paused
19
- // it, and a pause it cannot complete becomes an ABORT. That turns an
20
- // integrator's unrelated POST into a failed request.
21
- //
22
- // hosts[] is synced from the API precisely so the client can apply the server's
23
- // own allowlist. Use it when present, and fall back to matching `match`
24
- // against "hostname + pathname" (not the query) for a registry served by an
25
- // older API that predates the field.
26
- let parsed;
27
- try {
28
- parsed = new URL(url);
29
- }
30
- catch {
31
- return null;
32
- }
33
- if (parsed.protocol !== 'https:' || parsed.port || parsed.username || parsed.password)
34
- return null;
35
- const hostPath = `${parsed.hostname.toLowerCase()}${parsed.pathname}`;
36
- return (registry.find((r) => {
37
- if (r.hosts && r.hosts.length > 0) {
38
- const hostAllowed = r.hosts.some((h) => new RegExp(h, 'i').test(parsed.hostname));
39
- if (!hostAllowed)
40
- return false;
41
- }
42
- // FULLY ANCHORED, exactly as the server does it. Unanchored, any EXTENSION
43
- // of a valid path is admitted on a legitimate processor host
44
- // (api.stripe.com/v1/tokens/anything), which the service then refuses —
45
- // and a pause the SDK cannot complete becomes an abort.
46
- return new RegExp(`^(?:${r.match.source})$`, 'i').test(hostPath);
47
- }) ?? null);
48
- }
49
- // ---------------------------------------------------------------------------
50
- // Registry -> CDP url patterns
51
- //
52
- // `Fetch.enable` takes GLOB url patterns (`*` = any run of characters, `?` =
53
- // exactly one, `\` escapes), never regexes, so the registry's anchored host
54
- // regexes have to be widened into globs before a raw-CDP integrator can arm
55
- // interception with them.
56
- //
57
- // These patterns are only a COARSE pre-filter. Every request they pause is
58
- // re-checked with the real recognizer (VaultClient.isCardRequest) before
59
- // anything is intercepted, so a pattern that is too BROAD costs one extra
60
- // continueRequest round-trip, while one that is too NARROW silently drops the
61
- // card request on the floor and the shopper pays with their own card. Every
62
- // conversion below therefore WIDENS: anything not expressible as a glob
63
- // (character classes, groups, quantifiers, lookarounds) becomes `*`.
64
- // ---------------------------------------------------------------------------
65
- /**
66
- * Glob url patterns covering every host in `registry` — what to hand
67
- * `Fetch.enable` so a card request is ever paused. Deliberately a superset of
68
- * the recognizers; isCardRequest is the exact check.
69
- */
70
- export function cardUrlPatterns(registry) {
71
- const out = new Set();
72
- for (const rec of registry ?? []) {
73
- for (const pattern of recognizerUrlPatterns(rec))
74
- out.add(pattern);
75
- }
76
- return [...out];
77
- }
78
- function recognizerUrlPatterns(rec) {
79
- const hosts = Array.isArray(rec?.hosts) ? rec.hosts.filter((h) => typeof h === 'string') : [];
80
- if (hosts.length > 0) {
81
- // Host text ANYWHERE in the URL, not `*://host/*`. Two reasons: a URL with
82
- // userinfo (`https://u:p@host/…`) still has `host` as its hostname so the
83
- // registry matches it, and isCardRequest tests `match` unanchored against
84
- // the raw URL, so a URL merely carrying the host in its query also
85
- // "matches" client-side. Anchoring the scheme would arm narrower than
86
- // either check.
87
- //
88
- // Lowercased because CDP matches these globs case-SENSITIVELY against the
89
- // canonical url, and a canonical url's authority is always lowercase (the
90
- // URL parser folds it before the request is ever issued) — the same
91
- // normalization resolveRecognizerForUrl does with url.hostname. Do not
92
- // "fix" this by wildcarding the letters: `*???.??????.???*` matches most of
93
- // the web and would pause every request on the page.
94
- return hosts.flatMap((h) => regexSourceToGlobs(h)).map((g) => wrap(g).toLowerCase());
95
- }
96
- // No hosts[] — a registry pinned before the field existed, or hand-built.
97
- // Fall back to widening `match`, which is the predicate isCardRequest
98
- // actually applies. Path case can matter to a glob (CDP matches
99
- // case-sensitively) while a hostname is always lowercased by the URL parser,
100
- // so arm both spellings rather than guess.
101
- return regexSourceToGlobs(rec?.match?.source ?? '')
102
- .map(wrap)
103
- .flatMap((g) => (g === g.toLowerCase() ? [g] : [g, g.toLowerCase()]));
104
- }
105
- /** `a|b` at the top level is two independent hostnames, so it is two globs. */
106
- function regexSourceToGlobs(source) {
107
- return splitTopLevelAlternation(source).map(branchToGlob);
108
- }
109
- function splitTopLevelAlternation(source) {
110
- const parts = [];
111
- let depth = 0;
112
- let inClass = false;
113
- let start = 0;
114
- for (let i = 0; i < source.length; i++) {
115
- const c = source[i];
116
- if (c === '\\') {
117
- i++;
118
- continue;
119
- }
120
- if (inClass) {
121
- if (c === ']')
122
- inClass = false;
123
- continue;
124
- }
125
- if (c === '[') {
126
- inClass = true;
127
- continue;
128
- }
129
- if (c === '(') {
130
- depth++;
131
- continue;
132
- }
133
- if (c === ')') {
134
- depth = Math.max(0, depth - 1);
135
- continue;
136
- }
137
- if (c === '|' && depth === 0) {
138
- parts.push(source.slice(start, i));
139
- start = i + 1;
140
- }
141
- }
142
- parts.push(source.slice(start));
143
- return parts;
144
- }
145
- /**
146
- * One alternation-free regex source -> one glob. Literal characters survive;
147
- * every construct that can match more than itself collapses to `*`. An empty
148
- * result is `*` on purpose: an empty regex matches every hostname, so the glob
149
- * that mirrors it has to match every URL.
150
- */
151
- function branchToGlob(branch) {
152
- const atoms = [];
153
- let i = 0;
154
- while (i < branch.length) {
155
- const c = branch[i];
156
- let atom;
157
- if (c === '^' || c === '$') {
158
- i += 1;
159
- continue;
160
- } // anchors constrain nothing in a glob
161
- if (c === '\\') {
162
- const next = branch[i + 1] ?? '';
163
- i += 2;
164
- // \d \w \s \b … are classes/assertions; only escaped punctuation (\., \-)
165
- // is a literal character.
166
- atom = /[A-Za-z0-9]/.test(next) ? '*' : globEscape(next);
167
- }
168
- else if (c === '[') {
169
- i = skipCharClass(branch, i);
170
- atom = '*';
171
- }
172
- else if (c === '(') {
173
- i = skipGroup(branch, i); // groups, non-capturing groups, lookarounds
174
- atom = '*';
175
- }
176
- else if (c === '.') {
177
- i += 1;
178
- atom = '*';
179
- }
180
- else if (c === ')' || c === ']') {
181
- i += 1; // unbalanced closer: ignore rather than throw
182
- continue;
183
- }
184
- else if (quantifierEnd(branch, i) !== null) {
185
- // A quantifier with nothing in front of it. Widen whatever preceded it.
186
- i = quantifierEnd(branch, i);
187
- if (atoms.length > 0)
188
- atoms[atoms.length - 1] = '*';
189
- else
190
- atoms.push('*');
191
- continue;
192
- }
193
- else {
194
- i += 1;
195
- atom = globEscape(c);
196
- }
197
- // `x?` / `x*` / `x+` / `x{2,3}`: the atom is optional or repeatable, so the
198
- // only glob that covers every string it accepts is `*`.
199
- const after = quantifierEnd(branch, i);
200
- if (after !== null) {
201
- i = after;
202
- atom = '*';
203
- }
204
- atoms.push(atom);
205
- }
206
- return collapse(atoms).join('');
207
- }
208
- /** Index just past a quantifier at `i`, or null when there isn't one. */
209
- function quantifierEnd(source, i) {
210
- const c = source[i];
211
- let end;
212
- if (c === '*' || c === '+' || c === '?') {
213
- end = i + 1;
214
- }
215
- else if (c === '{') {
216
- const close = source.indexOf('}', i);
217
- if (close === -1 || !/^\{\d+(,\d*)?\}$/.test(source.slice(i, close + 1)))
218
- return null;
219
- end = close + 1;
220
- }
221
- else {
222
- return null;
223
- }
224
- if (source[end] === '?')
225
- end += 1; // lazy quantifier
226
- return end;
227
- }
228
- function skipCharClass(source, i) {
229
- let j = i + 1;
230
- if (source[j] === '^')
231
- j++;
232
- if (source[j] === ']')
233
- j++; // a leading `]` is a literal
234
- for (; j < source.length; j++) {
235
- if (source[j] === '\\') {
236
- j++;
237
- continue;
238
- }
239
- if (source[j] === ']')
240
- return j + 1;
241
- }
242
- return source.length;
243
- }
244
- function skipGroup(source, i) {
245
- let depth = 0;
246
- let inClass = false;
247
- for (let j = i; j < source.length; j++) {
248
- const c = source[j];
249
- if (c === '\\') {
250
- j++;
251
- continue;
252
- }
253
- if (inClass) {
254
- if (c === ']')
255
- inClass = false;
256
- continue;
257
- }
258
- if (c === '[') {
259
- inClass = true;
260
- continue;
261
- }
262
- if (c === '(') {
263
- depth++;
264
- continue;
265
- }
266
- if (c === ')') {
267
- depth--;
268
- if (depth === 0)
269
- return j + 1;
270
- }
271
- }
272
- return source.length;
273
- }
274
- /** `*` and `?` are the glob's own wildcards; `\` is its escape. */
275
- function globEscape(ch) {
276
- return ch === '*' || ch === '?' || ch === '\\' ? `\\${ch}` : ch;
277
- }
278
- /** `**` matches exactly what `*` does; keep patterns readable. */
279
- function collapse(atoms) {
280
- const out = [];
281
- for (const a of atoms) {
282
- if (a === '*' && out[out.length - 1] === '*')
283
- continue;
284
- out.push(a);
285
- }
286
- return out;
287
- }
288
- /** `…host…` -> `*host*`, without doubling a `*` the conversion already emitted. */
289
- function wrap(glob) {
290
- let body = glob;
291
- while (body.startsWith('*'))
292
- body = body.slice(1);
293
- while (body.length > 0 && body.endsWith('*') && !body.endsWith('\\*'))
294
- body = body.slice(0, -1);
295
- return body.length > 0 ? `*${body}*` : '*';
296
- }
@@ -1,10 +0,0 @@
1
- declare var SPREEDLY_TOKEN_ENDPOINT: string;
2
- declare function isSpreedlyTokenRequest(url: any, method?: string): boolean;
3
- declare function isPreparedSpreedlyRequest(body: any): boolean;
4
- declare function isSpreedlyRequest(request: any): boolean;
5
- declare function injectSpreedlyCard(body: any, card: any): {
6
- body: string;
7
- where: string[];
8
- };
9
- declare function sanitizeSpreedlyTokenResponse(body: any): string;
10
- export { SPREEDLY_TOKEN_ENDPOINT, injectSpreedlyCard, isPreparedSpreedlyRequest, isSpreedlyRequest, isSpreedlyTokenRequest, sanitizeSpreedlyTokenResponse };
@@ -1,332 +0,0 @@
1
- // @ts-nocheck
2
- // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: bd953f414724afdcc2930f895f106fbc908b78875358b33d01c0ee55985b9a29
4
- // src/spreedly.js
5
- var SPREEDLY_TOKEN_ENDPOINT = "https://core.spreedly.com/v1/payment_methods/restricted.json";
6
- var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
7
- var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
8
- var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
9
- var text = (value, max = 1024) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
10
- var id = (value) => exact(/^[A-Za-z0-9_-]{1,128}$/, value);
11
- var keys = (value, allowed) => record(value) && Object.keys(value).every((key) => allowed.includes(key));
12
- var digits = (value, min, max) => typeof value === "string" && value.length >= min && value.length <= max && !/[^0-9]/.test(value);
13
- var month = (value) => (Number.isInteger(value) || digits(value, 1, 2)) && Number(value) >= 1 && Number(value) <= 12;
14
- var year = (value) => (Number.isInteger(value) || digits(value, 2, 4)) && (Number(value) >= 0 && Number(value) <= 99 || Number(value) >= 2e3 && Number(value) <= 9999);
15
- var CARD_TEXT = [
16
- "first_name",
17
- "last_name",
18
- "full_name",
19
- "email",
20
- "address1",
21
- "address2",
22
- "city",
23
- "state",
24
- "zip",
25
- "country",
26
- "phone_number",
27
- "company",
28
- "shipping_address1",
29
- "shipping_address2",
30
- "shipping_city",
31
- "shipping_state",
32
- "shipping_zip",
33
- "shipping_country",
34
- "shipping_phone_number"
35
- ];
36
- var AUTH = ["nonce", "timestamp", "certificate_token", "signature"];
37
- var CARD_TYPES = /* @__PURE__ */ new Set([
38
- "visa",
39
- "master",
40
- "american_express",
41
- "diners_club",
42
- "discover",
43
- "jcb",
44
- "maestro",
45
- "maestro_no_luhn",
46
- "elo",
47
- "carnet",
48
- "dankort",
49
- "alelo",
50
- "cabal",
51
- "naranja",
52
- "unionpay",
53
- "alia",
54
- "creditel",
55
- "confiable",
56
- "synchrony",
57
- "routex",
58
- "mada",
59
- "passcard",
60
- "edenred",
61
- "anda",
62
- "tarjeta_d",
63
- "sodexo",
64
- "sodexo_no_luhn",
65
- "panal",
66
- "verve",
67
- "tuya",
68
- "uatp",
69
- "patagonia_365",
70
- "tarjeta_sol",
71
- "tarjeta_unica",
72
- "consumax",
73
- "tarjeta_fertil",
74
- "favacard",
75
- "elebar",
76
- "tarjeta_axis",
77
- "tarjeta_plan_platino",
78
- "tarjeta_fiel",
79
- "copeplus",
80
- "tarjeta_credimas",
81
- "tarjeta_local",
82
- "tarjeta_sucredito",
83
- "sidecreer",
84
- "cliper",
85
- "centrocard",
86
- "wex",
87
- "codensa"
88
- ]);
89
- var panLike = (value) => /(?:[0-9][ _.:-]*){12,}/.test(value);
90
- var opaqueId = (value) => id(value) && /[A-Za-z]/.test(value) && !panLike(value);
91
- function readJson(body) {
92
- if (typeof body !== "string" || !body || body.length > 65536)
93
- return null;
94
- let at = 0, nodes = 0;
95
- const fail = () => {
96
- throw new Error("invalid_spreedly_json");
97
- };
98
- const space = () => {
99
- while (" \r\n".includes(body[at] ?? "\0"))
100
- at++;
101
- };
102
- const string = () => {
103
- space();
104
- const start = at;
105
- if (body[at++] !== '"')
106
- fail();
107
- while (at < body.length) {
108
- const char = body[at++];
109
- if (char === "\\") {
110
- at++;
111
- continue;
112
- }
113
- if (char === '"')
114
- return JSON.parse(body.slice(start, at));
115
- }
116
- return fail();
117
- };
118
- const value = (depth) => {
119
- if (depth > 12 || ++nodes > 1e3)
120
- fail();
121
- space();
122
- const char = body[at];
123
- if (char === '"')
124
- return string();
125
- if (char === "{") {
126
- at++;
127
- const result = /* @__PURE__ */ Object.create(null), seen = /* @__PURE__ */ new Set();
128
- space();
129
- if (body[at] === "}") {
130
- at++;
131
- return result;
132
- }
133
- while (true) {
134
- const key = string();
135
- if (seen.has(key) || ["__proto__", "prototype", "constructor"].includes(key))
136
- fail();
137
- seen.add(key);
138
- space();
139
- if (body[at++] !== ":")
140
- fail();
141
- result[key] = value(depth + 1);
142
- space();
143
- const next = body[at++];
144
- if (next === "}")
145
- return result;
146
- if (next !== ",")
147
- fail();
148
- }
149
- }
150
- if (char === "[") {
151
- at++;
152
- const result = [];
153
- space();
154
- if (body[at] === "]") {
155
- at++;
156
- return result;
157
- }
158
- while (true) {
159
- result.push(value(depth + 1));
160
- space();
161
- const next = body[at++];
162
- if (next === "]")
163
- return result;
164
- if (next !== ",")
165
- fail();
166
- }
167
- }
168
- for (const literal of ["true", "false", "null"]) {
169
- if (body.startsWith(literal, at)) {
170
- at += literal.length;
171
- return JSON.parse(literal);
172
- }
173
- }
174
- const number = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/.exec(body.slice(at));
175
- if (!number)
176
- fail();
177
- at += number[0].length;
178
- const parsed = Number(number[0]);
179
- if (!Number.isFinite(parsed))
180
- fail();
181
- return parsed;
182
- };
183
- try {
184
- const parsed = value(0);
185
- space();
186
- return at === body.length && record(parsed) ? parsed : null;
187
- }
188
- catch {
189
- return null;
190
- }
191
- }
192
- function isSpreedlyTokenRequest(url, method = "POST") {
193
- if (method !== "POST" || typeof url !== "string" || !url.startsWith(SPREEDLY_TOKEN_ENDPOINT + "?"))
194
- return false;
195
- try {
196
- const parsed = new URL(url);
197
- if (parsed.href !== url || parsed.origin + parsed.pathname !== SPREEDLY_TOKEN_ENDPOINT || parsed.hash)
198
- return false;
199
- const seen = /* @__PURE__ */ new Set();
200
- for (const pair of parsed.search.slice(1).split("&")) {
201
- const separator = pair.indexOf("=");
202
- const name = pair.slice(0, separator), value = pair.slice(separator + 1);
203
- if (separator < 1 || seen.has(name))
204
- return false;
205
- seen.add(name);
206
- if (name === "from" ? value !== "iframe" : name === "v" ? !exact(/^1\.[0-9]{1,4}$/, value) : name === "client" ? !exact(/^[A-Za-z0-9_.-]{1,64}$/, value) : true)
207
- return false;
208
- }
209
- return seen.has("from") && seen.has("v");
210
- }
211
- catch {
212
- return false;
213
- }
214
- }
215
- function preparedBody(body) {
216
- const value = readJson(body);
217
- if (!keys(value, ["environment_key", "payment_method", ...AUTH]) || !id(value.environment_key) || !keys(value.payment_method, ["credit_card", "allow_blank_name", "allow_expired_date", "metadata"]))
218
- return null;
219
- const suppliedAuth = AUTH.filter((key) => owns(value, key));
220
- if (suppliedAuth.length && (suppliedAuth.length !== AUTH.length || !text(value.nonce) || !value.nonce || !(digits(value.timestamp, 1, 16) || Number.isSafeInteger(value.timestamp) && value.timestamp > 0) || !id(value.certificate_token) || !exact(/^[A-Za-z0-9+/=_-]{1,8192}$/, value.signature)))
221
- return null;
222
- const pm = value.payment_method, cc = pm.credit_card;
223
- if (!keys(cc, ["number", "verification_value", "month", "year", "eligible_for_card_updater", ...CARD_TEXT]) || !digits(cc.number, 12, 19) || !digits(cc.verification_value, 3, 4) || !month(cc.month) || !year(cc.year) || CARD_TEXT.some((key) => owns(cc, key) && !text(cc[key])) || owns(cc, "eligible_for_card_updater") && cc.eligible_for_card_updater !== false)
224
- return null;
225
- if (owns(pm, "allow_blank_name") && typeof pm.allow_blank_name !== "boolean" || owns(pm, "allow_expired_date") && pm.allow_expired_date !== false)
226
- return null;
227
- if (owns(pm, "metadata") && (!record(pm.metadata) || Object.keys(pm.metadata).length > 20 || Object.entries(pm.metadata).some(([key, value2]) => !exact(/^[A-Za-z][A-Za-z0-9_-]{0,63}$/, key) || !(value2 === null || typeof value2 === "boolean" || Number.isFinite(value2) || text(value2)))))
228
- return null;
229
- return value;
230
- }
231
- function isPreparedSpreedlyRequest(body) {
232
- return preparedBody(body) !== null;
233
- }
234
- function isSpreedlyRequest(request) {
235
- if (!record(request) || request.method !== "POST" || !isSpreedlyTokenRequest(request.url, request.method) || !record(request.headers))
236
- return false;
237
- const body = preparedBody(request.body);
238
- if (!body)
239
- return false;
240
- const headers = /* @__PURE__ */ new Map();
241
- for (const [name, value] of Object.entries(request.headers)) {
242
- const lower = name.toLowerCase();
243
- if (headers.has(lower) || !text(value, 8192))
244
- return false;
245
- headers.set(lower, value);
246
- }
247
- return exact(/^application\/json(?:;\s*charset=utf-8)?$/i, headers.get("content-type")) && headers.get("spreedly-environment-key") === body.environment_key;
248
- }
249
- function injectSpreedlyCard(body, card) {
250
- const value = preparedBody(body);
251
- if (!value)
252
- throw new Error("Refusing to pay: unsupported Spreedly new-card request.");
253
- if (!card || !digits(card.number, 12, 19) || !digits(card.cvc, 3, 4) || !month(card.month) || !year(card.year)) {
254
- throw new Error("Could not fill the card fields for Spreedly. Nothing was sent.");
255
- }
256
- const cc = value.payment_method.credit_card;
257
- cc.number = card.number;
258
- cc.verification_value = card.cvc;
259
- cc.month = typeof cc.month === "number" ? Number(card.month) : String(card.month);
260
- const selectedYear = String(card.year), nativeYear = String(cc.year);
261
- const replacementYear = nativeYear.length === 2 && selectedYear.length === 4 ? selectedYear.slice(-2) : nativeYear.length === 4 && selectedYear.length === 2 ? "20" + selectedYear : selectedYear;
262
- cc.year = typeof cc.year === "number" ? Number(replacementYear) : replacementYear;
263
- return { body: JSON.stringify(value), where: ["number", "verification_value", "month", "year"].map((key) => "payment_method.credit_card." + key) };
264
- }
265
- function sanitizeSpreedlyTokenResponse(body) {
266
- const value = readJson(body), transaction = value?.transaction, pm = transaction?.payment_method;
267
- const fail = () => {
268
- throw new Error("Invalid Spreedly token response.");
269
- };
270
- if (!record(transaction) || !record(pm) || transaction.succeeded !== true || transaction.transaction_type !== "AddPaymentMethod" || transaction.state !== "succeeded" || pm.payment_method_type !== "credit_card" || !opaqueId(pm.token))
271
- return fail();
272
- if (!text(pm.number, 32) || !exact(/^[Xx*]{8,15}[0-9]{4}$/, pm.number.replace(/-/g, "")) || !digits(pm.last_four_digits, 4, 4) || !pm.number.endsWith(pm.last_four_digits) || !exact(/^[Xx*]{3,4}$/, pm.verification_value) || !month(pm.month) || !year(pm.year))
273
- return fail();
274
- const safePm = {
275
- token: pm.token,
276
- payment_method_type: "credit_card",
277
- number: pm.number,
278
- verification_value: pm.verification_value,
279
- last_four_digits: pm.last_four_digits,
280
- month: pm.month,
281
- year: pm.year
282
- };
283
- if (owns(pm, "card_type")) {
284
- if (!CARD_TYPES.has(pm.card_type))
285
- return fail();
286
- safePm.card_type = pm.card_type;
287
- }
288
- if (owns(pm, "storage_state")) {
289
- if (!["cached", "retained"].includes(pm.storage_state))
290
- return fail();
291
- safePm.storage_state = pm.storage_state;
292
- }
293
- if (owns(pm, "fingerprint")) {
294
- if (!opaqueId(pm.fingerprint))
295
- return fail();
296
- safePm.fingerprint = pm.fingerprint;
297
- }
298
- for (const [key, width] of [["first_six_digits", 6], ["first_eight_digits", 8]]) {
299
- if (owns(pm, key) && pm[key] !== null) {
300
- if (!digits(pm[key], width, width))
301
- return fail();
302
- safePm[key] = pm[key];
303
- }
304
- }
305
- for (const key of ["test", "eligible_for_card_updater"]) {
306
- if (owns(pm, key)) {
307
- if (typeof pm[key] !== "boolean")
308
- return fail();
309
- safePm[key] = pm[key];
310
- }
311
- }
312
- for (const key of CARD_TEXT) {
313
- if (!owns(pm, key))
314
- continue;
315
- if (pm[key] !== null && (!text(pm[key]) || panLike(pm[key])))
316
- return fail();
317
- safePm[key] = pm[key];
318
- }
319
- if (owns(pm, "errors")) {
320
- if (!Array.isArray(pm.errors) || pm.errors.length)
321
- return fail();
322
- safePm.errors = [];
323
- }
324
- const safeTransaction = { succeeded: true, transaction_type: "AddPaymentMethod", state: "succeeded", payment_method: safePm };
325
- if (owns(transaction, "token")) {
326
- if (!opaqueId(transaction.token))
327
- return fail();
328
- safeTransaction.token = transaction.token;
329
- }
330
- return JSON.stringify({ transaction: safeTransaction });
331
- }
332
- export { SPREEDLY_TOKEN_ENDPOINT, injectSpreedlyCard, isPreparedSpreedlyRequest, isSpreedlyRequest, isSpreedlyTokenRequest, sanitizeSpreedlyTokenResponse };