@agent-cards/sdk 0.21.0 → 0.23.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/CHANGELOG.md +87 -2
- package/PREFLIGHT.md +32 -0
- package/README.md +1177 -23
- package/dist/aci.d.ts +173 -0
- package/dist/aci.generated.d.ts +87 -0
- package/dist/aci.generated.js +306 -0
- package/dist/aci.js +371 -0
- package/dist/adyen-merchant-hosted.generated.d.ts +3 -3
- package/dist/adyen-merchant-hosted.generated.js +302 -56
- package/dist/agentcard.d.ts +9 -2
- package/dist/agentcard.js +4 -2
- package/dist/amadeus-checkout.generated.d.ts +57 -0
- package/dist/amadeus-checkout.generated.js +549 -0
- package/dist/amadeus.d.ts +41 -0
- package/dist/amadeus.js +81 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/card-endpoint-facts.d.ts +96 -0
- package/dist/card-endpoint-facts.js +275 -0
- package/dist/card-endpoint-page.d.ts +54 -0
- package/dist/card-endpoint-page.js +69 -0
- package/dist/card-endpoint.d.ts +298 -0
- package/dist/card-endpoint.js +438 -0
- package/dist/card-fields.generated.d.ts +5 -3
- package/dist/card-fields.generated.js +284 -7
- package/dist/cdp.d.ts +5 -1
- package/dist/cdp.js +1155 -141
- package/dist/client.d.ts +456 -20
- package/dist/client.js +1072 -104
- package/dist/creditguard.generated.d.ts +79 -0
- package/dist/creditguard.generated.js +662 -0
- package/dist/cse-body.js +11 -1
- package/dist/cybersource-flex.d.ts +30 -0
- package/dist/cybersource-flex.generated.d.ts +27 -1
- package/dist/cybersource-flex.generated.js +42 -2
- package/dist/cybersource-flex.js +46 -0
- package/dist/datatrans.d.ts +96 -0
- package/dist/datatrans.generated.d.ts +64 -0
- package/dist/datatrans.generated.js +426 -0
- package/dist/datatrans.js +178 -0
- package/dist/ekashu.d.ts +35 -0
- package/dist/ekashu.js +55 -0
- package/dist/elavon-converge.d.ts +37 -0
- package/dist/elavon-converge.generated.d.ts +49 -0
- package/dist/elavon-converge.generated.js +439 -0
- package/dist/elavon-converge.js +84 -0
- package/dist/eprotect.d.ts +152 -0
- package/dist/eprotect.generated.d.ts +77 -0
- package/dist/eprotect.generated.js +498 -0
- package/dist/eprotect.js +222 -0
- package/dist/hosted-form.d.ts +3 -2
- package/dist/hosted-form.js +3 -2
- package/dist/index.d.ts +17 -3
- package/dist/index.js +6 -1
- package/dist/juspay-gate.d.ts +82 -0
- package/dist/juspay-gate.js +126 -0
- package/dist/juspay.generated.d.ts +89 -0
- package/dist/juspay.generated.js +705 -0
- package/dist/lifecycle.d.ts +60 -6
- package/dist/lifecycle.js +122 -8
- package/dist/merchant-card-endpoints.generated.d.ts +122 -0
- package/dist/merchant-card-endpoints.generated.js +4261 -0
- package/dist/merchant-hosted.d.ts +4 -1
- package/dist/merchant-hosted.js +5 -2
- package/dist/merchant-session-probe.d.ts +48 -0
- package/dist/merchant-session-probe.js +53 -0
- package/dist/merchant-session.generated.d.ts +69 -0
- package/dist/merchant-session.generated.js +919 -0
- package/dist/payrails.d.ts +133 -0
- package/dist/payrails.generated.d.ts +73 -0
- package/dist/payrails.generated.js +596 -0
- package/dist/payrails.js +305 -0
- package/dist/pelecard.d.ts +32 -0
- package/dist/pelecard.generated.d.ts +32 -0
- package/dist/pelecard.generated.js +283 -0
- package/dist/pelecard.js +42 -0
- package/dist/pre-claim.d.ts +81 -2
- package/dist/pre-claim.js +1023 -5
- package/dist/preflight-catalog.json +2267 -288
- package/dist/preflight-schemas.json +183 -6
- package/dist/preflight.generated.d.ts +1 -1
- package/dist/preflight.generated.js +243 -4
- package/dist/preparation.d.ts +12 -3
- package/dist/preparation.js +63 -12
- package/dist/prepared-processor.d.ts +84 -5
- package/dist/prepared-processor.js +147 -6
- package/dist/primer.d.ts +53 -0
- package/dist/primer.generated.d.ts +38 -0
- package/dist/primer.generated.js +624 -0
- package/dist/primer.js +82 -0
- package/dist/registry.d.ts +100 -8
- package/dist/registry.js +85 -11
- package/dist/shiji-sts.d.ts +144 -0
- package/dist/shiji-sts.generated.d.ts +72 -0
- package/dist/shiji-sts.generated.js +913 -0
- package/dist/shiji-sts.js +222 -0
- package/dist/stripe-checkout.generated.d.ts +1 -1
- package/dist/stripe-checkout.generated.js +764 -9
- package/dist/stripe-setup.d.ts +16 -0
- package/dist/stripe-setup.js +61 -0
- package/dist/substitutions.generated.js +187 -29
- package/dist/verifone.d.ts +68 -0
- package/dist/verifone.generated.d.ts +51 -0
- package/dist/verifone.generated.js +1413 -0
- package/dist/verifone.js +119 -0
- package/dist/voice-types.d.ts +27 -0
- package/dist/voice.d.ts +6 -1
- package/dist/voice.js +6 -1
- package/dist/wspay.d.ts +41 -0
- package/dist/wspay.generated.d.ts +41 -0
- package/dist/wspay.generated.js +370 -0
- package/dist/wspay.js +65 -0
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
+
// artifact-sha256: 2bf5a6f966e390caa86efaa4be753222abb5606a8689cc8cfa42c2466c65ded9
|
|
4
|
+
// src/json-path.js
|
|
5
|
+
function roundTrips(parsed, body) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.stringify(parsed) === body;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// src/sha256.js
|
|
14
|
+
var K = /* @__PURE__ */ new Uint32Array([
|
|
15
|
+
1116352408,
|
|
16
|
+
1899447441,
|
|
17
|
+
3049323471,
|
|
18
|
+
3921009573,
|
|
19
|
+
961987163,
|
|
20
|
+
1508970993,
|
|
21
|
+
2453635748,
|
|
22
|
+
2870763221,
|
|
23
|
+
3624381080,
|
|
24
|
+
310598401,
|
|
25
|
+
607225278,
|
|
26
|
+
1426881987,
|
|
27
|
+
1925078388,
|
|
28
|
+
2162078206,
|
|
29
|
+
2614888103,
|
|
30
|
+
3248222580,
|
|
31
|
+
3835390401,
|
|
32
|
+
4022224774,
|
|
33
|
+
264347078,
|
|
34
|
+
604807628,
|
|
35
|
+
770255983,
|
|
36
|
+
1249150122,
|
|
37
|
+
1555081692,
|
|
38
|
+
1996064986,
|
|
39
|
+
2554220882,
|
|
40
|
+
2821834349,
|
|
41
|
+
2952996808,
|
|
42
|
+
3210313671,
|
|
43
|
+
3336571891,
|
|
44
|
+
3584528711,
|
|
45
|
+
113926993,
|
|
46
|
+
338241895,
|
|
47
|
+
666307205,
|
|
48
|
+
773529912,
|
|
49
|
+
1294757372,
|
|
50
|
+
1396182291,
|
|
51
|
+
1695183700,
|
|
52
|
+
1986661051,
|
|
53
|
+
2177026350,
|
|
54
|
+
2456956037,
|
|
55
|
+
2730485921,
|
|
56
|
+
2820302411,
|
|
57
|
+
3259730800,
|
|
58
|
+
3345764771,
|
|
59
|
+
3516065817,
|
|
60
|
+
3600352804,
|
|
61
|
+
4094571909,
|
|
62
|
+
275423344,
|
|
63
|
+
430227734,
|
|
64
|
+
506948616,
|
|
65
|
+
659060556,
|
|
66
|
+
883997877,
|
|
67
|
+
958139571,
|
|
68
|
+
1322822218,
|
|
69
|
+
1537002063,
|
|
70
|
+
1747873779,
|
|
71
|
+
1955562222,
|
|
72
|
+
2024104815,
|
|
73
|
+
2227730452,
|
|
74
|
+
2361852424,
|
|
75
|
+
2428436474,
|
|
76
|
+
2756734187,
|
|
77
|
+
3204031479,
|
|
78
|
+
3329325298
|
|
79
|
+
]);
|
|
80
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
81
|
+
function utf8(text) {
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const char of text) {
|
|
84
|
+
let cp = char.codePointAt(0);
|
|
85
|
+
if (cp >= 55296 && cp <= 57343)
|
|
86
|
+
cp = 65533;
|
|
87
|
+
if (cp < 128)
|
|
88
|
+
out.push(cp);
|
|
89
|
+
else if (cp < 2048)
|
|
90
|
+
out.push(192 | cp >> 6, 128 | cp & 63);
|
|
91
|
+
else if (cp < 65536)
|
|
92
|
+
out.push(224 | cp >> 12, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
93
|
+
else
|
|
94
|
+
out.push(240 | cp >> 18, 128 | cp >> 12 & 63, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
95
|
+
}
|
|
96
|
+
return Uint8Array.from(out);
|
|
97
|
+
}
|
|
98
|
+
function sha256Hex(input) {
|
|
99
|
+
const isBytes = Object.prototype.toString.call(input) === "[object Uint8Array]";
|
|
100
|
+
if (typeof input !== "string" && !isBytes)
|
|
101
|
+
throw new TypeError("sha256Hex takes a string or a Uint8Array.");
|
|
102
|
+
const bytes = typeof input === "string" ? utf8(input) : input;
|
|
103
|
+
const length = bytes.length;
|
|
104
|
+
const padded = new Uint8Array((length + 8 >> 6) + 1 << 6);
|
|
105
|
+
padded.set(bytes);
|
|
106
|
+
padded[length] = 128;
|
|
107
|
+
const view = new DataView(padded.buffer);
|
|
108
|
+
view.setUint32(padded.length - 8, Math.floor(length / 536870912));
|
|
109
|
+
view.setUint32(padded.length - 4, length << 3 >>> 0);
|
|
110
|
+
const H = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
111
|
+
const W = new Uint32Array(64);
|
|
112
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
113
|
+
for (let t = 0; t < 16; t++)
|
|
114
|
+
W[t] = view.getUint32(offset + t * 4);
|
|
115
|
+
for (let t = 16; t < 64; t++) {
|
|
116
|
+
const s0 = rotr(W[t - 15], 7) ^ rotr(W[t - 15], 18) ^ W[t - 15] >>> 3;
|
|
117
|
+
const s1 = rotr(W[t - 2], 17) ^ rotr(W[t - 2], 19) ^ W[t - 2] >>> 10;
|
|
118
|
+
W[t] = W[t - 16] + s0 + W[t - 7] + s1 >>> 0;
|
|
119
|
+
}
|
|
120
|
+
let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
|
|
121
|
+
for (let t = 0; t < 64; t++) {
|
|
122
|
+
const t1 = h + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[t] + W[t] >>> 0;
|
|
123
|
+
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
124
|
+
h = g;
|
|
125
|
+
g = f;
|
|
126
|
+
f = e;
|
|
127
|
+
e = d + t1 >>> 0;
|
|
128
|
+
d = c;
|
|
129
|
+
c = b;
|
|
130
|
+
b = a;
|
|
131
|
+
a = t1 + t2 >>> 0;
|
|
132
|
+
}
|
|
133
|
+
H[0] += a;
|
|
134
|
+
H[1] += b;
|
|
135
|
+
H[2] += c;
|
|
136
|
+
H[3] += d;
|
|
137
|
+
H[4] += e;
|
|
138
|
+
H[5] += f;
|
|
139
|
+
H[6] += g;
|
|
140
|
+
H[7] += h;
|
|
141
|
+
}
|
|
142
|
+
return Array.from(H, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
143
|
+
}
|
|
144
|
+
// src/rsa-pkcs1v15.js
|
|
145
|
+
var RsaPkcs1v15Error = class extends Error {
|
|
146
|
+
constructor(reason) {
|
|
147
|
+
super("RSA PKCS#1 v1.5 encryption refused.");
|
|
148
|
+
this.name = "RsaPkcs1v15Error";
|
|
149
|
+
this.code = "rsa_pkcs1v15_refused";
|
|
150
|
+
this.reason = reason;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var refuse = (reason) => new RsaPkcs1v15Error(reason);
|
|
154
|
+
var BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
155
|
+
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
156
|
+
var RSA_ALGORITHM = [48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0];
|
|
157
|
+
var MODULUS_BYTES = [128, 256, 384, 512];
|
|
158
|
+
var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
159
|
+
var typedArrayName = Object.getOwnPropertyDescriptor(TYPED_ARRAY, Symbol.toStringTag).get;
|
|
160
|
+
function parseRsaPublicKey(key) {
|
|
161
|
+
const spki = key && typeof key === "object" ? key.spki : void 0;
|
|
162
|
+
const pin = key && typeof key === "object" ? key.sha256 : void 0;
|
|
163
|
+
if (typeof spki !== "string" || spki.length > 1024 || spki.length % 4 !== 0 || !BASE64.test(spki))
|
|
164
|
+
throw refuse("key");
|
|
165
|
+
if (typeof pin !== "string" || !SHA256_HEX.test(pin))
|
|
166
|
+
throw refuse("key");
|
|
167
|
+
let binary;
|
|
168
|
+
try {
|
|
169
|
+
binary = atob(spki);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
throw refuse("key");
|
|
173
|
+
}
|
|
174
|
+
if (btoa(binary) !== spki)
|
|
175
|
+
throw refuse("key");
|
|
176
|
+
const der = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
177
|
+
if (sha256Hex(der) !== pin)
|
|
178
|
+
throw refuse("key");
|
|
179
|
+
const outer = readElement(der, 0, 48);
|
|
180
|
+
if (outer.end !== der.length || outer.start + RSA_ALGORITHM.length > der.length)
|
|
181
|
+
throw refuse("key");
|
|
182
|
+
if (!RSA_ALGORITHM.every((byte, i) => der[outer.start + i] === byte))
|
|
183
|
+
throw refuse("key");
|
|
184
|
+
const bits = readElement(der, outer.start + RSA_ALGORITHM.length, 3);
|
|
185
|
+
if (bits.end !== outer.end || bits.start === bits.end || der[bits.start] !== 0)
|
|
186
|
+
throw refuse("key");
|
|
187
|
+
const rsa = readElement(der, bits.start + 1, 48);
|
|
188
|
+
if (rsa.end !== bits.end)
|
|
189
|
+
throw refuse("key");
|
|
190
|
+
const modulus = readPositiveInteger(der, rsa.start);
|
|
191
|
+
const exponent = readPositiveInteger(der, modulus.end);
|
|
192
|
+
if (exponent.end !== rsa.end)
|
|
193
|
+
throw refuse("key");
|
|
194
|
+
const n = modulus.bytes;
|
|
195
|
+
if (!MODULUS_BYTES.includes(n.length) || !(n[0] & 128) || !(n[n.length - 1] & 1))
|
|
196
|
+
throw refuse("key");
|
|
197
|
+
const e = exponent.bytes;
|
|
198
|
+
if (e.length !== 3 || e[0] !== 1 || e[1] !== 0 || e[2] !== 1)
|
|
199
|
+
throw refuse("key");
|
|
200
|
+
return Object.freeze({ n: toBigInt(n), e: 65537n, size: n.length });
|
|
201
|
+
}
|
|
202
|
+
function readElement(der, at, tag) {
|
|
203
|
+
if (at + 2 > der.length || der[at] !== tag)
|
|
204
|
+
throw refuse("key");
|
|
205
|
+
let length = der[at + 1];
|
|
206
|
+
let start = at + 2;
|
|
207
|
+
if (length & 128) {
|
|
208
|
+
const count = length & 127;
|
|
209
|
+
if (count === 0 || count > 2 || start + count > der.length || der[start] === 0)
|
|
210
|
+
throw refuse("key");
|
|
211
|
+
length = count === 1 ? der[start] : der[start] * 256 + der[start + 1];
|
|
212
|
+
if (length < 128)
|
|
213
|
+
throw refuse("key");
|
|
214
|
+
start += count;
|
|
215
|
+
}
|
|
216
|
+
if (start + length > der.length)
|
|
217
|
+
throw refuse("key");
|
|
218
|
+
return { start, end: start + length };
|
|
219
|
+
}
|
|
220
|
+
function readPositiveInteger(der, at) {
|
|
221
|
+
const { start, end } = readElement(der, at, 2);
|
|
222
|
+
if (end === start || der[start] & 128)
|
|
223
|
+
throw refuse("key");
|
|
224
|
+
if (der[start] === 0 && (end - start === 1 || !(der[start + 1] & 128)))
|
|
225
|
+
throw refuse("key");
|
|
226
|
+
return { bytes: der.subarray(der[start] === 0 ? start + 1 : start, end), end };
|
|
227
|
+
}
|
|
228
|
+
function toBigInt(bytes) {
|
|
229
|
+
let hex = "";
|
|
230
|
+
for (let i = 0; i < bytes.length; i++)
|
|
231
|
+
hex += (bytes[i] < 16 ? "0" : "") + bytes[i].toString(16);
|
|
232
|
+
return BigInt(`0x${hex}`);
|
|
233
|
+
}
|
|
234
|
+
// src/payrails.js
|
|
235
|
+
var PAYRAILS_PSP = "payrails";
|
|
236
|
+
var PAYRAILS_CAPABILITY = "payrails_device_encrypt";
|
|
237
|
+
var PAYRAILS_OPERATION = "payrails.device_encrypted_card";
|
|
238
|
+
var PAYRAILS_SAVE_PATH = "/public/payment/instruments";
|
|
239
|
+
var PAYRAILS_SAVE_HOSTS = Object.freeze(["api-pub.payrails.io", "api-pub.staging.payrails.io", "sandbox-api-pub.staging.payrails.io"]);
|
|
240
|
+
var PAYRAILS_JWE_HEADER = '{"alg":"RSA-OAEP-256","enc":"A256CBC-HS512"}';
|
|
241
|
+
var PAYRAILS_JWE_HEADER_B64 = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0";
|
|
242
|
+
var PAYRAILS_KEY_BITS = Object.freeze([2048, 3072, 4096]);
|
|
243
|
+
var PAYRAILS_FUTURE_USAGES = Object.freeze(["CardOnFile", "UnscheduledCardOnFile"]);
|
|
244
|
+
var PAYRAILS_CARD_ON_FILE_CONSENTED = Object.freeze([]);
|
|
245
|
+
var PAYRAILS_SESSION_MIN_SECONDS = 180;
|
|
246
|
+
var NOT_THE_SAVE = "Refusing to pay: this is not the Payrails card save the checkout sends.";
|
|
247
|
+
var KEY = "Refusing to pay: the Payrails session key does not match this checkout. Reload the checkout.";
|
|
248
|
+
var ACCOUNT = "Refusing to pay: Agentcard has not reviewed the Payrails account behind this checkout.";
|
|
249
|
+
var PAYRAILS_REFUSALS = Object.freeze({
|
|
250
|
+
payrails_request_malformed: NOT_THE_SAVE,
|
|
251
|
+
payrails_not_card_save: NOT_THE_SAVE,
|
|
252
|
+
payrails_jwe_malformed: NOT_THE_SAVE,
|
|
253
|
+
payrails_future_usage_refused: "Refusing to pay: this Payrails save asks to keep the card for a use the Vault cannot approve yet. Nothing was sent.",
|
|
254
|
+
payrails_card_on_file_consent_required: "Refusing to pay: this Payrails save would keep the card on file without your consent. Nothing was sent.",
|
|
255
|
+
payrails_key_invalid: KEY,
|
|
256
|
+
payrails_key_mismatch: KEY,
|
|
257
|
+
payrails_token_invalid: ACCOUNT,
|
|
258
|
+
payrails_workspace_unpinned: ACCOUNT,
|
|
259
|
+
payrails_workspace_mismatch: ACCOUNT,
|
|
260
|
+
payrails_session_expiring: "Refusing to pay: this Payrails session expires before it can be approved. Reload the checkout."
|
|
261
|
+
});
|
|
262
|
+
function payrailsRefusalMessage(code) {
|
|
263
|
+
return typeof code === "string" && Object.prototype.hasOwnProperty.call(PAYRAILS_REFUSALS, code) ? PAYRAILS_REFUSALS[code] : NOT_THE_SAVE;
|
|
264
|
+
}
|
|
265
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
266
|
+
var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
267
|
+
var sameKeys = (value, keys) => {
|
|
268
|
+
const actual = Object.keys(value);
|
|
269
|
+
return actual.length === keys.length && actual.every((key, i) => key === keys[i]);
|
|
270
|
+
};
|
|
271
|
+
var plainText = (value, max) => typeof value === "string" && value.length > 0 && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
272
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
273
|
+
var BODY_LIMIT = 16384;
|
|
274
|
+
var B64URL = /^[A-Za-z0-9_-]+$/;
|
|
275
|
+
function base64UrlEncode(bytes) {
|
|
276
|
+
let binary = "";
|
|
277
|
+
for (let i = 0; i < bytes.length; i++)
|
|
278
|
+
binary += String.fromCharCode(bytes[i]);
|
|
279
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
280
|
+
}
|
|
281
|
+
function base64UrlDecode(text, max) {
|
|
282
|
+
if (typeof text !== "string" || text.length === 0 || text.length > max || !B64URL.test(text) || text.length % 4 === 1)
|
|
283
|
+
return null;
|
|
284
|
+
const padded = text.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - text.length % 4) % 4);
|
|
285
|
+
let binary;
|
|
286
|
+
try {
|
|
287
|
+
binary = atob(padded);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
293
|
+
return base64UrlEncode(bytes) === text ? bytes : null;
|
|
294
|
+
}
|
|
295
|
+
function isPayrailsSaveUrl(url, { exact: exact2 = false } = {}) {
|
|
296
|
+
if (typeof url !== "string" || url.length > 2048)
|
|
297
|
+
return false;
|
|
298
|
+
let parsed;
|
|
299
|
+
try {
|
|
300
|
+
parsed = new URL(url);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
if (parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password)
|
|
306
|
+
return false;
|
|
307
|
+
if (!PAYRAILS_SAVE_HOSTS.includes(parsed.hostname) || parsed.pathname !== PAYRAILS_SAVE_PATH)
|
|
308
|
+
return false;
|
|
309
|
+
return !exact2 || parsed.search === "" && parsed.hash === "" && !/[?#]/.test(url);
|
|
310
|
+
}
|
|
311
|
+
function payrailsJweParts(jwe) {
|
|
312
|
+
if (typeof jwe !== "string" || jwe.length > 8192)
|
|
313
|
+
return null;
|
|
314
|
+
const parts = jwe.split(".");
|
|
315
|
+
if (parts.length !== 5 || parts[0] !== PAYRAILS_JWE_HEADER_B64)
|
|
316
|
+
return null;
|
|
317
|
+
const wrappedKey = base64UrlDecode(parts[1], 700);
|
|
318
|
+
const iv = base64UrlDecode(parts[2], 22);
|
|
319
|
+
const ciphertext = base64UrlDecode(parts[3], 5462);
|
|
320
|
+
const tag = base64UrlDecode(parts[4], 43);
|
|
321
|
+
if (!wrappedKey || !PAYRAILS_KEY_BITS.includes(wrappedKey.length * 8))
|
|
322
|
+
return null;
|
|
323
|
+
if (!iv || iv.length !== 16 || !tag || tag.length !== 32)
|
|
324
|
+
return null;
|
|
325
|
+
if (!ciphertext || ciphertext.length === 0 || ciphertext.length % 16 !== 0 || ciphertext.length > 4096)
|
|
326
|
+
return null;
|
|
327
|
+
return { wrappedKey, iv, ciphertext, tag };
|
|
328
|
+
}
|
|
329
|
+
var SAVE_KEYS = Object.freeze(["holderReference", "paymentMethod", "storeInstrument", "futureUsage", "data"]);
|
|
330
|
+
var DATA_KEYS = Object.freeze(["encryptedData", "vaultProviderConfigId"]);
|
|
331
|
+
var refusal = (code) => ({ code });
|
|
332
|
+
function readSaveBody(body) {
|
|
333
|
+
if (typeof body !== "string" || body.length === 0 || body.length > BODY_LIMIT)
|
|
334
|
+
return refusal("payrails_request_malformed");
|
|
335
|
+
let parsed;
|
|
336
|
+
try {
|
|
337
|
+
parsed = JSON.parse(body);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return refusal("payrails_request_malformed");
|
|
341
|
+
}
|
|
342
|
+
if (!record(parsed) || !roundTrips(parsed, body))
|
|
343
|
+
return refusal("payrails_request_malformed");
|
|
344
|
+
const data = parsed.data;
|
|
345
|
+
if (parsed.paymentMethod !== "card" || !record(data) || !owns(data, "encryptedData"))
|
|
346
|
+
return refusal("payrails_not_card_save");
|
|
347
|
+
if (!sameKeys(parsed, SAVE_KEYS) || !sameKeys(data, DATA_KEYS))
|
|
348
|
+
return refusal("payrails_request_malformed");
|
|
349
|
+
if (!plainText(parsed.holderReference, 256) || typeof parsed.storeInstrument !== "boolean")
|
|
350
|
+
return refusal("payrails_request_malformed");
|
|
351
|
+
if (typeof data.vaultProviderConfigId !== "string" || !UUID.test(data.vaultProviderConfigId))
|
|
352
|
+
return refusal("payrails_request_malformed");
|
|
353
|
+
if (!PAYRAILS_FUTURE_USAGES.includes(parsed.futureUsage))
|
|
354
|
+
return refusal("payrails_future_usage_refused");
|
|
355
|
+
const jwe = payrailsJweParts(data.encryptedData);
|
|
356
|
+
if (!jwe)
|
|
357
|
+
return refusal("payrails_jwe_malformed");
|
|
358
|
+
return {
|
|
359
|
+
value: Object.freeze({
|
|
360
|
+
holderReference: parsed.holderReference,
|
|
361
|
+
storeInstrument: parsed.storeInstrument,
|
|
362
|
+
futureUsage: parsed.futureUsage,
|
|
363
|
+
encryptedData: data.encryptedData,
|
|
364
|
+
vaultProviderConfigId: data.vaultProviderConfigId,
|
|
365
|
+
wrappedKeyBytes: jwe.wrappedKey.length
|
|
366
|
+
})
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function payrailsTemplateRefusal(body) {
|
|
370
|
+
return readSaveBody(body).code ?? null;
|
|
371
|
+
}
|
|
372
|
+
function parsePayrailsSave(body) {
|
|
373
|
+
return readSaveBody(body).value ?? null;
|
|
374
|
+
}
|
|
375
|
+
function payrailsKeepsCardFor(save) {
|
|
376
|
+
if (!record(save))
|
|
377
|
+
return null;
|
|
378
|
+
if (save.storeInstrument === true)
|
|
379
|
+
return save.futureUsage;
|
|
380
|
+
return save.futureUsage === "CardOnFile" ? null : save.futureUsage;
|
|
381
|
+
}
|
|
382
|
+
function payrailsCardOnFileRefusal(body) {
|
|
383
|
+
const save = readSaveBody(body);
|
|
384
|
+
if (save.code)
|
|
385
|
+
return save.code;
|
|
386
|
+
const usage = payrailsKeepsCardFor(save.value);
|
|
387
|
+
return usage === null || PAYRAILS_CARD_ON_FILE_CONSENTED.includes(usage) ? null : "payrails_card_on_file_consent_required";
|
|
388
|
+
}
|
|
389
|
+
function classifyPayrailsRequest(url, method, body) {
|
|
390
|
+
if (method !== "POST" || !isPayrailsSaveUrl(url))
|
|
391
|
+
return null;
|
|
392
|
+
if (typeof body !== "string" || body === "")
|
|
393
|
+
return "continue";
|
|
394
|
+
let parsed;
|
|
395
|
+
try {
|
|
396
|
+
parsed = JSON.parse(body);
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return "continue";
|
|
400
|
+
}
|
|
401
|
+
const data = record(parsed) ? parsed.data : null;
|
|
402
|
+
const value = record(data) && owns(data, "encryptedData") ? data.encryptedData : void 0;
|
|
403
|
+
return typeof value === "string" && value !== "" || typeof value === "number" ? "card" : "continue";
|
|
404
|
+
}
|
|
405
|
+
function payrailsAuthorizeCarriesCard(url, method, body) {
|
|
406
|
+
if (method !== "POST" || typeof url !== "string" || typeof body !== "string" || body.length > 262144)
|
|
407
|
+
return false;
|
|
408
|
+
let parsed;
|
|
409
|
+
try {
|
|
410
|
+
parsed = new URL(url);
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
if (!/(?:^|\.)payrails\.io$/.test(parsed.hostname))
|
|
416
|
+
return false;
|
|
417
|
+
let value;
|
|
418
|
+
try {
|
|
419
|
+
value = JSON.parse(body);
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
const composition = record(value) ? value.paymentComposition : null;
|
|
425
|
+
return Array.isArray(composition) && composition.some((part) => record(part) && record(part.paymentInstrumentData) && typeof part.paymentInstrumentData.encryptedData === "string" && part.paymentInstrumentData.encryptedData !== "");
|
|
426
|
+
}
|
|
427
|
+
function payrailsSessionKeyBytes(spki) {
|
|
428
|
+
if (typeof spki !== "string" || spki.length === 0 || spki.length > 1024)
|
|
429
|
+
return null;
|
|
430
|
+
try {
|
|
431
|
+
const der = Uint8Array.from(atob(spki), (c) => c.charCodeAt(0));
|
|
432
|
+
const { size } = parseRsaPublicKey({ spki, sha256: sha256Hex(der) });
|
|
433
|
+
return PAYRAILS_KEY_BITS.includes(size * 8) ? size : null;
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function payrailsSessionKeyRefusal(spki, body) {
|
|
440
|
+
const save = readSaveBody(body);
|
|
441
|
+
if (save.code)
|
|
442
|
+
return save.code;
|
|
443
|
+
const size = payrailsSessionKeyBytes(spki);
|
|
444
|
+
if (size === null)
|
|
445
|
+
return "payrails_key_invalid";
|
|
446
|
+
return size === save.value.wrappedKeyBytes ? null : "payrails_key_mismatch";
|
|
447
|
+
}
|
|
448
|
+
var TOKEN_LIMIT = 8192;
|
|
449
|
+
var CLAIM_NAME = /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/;
|
|
450
|
+
function payrailsTokenClaims(authorization) {
|
|
451
|
+
if (typeof authorization !== "string" || !authorization.startsWith("Bearer ") || authorization.length > TOKEN_LIMIT + 7)
|
|
452
|
+
return null;
|
|
453
|
+
const parts = authorization.slice(7).split(".");
|
|
454
|
+
if (parts.length !== 3 || parts[2] === "")
|
|
455
|
+
return null;
|
|
456
|
+
const decoded = [];
|
|
457
|
+
for (const part of parts.slice(0, 2)) {
|
|
458
|
+
const bytes = base64UrlDecode(part, TOKEN_LIMIT);
|
|
459
|
+
let value;
|
|
460
|
+
try {
|
|
461
|
+
value = bytes && JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
if (!record(value))
|
|
467
|
+
return null;
|
|
468
|
+
decoded.push(value);
|
|
469
|
+
}
|
|
470
|
+
if (!base64UrlDecode(parts[2], TOKEN_LIMIT))
|
|
471
|
+
return null;
|
|
472
|
+
const [header, payload] = decoded;
|
|
473
|
+
if (typeof header.alg !== "string" || header.alg === "" || header.alg.toLowerCase() === "none")
|
|
474
|
+
return null;
|
|
475
|
+
const claims = {};
|
|
476
|
+
for (const [name, value] of Object.entries(payload)) {
|
|
477
|
+
if (CLAIM_NAME.test(name) && (typeof value === "number" || typeof value === "boolean" || typeof value === "string" && value.length <= 256))
|
|
478
|
+
claims[name] = value;
|
|
479
|
+
}
|
|
480
|
+
if (owns(payload, "exp") && !(Number.isSafeInteger(payload.exp) && payload.exp > 0))
|
|
481
|
+
return null;
|
|
482
|
+
return Object.freeze({ claims: Object.freeze(claims), exp: owns(payload, "exp") ? payload.exp : null });
|
|
483
|
+
}
|
|
484
|
+
function payrailsWorkspacePinRefusal(authorization, pin, now = Math.floor(Date.now() / 1e3)) {
|
|
485
|
+
if (!record(pin) || !CLAIM_NAME.test(pin.claim) || !plainText(pin.value, 256))
|
|
486
|
+
return "payrails_workspace_unpinned";
|
|
487
|
+
const token = payrailsTokenClaims(authorization);
|
|
488
|
+
if (!token)
|
|
489
|
+
return "payrails_token_invalid";
|
|
490
|
+
if (!owns(token.claims, pin.claim) || token.claims[pin.claim] !== pin.value)
|
|
491
|
+
return "payrails_workspace_mismatch";
|
|
492
|
+
if (token.exp !== null && !(Number.isFinite(now) && token.exp - now >= PAYRAILS_SESSION_MIN_SECONDS))
|
|
493
|
+
return "payrails_session_expiring";
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
var ANSWER_LIMIT = 65536;
|
|
497
|
+
var IDENTIFIER = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
498
|
+
var INSTRUMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
499
|
+
var INSTRUMENT_STATUS = /^[a-z][a-z_]{0,31}$/;
|
|
500
|
+
var PAYRAILS_USABLE_STATUSES = Object.freeze(["created", "enabled"]);
|
|
501
|
+
var PAYRAILS_UNUSABLE_STATUSES = Object.freeze(["invalid", "disabled", "deleted"]);
|
|
502
|
+
var PAYRAILS_SERVED_CARD_FACTS = Object.freeze(["bin", "binLookup", "holderName", "network", "suffix", "expiryMonth", "expiryYear", "fingerprint"]);
|
|
503
|
+
var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
|
|
504
|
+
var bin6 = (value) => exact(/^[0-9]{6,8}$/, value) ? value.slice(0, 6) : void 0;
|
|
505
|
+
var label = (value, max) => plainText(value, max) ? value : void 0;
|
|
506
|
+
var pick = (value, pattern) => exact(pattern, value) ? value : void 0;
|
|
507
|
+
var compact = (object) => Object.fromEntries(Object.entries(object).filter(([, value]) => value !== void 0));
|
|
508
|
+
var nonEmpty = (object) => Object.keys(object).length ? object : void 0;
|
|
509
|
+
function readJson(text) {
|
|
510
|
+
if (typeof text !== "string" || text.length === 0 || text.length > ANSWER_LIMIT)
|
|
511
|
+
return null;
|
|
512
|
+
try {
|
|
513
|
+
return JSON.parse(text);
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function readInstrument(text) {
|
|
520
|
+
const value = readJson(text);
|
|
521
|
+
if (!record(value) || !exact(INSTRUMENT_ID, value.id) || value.paymentMethod !== "card" || !exact(INSTRUMENT_STATUS, value.status))
|
|
522
|
+
return null;
|
|
523
|
+
if (!record(value.data) || !exact(/^[0-9]{4}$/, value.data.suffix))
|
|
524
|
+
return null;
|
|
525
|
+
return value;
|
|
526
|
+
}
|
|
527
|
+
function payrailsFacts(text) {
|
|
528
|
+
const value = readJson(text);
|
|
529
|
+
const first = record(value) && Array.isArray(value.errors) ? value.errors[0] : void 0;
|
|
530
|
+
const code = record(first) ? first.code : void 0;
|
|
531
|
+
return { refusalCode: exact(IDENTIFIER, code) ? code : null };
|
|
532
|
+
}
|
|
533
|
+
function sanitizePayrailsResponse(text) {
|
|
534
|
+
const value = readInstrument(text);
|
|
535
|
+
if (!value)
|
|
536
|
+
throw new Error("Refusing to record: the Payrails answer is not a card instrument.");
|
|
537
|
+
const data = value.data;
|
|
538
|
+
const served = new Set(PAYRAILS_SERVED_CARD_FACTS);
|
|
539
|
+
const lookup = served.has("binLookup") && record(data.binLookup) ? data.binLookup : null;
|
|
540
|
+
const country = lookup && record(lookup.issuerCountry) ? lookup.issuerCountry : null;
|
|
541
|
+
const card = compact({
|
|
542
|
+
bin: served.has("bin") ? bin6(data.bin) : void 0,
|
|
543
|
+
binLookup: lookup ? nonEmpty(compact({
|
|
544
|
+
bin: bin6(lookup.bin),
|
|
545
|
+
network: label(lookup.network, 32),
|
|
546
|
+
issuer: label(lookup.issuer, 128),
|
|
547
|
+
issuerCountry: country ? nonEmpty(compact({ code: pick(country.code, /^[A-Z]{2}$/), name: label(country.name, 64), iso3: pick(country.iso3, /^[A-Z]{3}$/) })) : void 0,
|
|
548
|
+
type: label(lookup.type, 32)
|
|
549
|
+
})) : void 0,
|
|
550
|
+
holderName: served.has("holderName") ? label(data.holderName, 200) : void 0,
|
|
551
|
+
network: served.has("network") ? label(data.network, 32) : void 0,
|
|
552
|
+
suffix: data.suffix,
|
|
553
|
+
expiryMonth: served.has("expiryMonth") ? pick(data.expiryMonth, /^(?:0[1-9]|1[0-2])$/) : void 0,
|
|
554
|
+
expiryYear: served.has("expiryYear") ? pick(data.expiryYear, /^(?:[0-9]{2}|[0-9]{4})$/) : void 0
|
|
555
|
+
});
|
|
556
|
+
return JSON.stringify(compact({
|
|
557
|
+
id: value.id,
|
|
558
|
+
createdAt: pick(value.createdAt, /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]{1,9})?(?:Z|[+-][0-9]{2}:[0-9]{2})$/),
|
|
559
|
+
holderId: pick(value.holderId, INSTRUMENT_ID),
|
|
560
|
+
paymentMethod: "card",
|
|
561
|
+
status: value.status,
|
|
562
|
+
data: card,
|
|
563
|
+
fingerprint: served.has("fingerprint") ? pick(value.fingerprint, /^[A-Za-z0-9_+/=-]{1,128}$/) : void 0,
|
|
564
|
+
futureUsage: ["CardOnFile", "Subscription", "UnscheduledCardOnFile"].includes(value.futureUsage) ? value.futureUsage : void 0
|
|
565
|
+
}));
|
|
566
|
+
}
|
|
567
|
+
function instrumentMatchesCard(instrument, card) {
|
|
568
|
+
const number = String(card.number ?? "").replace(/\D/g, "");
|
|
569
|
+
const month = Number(card.month);
|
|
570
|
+
const year = Number(card.year);
|
|
571
|
+
const data = instrument.data;
|
|
572
|
+
return number.length >= 12 && data.suffix === number.slice(-4) && exact(/^(?:0[1-9]|1[0-2])$/, data.expiryMonth) && Number(data.expiryMonth) === month && exact(/^(?:[0-9]{2}|[0-9]{4})$/, data.expiryYear) && Number(data.expiryYear) % 100 === year % 100;
|
|
573
|
+
}
|
|
574
|
+
function payrailsResponseOutcome(status, text, { card } = {}) {
|
|
575
|
+
if (!Number.isInteger(status))
|
|
576
|
+
return { outcome: "unknown", reason: "payrails_unconfirmed" };
|
|
577
|
+
if (status === 409 || status === 429)
|
|
578
|
+
return { outcome: "unknown", reason: "payrails_not_judged" };
|
|
579
|
+
if (status >= 400 && status < 500)
|
|
580
|
+
return { outcome: "refused", code: payrailsFacts(text).refusalCode || `http_${status}` };
|
|
581
|
+
if (status >= 500)
|
|
582
|
+
return { outcome: "unknown", reason: "server_error" };
|
|
583
|
+
if (status !== 201)
|
|
584
|
+
return { outcome: "unknown", reason: "payrails_unconfirmed" };
|
|
585
|
+
const instrument = readInstrument(text);
|
|
586
|
+
if (!instrument)
|
|
587
|
+
return { outcome: "unknown", reason: "payrails_unsafe_response" };
|
|
588
|
+
if (PAYRAILS_UNUSABLE_STATUSES.includes(instrument.status))
|
|
589
|
+
return { outcome: "refused", code: `payrails_instrument_${instrument.status}` };
|
|
590
|
+
if (!PAYRAILS_USABLE_STATUSES.includes(instrument.status))
|
|
591
|
+
return { outcome: "unknown", reason: "payrails_instrument_unconfirmed" };
|
|
592
|
+
if (card && !instrumentMatchesCard(instrument, card))
|
|
593
|
+
return { outcome: "unknown", reason: "payrails_instrument_mismatch" };
|
|
594
|
+
return { outcome: "approved", body: sanitizePayrailsResponse(text) };
|
|
595
|
+
}
|
|
596
|
+
export { PAYRAILS_CAPABILITY, PAYRAILS_CARD_ON_FILE_CONSENTED, PAYRAILS_FUTURE_USAGES, PAYRAILS_JWE_HEADER, PAYRAILS_JWE_HEADER_B64, PAYRAILS_KEY_BITS, PAYRAILS_OPERATION, PAYRAILS_PSP, PAYRAILS_REFUSALS, PAYRAILS_SAVE_HOSTS, PAYRAILS_SAVE_PATH, PAYRAILS_SERVED_CARD_FACTS, PAYRAILS_SESSION_MIN_SECONDS, PAYRAILS_UNUSABLE_STATUSES, PAYRAILS_USABLE_STATUSES, base64UrlDecode, base64UrlEncode, classifyPayrailsRequest, isPayrailsSaveUrl, parsePayrailsSave, payrailsAuthorizeCarriesCard, payrailsCardOnFileRefusal, payrailsFacts, payrailsJweParts, payrailsKeepsCardFor, payrailsRefusalMessage, payrailsResponseOutcome, payrailsSessionKeyBytes, payrailsSessionKeyRefusal, payrailsTemplateRefusal, payrailsTokenClaims, payrailsWorkspacePinRefusal, sanitizePayrailsResponse };
|