@agent-cards/checkout 0.19.0 → 0.22.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 -753
- 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 -132
- package/PREFLIGHT.md +0 -312
- package/dist/adyen-merchant-hosted.generated.d.ts +0 -277
- package/dist/adyen-merchant-hosted.generated.js +0 -1902
- 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 -192
- package/dist/cdp.js +0 -2393
- package/dist/checkout-com.generated.d.ts +0 -4
- package/dist/checkout-com.generated.js +0 -183
- package/dist/client.d.ts +0 -849
- package/dist/client.js +0 -1754
- package/dist/cse-body.d.ts +0 -25
- package/dist/cse-body.js +0 -41
- package/dist/fiserv.d.ts +0 -65
- package/dist/fiserv.generated.d.ts +0 -73
- package/dist/fiserv.generated.js +0 -830
- package/dist/fiserv.js +0 -104
- package/dist/hosted-form.d.ts +0 -44
- package/dist/hosted-form.js +0 -78
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -10
- package/dist/lifecycle.d.ts +0 -179
- package/dist/lifecycle.js +0 -395
- 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/merchant-handoff.d.ts +0 -54
- package/dist/merchant-handoff.js +0 -100
- package/dist/merchant-hosted.d.ts +0 -140
- package/dist/merchant-hosted.js +0 -170
- package/dist/merchant-total-watch.d.ts +0 -115
- package/dist/merchant-total-watch.js +0 -268
- package/dist/merchant-total.d.ts +0 -257
- package/dist/merchant-total.js +0 -383
- 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/pre-claim.d.ts +0 -123
- package/dist/pre-claim.js +0 -386
- package/dist/preflight-capabilities.generated.d.ts +0 -1253
- package/dist/preflight-capabilities.generated.js +0 -1929
- package/dist/preflight-catalog.json +0 -4727
- package/dist/preflight-playwright.d.ts +0 -34
- package/dist/preflight-playwright.js +0 -355
- package/dist/preflight-schemas.json +0 -1122
- package/dist/preflight.d.ts +0 -1
- package/dist/preflight.generated.d.ts +0 -1965
- package/dist/preflight.generated.js +0 -570
- package/dist/preflight.js +0 -2
- package/dist/preparation.d.ts +0 -38
- package/dist/preparation.js +0 -191
- package/dist/prepared-processor.d.ts +0 -43
- package/dist/prepared-processor.js +0 -172
- package/dist/recurly.generated.d.ts +0 -1
- package/dist/recurly.generated.js +0 -87
- package/dist/registry.d.ts +0 -121
- package/dist/registry.js +0 -310
- 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 -1093
- 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 -11
- package/dist/substitutions.generated.js +0 -818
- 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/fiserv.generated.js
DELETED
|
@@ -1,830 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256: abb8663c9be443f4f09ced0b06df7e3997cfbc7cae7bea1a1c341af1cea93524
|
|
4
|
-
// src/fiserv-environment.js
|
|
5
|
-
var FISERV_CARD_CAPTURE_ENDPOINTS = /* @__PURE__ */ Object.freeze({
|
|
6
|
-
production: "https://connect.fiservapis.com/ch/payments-vas/v1/card-capture",
|
|
7
|
-
sandbox: "https://connect-cert.fiservapis.com/ch/payments-vas/v1/card-capture"
|
|
8
|
-
});
|
|
9
|
-
function fiservEnvironment(url) {
|
|
10
|
-
if (url === FISERV_CARD_CAPTURE_ENDPOINTS.production)
|
|
11
|
-
return "production";
|
|
12
|
-
if (url === FISERV_CARD_CAPTURE_ENDPOINTS.sandbox)
|
|
13
|
-
return "sandbox";
|
|
14
|
-
return void 0;
|
|
15
|
-
}
|
|
16
|
-
function isFiservCardCaptureUrl(url) {
|
|
17
|
-
return fiservEnvironment(url) !== void 0;
|
|
18
|
-
}
|
|
19
|
-
var FISERV_SIMULATOR_PANS = /* @__PURE__ */ Object.freeze([
|
|
20
|
-
// Approval: any total under 5,000.
|
|
21
|
-
"4012001928706700",
|
|
22
|
-
"5500007287297385",
|
|
23
|
-
"341111227713933",
|
|
24
|
-
"6011114305944707",
|
|
25
|
-
// Security code responses.
|
|
26
|
-
"4012001472472642",
|
|
27
|
-
"5500008286090219",
|
|
28
|
-
"341111880552552",
|
|
29
|
-
"6011111273436666",
|
|
30
|
-
// Address verification responses.
|
|
31
|
-
"4012008565774776",
|
|
32
|
-
"5500002389250960",
|
|
33
|
-
"341111565369561",
|
|
34
|
-
"6011116171399338"
|
|
35
|
-
]);
|
|
36
|
-
// src/fiserv-envelope.js
|
|
37
|
-
var KEY_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
38
|
-
var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
39
|
-
var BASE64_VALUE = /* @__PURE__ */ (() => {
|
|
40
|
-
const table = /* @__PURE__ */ new Map();
|
|
41
|
-
for (let i = 0; i < BASE64_ALPHABET.length; i++)
|
|
42
|
-
table.set(BASE64_ALPHABET[i], i);
|
|
43
|
-
return table;
|
|
44
|
-
})();
|
|
45
|
-
var FISERV_ENVELOPE_CONSTANTS = Object.freeze({ aesTagLength: 128, encryptionType: "AES-GCM", encryptionTarget: "MANUAL" });
|
|
46
|
-
function encodeBase64(bytes) {
|
|
47
|
-
let out = "";
|
|
48
|
-
for (let i = 0; i < bytes.length; i += 3) {
|
|
49
|
-
const a = bytes[i], b = i + 1 < bytes.length ? bytes[i + 1] : 0, c = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
50
|
-
const triple = a << 16 | b << 8 | c;
|
|
51
|
-
out += BASE64_ALPHABET[triple >> 18 & 63] + BASE64_ALPHABET[triple >> 12 & 63] + (i + 1 < bytes.length ? BASE64_ALPHABET[triple >> 6 & 63] : "=") + (i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : "=");
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
}
|
|
55
|
-
function decodeBase64(text2) {
|
|
56
|
-
if (typeof text2 !== "string" || text2.length === 0 || text2.length % 4 !== 0 || text2.length > 65536)
|
|
57
|
-
return null;
|
|
58
|
-
const padding = text2.endsWith("==") ? 2 : text2.endsWith("=") ? 1 : 0;
|
|
59
|
-
const body = text2.length - padding;
|
|
60
|
-
const out = new Uint8Array(text2.length / 4 * 3 - padding);
|
|
61
|
-
let at = 0;
|
|
62
|
-
for (let i = 0; i < text2.length; i += 4) {
|
|
63
|
-
const quad = [0, 0, 0, 0];
|
|
64
|
-
for (let j = 0; j < 4; j++) {
|
|
65
|
-
const index = i + j;
|
|
66
|
-
if (index >= body) {
|
|
67
|
-
quad[j] = 0;
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const value = BASE64_VALUE.get(text2[index]);
|
|
71
|
-
if (value === void 0)
|
|
72
|
-
return null;
|
|
73
|
-
quad[j] = value;
|
|
74
|
-
}
|
|
75
|
-
const triple = quad[0] << 18 | quad[1] << 12 | quad[2] << 6 | quad[3];
|
|
76
|
-
if (at < out.length)
|
|
77
|
-
out[at++] = triple >> 16 & 255;
|
|
78
|
-
if (at < out.length)
|
|
79
|
-
out[at++] = triple >> 8 & 255;
|
|
80
|
-
if (at < out.length)
|
|
81
|
-
out[at++] = triple & 255;
|
|
82
|
-
}
|
|
83
|
-
return encodeBase64(out) === text2 ? out : null;
|
|
84
|
-
}
|
|
85
|
-
var digits = (value, min, max) => typeof value === "string" && value.length >= min && value.length <= max && !/[^0-9]/.test(value);
|
|
86
|
-
function fiservCaptureSource(card) {
|
|
87
|
-
const number = card && (typeof card.number === "string" || typeof card.number === "number") ? String(card.number).replace(/[ -]/g, "") : "";
|
|
88
|
-
const month = card && (typeof card.month === "string" || typeof card.month === "number") ? String(card.month) : "";
|
|
89
|
-
const year = card && (typeof card.year === "string" || typeof card.year === "number") ? String(card.year) : "";
|
|
90
|
-
const cvc = card && (typeof card.cvc === "string" || typeof card.cvc === "number") ? String(card.cvc) : "";
|
|
91
|
-
if (!digits(number, 12, 19) || !digits(month, 1, 2) || Number(month) < 1 || Number(month) > 12 || !(digits(year, 2, 2) || digits(year, 4, 4)) || !digits(cvc, 3, 4)) {
|
|
92
|
-
throw new Error("Could not fill the card fields for Fiserv. Nothing was sent.");
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
sourceType: "PaymentCard",
|
|
96
|
-
card: {
|
|
97
|
-
cardData: number,
|
|
98
|
-
expirationMonth: month.padStart(2, "0"),
|
|
99
|
-
expirationYear: year.length === 2 ? "20" + year : year,
|
|
100
|
-
securityCode: cvc
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
function aesKeyBytes(crypto) {
|
|
105
|
-
const key = new Uint8Array(32);
|
|
106
|
-
const pool = new Uint8Array(64);
|
|
107
|
-
let filled = 0;
|
|
108
|
-
while (filled < key.length) {
|
|
109
|
-
crypto.getRandomValues(pool);
|
|
110
|
-
for (let i = 0; i < pool.length && filled < key.length; i++) {
|
|
111
|
-
if (pool[i] < 248)
|
|
112
|
-
key[filled++] = KEY_ALPHABET.charCodeAt(pool[i] % 62);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
pool.fill(0);
|
|
116
|
-
return key;
|
|
117
|
-
}
|
|
118
|
-
var isBytes = (value) => Object.prototype.toString.call(value) === "[object Uint8Array]";
|
|
119
|
-
function isExponent65537(exponent) {
|
|
120
|
-
return isBytes(exponent) && exponent.length === 3 && exponent[0] === 1 && exponent[1] === 0 && exponent[2] === 1;
|
|
121
|
-
}
|
|
122
|
-
async function sealFiservEnvelope(keyId, spki, source, crypto) {
|
|
123
|
-
if (typeof keyId !== "string" || !keyId)
|
|
124
|
-
throw new Error("Refusing to encrypt: the Fiserv key has no id.");
|
|
125
|
-
if (!isBytes(spki))
|
|
126
|
-
throw new Error("Refusing to encrypt: the Fiserv key is malformed.");
|
|
127
|
-
if (!crypto?.subtle || typeof crypto.getRandomValues !== "function")
|
|
128
|
-
throw new Error("Processor cryptography is unavailable.");
|
|
129
|
-
let rsa;
|
|
130
|
-
try {
|
|
131
|
-
rsa = await crypto.subtle.importKey("spki", spki, { name: "RSA-OAEP", hash: "SHA-256" }, false, ["encrypt"]);
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
throw new Error("Refusing to encrypt: the Fiserv key is malformed.");
|
|
135
|
-
}
|
|
136
|
-
const algorithm = (
|
|
137
|
-
/** @type {RsaHashedKeyAlgorithm} */
|
|
138
|
-
rsa.algorithm);
|
|
139
|
-
if (algorithm.modulusLength !== 2048 || !isExponent65537(algorithm.publicExponent)) {
|
|
140
|
-
throw new Error("Refusing to encrypt: unexpected Fiserv key size.");
|
|
141
|
-
}
|
|
142
|
-
const keyBytes = aesKeyBytes(crypto);
|
|
143
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(source));
|
|
144
|
-
try {
|
|
145
|
-
const encryptedKey = new Uint8Array(await crypto.subtle.encrypt({ name: "RSA-OAEP" }, rsa, keyBytes));
|
|
146
|
-
const aes = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM" }, false, ["encrypt"]);
|
|
147
|
-
keyBytes.fill(0);
|
|
148
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
149
|
-
const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv, tagLength: 128 }, aes, plaintext));
|
|
150
|
-
return {
|
|
151
|
-
keyId,
|
|
152
|
-
encryptedKey: encodeBase64(encryptedKey),
|
|
153
|
-
encryptionBlock: encodeBase64(sealed),
|
|
154
|
-
aesIv: encodeBase64(iv),
|
|
155
|
-
aesTagLength: FISERV_ENVELOPE_CONSTANTS.aesTagLength,
|
|
156
|
-
encryptionType: FISERV_ENVELOPE_CONSTANTS.encryptionType,
|
|
157
|
-
encryptionTarget: FISERV_ENVELOPE_CONSTANTS.encryptionTarget
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
finally {
|
|
161
|
-
keyBytes.fill(0);
|
|
162
|
-
plaintext.fill(0);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
// src/deep-freeze.js
|
|
166
|
-
function deepFreeze(value) {
|
|
167
|
-
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
168
|
-
for (const child of Object.values(value))
|
|
169
|
-
deepFreeze(child);
|
|
170
|
-
Object.freeze(value);
|
|
171
|
-
}
|
|
172
|
-
return value;
|
|
173
|
-
}
|
|
174
|
-
// src/sha256.js
|
|
175
|
-
var K = /* @__PURE__ */ new Uint32Array([
|
|
176
|
-
1116352408,
|
|
177
|
-
1899447441,
|
|
178
|
-
3049323471,
|
|
179
|
-
3921009573,
|
|
180
|
-
961987163,
|
|
181
|
-
1508970993,
|
|
182
|
-
2453635748,
|
|
183
|
-
2870763221,
|
|
184
|
-
3624381080,
|
|
185
|
-
310598401,
|
|
186
|
-
607225278,
|
|
187
|
-
1426881987,
|
|
188
|
-
1925078388,
|
|
189
|
-
2162078206,
|
|
190
|
-
2614888103,
|
|
191
|
-
3248222580,
|
|
192
|
-
3835390401,
|
|
193
|
-
4022224774,
|
|
194
|
-
264347078,
|
|
195
|
-
604807628,
|
|
196
|
-
770255983,
|
|
197
|
-
1249150122,
|
|
198
|
-
1555081692,
|
|
199
|
-
1996064986,
|
|
200
|
-
2554220882,
|
|
201
|
-
2821834349,
|
|
202
|
-
2952996808,
|
|
203
|
-
3210313671,
|
|
204
|
-
3336571891,
|
|
205
|
-
3584528711,
|
|
206
|
-
113926993,
|
|
207
|
-
338241895,
|
|
208
|
-
666307205,
|
|
209
|
-
773529912,
|
|
210
|
-
1294757372,
|
|
211
|
-
1396182291,
|
|
212
|
-
1695183700,
|
|
213
|
-
1986661051,
|
|
214
|
-
2177026350,
|
|
215
|
-
2456956037,
|
|
216
|
-
2730485921,
|
|
217
|
-
2820302411,
|
|
218
|
-
3259730800,
|
|
219
|
-
3345764771,
|
|
220
|
-
3516065817,
|
|
221
|
-
3600352804,
|
|
222
|
-
4094571909,
|
|
223
|
-
275423344,
|
|
224
|
-
430227734,
|
|
225
|
-
506948616,
|
|
226
|
-
659060556,
|
|
227
|
-
883997877,
|
|
228
|
-
958139571,
|
|
229
|
-
1322822218,
|
|
230
|
-
1537002063,
|
|
231
|
-
1747873779,
|
|
232
|
-
1955562222,
|
|
233
|
-
2024104815,
|
|
234
|
-
2227730452,
|
|
235
|
-
2361852424,
|
|
236
|
-
2428436474,
|
|
237
|
-
2756734187,
|
|
238
|
-
3204031479,
|
|
239
|
-
3329325298
|
|
240
|
-
]);
|
|
241
|
-
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
242
|
-
function utf8(text2) {
|
|
243
|
-
const out = [];
|
|
244
|
-
for (const char of text2) {
|
|
245
|
-
let cp = char.codePointAt(0);
|
|
246
|
-
if (cp >= 55296 && cp <= 57343)
|
|
247
|
-
cp = 65533;
|
|
248
|
-
if (cp < 128)
|
|
249
|
-
out.push(cp);
|
|
250
|
-
else if (cp < 2048)
|
|
251
|
-
out.push(192 | cp >> 6, 128 | cp & 63);
|
|
252
|
-
else if (cp < 65536)
|
|
253
|
-
out.push(224 | cp >> 12, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
254
|
-
else
|
|
255
|
-
out.push(240 | cp >> 18, 128 | cp >> 12 & 63, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
256
|
-
}
|
|
257
|
-
return Uint8Array.from(out);
|
|
258
|
-
}
|
|
259
|
-
function sha256Hex(input) {
|
|
260
|
-
const isBytes2 = Object.prototype.toString.call(input) === "[object Uint8Array]";
|
|
261
|
-
if (typeof input !== "string" && !isBytes2)
|
|
262
|
-
throw new TypeError("sha256Hex takes a string or a Uint8Array.");
|
|
263
|
-
const bytes = typeof input === "string" ? utf8(input) : input;
|
|
264
|
-
const length = bytes.length;
|
|
265
|
-
const padded = new Uint8Array((length + 8 >> 6) + 1 << 6);
|
|
266
|
-
padded.set(bytes);
|
|
267
|
-
padded[length] = 128;
|
|
268
|
-
const view = new DataView(padded.buffer);
|
|
269
|
-
view.setUint32(padded.length - 8, Math.floor(length / 536870912));
|
|
270
|
-
view.setUint32(padded.length - 4, length << 3 >>> 0);
|
|
271
|
-
const H = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
272
|
-
const W = new Uint32Array(64);
|
|
273
|
-
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
274
|
-
for (let t = 0; t < 16; t++)
|
|
275
|
-
W[t] = view.getUint32(offset + t * 4);
|
|
276
|
-
for (let t = 16; t < 64; t++) {
|
|
277
|
-
const s0 = rotr(W[t - 15], 7) ^ rotr(W[t - 15], 18) ^ W[t - 15] >>> 3;
|
|
278
|
-
const s1 = rotr(W[t - 2], 17) ^ rotr(W[t - 2], 19) ^ W[t - 2] >>> 10;
|
|
279
|
-
W[t] = W[t - 16] + s0 + W[t - 7] + s1 >>> 0;
|
|
280
|
-
}
|
|
281
|
-
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];
|
|
282
|
-
for (let t = 0; t < 64; t++) {
|
|
283
|
-
const t1 = h + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[t] + W[t] >>> 0;
|
|
284
|
-
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
285
|
-
h = g;
|
|
286
|
-
g = f;
|
|
287
|
-
f = e;
|
|
288
|
-
e = d + t1 >>> 0;
|
|
289
|
-
d = c;
|
|
290
|
-
c = b;
|
|
291
|
-
b = a;
|
|
292
|
-
a = t1 + t2 >>> 0;
|
|
293
|
-
}
|
|
294
|
-
H[0] += a;
|
|
295
|
-
H[1] += b;
|
|
296
|
-
H[2] += c;
|
|
297
|
-
H[3] += d;
|
|
298
|
-
H[4] += e;
|
|
299
|
-
H[5] += f;
|
|
300
|
-
H[6] += g;
|
|
301
|
-
H[7] += h;
|
|
302
|
-
}
|
|
303
|
-
return Array.from(H, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
304
|
-
}
|
|
305
|
-
// src/fiserv-keys.js
|
|
306
|
-
var ISO_UTC = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?Z$/;
|
|
307
|
-
function fiservTimestampMs(text2) {
|
|
308
|
-
const match = typeof text2 === "string" ? ISO_UTC.exec(text2) : null;
|
|
309
|
-
if (!match || match[0] !== text2)
|
|
310
|
-
return NaN;
|
|
311
|
-
const [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number);
|
|
312
|
-
const millis = Number(`${match[7] ?? ""}000`.slice(0, 3));
|
|
313
|
-
const value = Date.UTC(year, month - 1, day, hour, minute, second, millis);
|
|
314
|
-
const date = new Date(value);
|
|
315
|
-
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day && hour <= 23 && minute <= 59 && second <= 59 ? value : NaN;
|
|
316
|
-
}
|
|
317
|
-
var pin = (entry) => ({ ...entry, validFromMs: fiservTimestampMs(entry.validFrom), validToMs: fiservTimestampMs(entry.validTo) });
|
|
318
|
-
var FISERV_KEY_PINS = /* @__PURE__ */ deepFreeze([
|
|
319
|
-
pin({
|
|
320
|
-
id: "homedepot_orangepay",
|
|
321
|
-
merchant: "Home Depot",
|
|
322
|
-
environment: "production",
|
|
323
|
-
// Empty until a card-free dry run records the merchantId Home Depot's
|
|
324
|
-
// capture carries, so this key encrypts no card yet. env.js names
|
|
325
|
-
// merchantId TZOPBKED, but that is what OrangePay sends its own authenticate
|
|
326
|
-
// call and may not be the Commerce Hub id in the capture body, and the
|
|
327
|
-
// payment step has never been observed.
|
|
328
|
-
merchantIds: [],
|
|
329
|
-
keyId: "d51d5faeb0261f664c99aac27cd131db",
|
|
330
|
-
spki: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArobUom7fqw1aWE03z0OFQhnwzXPmh/rw0GxxEEdDsV07ysH/xtEAggNO46sWeWh8UEBat/mleL6wart8XEDkP1z67mSBhvu9Y3MqrbTMilxkb7NxdsMW26ukYx7Q9QGq+llTuapwBhLeU3uVsu0keUcs1kqc32fisCMv6hd7Nvh9niVp6dxPpqWnlmrB3PqQI/Y9wIOKlLtQ8n/1b5V3UJUjEXgJlDBzBn13ATj3vrl2aJxey6A99DL75md8RBF/rd2x/OxPKHkqWQWbRIgd++dtjxLVUYe5vRnq8OtqvrWdY0SCRoG61PgHqIPvoaokovJNi8utp95WuCww5jX8MQIDAQAB",
|
|
331
|
-
spkiSha256: "49c3ac40078bfb4d5a08ebf1f6009d0b4b25b9cac58a4046fe64f1fa917a0e84",
|
|
332
|
-
validFrom: "2025-12-20T09:00:00.980742211Z",
|
|
333
|
-
validTo: "2027-12-20T09:00:00.980742211Z",
|
|
334
|
-
status: "ACTIVE",
|
|
335
|
-
published: "https://orangepay-ecommerce.hdpayments.homedepot.com/rsaKey.js"
|
|
336
|
-
}),
|
|
337
|
-
pin({
|
|
338
|
-
id: "agentcard_sandbox",
|
|
339
|
-
merchant: "Agentcard sandbox",
|
|
340
|
-
environment: "sandbox",
|
|
341
|
-
// The sandbox merchant the key was generated under, which every Wave 0
|
|
342
|
-
// capture named.
|
|
343
|
-
merchantIds: ["100008000003683"],
|
|
344
|
-
keyId: "77eba5e58f007456ce2c35527bec2877",
|
|
345
|
-
spki: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqbsaa0wpsHUuNIAr5LVYAbID77PB4d0qYnaIsTFGa0JZMIj/egbfdkl4DCaEhV3dzRDMivr+EB5643Xut00lT+SoPonc0Hps+Z5ejcVIZrtIvx/kSxJEqYK70HZHxJewh6P5qFUb+2ZPLSWQQpMqNMpDQhlISt3XtnO1JWmTvCUrjs4PKyVbexIq7+Di36HzwAnatSRP+mfgBnRsXnFmF/oOZKijsCYswqfomIom8eb9kpU1mVuSCPXmxivPOXPdFARFGd23PhdHpLhx0E7OIXpm2xEDmizeit56KVrP4PUHT08tZ4x26cSj7Po1oFpJ4XtsjSrXxmJCdE/LQFIZUwIDAQAB",
|
|
346
|
-
spkiSha256: "7a62942573e251eeffa856ce6f307d8684aad39eec43e9eb48badb450619296a",
|
|
347
|
-
validFrom: "2026-09-25T22:33:01.658638461Z",
|
|
348
|
-
validTo: "2026-12-24T22:33:01.658638461Z",
|
|
349
|
-
status: "ACTIVE",
|
|
350
|
-
published: null
|
|
351
|
-
})
|
|
352
|
-
]);
|
|
353
|
-
function fiservKeyPin(keyId, environment) {
|
|
354
|
-
if (typeof keyId !== "string" || environment !== "production" && environment !== "sandbox")
|
|
355
|
-
return null;
|
|
356
|
-
return FISERV_KEY_PINS.find((entry) => entry.keyId === keyId && entry.environment === environment) ?? null;
|
|
357
|
-
}
|
|
358
|
-
function isFiservPinnedKeyId(keyId) {
|
|
359
|
-
return typeof keyId === "string" && FISERV_KEY_PINS.some((entry) => entry.keyId === keyId);
|
|
360
|
-
}
|
|
361
|
-
function fiservKeyRefusal(keyId, environment, nowMs) {
|
|
362
|
-
const entry = fiservKeyPin(keyId, environment);
|
|
363
|
-
if (!entry)
|
|
364
|
-
return "fiserv_key_unpinned";
|
|
365
|
-
if (entry.status !== "ACTIVE")
|
|
366
|
-
return "fiserv_key_inactive";
|
|
367
|
-
if (!Number.isSafeInteger(nowMs) || nowMs < entry.validFromMs || nowMs >= entry.validToMs)
|
|
368
|
-
return "fiserv_key_outside_validity";
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
|
-
function fiservPinServesMerchant(entry, merchantId) {
|
|
372
|
-
return !!entry && Array.isArray(entry.merchantIds) && typeof merchantId === "string" && entry.merchantIds.includes(merchantId);
|
|
373
|
-
}
|
|
374
|
-
function pinnedSpki(entry) {
|
|
375
|
-
const der = entry && typeof entry.spki === "string" ? decodeBase64(entry.spki) : null;
|
|
376
|
-
return der && sha256Hex(der) === entry.spkiSha256 ? der : null;
|
|
377
|
-
}
|
|
378
|
-
var PUBLISHED_SCRIPT = /^const rsaKey = (\{[\s\S]{1,4096}\});\s*window\.rsaKey = rsaKey;\s*$/;
|
|
379
|
-
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
380
|
-
var record = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
381
|
-
function parseFiservPublishedKey(text2) {
|
|
382
|
-
if (typeof text2 !== "string" || text2.length > 8192)
|
|
383
|
-
return null;
|
|
384
|
-
const script = PUBLISHED_SCRIPT.exec(text2);
|
|
385
|
-
let value;
|
|
386
|
-
try {
|
|
387
|
-
value = JSON.parse(script ? script[1] : text2);
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
return null;
|
|
391
|
-
}
|
|
392
|
-
const key = record(value) ? value.asymmetricKeyDetails : null;
|
|
393
|
-
const symmetric = record(value) ? value.symmetricKeyDetails : null;
|
|
394
|
-
if (!record(key) || !record(symmetric))
|
|
395
|
-
return null;
|
|
396
|
-
for (const name of ["keyId", "encryptionType", "encodedPublicKey", "validFrom", "validTo", "status"]) {
|
|
397
|
-
if (!own(key, name) || typeof key[name] !== "string")
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
return {
|
|
401
|
-
keyId: key.keyId,
|
|
402
|
-
encryptionType: key.encryptionType,
|
|
403
|
-
modulus: key.modulus,
|
|
404
|
-
encodedPublicKey: key.encodedPublicKey,
|
|
405
|
-
validFrom: key.validFrom,
|
|
406
|
-
validTo: key.validTo,
|
|
407
|
-
status: key.status,
|
|
408
|
-
symmetric: { encryptionType: symmetric.encryptionType, modulus: symmetric.modulus }
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
function fiservPublishedKeyRefusal(text2, keyId, environment, nowMs) {
|
|
412
|
-
const entry = fiservKeyPin(keyId, environment);
|
|
413
|
-
if (!entry)
|
|
414
|
-
return "fiserv_key_unpinned";
|
|
415
|
-
const published = parseFiservPublishedKey(text2);
|
|
416
|
-
if (!published)
|
|
417
|
-
return "fiserv_published_key_unreadable";
|
|
418
|
-
if (published.keyId !== entry.keyId)
|
|
419
|
-
return "fiserv_published_key_rotated";
|
|
420
|
-
const der = decodeBase64(published.encodedPublicKey);
|
|
421
|
-
if (!der || sha256Hex(der) !== entry.spkiSha256)
|
|
422
|
-
return "fiserv_published_key_mismatch";
|
|
423
|
-
if (published.encryptionType !== "RSA" || published.modulus !== 2048 || published.symmetric.encryptionType !== "AES-GCM" || published.symmetric.modulus !== 256)
|
|
424
|
-
return "fiserv_published_key_mismatch";
|
|
425
|
-
if (published.status !== "ACTIVE")
|
|
426
|
-
return "fiserv_key_inactive";
|
|
427
|
-
if (published.validFrom !== entry.validFrom || published.validTo !== entry.validTo)
|
|
428
|
-
return "fiserv_published_key_mismatch";
|
|
429
|
-
return fiservKeyRefusal(keyId, environment, nowMs);
|
|
430
|
-
}
|
|
431
|
-
// src/json-path.js
|
|
432
|
-
function isPlainObject(v) {
|
|
433
|
-
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
434
|
-
}
|
|
435
|
-
function roundTrips(parsed, body) {
|
|
436
|
-
try {
|
|
437
|
-
return JSON.stringify(parsed) === body;
|
|
438
|
-
}
|
|
439
|
-
catch {
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
// src/fiserv-response.js
|
|
444
|
-
var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
|
|
445
|
-
var own2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
446
|
-
var ENUM = /^[A-Z][A-Z_]{0,31}$/;
|
|
447
|
-
var PROCESSING_IDS = Object.freeze({
|
|
448
|
-
transactionTimestamp: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/,
|
|
449
|
-
apiTraceId: /^[A-Za-z0-9-]{1,64}$/,
|
|
450
|
-
clientRequestId: /^[A-Za-z0-9-]{1,64}$/,
|
|
451
|
-
transactionId: /^[A-Za-z0-9-]{1,64}$/
|
|
452
|
-
});
|
|
453
|
-
function readObject(text2) {
|
|
454
|
-
if (typeof text2 !== "string" || text2.length > 65536)
|
|
455
|
-
return null;
|
|
456
|
-
try {
|
|
457
|
-
const value = JSON.parse(text2);
|
|
458
|
-
return isPlainObject(value) ? value : null;
|
|
459
|
-
}
|
|
460
|
-
catch {
|
|
461
|
-
return null;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
function sanitizeFiservCaptureResponse(text2) {
|
|
465
|
-
const value = readObject(text2);
|
|
466
|
-
const gateway = value && isPlainObject(value.gatewayResponse) ? value.gatewayResponse : null;
|
|
467
|
-
if (!gateway || gateway.transactionType !== "CARD_CAPTURE" || gateway.transactionState !== "CAPTURED") {
|
|
468
|
-
throw new Error("Invalid Fiserv card-capture response.");
|
|
469
|
-
}
|
|
470
|
-
const safe = { transactionType: "CARD_CAPTURE", transactionState: "CAPTURED" };
|
|
471
|
-
if (exact(ENUM, gateway.transactionOrigin))
|
|
472
|
-
safe.transactionOrigin = gateway.transactionOrigin;
|
|
473
|
-
const details = gateway.transactionProcessingDetails;
|
|
474
|
-
if (isPlainObject(details)) {
|
|
475
|
-
const ids = {};
|
|
476
|
-
for (const [name, pattern] of Object.entries(PROCESSING_IDS))
|
|
477
|
-
if (exact(pattern, details[name]))
|
|
478
|
-
ids[name] = details[name];
|
|
479
|
-
if (Object.keys(ids).length)
|
|
480
|
-
safe.transactionProcessingDetails = ids;
|
|
481
|
-
}
|
|
482
|
-
return JSON.stringify({ gatewayResponse: safe });
|
|
483
|
-
}
|
|
484
|
-
function fiservCaptureCardMismatch(text2, card) {
|
|
485
|
-
const value = readObject(text2);
|
|
486
|
-
const reported = value && isPlainObject(value.source) && isPlainObject(value.source.card) ? value.source.card : null;
|
|
487
|
-
if (!reported)
|
|
488
|
-
return false;
|
|
489
|
-
const number = String(card?.number ?? "").replace(/[ -]/g, "");
|
|
490
|
-
if (own2(reported, "last4") && (typeof reported.last4 !== "string" || reported.last4 !== number.slice(-4)))
|
|
491
|
-
return true;
|
|
492
|
-
if (own2(reported, "expirationMonth") && Number(reported.expirationMonth) !== Number(card?.month))
|
|
493
|
-
return true;
|
|
494
|
-
if (own2(reported, "expirationYear") && Number(reported.expirationYear) % 100 !== Number(card?.year) % 100)
|
|
495
|
-
return true;
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
function fiservErrorIdentifiers(text2) {
|
|
499
|
-
const value = readObject(text2);
|
|
500
|
-
const first = value && Array.isArray(value.error) ? value.error.slice(0, 8).find(isPlainObject) : null;
|
|
501
|
-
const identifier = (item) => exact(/^[A-Za-z0-9_.:-]{1,64}$/, item) ? item : null;
|
|
502
|
-
return { code: first ? identifier(first.code) : null, type: first ? identifier(first.type) : null };
|
|
503
|
-
}
|
|
504
|
-
// src/fiserv.js
|
|
505
|
-
var own3 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
506
|
-
var onlyKeys = (value, allowed) => isPlainObject(value) && Object.keys(value).every((key) => allowed.includes(key));
|
|
507
|
-
var exact2 = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
|
|
508
|
-
var text = (value, min, max) => typeof value === "string" && value.length >= min && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
509
|
-
var decodedLength = (value, min, max) => {
|
|
510
|
-
const bytes = decodeBase64(value);
|
|
511
|
-
return !!bytes && bytes.length >= min && bytes.length <= max;
|
|
512
|
-
};
|
|
513
|
-
var IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
514
|
-
var KEY_ID = /^[A-Za-z0-9][A-Za-z0-9-]{7,63}$/;
|
|
515
|
-
var BODY_LIMIT = 65536;
|
|
516
|
-
var TOP_LEVEL = ["source", "merchantDetails", "transactionDetails", "billingAddress"];
|
|
517
|
-
var ENVELOPE = ["keyId", "encryptedKey", "encryptionBlock", "aesIv", "aesTagLength", "encryptionType", "encryptionTarget"];
|
|
518
|
-
var NAME_FIELDS = ["firstName", "middleName", "lastName"];
|
|
519
|
-
var ADDRESS_FIELDS = [
|
|
520
|
-
"street",
|
|
521
|
-
"houseNumberOrName",
|
|
522
|
-
"recipientNameOrAddress",
|
|
523
|
-
"city",
|
|
524
|
-
"stateOrProvince",
|
|
525
|
-
"postalCode",
|
|
526
|
-
"country",
|
|
527
|
-
"county",
|
|
528
|
-
"addressHistory"
|
|
529
|
-
];
|
|
530
|
-
var PHONE_FIELDS = ["countryCode", "phoneNumber"];
|
|
531
|
-
function readCaptureBody(body) {
|
|
532
|
-
if (typeof body !== "string" || body.length === 0 || body.length > BODY_LIMIT)
|
|
533
|
-
return null;
|
|
534
|
-
let parsed;
|
|
535
|
-
try {
|
|
536
|
-
parsed = JSON.parse(body);
|
|
537
|
-
}
|
|
538
|
-
catch {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
return isPlainObject(parsed) && roundTrips(parsed, body) ? parsed : null;
|
|
542
|
-
}
|
|
543
|
-
function envelopeOk(value) {
|
|
544
|
-
return onlyKeys(value, ENVELOPE) && ENVELOPE.every((key) => own3(value, key)) && exact2(KEY_ID, value.keyId) && decodedLength(value.encryptedKey, 128, 512) && decodedLength(value.encryptionBlock, 17, 4096) && decodedLength(value.aesIv, 12, 12) && value.aesTagLength === FISERV_ENVELOPE_CONSTANTS.aesTagLength && value.encryptionType === FISERV_ENVELOPE_CONSTANTS.encryptionType && value.encryptionTarget === FISERV_ENVELOPE_CONSTANTS.encryptionTarget;
|
|
545
|
-
}
|
|
546
|
-
function merchantOk(value) {
|
|
547
|
-
return onlyKeys(value, ["merchantId", "terminalId"]) && own3(value, "merchantId") && exact2(IDENTIFIER, value.merchantId) && (!own3(value, "terminalId") || exact2(IDENTIFIER, value.terminalId));
|
|
548
|
-
}
|
|
549
|
-
var CAPTCHA_MODES = ["ALWAYS_CHALLENGE", "SOMETIMES_CHALLENGE", "INVISIBLE"];
|
|
550
|
-
function transactionDetailsOk(value) {
|
|
551
|
-
if (!onlyKeys(value, ["deviceFingerprint"]))
|
|
552
|
-
return false;
|
|
553
|
-
if (!own3(value, "deviceFingerprint"))
|
|
554
|
-
return true;
|
|
555
|
-
const list = value.deviceFingerprint;
|
|
556
|
-
return Array.isArray(list) && list.length === 1 && list.every((entry) => onlyKeys(entry, ["provider", "dataCapture"]) && entry.provider === "HCAPTCHA" && onlyKeys(entry.dataCapture, ["dataEventId", "rawData"]) && CAPTCHA_MODES.includes(entry.dataCapture.dataEventId) && text(entry.dataCapture.rawData, 1, 32768));
|
|
557
|
-
}
|
|
558
|
-
function billingOk(value) {
|
|
559
|
-
if (!onlyKeys(value, [...NAME_FIELDS, "address", "phone"]))
|
|
560
|
-
return false;
|
|
561
|
-
if (NAME_FIELDS.some((key) => own3(value, key) && !text(value[key], 0, 256)))
|
|
562
|
-
return false;
|
|
563
|
-
if (own3(value, "address") && !(onlyKeys(value.address, ADDRESS_FIELDS) && Object.values(value.address).every((field) => text(field, 0, 256))))
|
|
564
|
-
return false;
|
|
565
|
-
return !own3(value, "phone") || onlyKeys(value.phone, PHONE_FIELDS) && Object.values(value.phone).every((field) => text(field, 0, 32));
|
|
566
|
-
}
|
|
567
|
-
function captureBodyRefusal(value) {
|
|
568
|
-
if (!onlyKeys(value, TOP_LEVEL))
|
|
569
|
-
return "fiserv_body_member_not_allowed";
|
|
570
|
-
const source = value.source;
|
|
571
|
-
if (!onlyKeys(source, ["sourceType", "encryptionData"]) || source.sourceType !== "PaymentCard" || !own3(source, "encryptionData")) {
|
|
572
|
-
return "fiserv_source_not_encrypted_card";
|
|
573
|
-
}
|
|
574
|
-
if (!envelopeOk(source.encryptionData))
|
|
575
|
-
return "fiserv_envelope_invalid";
|
|
576
|
-
if (!merchantOk(value.merchantDetails))
|
|
577
|
-
return "fiserv_merchant_invalid";
|
|
578
|
-
if (own3(value, "transactionDetails") && !transactionDetailsOk(value.transactionDetails))
|
|
579
|
-
return "fiserv_transaction_details_invalid";
|
|
580
|
-
if (own3(value, "billingAddress") && !billingOk(value.billingAddress))
|
|
581
|
-
return "fiserv_billing_address_invalid";
|
|
582
|
-
return null;
|
|
583
|
-
}
|
|
584
|
-
var FRAME_HEADERS = [
|
|
585
|
-
"content-type",
|
|
586
|
-
"authorization",
|
|
587
|
-
"api-key",
|
|
588
|
-
"client-request-id",
|
|
589
|
-
"timestamp",
|
|
590
|
-
"auth-token-type",
|
|
591
|
-
"x-integration",
|
|
592
|
-
"x-integration-version",
|
|
593
|
-
"x-integration-merchant-id",
|
|
594
|
-
"x-integration-terminal-id",
|
|
595
|
-
"x-integration-origin"
|
|
596
|
-
];
|
|
597
|
-
var BROWSER_HEADERS = [
|
|
598
|
-
"accept",
|
|
599
|
-
"accept-language",
|
|
600
|
-
"accept-encoding",
|
|
601
|
-
"content-length",
|
|
602
|
-
"origin",
|
|
603
|
-
"referer",
|
|
604
|
-
"user-agent",
|
|
605
|
-
"host",
|
|
606
|
-
"connection",
|
|
607
|
-
"cache-control",
|
|
608
|
-
"pragma",
|
|
609
|
-
"priority",
|
|
610
|
-
"dnt",
|
|
611
|
-
"sec-gpc",
|
|
612
|
-
"sec-fetch-dest",
|
|
613
|
-
"sec-fetch-mode",
|
|
614
|
-
"sec-fetch-site",
|
|
615
|
-
"sec-fetch-user"
|
|
616
|
-
];
|
|
617
|
-
var HEADER_VALUES = Object.freeze({
|
|
618
|
-
authorization: /^Bearer [A-Za-z0-9._~+/=-]{1,4096}$/,
|
|
619
|
-
"api-key": /^[A-Za-z0-9._~+/=-]{1,256}$/,
|
|
620
|
-
"client-request-id": /^[A-Za-z0-9-]{1,64}$/,
|
|
621
|
-
timestamp: /^[0-9]{1,16}$/,
|
|
622
|
-
"x-integration": /^[\x21-\x7e][\x20-\x7e]{0,127}$/,
|
|
623
|
-
"x-integration-version": /^[\x21-\x7e][\x20-\x7e]{0,63}$/
|
|
624
|
-
});
|
|
625
|
-
function httpsOrigin(value) {
|
|
626
|
-
try {
|
|
627
|
-
const url = new URL(value);
|
|
628
|
-
return url.protocol === "https:" && (value === url.origin || value === `${url.origin}/`);
|
|
629
|
-
}
|
|
630
|
-
catch {
|
|
631
|
-
return false;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
function headerRefusal(headers, body) {
|
|
635
|
-
if (!isPlainObject(headers))
|
|
636
|
-
return "fiserv_headers_invalid";
|
|
637
|
-
const seen = /* @__PURE__ */ new Map();
|
|
638
|
-
let bytes = 0;
|
|
639
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
640
|
-
const lower = name.toLowerCase();
|
|
641
|
-
if (seen.size >= 64 || seen.has(lower) || !exact2(/^[A-Za-z0-9-]{1,64}$/, name) || !text(value, 0, 8192) || (bytes += name.length + value.length) > 32768)
|
|
642
|
-
return "fiserv_headers_invalid";
|
|
643
|
-
if (!FRAME_HEADERS.includes(lower) && !BROWSER_HEADERS.includes(lower) && !exact2(/^sec-ch-ua(?:-[a-z0-9-]+)?$/, lower)) {
|
|
644
|
-
return "fiserv_header_not_allowed";
|
|
645
|
-
}
|
|
646
|
-
seen.set(lower, value);
|
|
647
|
-
}
|
|
648
|
-
if (!exact2(/^application\/json(?:;\s*charset=utf-8)?$/i, seen.get("content-type")))
|
|
649
|
-
return "fiserv_content_type_invalid";
|
|
650
|
-
if (seen.get("auth-token-type") !== "AccessToken")
|
|
651
|
-
return "fiserv_auth_token_type_invalid";
|
|
652
|
-
if (seen.get("x-integration-merchant-id") !== body.merchantDetails.merchantId)
|
|
653
|
-
return "fiserv_merchant_header_mismatch";
|
|
654
|
-
if (seen.get("x-integration-terminal-id") !== body.merchantDetails.terminalId)
|
|
655
|
-
return "fiserv_terminal_header_mismatch";
|
|
656
|
-
for (const [name, pattern] of Object.entries(HEADER_VALUES)) {
|
|
657
|
-
if (seen.has(name) && !exact2(pattern, seen.get(name)))
|
|
658
|
-
return "fiserv_header_value_invalid";
|
|
659
|
-
}
|
|
660
|
-
if (seen.has("x-integration-origin") && !httpsOrigin(seen.get("x-integration-origin")))
|
|
661
|
-
return "fiserv_header_value_invalid";
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
function fiservCardCaptureRefusal(request) {
|
|
665
|
-
if (!isPlainObject(request))
|
|
666
|
-
return "fiserv_request_invalid";
|
|
667
|
-
if (!isFiservCardCaptureUrl(request.url))
|
|
668
|
-
return "fiserv_endpoint_invalid";
|
|
669
|
-
if (request.method !== "POST")
|
|
670
|
-
return "fiserv_method_invalid";
|
|
671
|
-
const body = readCaptureBody(request.body);
|
|
672
|
-
if (!body)
|
|
673
|
-
return "fiserv_body_invalid";
|
|
674
|
-
return captureBodyRefusal(body) ?? headerRefusal(request.headers, body);
|
|
675
|
-
}
|
|
676
|
-
function isFiservCardCaptureRequest(request) {
|
|
677
|
-
return fiservCardCaptureRefusal(request) === null;
|
|
678
|
-
}
|
|
679
|
-
var TOKEN_MEMBERS = ["walletType", "tokenData", "tokenSource"];
|
|
680
|
-
var NON_CARD_CATEGORIES = ["GIFT", "EBT"];
|
|
681
|
-
function isNonCardSource(source) {
|
|
682
|
-
if (!isPlainObject(source))
|
|
683
|
-
return false;
|
|
684
|
-
if (source.sourceType === "PaymentToken") {
|
|
685
|
-
return onlyKeys(source, ["sourceType", ...TOKEN_MEMBERS]) && own3(source, "tokenData") && TOKEN_MEMBERS.every((key) => !own3(source, key) || text(source[key], 1, 4096));
|
|
686
|
-
}
|
|
687
|
-
if (source.sourceType === "PaymentCheck") {
|
|
688
|
-
return onlyKeys(source, ["sourceType", "encryptionData"]) && envelopeOk(source.encryptionData);
|
|
689
|
-
}
|
|
690
|
-
return source.sourceType === "PaymentCard" && onlyKeys(source, ["sourceType", "encryptionData", "card"]) && envelopeOk(source.encryptionData) && onlyKeys(source.card, ["category", "subCategory"]) && NON_CARD_CATEGORIES.includes(source.card.category) && (!own3(source.card, "subCategory") || text(source.card.subCategory, 1, 64));
|
|
691
|
-
}
|
|
692
|
-
function isNonCardCapture(body) {
|
|
693
|
-
const parsed = readCaptureBody(body);
|
|
694
|
-
return parsed !== null && isNonCardSource(parsed.source);
|
|
695
|
-
}
|
|
696
|
-
function isFiservPausableBody(body) {
|
|
697
|
-
return !isNonCardCapture(body);
|
|
698
|
-
}
|
|
699
|
-
function fiservTemplateKeyId(body) {
|
|
700
|
-
const parsed = readCaptureBody(body);
|
|
701
|
-
return parsed && captureBodyRefusal(parsed) === null ? parsed.source.encryptionData.keyId : null;
|
|
702
|
-
}
|
|
703
|
-
function fiservCaptureMerchant(body) {
|
|
704
|
-
const parsed = readCaptureBody(body);
|
|
705
|
-
if (!parsed || captureBodyRefusal(parsed) !== null)
|
|
706
|
-
return null;
|
|
707
|
-
const merchant = parsed.merchantDetails;
|
|
708
|
-
return { merchantId: merchant.merchantId, terminalId: own3(merchant, "terminalId") ? merchant.terminalId : null };
|
|
709
|
-
}
|
|
710
|
-
function capturePin(capture) {
|
|
711
|
-
if (!isPlainObject(capture))
|
|
712
|
-
return { refusal: "fiserv_request_invalid" };
|
|
713
|
-
const environment = fiservEnvironment(capture.url);
|
|
714
|
-
if (!environment)
|
|
715
|
-
return { refusal: "fiserv_endpoint_invalid" };
|
|
716
|
-
const body = readCaptureBody(capture.body);
|
|
717
|
-
if (!body)
|
|
718
|
-
return { refusal: "fiserv_body_invalid" };
|
|
719
|
-
const refusal = captureBodyRefusal(body);
|
|
720
|
-
if (refusal)
|
|
721
|
-
return { refusal };
|
|
722
|
-
const pin2 = fiservKeyPin(body.source.encryptionData.keyId, environment);
|
|
723
|
-
if (!pin2)
|
|
724
|
-
return { refusal: "fiserv_key_unpinned" };
|
|
725
|
-
if (!fiservPinServesMerchant(pin2, body.merchantDetails.merchantId))
|
|
726
|
-
return { refusal: "fiserv_key_merchant_unlisted" };
|
|
727
|
-
return { pin: pin2, environment };
|
|
728
|
-
}
|
|
729
|
-
function fiservCapturePin(capture) {
|
|
730
|
-
return capturePin(capture).pin ?? null;
|
|
731
|
-
}
|
|
732
|
-
function fiservCaptureKeyRefusal(capture, nowMs) {
|
|
733
|
-
const found = capturePin(capture);
|
|
734
|
-
return found.refusal ?? fiservKeyRefusal(found.pin.keyId, found.environment, nowMs);
|
|
735
|
-
}
|
|
736
|
-
function fiservPreparationUrl(environment) {
|
|
737
|
-
if (environment !== "production" && environment !== "sandbox")
|
|
738
|
-
throw new Error("unsupported_preparation_environment");
|
|
739
|
-
return FISERV_CARD_CAPTURE_ENDPOINTS[environment];
|
|
740
|
-
}
|
|
741
|
-
function isPreparedFiservRequest(url, method, body, environment, headers) {
|
|
742
|
-
return (environment === "production" || environment === "sandbox") && fiservEnvironment(url) === environment && fiservCardCaptureRefusal({ url, method, body, headers }) === null && fiservCapturePin({ url, body }) !== null;
|
|
743
|
-
}
|
|
744
|
-
var digitsOf = (value) => typeof value === "string" || typeof value === "number" ? String(value).replace(/[ -]/g, "") : "";
|
|
745
|
-
var KEY_REFUSALS = Object.freeze({
|
|
746
|
-
fiserv_key_unpinned: "Refusing to encrypt: Agentcard has not pinned this Fiserv key.",
|
|
747
|
-
fiserv_key_merchant_unlisted: "Refusing to encrypt: Agentcard has not pinned this Fiserv key for the merchant this payment names.",
|
|
748
|
-
fiserv_key_inactive: "Refusing to encrypt: this Fiserv key is no longer active.",
|
|
749
|
-
fiserv_key_outside_validity: "Refusing to encrypt: this Fiserv key is outside its validity window."
|
|
750
|
-
});
|
|
751
|
-
var CAPTURE_REFUSED = "Refusing to encrypt: unsupported Fiserv card capture request.";
|
|
752
|
-
async function encryptFiservCard(card, capture, facilities) {
|
|
753
|
-
const found = capturePin(capture);
|
|
754
|
-
const refusal = found.refusal ?? fiservKeyRefusal(found.pin.keyId, found.environment, facilities?.nowMs);
|
|
755
|
-
if (refusal)
|
|
756
|
-
throw new Error(KEY_REFUSALS[refusal] ?? CAPTURE_REFUSED);
|
|
757
|
-
const { pin: pin2, environment } = found;
|
|
758
|
-
if (environment === "sandbox" && !FISERV_SIMULATOR_PANS.includes(digitsOf(card?.number))) {
|
|
759
|
-
throw new Error("Refusing to encrypt: the Fiserv sandbox key encrypts only Fiserv's documented test cards.");
|
|
760
|
-
}
|
|
761
|
-
const spki = pinnedSpki(pin2);
|
|
762
|
-
if (!spki)
|
|
763
|
-
throw new Error("Refusing to encrypt: the Fiserv key is malformed.");
|
|
764
|
-
return sealFiservEnvelope(pin2.keyId, spki, fiservCaptureSource(card), facilities.crypto);
|
|
765
|
-
}
|
|
766
|
-
var FISERV_SUBSTITUTION = Object.freeze({
|
|
767
|
-
at: "source.encryptionData",
|
|
768
|
-
fields: Object.freeze(["keyId", "encryptedKey", "encryptionBlock", "aesIv"])
|
|
769
|
-
});
|
|
770
|
-
function fiservSubstitutionFieldsRefusal(fields) {
|
|
771
|
-
if (!isPlainObject(fields))
|
|
772
|
-
return "fiserv_substitution_invalid";
|
|
773
|
-
const names = Object.keys(fields);
|
|
774
|
-
if (names.length !== FISERV_SUBSTITUTION.fields.length || !FISERV_SUBSTITUTION.fields.every((name) => own3(fields, name))) {
|
|
775
|
-
return "fiserv_substitution_fields_invalid";
|
|
776
|
-
}
|
|
777
|
-
if (!isFiservPinnedKeyId(fields.keyId))
|
|
778
|
-
return "fiserv_key_unpinned";
|
|
779
|
-
if (!decodedLength(fields.encryptedKey, 256, 256) || !decodedLength(fields.encryptionBlock, 17, 4096) || !decodedLength(fields.aesIv, 12, 12))
|
|
780
|
-
return "fiserv_substitution_fields_invalid";
|
|
781
|
-
return null;
|
|
782
|
-
}
|
|
783
|
-
function fiservApprovedEnvelopeRefusal(fields, context) {
|
|
784
|
-
const shape = fiservSubstitutionFieldsRefusal(fields);
|
|
785
|
-
if (shape)
|
|
786
|
-
return shape;
|
|
787
|
-
if (!isPlainObject(context) || typeof context.sandbox !== "boolean")
|
|
788
|
-
return "fiserv_approval_context_invalid";
|
|
789
|
-
const found = capturePin(context.template);
|
|
790
|
-
if (found.refusal)
|
|
791
|
-
return found.refusal;
|
|
792
|
-
if (found.environment === "sandbox" !== context.sandbox)
|
|
793
|
-
return "fiserv_environment_mismatch";
|
|
794
|
-
if (
|
|
795
|
-
/** @type {Record<string, unknown>} */
|
|
796
|
-
fields.keyId !== found.pin.keyId)
|
|
797
|
-
return "fiserv_key_mismatch";
|
|
798
|
-
return fiservKeyRefusal(found.pin.keyId, found.environment, context.nowMs);
|
|
799
|
-
}
|
|
800
|
-
function fiservSubstitutions(envelope) {
|
|
801
|
-
if (!isPlainObject(envelope) || envelope.aesTagLength !== FISERV_ENVELOPE_CONSTANTS.aesTagLength || envelope.encryptionType !== FISERV_ENVELOPE_CONSTANTS.encryptionType || envelope.encryptionTarget !== FISERV_ENVELOPE_CONSTANTS.encryptionTarget) {
|
|
802
|
-
throw new Error("Refusing the Fiserv envelope: it is not the Secure Data Capture form.");
|
|
803
|
-
}
|
|
804
|
-
const fields = Object.fromEntries(FISERV_SUBSTITUTION.fields.map((name) => [name, envelope[name]]));
|
|
805
|
-
const refusal = fiservSubstitutionFieldsRefusal(fields);
|
|
806
|
-
if (refusal)
|
|
807
|
-
throw new Error(`Refusing the Fiserv envelope: ${refusal}.`);
|
|
808
|
-
return { encoding: "json", at: FISERV_SUBSTITUTION.at, fields };
|
|
809
|
-
}
|
|
810
|
-
var SUBSTITUTION_MEMBERS = ["encoding", "at", "fields", "remove"];
|
|
811
|
-
function substituteFiservEnvelope(body, sub) {
|
|
812
|
-
if (!isPlainObject(sub) || !onlyKeys(sub, SUBSTITUTION_MEMBERS) || sub.encoding !== "json" || sub.at !== FISERV_SUBSTITUTION.at || own3(sub, "remove") && !(Array.isArray(sub.remove) && sub.remove.length === 0)) {
|
|
813
|
-
throw new Error("Refusing to substitute: not a Fiserv envelope substitution.");
|
|
814
|
-
}
|
|
815
|
-
const fields = isPlainObject(sub.fields) ? Object.fromEntries(Object.entries(sub.fields)) : null;
|
|
816
|
-
const refusal = fiservSubstitutionFieldsRefusal(fields);
|
|
817
|
-
if (refusal)
|
|
818
|
-
throw new Error(`Refusing to substitute: ${refusal}.`);
|
|
819
|
-
const parsed = readCaptureBody(body);
|
|
820
|
-
const bodyRefusal = parsed ? captureBodyRefusal(parsed) : "fiserv_body_invalid";
|
|
821
|
-
if (bodyRefusal)
|
|
822
|
-
throw new Error(`Refusing to substitute: ${bodyRefusal}.`);
|
|
823
|
-
const envelope = parsed.source.encryptionData;
|
|
824
|
-
if (envelope.keyId !== fields.keyId)
|
|
825
|
-
throw new Error("Refusing to substitute: the envelope names another key than the paused capture.");
|
|
826
|
-
for (const name of FISERV_SUBSTITUTION.fields)
|
|
827
|
-
envelope[name] = fields[name];
|
|
828
|
-
return JSON.stringify(parsed);
|
|
829
|
-
}
|
|
830
|
-
export { FISERV_CARD_CAPTURE_ENDPOINTS, FISERV_ENVELOPE_CONSTANTS, FISERV_KEY_PINS, FISERV_SIMULATOR_PANS, FISERV_SUBSTITUTION, encryptFiservCard, fiservApprovedEnvelopeRefusal, fiservCaptureCardMismatch, fiservCaptureKeyRefusal, fiservCaptureMerchant, fiservCapturePin, fiservCardCaptureRefusal, fiservEnvironment, fiservErrorIdentifiers, fiservKeyPin, fiservKeyRefusal, fiservPreparationUrl, fiservPublishedKeyRefusal, fiservSubstitutionFieldsRefusal, fiservSubstitutions, fiservTemplateKeyId, fiservTimestampMs, isFiservCardCaptureRequest, isFiservCardCaptureUrl, isFiservPausableBody, isFiservPinnedKeyId, isPreparedFiservRequest, parseFiservPublishedKey, sanitizeFiservCaptureResponse, substituteFiservEnvelope };
|