@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.
- package/README.md +6 -669
- package/cdp.d.ts +1 -0
- package/cdp.js +2 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +33 -33
- package/playwright.d.ts +1 -0
- package/playwright.js +2 -0
- package/preflight.d.ts +1 -0
- package/preflight.js +2 -0
- package/CHANGELOG.md +0 -124
- package/PREFLIGHT.md +0 -308
- package/dist/adyen.generated.d.ts +0 -24
- package/dist/adyen.generated.js +0 -64
- package/dist/attachment.d.ts +0 -11
- package/dist/attachment.js +0 -50
- package/dist/braintree.d.ts +0 -2
- package/dist/braintree.generated.d.ts +0 -10
- package/dist/braintree.generated.js +0 -302
- package/dist/braintree.js +0 -2
- package/dist/builtin-registry.generated.d.ts +0 -2
- package/dist/builtin-registry.generated.js +0 -1
- package/dist/card-fields.generated.d.ts +0 -3
- package/dist/card-fields.generated.js +0 -46
- package/dist/cdp.d.ts +0 -189
- package/dist/cdp.js +0 -2194
- package/dist/checkout-com.generated.d.ts +0 -4
- package/dist/checkout-com.generated.js +0 -183
- package/dist/client.d.ts +0 -618
- package/dist/client.js +0 -1251
- package/dist/hosted-form.d.ts +0 -44
- package/dist/hosted-form.js +0 -78
- package/dist/index.d.ts +0 -13
- package/dist/index.js +0 -6
- package/dist/lifecycle.d.ts +0 -165
- package/dist/lifecycle.js +0 -370
- package/dist/mercado-checkout.d.ts +0 -20
- package/dist/mercado-checkout.generated.d.ts +0 -52
- package/dist/mercado-checkout.generated.js +0 -198
- package/dist/mercado-checkout.js +0 -108
- package/dist/owned-shop.generated.d.ts +0 -24
- package/dist/owned-shop.generated.js +0 -108
- package/dist/paysafe.generated.d.ts +0 -12
- package/dist/paysafe.generated.js +0 -87
- package/dist/playwright.d.ts +0 -3
- package/dist/playwright.js +0 -3
- package/dist/preflight-capabilities.generated.d.ts +0 -1253
- package/dist/preflight-capabilities.generated.js +0 -1929
- package/dist/preflight-catalog.json +0 -4595
- package/dist/preflight-playwright.d.ts +0 -34
- package/dist/preflight-playwright.js +0 -355
- package/dist/preflight-schemas.json +0 -1110
- package/dist/preflight.d.ts +0 -1
- package/dist/preflight.generated.d.ts +0 -1965
- package/dist/preflight.generated.js +0 -556
- package/dist/preflight.js +0 -2
- package/dist/preparation.d.ts +0 -31
- package/dist/preparation.js +0 -164
- package/dist/prepared-processor.d.ts +0 -10
- package/dist/prepared-processor.js +0 -122
- package/dist/recurly.generated.d.ts +0 -1
- package/dist/recurly.generated.js +0 -87
- package/dist/registry.d.ts +0 -76
- package/dist/registry.js +0 -296
- package/dist/spreedly.generated.d.ts +0 -10
- package/dist/spreedly.generated.js +0 -332
- package/dist/stripe-checkout.d.ts +0 -81
- package/dist/stripe-checkout.generated.d.ts +0 -82
- package/dist/stripe-checkout.generated.js +0 -1004
- package/dist/stripe-checkout.js +0 -140
- package/dist/substitute.d.ts +0 -38
- package/dist/substitute.js +0 -23
- package/dist/substitutions.generated.d.ts +0 -10
- package/dist/substitutions.generated.js +0 -66
- package/examples/existing-browser.mjs +0 -63
- package/examples/preflight/classify-direct.mjs +0 -21
- package/examples/preflight/classify-kernel.mjs +0 -30
- package/examples/preflight/inspect-browser.mjs +0 -44
- package/examples/preflight/kernel-native/README.md +0 -112
- package/examples/preflight/kernel-native/documented-adapters.json +0 -113
- package/examples/preflight/kernel-native/inventory.json +0 -233
- package/examples/preflight/kernel-native/qualification.mjs +0 -182
- package/examples/preflight/kernel-profile.empty.json +0 -11
- package/examples/preflight/mollie-hosted.observations.json +0 -23
- package/examples/preflight/mollie-hosted.result.json +0 -103
- package/examples/preflight/stripe-script.direct.result.json +0 -92
- package/examples/preflight/stripe-script.observations.json +0 -16
- 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 };
|