@agent-cards/sdk 0.23.19 → 0.23.21
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 +61 -2
- package/PREFLIGHT.md +4 -0
- package/README.md +175 -7
- package/dist/amadeus.d.ts +4 -2
- package/dist/amadeus.js +16 -3
- package/dist/buy.d.ts +84 -2
- package/dist/buy.js +16 -1
- package/dist/card-endpoint-facts.d.ts +2 -1
- package/dist/card-endpoint-facts.js +18 -9
- package/dist/card-endpoint.d.ts +20 -4
- package/dist/card-endpoint.js +38 -4
- package/dist/card-fields.generated.js +1444 -51
- package/dist/cdp.d.ts +29 -0
- package/dist/cdp.js +444 -24
- package/dist/client.d.ts +134 -7
- package/dist/client.js +350 -25
- package/dist/comgate.d.ts +50 -0
- package/dist/comgate.js +80 -0
- package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
- package/dist/eprotect.generated.d.ts +1 -1
- package/dist/gmo.generated.js +7 -3
- package/dist/hipay.generated.d.ts +56 -0
- package/dist/hipay.generated.js +392 -0
- package/dist/hold.d.ts +180 -0
- package/dist/hold.js +270 -0
- package/dist/hosted-entry.d.ts +4 -0
- package/dist/hosted-entry.js +32 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.js +21 -0
- package/dist/merchant-card-endpoints.generated.js +102 -11
- package/dist/paddle-tokenex.generated.js +7 -3
- package/dist/paddle-vgs.generated.d.ts +3 -2
- package/dist/paddle-vgs.generated.js +9 -5
- package/dist/payrails.generated.js +7 -3
- package/dist/pre-claim.d.ts +20 -0
- package/dist/pre-claim.js +202 -22
- package/dist/preflight-catalog.json +58 -0
- package/dist/preflight-schemas.json +4 -0
- package/dist/preflight.generated.js +26 -2
- package/dist/prepared-processor.d.ts +6 -2
- package/dist/prepared-processor.js +18 -2
- package/dist/primer.generated.js +1487 -94
- package/dist/przelewy24.d.ts +47 -0
- package/dist/przelewy24.js +88 -0
- package/dist/quickpay.d.ts +44 -0
- package/dist/quickpay.js +65 -0
- package/dist/registry.d.ts +6 -5
- package/dist/registry.js +27 -7
- package/dist/shiji-sts.generated.js +7 -3
- package/dist/stripe-checkout.generated.js +744 -13
- package/dist/tokenex-iframe.d.ts +93 -0
- package/dist/tokenex-iframe.generated.d.ts +81 -0
- package/dist/tokenex-iframe.generated.js +403 -0
- package/dist/tokenex-iframe.js +195 -0
- package/dist/transbank.d.ts +45 -0
- package/dist/transbank.js +73 -0
- package/dist/twoc2p.d.ts +54 -0
- package/dist/twoc2p.generated.d.ts +33 -0
- package/dist/twoc2p.generated.js +1149 -0
- package/dist/twoc2p.js +85 -0
- package/dist/voice.d.ts +16 -13
- package/dist/voice.js +13 -10
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,1149 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
+
// artifact-sha256: 3c735198962459f0b06c7fbbdfa58de91ab1f76adeec6fa17b4e1a8d33e4b8c3
|
|
4
|
+
// src/sha256.js
|
|
5
|
+
var K = /* @__PURE__ */ new Uint32Array([
|
|
6
|
+
1116352408,
|
|
7
|
+
1899447441,
|
|
8
|
+
3049323471,
|
|
9
|
+
3921009573,
|
|
10
|
+
961987163,
|
|
11
|
+
1508970993,
|
|
12
|
+
2453635748,
|
|
13
|
+
2870763221,
|
|
14
|
+
3624381080,
|
|
15
|
+
310598401,
|
|
16
|
+
607225278,
|
|
17
|
+
1426881987,
|
|
18
|
+
1925078388,
|
|
19
|
+
2162078206,
|
|
20
|
+
2614888103,
|
|
21
|
+
3248222580,
|
|
22
|
+
3835390401,
|
|
23
|
+
4022224774,
|
|
24
|
+
264347078,
|
|
25
|
+
604807628,
|
|
26
|
+
770255983,
|
|
27
|
+
1249150122,
|
|
28
|
+
1555081692,
|
|
29
|
+
1996064986,
|
|
30
|
+
2554220882,
|
|
31
|
+
2821834349,
|
|
32
|
+
2952996808,
|
|
33
|
+
3210313671,
|
|
34
|
+
3336571891,
|
|
35
|
+
3584528711,
|
|
36
|
+
113926993,
|
|
37
|
+
338241895,
|
|
38
|
+
666307205,
|
|
39
|
+
773529912,
|
|
40
|
+
1294757372,
|
|
41
|
+
1396182291,
|
|
42
|
+
1695183700,
|
|
43
|
+
1986661051,
|
|
44
|
+
2177026350,
|
|
45
|
+
2456956037,
|
|
46
|
+
2730485921,
|
|
47
|
+
2820302411,
|
|
48
|
+
3259730800,
|
|
49
|
+
3345764771,
|
|
50
|
+
3516065817,
|
|
51
|
+
3600352804,
|
|
52
|
+
4094571909,
|
|
53
|
+
275423344,
|
|
54
|
+
430227734,
|
|
55
|
+
506948616,
|
|
56
|
+
659060556,
|
|
57
|
+
883997877,
|
|
58
|
+
958139571,
|
|
59
|
+
1322822218,
|
|
60
|
+
1537002063,
|
|
61
|
+
1747873779,
|
|
62
|
+
1955562222,
|
|
63
|
+
2024104815,
|
|
64
|
+
2227730452,
|
|
65
|
+
2361852424,
|
|
66
|
+
2428436474,
|
|
67
|
+
2756734187,
|
|
68
|
+
3204031479,
|
|
69
|
+
3329325298
|
|
70
|
+
]);
|
|
71
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
72
|
+
function utf8(text2) {
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const char of text2) {
|
|
75
|
+
let cp = char.codePointAt(0);
|
|
76
|
+
if (cp >= 55296 && cp <= 57343)
|
|
77
|
+
cp = 65533;
|
|
78
|
+
if (cp < 128)
|
|
79
|
+
out.push(cp);
|
|
80
|
+
else if (cp < 2048)
|
|
81
|
+
out.push(192 | cp >> 6, 128 | cp & 63);
|
|
82
|
+
else if (cp < 65536)
|
|
83
|
+
out.push(224 | cp >> 12, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
84
|
+
else
|
|
85
|
+
out.push(240 | cp >> 18, 128 | cp >> 12 & 63, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
86
|
+
}
|
|
87
|
+
return Uint8Array.from(out);
|
|
88
|
+
}
|
|
89
|
+
function sha256Hex(input) {
|
|
90
|
+
const isBytes = Object.prototype.toString.call(input) === "[object Uint8Array]";
|
|
91
|
+
if (typeof input !== "string" && !isBytes)
|
|
92
|
+
throw new TypeError("sha256Hex takes a string or a Uint8Array.");
|
|
93
|
+
const bytes = typeof input === "string" ? utf8(input) : input;
|
|
94
|
+
const length = bytes.length;
|
|
95
|
+
const padded = new Uint8Array((length + 8 >> 6) + 1 << 6);
|
|
96
|
+
padded.set(bytes);
|
|
97
|
+
padded[length] = 128;
|
|
98
|
+
const view = new DataView(padded.buffer);
|
|
99
|
+
view.setUint32(padded.length - 8, Math.floor(length / 536870912));
|
|
100
|
+
view.setUint32(padded.length - 4, length << 3 >>> 0);
|
|
101
|
+
const H = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
102
|
+
const W = new Uint32Array(64);
|
|
103
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
104
|
+
for (let t = 0; t < 16; t++)
|
|
105
|
+
W[t] = view.getUint32(offset + t * 4);
|
|
106
|
+
for (let t = 16; t < 64; t++) {
|
|
107
|
+
const s0 = rotr(W[t - 15], 7) ^ rotr(W[t - 15], 18) ^ W[t - 15] >>> 3;
|
|
108
|
+
const s1 = rotr(W[t - 2], 17) ^ rotr(W[t - 2], 19) ^ W[t - 2] >>> 10;
|
|
109
|
+
W[t] = W[t - 16] + s0 + W[t - 7] + s1 >>> 0;
|
|
110
|
+
}
|
|
111
|
+
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];
|
|
112
|
+
for (let t = 0; t < 64; t++) {
|
|
113
|
+
const t1 = h + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[t] + W[t] >>> 0;
|
|
114
|
+
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
115
|
+
h = g;
|
|
116
|
+
g = f;
|
|
117
|
+
f = e;
|
|
118
|
+
e = d + t1 >>> 0;
|
|
119
|
+
d = c;
|
|
120
|
+
c = b;
|
|
121
|
+
b = a;
|
|
122
|
+
a = t1 + t2 >>> 0;
|
|
123
|
+
}
|
|
124
|
+
H[0] += a;
|
|
125
|
+
H[1] += b;
|
|
126
|
+
H[2] += c;
|
|
127
|
+
H[3] += d;
|
|
128
|
+
H[4] += e;
|
|
129
|
+
H[5] += f;
|
|
130
|
+
H[6] += g;
|
|
131
|
+
H[7] += h;
|
|
132
|
+
}
|
|
133
|
+
return Array.from(H, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
134
|
+
}
|
|
135
|
+
// src/rsa-pkcs1v15.js
|
|
136
|
+
var RsaPkcs1v15Error = class extends Error {
|
|
137
|
+
constructor(reason) {
|
|
138
|
+
super("RSA PKCS#1 v1.5 encryption refused.");
|
|
139
|
+
this.name = "RsaPkcs1v15Error";
|
|
140
|
+
this.code = "rsa_pkcs1v15_refused";
|
|
141
|
+
this.reason = reason;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var refuse = (reason) => new RsaPkcs1v15Error(reason);
|
|
145
|
+
var BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
146
|
+
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
147
|
+
var RSA_ALGORITHM = [48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0];
|
|
148
|
+
var MODULUS_BYTES = [128, 256, 384, 512];
|
|
149
|
+
var MIN_PADDING = 8;
|
|
150
|
+
var MAX_DRAWS = 16;
|
|
151
|
+
var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
152
|
+
var typedArrayName = Object.getOwnPropertyDescriptor(TYPED_ARRAY, Symbol.toStringTag).get;
|
|
153
|
+
function parseRsaPublicKey(key) {
|
|
154
|
+
const spki = key && typeof key === "object" ? key.spki : void 0;
|
|
155
|
+
const pin = key && typeof key === "object" ? key.sha256 : void 0;
|
|
156
|
+
const named = key && typeof key === "object" && Object.prototype.hasOwnProperty.call(key, "exponent") ? key.exponent : void 0;
|
|
157
|
+
if (named !== void 0 && named !== 17)
|
|
158
|
+
throw refuse("key");
|
|
159
|
+
if (typeof spki !== "string" || spki.length > 1024 || spki.length % 4 !== 0 || !BASE64.test(spki))
|
|
160
|
+
throw refuse("key");
|
|
161
|
+
if (typeof pin !== "string" || !SHA256_HEX.test(pin))
|
|
162
|
+
throw refuse("key");
|
|
163
|
+
let binary;
|
|
164
|
+
try {
|
|
165
|
+
binary = atob(spki);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
throw refuse("key");
|
|
169
|
+
}
|
|
170
|
+
if (btoa(binary) !== spki)
|
|
171
|
+
throw refuse("key");
|
|
172
|
+
const der = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
173
|
+
if (sha256Hex(der) !== pin)
|
|
174
|
+
throw refuse("key");
|
|
175
|
+
const outer = readElement(der, 0, 48);
|
|
176
|
+
if (outer.end !== der.length || outer.start + RSA_ALGORITHM.length > der.length)
|
|
177
|
+
throw refuse("key");
|
|
178
|
+
if (!RSA_ALGORITHM.every((byte, i) => der[outer.start + i] === byte))
|
|
179
|
+
throw refuse("key");
|
|
180
|
+
const bits = readElement(der, outer.start + RSA_ALGORITHM.length, 3);
|
|
181
|
+
if (bits.end !== outer.end || bits.start === bits.end || der[bits.start] !== 0)
|
|
182
|
+
throw refuse("key");
|
|
183
|
+
const rsa = readElement(der, bits.start + 1, 48);
|
|
184
|
+
if (rsa.end !== bits.end)
|
|
185
|
+
throw refuse("key");
|
|
186
|
+
const modulus = readPositiveInteger(der, rsa.start);
|
|
187
|
+
const exponent = readPositiveInteger(der, modulus.end);
|
|
188
|
+
if (exponent.end !== rsa.end)
|
|
189
|
+
throw refuse("key");
|
|
190
|
+
const n = modulus.bytes;
|
|
191
|
+
if (!MODULUS_BYTES.includes(n.length) || !(n[0] & 128) || !(n[n.length - 1] & 1))
|
|
192
|
+
throw refuse("key");
|
|
193
|
+
const e = exponent.bytes;
|
|
194
|
+
const expected = named === 17 ? [17] : [1, 0, 1];
|
|
195
|
+
if (e.length !== expected.length || !expected.every((byte, i) => e[i] === byte))
|
|
196
|
+
throw refuse("key");
|
|
197
|
+
return Object.freeze({ n: toBigInt(n), e: named === 17 ? 17n : 65537n, size: n.length });
|
|
198
|
+
}
|
|
199
|
+
function rsaesPkcs1v15Encrypt(key, message, random) {
|
|
200
|
+
const { n, e, size } = parseRsaPublicKey(key);
|
|
201
|
+
if (typedArrayName.call(message) !== "Uint8Array")
|
|
202
|
+
throw refuse("message");
|
|
203
|
+
let bytes;
|
|
204
|
+
try {
|
|
205
|
+
bytes = new Uint8Array(message);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
throw refuse("message");
|
|
209
|
+
}
|
|
210
|
+
const em = new Uint8Array(size);
|
|
211
|
+
try {
|
|
212
|
+
if (bytes.length > size - MIN_PADDING - 3)
|
|
213
|
+
throw refuse("message");
|
|
214
|
+
em[1] = 2;
|
|
215
|
+
fillNonZero(em.subarray(2, size - bytes.length - 1), random);
|
|
216
|
+
em.set(bytes, size - bytes.length);
|
|
217
|
+
return toBytes(modPow(toBigInt(em), e, n), size);
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
em.fill(0);
|
|
221
|
+
bytes.fill(0);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function readElement(der, at, tag) {
|
|
225
|
+
if (at + 2 > der.length || der[at] !== tag)
|
|
226
|
+
throw refuse("key");
|
|
227
|
+
let length = der[at + 1];
|
|
228
|
+
let start = at + 2;
|
|
229
|
+
if (length & 128) {
|
|
230
|
+
const count = length & 127;
|
|
231
|
+
if (count === 0 || count > 2 || start + count > der.length || der[start] === 0)
|
|
232
|
+
throw refuse("key");
|
|
233
|
+
length = count === 1 ? der[start] : der[start] * 256 + der[start + 1];
|
|
234
|
+
if (length < 128)
|
|
235
|
+
throw refuse("key");
|
|
236
|
+
start += count;
|
|
237
|
+
}
|
|
238
|
+
if (start + length > der.length)
|
|
239
|
+
throw refuse("key");
|
|
240
|
+
return { start, end: start + length };
|
|
241
|
+
}
|
|
242
|
+
function readPositiveInteger(der, at) {
|
|
243
|
+
const { start, end } = readElement(der, at, 2);
|
|
244
|
+
if (end === start || der[start] & 128)
|
|
245
|
+
throw refuse("key");
|
|
246
|
+
if (der[start] === 0 && (end - start === 1 || !(der[start + 1] & 128)))
|
|
247
|
+
throw refuse("key");
|
|
248
|
+
return { bytes: der.subarray(der[start] === 0 ? start + 1 : start, end), end };
|
|
249
|
+
}
|
|
250
|
+
function fillNonZero(target, random) {
|
|
251
|
+
if (!random || typeof random.getRandomValues !== "function")
|
|
252
|
+
throw refuse("random");
|
|
253
|
+
const draw = new Uint8Array(target.length);
|
|
254
|
+
let filled = 0;
|
|
255
|
+
try {
|
|
256
|
+
for (let round = 0; round < MAX_DRAWS && filled < target.length; round++) {
|
|
257
|
+
draw.fill(0);
|
|
258
|
+
random.getRandomValues(draw);
|
|
259
|
+
for (let i = 0; i < draw.length && filled < target.length; i++)
|
|
260
|
+
if (draw[i] !== 0)
|
|
261
|
+
target[filled++] = draw[i];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
throw refuse("random");
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
draw.fill(0);
|
|
269
|
+
}
|
|
270
|
+
if (filled < target.length)
|
|
271
|
+
throw refuse("random");
|
|
272
|
+
}
|
|
273
|
+
function toBigInt(bytes) {
|
|
274
|
+
let hex = "";
|
|
275
|
+
for (let i = 0; i < bytes.length; i++)
|
|
276
|
+
hex += (bytes[i] < 16 ? "0" : "") + bytes[i].toString(16);
|
|
277
|
+
return BigInt(`0x${hex}`);
|
|
278
|
+
}
|
|
279
|
+
function toBytes(value, size) {
|
|
280
|
+
const hex = value.toString(16).padStart(size * 2, "0");
|
|
281
|
+
const out = new Uint8Array(size);
|
|
282
|
+
for (let i = 0; i < size; i++)
|
|
283
|
+
out[i] = parseInt(hex.slice(2 * i, 2 * i + 2), 16);
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
function modPow(base, exponent, modulus) {
|
|
287
|
+
let result = 1n;
|
|
288
|
+
let square = base % modulus;
|
|
289
|
+
for (let rest = exponent; rest > 0n; rest >>= 1n) {
|
|
290
|
+
if (rest & 1n)
|
|
291
|
+
result = result * square % modulus;
|
|
292
|
+
square = square * square % modulus;
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
// src/twoc2p-secure-pay.js
|
|
297
|
+
var TWOC2P_PASSPHRASE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-+=_";
|
|
298
|
+
var PASSPHRASE_LENGTH = 8;
|
|
299
|
+
var UNBIASED_BELOW = 228;
|
|
300
|
+
var MAX_DRAWS2 = 16;
|
|
301
|
+
var SALTED = /* @__PURE__ */ Object.freeze([83, 97, 108, 116, 101, 100, 95, 95]);
|
|
302
|
+
var MAX_PLAINTEXT = 47;
|
|
303
|
+
var TwoC2pSecurePayError = class extends Error {
|
|
304
|
+
constructor(code) {
|
|
305
|
+
super(code === "twoc2p_card_unsupported" ? "Refusing to encrypt: this card cannot be written in 2C2P's card format." : code === "twoc2p_key_unavailable" ? "Refusing to encrypt: no reviewed 2C2P key is pinned for this payment." : "Refusing to encrypt: this device cannot produce 2C2P's card field.");
|
|
306
|
+
this.name = "TwoC2pSecurePayError";
|
|
307
|
+
this.code = code;
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
var MD5_SHIFTS = /* @__PURE__ */ Object.freeze([7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21]);
|
|
311
|
+
var MD5_K = /* @__PURE__ */ Object.freeze([
|
|
312
|
+
3614090360,
|
|
313
|
+
3905402710,
|
|
314
|
+
606105819,
|
|
315
|
+
3250441966,
|
|
316
|
+
4118548399,
|
|
317
|
+
1200080426,
|
|
318
|
+
2821735955,
|
|
319
|
+
4249261313,
|
|
320
|
+
1770035416,
|
|
321
|
+
2336552879,
|
|
322
|
+
4294925233,
|
|
323
|
+
2304563134,
|
|
324
|
+
1804603682,
|
|
325
|
+
4254626195,
|
|
326
|
+
2792965006,
|
|
327
|
+
1236535329,
|
|
328
|
+
4129170786,
|
|
329
|
+
3225465664,
|
|
330
|
+
643717713,
|
|
331
|
+
3921069994,
|
|
332
|
+
3593408605,
|
|
333
|
+
38016083,
|
|
334
|
+
3634488961,
|
|
335
|
+
3889429448,
|
|
336
|
+
568446438,
|
|
337
|
+
3275163606,
|
|
338
|
+
4107603335,
|
|
339
|
+
1163531501,
|
|
340
|
+
2850285829,
|
|
341
|
+
4243563512,
|
|
342
|
+
1735328473,
|
|
343
|
+
2368359562,
|
|
344
|
+
4294588738,
|
|
345
|
+
2272392833,
|
|
346
|
+
1839030562,
|
|
347
|
+
4259657740,
|
|
348
|
+
2763975236,
|
|
349
|
+
1272893353,
|
|
350
|
+
4139469664,
|
|
351
|
+
3200236656,
|
|
352
|
+
681279174,
|
|
353
|
+
3936430074,
|
|
354
|
+
3572445317,
|
|
355
|
+
76029189,
|
|
356
|
+
3654602809,
|
|
357
|
+
3873151461,
|
|
358
|
+
530742520,
|
|
359
|
+
3299628645,
|
|
360
|
+
4096336452,
|
|
361
|
+
1126891415,
|
|
362
|
+
2878612391,
|
|
363
|
+
4237533241,
|
|
364
|
+
1700485571,
|
|
365
|
+
2399980690,
|
|
366
|
+
4293915773,
|
|
367
|
+
2240044497,
|
|
368
|
+
1873313359,
|
|
369
|
+
4264355552,
|
|
370
|
+
2734768916,
|
|
371
|
+
1309151649,
|
|
372
|
+
4149444226,
|
|
373
|
+
3174756917,
|
|
374
|
+
718787259,
|
|
375
|
+
3951481745
|
|
376
|
+
]);
|
|
377
|
+
function twoC2pMd5(message) {
|
|
378
|
+
const length = message.length;
|
|
379
|
+
const padded = new Uint8Array((length + 8 >>> 6) + 1 << 6);
|
|
380
|
+
padded.set(message);
|
|
381
|
+
padded[length] = 128;
|
|
382
|
+
const view = new DataView(padded.buffer);
|
|
383
|
+
view.setUint32(padded.length - 8, length * 8 >>> 0, true);
|
|
384
|
+
view.setUint32(padded.length - 4, Math.floor(length / 536870912), true);
|
|
385
|
+
let a0 = 1732584193, b0 = 4023233417, c0 = 2562383102, d0 = 271733878;
|
|
386
|
+
try {
|
|
387
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
388
|
+
let a = a0, b = b0, c = c0, d = d0;
|
|
389
|
+
for (let i = 0; i < 64; i++) {
|
|
390
|
+
const round = i >>> 4;
|
|
391
|
+
const f = round === 0 ? b & c | ~b & d : round === 1 ? d & b | ~d & c : round === 2 ? b ^ c ^ d : c ^ (b | ~d);
|
|
392
|
+
const g = round === 0 ? i : round === 1 ? 5 * i + 1 & 15 : round === 2 ? 3 * i + 5 & 15 : 7 * i & 15;
|
|
393
|
+
const sum = f + a + MD5_K[i] + view.getUint32(offset + g * 4, true) | 0;
|
|
394
|
+
const shift = MD5_SHIFTS[round << 2 | i & 3];
|
|
395
|
+
a = d;
|
|
396
|
+
d = c;
|
|
397
|
+
c = b;
|
|
398
|
+
b = b + (sum << shift | sum >>> 32 - shift) | 0;
|
|
399
|
+
}
|
|
400
|
+
a0 = a0 + a | 0;
|
|
401
|
+
b0 = b0 + b | 0;
|
|
402
|
+
c0 = c0 + c | 0;
|
|
403
|
+
d0 = d0 + d | 0;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
padded.fill(0);
|
|
408
|
+
}
|
|
409
|
+
const out = new Uint8Array(16);
|
|
410
|
+
const words = new DataView(out.buffer);
|
|
411
|
+
words.setUint32(0, a0 >>> 0, true);
|
|
412
|
+
words.setUint32(4, b0 >>> 0, true);
|
|
413
|
+
words.setUint32(8, c0 >>> 0, true);
|
|
414
|
+
words.setUint32(12, d0 >>> 0, true);
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
function evpBytesToKey(password, salt) {
|
|
418
|
+
const seed = new Uint8Array(password.length + salt.length);
|
|
419
|
+
seed.set(password);
|
|
420
|
+
seed.set(salt, password.length);
|
|
421
|
+
const blocks = [];
|
|
422
|
+
let previous = new Uint8Array(0);
|
|
423
|
+
try {
|
|
424
|
+
for (let round = 0; round < 3; round++) {
|
|
425
|
+
const input = new Uint8Array(previous.length + seed.length);
|
|
426
|
+
input.set(previous);
|
|
427
|
+
input.set(seed, previous.length);
|
|
428
|
+
previous = twoC2pMd5(input);
|
|
429
|
+
input.fill(0);
|
|
430
|
+
blocks.push(previous);
|
|
431
|
+
}
|
|
432
|
+
const material = new Uint8Array(48);
|
|
433
|
+
blocks.forEach((block, at) => material.set(block, at * 16));
|
|
434
|
+
return { key: material.slice(0, 32), iv: material.slice(32, 48), material };
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
seed.fill(0);
|
|
438
|
+
for (const block of blocks)
|
|
439
|
+
block.fill(0);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
var usable = (facilities) => {
|
|
443
|
+
const crypto = facilities && facilities.crypto ? facilities.crypto : globalThis.crypto;
|
|
444
|
+
if (!crypto || typeof crypto.getRandomValues !== "function" || !crypto.subtle)
|
|
445
|
+
throw new TwoC2pSecurePayError("twoc2p_crypto_unavailable");
|
|
446
|
+
return crypto;
|
|
447
|
+
};
|
|
448
|
+
function base64(bytes) {
|
|
449
|
+
let binary = "";
|
|
450
|
+
for (let i = 0; i < bytes.length; i++)
|
|
451
|
+
binary += String.fromCharCode(bytes[i]);
|
|
452
|
+
return btoa(binary);
|
|
453
|
+
}
|
|
454
|
+
async function twoC2pSaltedAes(plaintext, passphrase, salt, facilities = {}) {
|
|
455
|
+
const crypto = usable(facilities);
|
|
456
|
+
const text2 = new TextEncoder().encode(plaintext);
|
|
457
|
+
const password = new TextEncoder().encode(passphrase);
|
|
458
|
+
if (!(salt instanceof Uint8Array) || salt.length !== 8 || text2.length === 0 || text2.length > MAX_PLAINTEXT) {
|
|
459
|
+
text2.fill(0);
|
|
460
|
+
password.fill(0);
|
|
461
|
+
throw new TwoC2pSecurePayError("twoc2p_card_unsupported");
|
|
462
|
+
}
|
|
463
|
+
const { key, iv, material } = evpBytesToKey(password, salt);
|
|
464
|
+
try {
|
|
465
|
+
const aes = await crypto.subtle.importKey("raw", key, { name: "AES-CBC" }, false, ["encrypt"]);
|
|
466
|
+
const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-CBC", iv }, aes, text2));
|
|
467
|
+
const out = new Uint8Array(16 + sealed.length);
|
|
468
|
+
out.set(SALTED);
|
|
469
|
+
out.set(salt, 8);
|
|
470
|
+
out.set(sealed, 16);
|
|
471
|
+
return base64(out);
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
text2.fill(0);
|
|
475
|
+
password.fill(0);
|
|
476
|
+
key.fill(0);
|
|
477
|
+
iv.fill(0);
|
|
478
|
+
material.fill(0);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function drawPassphrase(random) {
|
|
482
|
+
const draw = new Uint8Array(PASSPHRASE_LENGTH * 2);
|
|
483
|
+
let out = "";
|
|
484
|
+
try {
|
|
485
|
+
for (let round = 0; round < MAX_DRAWS2 && out.length < PASSPHRASE_LENGTH; round++) {
|
|
486
|
+
draw.fill(255);
|
|
487
|
+
random.getRandomValues(draw);
|
|
488
|
+
for (let i = 0; i < draw.length && out.length < PASSPHRASE_LENGTH; i++) {
|
|
489
|
+
if (draw[i] < UNBIASED_BELOW)
|
|
490
|
+
out += TWOC2P_PASSPHRASE_ALPHABET[draw[i] % TWOC2P_PASSPHRASE_ALPHABET.length];
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
throw new TwoC2pSecurePayError("twoc2p_crypto_unavailable");
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
draw.fill(0);
|
|
499
|
+
}
|
|
500
|
+
if (out.length < PASSPHRASE_LENGTH)
|
|
501
|
+
throw new TwoC2pSecurePayError("twoc2p_crypto_unavailable");
|
|
502
|
+
return out;
|
|
503
|
+
}
|
|
504
|
+
function cardPlaintext(card) {
|
|
505
|
+
if (!card || typeof card !== "object")
|
|
506
|
+
throw new TwoC2pSecurePayError("twoc2p_card_unsupported");
|
|
507
|
+
const number = String(card.number ?? "").replace(/[\s-]/g, "");
|
|
508
|
+
const month = Number(card.month);
|
|
509
|
+
const rawYear = String(card.year ?? "");
|
|
510
|
+
const year = /^[0-9]{2}$/.test(rawYear) ? 2e3 + Number(rawYear) : /^[0-9]{4}$/.test(rawYear) ? Number(rawYear) : NaN;
|
|
511
|
+
const cvc = String(card.cvc ?? "");
|
|
512
|
+
if (!/^[0-9]{12,19}$/.test(number) || !Number.isInteger(month) || month < 1 || month > 12 || !(year >= 2e3 && year <= 2099) || !/^[0-9]{3,4}$/.test(cvc))
|
|
513
|
+
throw new TwoC2pSecurePayError("twoc2p_card_unsupported");
|
|
514
|
+
return `${number};${String(month).padStart(2, "0")};${year};${cvc};;`;
|
|
515
|
+
}
|
|
516
|
+
async function buildTwoC2pSecurePayToken(card, key, facilities = {}) {
|
|
517
|
+
let size;
|
|
518
|
+
try {
|
|
519
|
+
size = parseRsaPublicKey(key).size;
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
throw new TwoC2pSecurePayError("twoc2p_key_unavailable");
|
|
523
|
+
}
|
|
524
|
+
const crypto = usable(facilities);
|
|
525
|
+
const plaintext = cardPlaintext(card);
|
|
526
|
+
const passphrase = drawPassphrase(crypto);
|
|
527
|
+
const message = new TextEncoder().encode(passphrase);
|
|
528
|
+
let block = null;
|
|
529
|
+
try {
|
|
530
|
+
for (let round = 0; round < MAX_DRAWS2 && block === null; round++) {
|
|
531
|
+
let sealed;
|
|
532
|
+
try {
|
|
533
|
+
sealed = rsaesPkcs1v15Encrypt(key, message, crypto);
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
throw new TwoC2pSecurePayError("twoc2p_crypto_unavailable");
|
|
537
|
+
}
|
|
538
|
+
if (sealed[0] !== 0)
|
|
539
|
+
block = sealed;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
finally {
|
|
543
|
+
message.fill(0);
|
|
544
|
+
}
|
|
545
|
+
if (block === null || block.length !== size)
|
|
546
|
+
throw new TwoC2pSecurePayError("twoc2p_crypto_unavailable");
|
|
547
|
+
const salt = new Uint8Array(8);
|
|
548
|
+
try {
|
|
549
|
+
crypto.getRandomValues(salt);
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
throw new TwoC2pSecurePayError("twoc2p_crypto_unavailable");
|
|
553
|
+
}
|
|
554
|
+
const rsa = base64(block);
|
|
555
|
+
const aes = await twoC2pSaltedAes(plaintext, passphrase, salt, { crypto });
|
|
556
|
+
salt.fill(0);
|
|
557
|
+
return `${rsa.length.toString(16).padStart(4, "0")}${rsa}${aes}`;
|
|
558
|
+
}
|
|
559
|
+
var B64 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
560
|
+
var FIELD_LIMIT = 1024;
|
|
561
|
+
function decodeCanonical(text2) {
|
|
562
|
+
if (typeof text2 !== "string" || text2.length === 0 || text2.length % 4 !== 0 || !B64.test(text2))
|
|
563
|
+
return null;
|
|
564
|
+
let binary;
|
|
565
|
+
try {
|
|
566
|
+
binary = atob(text2);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
if (btoa(binary) !== text2)
|
|
572
|
+
return null;
|
|
573
|
+
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
574
|
+
}
|
|
575
|
+
function twoC2pSecurePayParts(value) {
|
|
576
|
+
if (typeof value !== "string" || value.length > FIELD_LIMIT || !/^[0-9a-f]{4}/.test(value))
|
|
577
|
+
return null;
|
|
578
|
+
const length = parseInt(value.slice(0, 4), 16);
|
|
579
|
+
if (length < 4 || 4 + length >= value.length)
|
|
580
|
+
return null;
|
|
581
|
+
const rsaText = value.slice(4, 4 + length);
|
|
582
|
+
const rsa = decodeCanonical(rsaText);
|
|
583
|
+
const cipher = decodeCanonical(value.slice(4 + length));
|
|
584
|
+
if (!rsa || !cipher || cipher.length < 32 || cipher.length > 80 || cipher.length % 16 !== 0)
|
|
585
|
+
return null;
|
|
586
|
+
if (!SALTED.every((byte, at) => cipher[at] === byte))
|
|
587
|
+
return null;
|
|
588
|
+
return { rsaText, rsa, salt: cipher.slice(8, 16), blocks: (cipher.length - 16) / 16 };
|
|
589
|
+
}
|
|
590
|
+
function isTwoC2pPageField(value, keyBytes) {
|
|
591
|
+
const parts = twoC2pSecurePayParts(value);
|
|
592
|
+
if (!parts || !Number.isInteger(keyBytes) || keyBytes < 128)
|
|
593
|
+
return false;
|
|
594
|
+
const expected = Math.ceil(keyBytes / 3) * 4;
|
|
595
|
+
return parts.rsaText.length === expected && (parts.rsa.length === keyBytes || parts.rsa.length === keyBytes - 1);
|
|
596
|
+
}
|
|
597
|
+
function isTwoC2pDeviceField(value, key) {
|
|
598
|
+
let parsed;
|
|
599
|
+
try {
|
|
600
|
+
parsed = parseRsaPublicKey(key);
|
|
601
|
+
}
|
|
602
|
+
catch {
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
const parts = twoC2pSecurePayParts(value);
|
|
606
|
+
if (!parts || parts.blocks < 2 || parts.blocks > 3 || parts.rsa.length !== parsed.size || parts.rsa[0] === 0)
|
|
607
|
+
return false;
|
|
608
|
+
if (value.slice(0, 4) !== parts.rsaText.length.toString(16).padStart(4, "0"))
|
|
609
|
+
return false;
|
|
610
|
+
let hex = "";
|
|
611
|
+
for (let i = 0; i < parts.rsa.length; i++)
|
|
612
|
+
hex += parts.rsa[i].toString(16).padStart(2, "0");
|
|
613
|
+
const c = BigInt(`0x${hex}`);
|
|
614
|
+
return c > 1n && c < parsed.n;
|
|
615
|
+
}
|
|
616
|
+
// src/twoc2p-keys.js
|
|
617
|
+
var TWOC2P_KEYS = /* @__PURE__ */ Object.freeze({
|
|
618
|
+
production: Object.freeze({
|
|
619
|
+
spki: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/1ljsxI+r6qQraTFrHfWNrV41vxumBF+4ILAG3WsN6HEBQU2wSUv/IyQmZkiys1ilOb+OOuQTH8OXMnqQg9E4epyePW3L7IeOieSbIIDeSX6UpTWmT/Fs7moWZkN8RpuCV0u87n3AY2tQbtcSqwEKi2sxzWLFwscoGn6N5yVIpQIDAQAB",
|
|
620
|
+
sha256: "bb8f27929ce140c6ef0ec9c4a20d7ca0b83376bc35a501985dc40d16669384fe",
|
|
621
|
+
script: Object.freeze({ url: "https://t.2c2p.com/SecurePayment/api/my2c2p.1.7.6.min.js", sha256: "4a745a37765a2e28e299d64f6ea5811aab27b176da61db6f840fa85d6a47e763" })
|
|
622
|
+
}),
|
|
623
|
+
sandbox: Object.freeze({
|
|
624
|
+
spki: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKakJC+2MSZikKoPzlNqXvJCuWNLhejg3gMRePSetYNNgnhyqFwwAR7t7i/B2QFZoixSYc2/23cJz0jA5O+q+Cu3fycqKlf1L9966+X3B9nyHe2eKQZr8KrcS/8+sEKcd0pLATY4AleW1iktuN+2oghrDKuv+aDNztWcOy+PTofwIDAQAB",
|
|
625
|
+
sha256: "7f80c07f1d72db28ede10dae942940657c72dfa789836c9fdf283cba6feff2e6",
|
|
626
|
+
script: Object.freeze({ url: "https://demo2.2c2p.com/2C2PFrontEnd/SecurePayment/api/my2c2p.1.7.6.min.js", sha256: "435723d92bfefb00607a56851967e0b745d52b8d49443de5f0000f0583fde5cc" })
|
|
627
|
+
})
|
|
628
|
+
});
|
|
629
|
+
function twoC2pKey(environment) {
|
|
630
|
+
if (environment !== "production" && environment !== "sandbox")
|
|
631
|
+
return null;
|
|
632
|
+
return TWOC2P_KEYS[environment] ?? null;
|
|
633
|
+
}
|
|
634
|
+
function twoC2pKeyBytes(environment) {
|
|
635
|
+
const key = twoC2pKey(environment);
|
|
636
|
+
if (!key)
|
|
637
|
+
return null;
|
|
638
|
+
try {
|
|
639
|
+
return parseRsaPublicKey(key).size;
|
|
640
|
+
}
|
|
641
|
+
catch {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// src/adyen-jwe.js
|
|
646
|
+
var ADYEN_JWE_HEADER = { alg: "RSA-OAEP-256", enc: "A256GCM", version: "1" };
|
|
647
|
+
var BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
648
|
+
function isAdyenJweCompact(s) {
|
|
649
|
+
if (typeof s !== "string" || s.length > 2048)
|
|
650
|
+
return false;
|
|
651
|
+
const parts = s.split(".");
|
|
652
|
+
if (parts.length !== 5)
|
|
653
|
+
return false;
|
|
654
|
+
const [header, encryptedKey, iv, ciphertext, tag] = parts;
|
|
655
|
+
if (!parts.every((p) => p.length > 0 && BASE64URL.test(p)))
|
|
656
|
+
return false;
|
|
657
|
+
if (encryptedKey.length !== 342 || iv.length !== 16 || tag.length !== 22)
|
|
658
|
+
return false;
|
|
659
|
+
if (ciphertext.length > 1024)
|
|
660
|
+
return false;
|
|
661
|
+
return isAdyenJweHeader(header);
|
|
662
|
+
}
|
|
663
|
+
function isAdyenJweHeader(segment) {
|
|
664
|
+
if (!BASE64URL.test(segment) || segment.length > 128)
|
|
665
|
+
return false;
|
|
666
|
+
let parsed;
|
|
667
|
+
try {
|
|
668
|
+
parsed = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(segment.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
674
|
+
return false;
|
|
675
|
+
const obj = parsed;
|
|
676
|
+
const keys = Object.keys(obj).sort();
|
|
677
|
+
if (keys.join(",") !== "alg,enc,version")
|
|
678
|
+
return false;
|
|
679
|
+
return obj.alg === ADYEN_JWE_HEADER.alg && obj.enc === ADYEN_JWE_HEADER.enc && obj.version === ADYEN_JWE_HEADER.version;
|
|
680
|
+
}
|
|
681
|
+
// src/json-path.js
|
|
682
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
683
|
+
function isPlainObject(v) {
|
|
684
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
685
|
+
}
|
|
686
|
+
var MAX_PATH_STEPS = 16;
|
|
687
|
+
function isJsonPath(path) {
|
|
688
|
+
return Array.isArray(path) && path.length <= MAX_PATH_STEPS && path.every((step) => typeof step === "string" ? step.length > 0 && !FORBIDDEN_KEYS.has(step) : Number.isSafeInteger(step) && step >= 0);
|
|
689
|
+
}
|
|
690
|
+
function jsonAt(root, path) {
|
|
691
|
+
let node = root;
|
|
692
|
+
for (const step of path) {
|
|
693
|
+
if (typeof step === "string") {
|
|
694
|
+
if (!isPlainObject(node) || FORBIDDEN_KEYS.has(step) || !Object.prototype.hasOwnProperty.call(node, step))
|
|
695
|
+
return { found: false };
|
|
696
|
+
}
|
|
697
|
+
else if (!Array.isArray(node) || !Number.isSafeInteger(step) || step < 0 || step >= node.length) {
|
|
698
|
+
return { found: false };
|
|
699
|
+
}
|
|
700
|
+
node = node[step];
|
|
701
|
+
}
|
|
702
|
+
return { found: true, value: node };
|
|
703
|
+
}
|
|
704
|
+
function roundTrips(parsed, body) {
|
|
705
|
+
try {
|
|
706
|
+
return JSON.stringify(parsed) === body;
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function sameSteps(a, b) {
|
|
713
|
+
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((step, i) => step === b[i]);
|
|
714
|
+
}
|
|
715
|
+
function pathsOverlap(a, b) {
|
|
716
|
+
const n = Math.min(a.length, b.length);
|
|
717
|
+
for (let i = 0; i < n; i += 1)
|
|
718
|
+
if (a[i] !== b[i])
|
|
719
|
+
return false;
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
function describePath(path) {
|
|
723
|
+
return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : `.${step}`).join("").replace(/^\./, "") : "(root)";
|
|
724
|
+
}
|
|
725
|
+
// src/substitution-primitives.js
|
|
726
|
+
var SubstitutionError = class extends Error {
|
|
727
|
+
constructor(message) {
|
|
728
|
+
super(message);
|
|
729
|
+
this.name = "SubstitutionError";
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
var SWAP_MEMBERS = /* @__PURE__ */ new Set(["path", "fields", "remove", "set"]);
|
|
733
|
+
function checkedSet(set, path, names, remove) {
|
|
734
|
+
if (!Array.isArray(set))
|
|
735
|
+
throw new SubstitutionError("substitutions.set is not a list");
|
|
736
|
+
const entries = [...set].map((raw) => {
|
|
737
|
+
const entry = isPlainObject(raw) ? Object.fromEntries(Object.entries(raw)) : null;
|
|
738
|
+
if (!entry || Object.keys(entry).sort().join(",") !== "path,value")
|
|
739
|
+
throw new SubstitutionError("a set entry is not a {path, value}");
|
|
740
|
+
const at = Array.isArray(entry.path) ? [...entry.path] : entry.path;
|
|
741
|
+
if (!isJsonPath(at) || at.length === 0 || typeof at[at.length - 1] !== "string")
|
|
742
|
+
throw new SubstitutionError("refusing set path");
|
|
743
|
+
const value = entry.value;
|
|
744
|
+
if (value !== null && !["string", "boolean"].includes(typeof value) && !(typeof value === "number" && Number.isFinite(value))) {
|
|
745
|
+
throw new SubstitutionError(`the set value at ${describePath(at)} is not a string, number, boolean or null`);
|
|
746
|
+
}
|
|
747
|
+
const key = at[at.length - 1];
|
|
748
|
+
if (sameSteps(at.slice(0, -1), path) && (names.includes(key) || remove.includes(key))) {
|
|
749
|
+
throw new SubstitutionError(`the set entry at ${describePath(at)} writes over an encrypted field or a removed key`);
|
|
750
|
+
}
|
|
751
|
+
return { path: at, value };
|
|
752
|
+
});
|
|
753
|
+
for (const [i, a] of entries.entries()) {
|
|
754
|
+
for (const b of entries.slice(i + 1)) {
|
|
755
|
+
if (pathsOverlap(a.path, b.path))
|
|
756
|
+
throw new SubstitutionError(`two set entries write at ${describePath(a.path)}`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return entries;
|
|
760
|
+
}
|
|
761
|
+
var ADYEN_JWE = /* @__PURE__ */ Object.freeze({ accepts: isAdyenJweCompact, label: "an Adyen JWE" });
|
|
762
|
+
function swapMerchantHostedFields(body, spec, ciphertext = ADYEN_JWE) {
|
|
763
|
+
if (!isPlainObject(spec))
|
|
764
|
+
throw new SubstitutionError("refusing a substitution that is not an object");
|
|
765
|
+
for (const member of Object.keys(spec)) {
|
|
766
|
+
if (!SWAP_MEMBERS.has(member))
|
|
767
|
+
throw new SubstitutionError(`unexpected substitution member ${member}`);
|
|
768
|
+
}
|
|
769
|
+
const path = Array.isArray(spec.path) ? [...spec.path] : spec.path;
|
|
770
|
+
const fields = isPlainObject(spec.fields) ? Object.fromEntries(Object.entries(spec.fields)) : null;
|
|
771
|
+
const remove = Array.isArray(spec.remove) ? [...spec.remove] : spec.remove ?? [];
|
|
772
|
+
if (!isJsonPath(path))
|
|
773
|
+
throw new SubstitutionError("refusing substitution path");
|
|
774
|
+
if (!fields || Object.keys(fields).length === 0)
|
|
775
|
+
throw new SubstitutionError("substitutions carry no fields");
|
|
776
|
+
const names = Object.keys(fields);
|
|
777
|
+
const values = names.map((name) => fields[name]);
|
|
778
|
+
for (const name of names) {
|
|
779
|
+
if (FORBIDDEN_KEYS.has(name))
|
|
780
|
+
throw new SubstitutionError(`refusing substitution field ${name}`);
|
|
781
|
+
if (!ciphertext.accepts(fields[name]))
|
|
782
|
+
throw new SubstitutionError(`substitution for ${name} is not ${ciphertext.label}`);
|
|
783
|
+
}
|
|
784
|
+
if (!Array.isArray(remove))
|
|
785
|
+
throw new SubstitutionError("substitutions.remove is not a list");
|
|
786
|
+
for (const key of remove) {
|
|
787
|
+
if (typeof key !== "string" || !key || FORBIDDEN_KEYS.has(key))
|
|
788
|
+
throw new SubstitutionError(`refusing removal of ${String(key)}`);
|
|
789
|
+
if (names.includes(key))
|
|
790
|
+
throw new SubstitutionError(`substitution field ${key} is also listed for removal`);
|
|
791
|
+
}
|
|
792
|
+
if (new Set(remove).size !== remove.length)
|
|
793
|
+
throw new SubstitutionError("substitutions.remove names a key twice");
|
|
794
|
+
const set = checkedSet(spec.set ?? [], path, names, remove);
|
|
795
|
+
if (typeof body !== "string")
|
|
796
|
+
throw new SubstitutionError("the paused body is not JSON");
|
|
797
|
+
let parsed;
|
|
798
|
+
try {
|
|
799
|
+
parsed = JSON.parse(body);
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
throw new SubstitutionError("the paused body is not JSON");
|
|
803
|
+
}
|
|
804
|
+
if (!isPlainObject(parsed))
|
|
805
|
+
throw new SubstitutionError("the paused body is not a JSON object");
|
|
806
|
+
const holder = jsonAt(parsed, path);
|
|
807
|
+
if (!holder.found || !isPlainObject(holder.value))
|
|
808
|
+
throw new SubstitutionError(`the paused body has no object at ${describePath(path)}`);
|
|
809
|
+
const swaps = /* @__PURE__ */ new Map();
|
|
810
|
+
for (const name of names) {
|
|
811
|
+
const dummy = Object.prototype.hasOwnProperty.call(holder.value, name) ? holder.value[name] : void 0;
|
|
812
|
+
if (typeof dummy !== "string")
|
|
813
|
+
throw new SubstitutionError(`the paused body has no string field ${name} at ${describePath(path)}`);
|
|
814
|
+
if (!ciphertext.accepts(dummy))
|
|
815
|
+
throw new SubstitutionError(`the paused field ${name} is not ${ciphertext.label}`);
|
|
816
|
+
if (swaps.has(dummy))
|
|
817
|
+
throw new SubstitutionError("two paused fields carry the same ciphertext");
|
|
818
|
+
swaps.set(dummy, fields[name]);
|
|
819
|
+
}
|
|
820
|
+
const dummies = [...swaps.keys()];
|
|
821
|
+
if (new Set(values).size !== values.length || values.some((value) => swaps.has(value))) {
|
|
822
|
+
throw new SubstitutionError("two ciphertexts in this substitution are the same");
|
|
823
|
+
}
|
|
824
|
+
const texts = [...dummies, ...values];
|
|
825
|
+
for (const a of texts)
|
|
826
|
+
for (const b of texts)
|
|
827
|
+
if (a !== b && b.includes(a))
|
|
828
|
+
throw new SubstitutionError("one ciphertext is part of another");
|
|
829
|
+
if ((remove.length > 0 || set.length > 0) && !roundTrips(parsed, body)) {
|
|
830
|
+
throw new SubstitutionError("the paused body does not round-trip through JSON, so it cannot be edited");
|
|
831
|
+
}
|
|
832
|
+
for (const entry of set) {
|
|
833
|
+
const parent = jsonAt(parsed, entry.path.slice(0, -1));
|
|
834
|
+
if (!parent.found || !isPlainObject(parent.value)) {
|
|
835
|
+
throw new SubstitutionError(`the paused body has no object at ${describePath(entry.path.slice(0, -1))} to write a value in`);
|
|
836
|
+
}
|
|
837
|
+
const key = entry.path[entry.path.length - 1];
|
|
838
|
+
const current = Object.prototype.hasOwnProperty.call(parent.value, key) ? parent.value[key] : void 0;
|
|
839
|
+
if (current !== null && typeof current === "object") {
|
|
840
|
+
throw new SubstitutionError(`refusing to write over the object at ${describePath(entry.path)}`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
const seen = /* @__PURE__ */ new Set();
|
|
844
|
+
const pattern = new RegExp(dummies.map((text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g");
|
|
845
|
+
const swapped = body.replace(pattern, (text2) => {
|
|
846
|
+
seen.add(text2);
|
|
847
|
+
return swaps.get(text2);
|
|
848
|
+
});
|
|
849
|
+
if (seen.size !== dummies.length)
|
|
850
|
+
throw new SubstitutionError("a paused field is not written verbatim in the body");
|
|
851
|
+
let out;
|
|
852
|
+
try {
|
|
853
|
+
out = JSON.parse(swapped);
|
|
854
|
+
}
|
|
855
|
+
catch {
|
|
856
|
+
throw new SubstitutionError("the substituted body is not JSON");
|
|
857
|
+
}
|
|
858
|
+
const written = jsonAt(out, path);
|
|
859
|
+
if (!written.found || !isPlainObject(written.value) || names.some((name) => written.value[name] !== fields[name]) || dummies.some((dummy) => swapped.includes(dummy))) {
|
|
860
|
+
throw new SubstitutionError(`the substitution did not land at ${describePath(path)}`);
|
|
861
|
+
}
|
|
862
|
+
if (remove.length === 0 && set.length === 0)
|
|
863
|
+
return swapped;
|
|
864
|
+
for (const key of remove)
|
|
865
|
+
if (Object.prototype.hasOwnProperty.call(written.value, key))
|
|
866
|
+
delete written.value[key];
|
|
867
|
+
for (const entry of set) {
|
|
868
|
+
const parent = jsonAt(out, entry.path.slice(0, -1));
|
|
869
|
+
if (!parent.found || !isPlainObject(parent.value))
|
|
870
|
+
throw new SubstitutionError(`the set entry at ${describePath(entry.path)} did not land`);
|
|
871
|
+
parent.value[entry.path[entry.path.length - 1]] = entry.value;
|
|
872
|
+
}
|
|
873
|
+
return JSON.stringify(out);
|
|
874
|
+
}
|
|
875
|
+
// src/twoc2p.js
|
|
876
|
+
var TWOC2P_PSP = "twoc2p";
|
|
877
|
+
var TWOC2P_CAPABILITY = "twoc2p_secure_pay";
|
|
878
|
+
var TWOC2P_OPERATION = "twoc2p.encrypted_card";
|
|
879
|
+
var TWOC2P_SCHEME = "twoc2p-secure-pay";
|
|
880
|
+
var TWOC2P_FIELD_AT = "payment.data";
|
|
881
|
+
var TWOC2P_FIELD = "securePayToken";
|
|
882
|
+
var TWOC2P_REVIEWED_BUILDS = /* @__PURE__ */ Object.freeze(["4.1", "4.2", "4.3", "4.4", "4.5"]);
|
|
883
|
+
var TWOC2P_REFUSAL_CODES = /* @__PURE__ */ Object.freeze([
|
|
884
|
+
"twoc2p_url_refused",
|
|
885
|
+
"twoc2p_build_unreviewed",
|
|
886
|
+
"twoc2p_body_unsupported",
|
|
887
|
+
"twoc2p_channel_unsupported",
|
|
888
|
+
"twoc2p_field_unreviewed",
|
|
889
|
+
"twoc2p_keeps_card",
|
|
890
|
+
"twoc2p_stored_card",
|
|
891
|
+
"twoc2p_card_in_clear",
|
|
892
|
+
"twoc2p_installments",
|
|
893
|
+
"twoc2p_currency_conversion",
|
|
894
|
+
"twoc2p_loyalty_redemption"
|
|
895
|
+
]);
|
|
896
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
897
|
+
var refuse2 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
898
|
+
var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
899
|
+
var CONTINUE = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
900
|
+
var PAYMENT_URL = /^https:\/\/(sandbox-)?pgwv4\.2c2p\.com\/payment\/(4\.[0-9]{1,2})\/Payment$/;
|
|
901
|
+
function twoC2pPaymentTarget(url) {
|
|
902
|
+
const match = typeof url === "string" ? PAYMENT_URL.exec(url) : null;
|
|
903
|
+
if (!match)
|
|
904
|
+
return null;
|
|
905
|
+
return Object.freeze({ environment: match[1] ? "sandbox" : "production", version: match[2], reviewed: TWOC2P_REVIEWED_BUILDS.includes(match[2]) });
|
|
906
|
+
}
|
|
907
|
+
function twoC2pPaymentCarriesCard(body) {
|
|
908
|
+
if (typeof body !== "string")
|
|
909
|
+
return true;
|
|
910
|
+
try {
|
|
911
|
+
return JSON.stringify(JSON.parse(body)).includes(`"${TWOC2P_FIELD}"`);
|
|
912
|
+
}
|
|
913
|
+
catch {
|
|
914
|
+
return true;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
var ROOT_KEYS = /* @__PURE__ */ Object.freeze(["paymentToken", "clientIP", "locale", "responseReturnUrl", "clientID", "payment"]);
|
|
918
|
+
var CODE_KEYS = /* @__PURE__ */ Object.freeze(["channelCode", "agentCode", "agentChannelCode"]);
|
|
919
|
+
var DATA_KEYS = /* @__PURE__ */ Object.freeze([
|
|
920
|
+
"name",
|
|
921
|
+
"isIppChosen",
|
|
922
|
+
"cardDetails",
|
|
923
|
+
"loyaltyPoints",
|
|
924
|
+
"fxRateID",
|
|
925
|
+
"email",
|
|
926
|
+
"mobileNo",
|
|
927
|
+
"mobileNoPrefix",
|
|
928
|
+
"customerNote",
|
|
929
|
+
"paymentExpiry",
|
|
930
|
+
"securePayToken",
|
|
931
|
+
"cardTokenize",
|
|
932
|
+
"accountTokenization",
|
|
933
|
+
"token",
|
|
934
|
+
"customerToken",
|
|
935
|
+
"accountNo",
|
|
936
|
+
"installmentPeriod",
|
|
937
|
+
"interestType",
|
|
938
|
+
"payLaterPeriod",
|
|
939
|
+
"billingAddress1",
|
|
940
|
+
"billingAddress2",
|
|
941
|
+
"billingCity",
|
|
942
|
+
"billingState",
|
|
943
|
+
"billingCountryCode",
|
|
944
|
+
"billingPostalCode",
|
|
945
|
+
"riskInfo"
|
|
946
|
+
]);
|
|
947
|
+
var RISK_KEYS = /* @__PURE__ */ Object.freeze(["customer_id", "shipping_method", "deviceSessionId", "customData"]);
|
|
948
|
+
var BODY_LIMIT = 16384;
|
|
949
|
+
var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
950
|
+
var CARD_LIKE = /(?:[0-9][ _.:/-]*){12,}/;
|
|
951
|
+
var PAYMENT_TOKEN = /^[A-Za-z0-9+/]{16,1000}={0,2}$/;
|
|
952
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
953
|
+
var IP = /^[0-9A-Fa-f.:]{2,45}$/;
|
|
954
|
+
var LOCALE = /^[A-Za-z]{2,3}(?:[-_][A-Za-z0-9]{2,8})?$/;
|
|
955
|
+
var AGENT = /^[A-Za-z0-9_]{1,32}$/;
|
|
956
|
+
var RISK_TOKEN = /^[\x21-\x7e]{1,512}$/;
|
|
957
|
+
var RETURN_URL = /^https:\/\/((?:sandbox-)?pgw-ui\.2c2p\.com)\/payment\/4\.[0-9]{1,2}(?:\/|\/index\.html)?#\/(?:info|mobile\/process)\/([A-Za-z0-9+/]{16,1000}={0,2})?$/;
|
|
958
|
+
var PAGE_HOSTS = /* @__PURE__ */ Object.freeze({ production: "pgw-ui.2c2p.com", sandbox: "sandbox-pgw-ui.2c2p.com" });
|
|
959
|
+
var returnUrlTaken = (value, paymentToken, environment) => {
|
|
960
|
+
if (value === void 0)
|
|
961
|
+
return true;
|
|
962
|
+
const match = typeof value === "string" ? RETURN_URL.exec(value) : null;
|
|
963
|
+
return !!match && (match[2] === void 0 || match[2] === paymentToken) && (environment === void 0 || match[1] === PAGE_HOSTS[environment]);
|
|
964
|
+
};
|
|
965
|
+
var inOrder = (value, order) => {
|
|
966
|
+
let at = -1;
|
|
967
|
+
for (const key of Object.keys(value)) {
|
|
968
|
+
const next = order.indexOf(key);
|
|
969
|
+
if (next <= at)
|
|
970
|
+
return false;
|
|
971
|
+
at = next;
|
|
972
|
+
}
|
|
973
|
+
return true;
|
|
974
|
+
};
|
|
975
|
+
var text = (value, limit) => typeof value === "string" && value.length <= limit && !CONTROL.test(value) && !CARD_LIKE.test(value);
|
|
976
|
+
var optionalText = (value, limit) => value === void 0 || text(value, limit);
|
|
977
|
+
function twoC2pBodyVerdict(body, keyBytes = 128, environment = void 0) {
|
|
978
|
+
if (!twoC2pPaymentCarriesCard(body))
|
|
979
|
+
return CONTINUE;
|
|
980
|
+
if (typeof body !== "string" || body.length > BODY_LIMIT)
|
|
981
|
+
return refuse2("twoc2p_body_unsupported");
|
|
982
|
+
let value;
|
|
983
|
+
try {
|
|
984
|
+
value = JSON.parse(body);
|
|
985
|
+
}
|
|
986
|
+
catch {
|
|
987
|
+
return refuse2("twoc2p_body_unsupported");
|
|
988
|
+
}
|
|
989
|
+
if (!record(value) || JSON.stringify(value) !== body || !inOrder(value, ROOT_KEYS))
|
|
990
|
+
return refuse2("twoc2p_body_unsupported");
|
|
991
|
+
if (typeof value.paymentToken !== "string" || !PAYMENT_TOKEN.test(value.paymentToken) || value.clientIP !== void 0 && !(typeof value.clientIP === "string" && IP.test(value.clientIP)) || value.locale !== void 0 && !(typeof value.locale === "string" && LOCALE.test(value.locale)) || !returnUrlTaken(value.responseReturnUrl, value.paymentToken, environment) || value.clientID !== void 0 && !(typeof value.clientID === "string" && UUID.test(value.clientID)))
|
|
992
|
+
return refuse2("twoc2p_body_unsupported");
|
|
993
|
+
const payment = value.payment;
|
|
994
|
+
if (!record(payment) || Object.keys(payment).join(",") !== "code,data")
|
|
995
|
+
return refuse2("twoc2p_body_unsupported");
|
|
996
|
+
const code = payment.code;
|
|
997
|
+
const data = payment.data;
|
|
998
|
+
if (!record(code) || !record(data))
|
|
999
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1000
|
+
if (Object.prototype.hasOwnProperty.call(code, "subChannelCode"))
|
|
1001
|
+
return refuse2("twoc2p_stored_card");
|
|
1002
|
+
if (code.channelCode === "IPP" || code.channelCode === "LIPP")
|
|
1003
|
+
return refuse2("twoc2p_installments");
|
|
1004
|
+
if (!inOrder(code, CODE_KEYS) || code.channelCode === void 0)
|
|
1005
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1006
|
+
if (code.channelCode !== "CC")
|
|
1007
|
+
return refuse2("twoc2p_channel_unsupported");
|
|
1008
|
+
if ([code.agentCode, code.agentChannelCode].some((part) => part !== void 0 && !(typeof part === "string" && AGENT.test(part))))
|
|
1009
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1010
|
+
return dataVerdict(data, keyBytes, body);
|
|
1011
|
+
}
|
|
1012
|
+
function dataVerdict(data, keyBytes, body) {
|
|
1013
|
+
if (data.cardTokenize !== void 0 || data.accountTokenization !== void 0)
|
|
1014
|
+
return refuse2("twoc2p_keeps_card");
|
|
1015
|
+
if (data.token !== void 0 || data.customerToken !== void 0)
|
|
1016
|
+
return refuse2("twoc2p_stored_card");
|
|
1017
|
+
if (data.accountNo !== void 0)
|
|
1018
|
+
return refuse2("twoc2p_card_in_clear");
|
|
1019
|
+
const details = data.cardDetails;
|
|
1020
|
+
if (record(details) && (details.cardNo !== void 0 || details.accountNo !== void 0))
|
|
1021
|
+
return refuse2("twoc2p_card_in_clear");
|
|
1022
|
+
if (record(details) && Object.keys(details).some((key) => key !== "email"))
|
|
1023
|
+
return refuse2("twoc2p_stored_card");
|
|
1024
|
+
if (data.isIppChosen === true || data.installmentPeriod !== void 0 || data.interestType !== void 0 || data.payLaterPeriod !== void 0) {
|
|
1025
|
+
return refuse2("twoc2p_installments");
|
|
1026
|
+
}
|
|
1027
|
+
if (data.fxRateID !== void 0)
|
|
1028
|
+
return refuse2("twoc2p_currency_conversion");
|
|
1029
|
+
if (data.loyaltyPoints !== void 0 && !(Array.isArray(data.loyaltyPoints) && data.loyaltyPoints.length === 0)) {
|
|
1030
|
+
return refuse2("twoc2p_loyalty_redemption");
|
|
1031
|
+
}
|
|
1032
|
+
if (!inOrder(data, DATA_KEYS) || data.paymentExpiry !== void 0)
|
|
1033
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1034
|
+
if (typeof data.securePayToken !== "string" || !isTwoC2pPageField(data.securePayToken, keyBytes))
|
|
1035
|
+
return refuse2("twoc2p_field_unreviewed");
|
|
1036
|
+
if (body.split(data.securePayToken).length !== 2)
|
|
1037
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1038
|
+
if (data.isIppChosen !== void 0 && data.isIppChosen !== false)
|
|
1039
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1040
|
+
if (details !== void 0 && !(record(details) && (details.email === void 0 || text(details.email, 254))))
|
|
1041
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1042
|
+
if (!optionalText(data.name, 128) || !optionalText(data.email, 254) || !optionalText(data.customerNote, 255) || !(data.mobileNo === void 0 || typeof data.mobileNo === "string" && /^[0-9]{4,11}$/.test(data.mobileNo)) || !(data.mobileNoPrefix === void 0 || typeof data.mobileNoPrefix === "string" && /^\+?[0-9]{1,4}$/.test(data.mobileNoPrefix))) {
|
|
1043
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1044
|
+
}
|
|
1045
|
+
for (const key of ["billingAddress1", "billingAddress2", "billingCity", "billingState", "billingPostalCode"]) {
|
|
1046
|
+
if (!optionalText(data[key], 128))
|
|
1047
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1048
|
+
}
|
|
1049
|
+
if (data.billingCountryCode !== void 0 && !(typeof data.billingCountryCode === "string" && /^[A-Za-z]{2}$/.test(data.billingCountryCode))) {
|
|
1050
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1051
|
+
}
|
|
1052
|
+
const risk = data.riskInfo;
|
|
1053
|
+
if (risk !== void 0 && risk !== null && !(record(risk) && Object.keys(risk).join(",") === RISK_KEYS.join(",") && risk.customer_id === "" && risk.shipping_method === "" && typeof risk.deviceSessionId === "string" && RISK_TOKEN.test(risk.deviceSessionId) && record(risk.customData) && Object.keys(risk.customData).length === 0)) {
|
|
1054
|
+
return refuse2("twoc2p_body_unsupported");
|
|
1055
|
+
}
|
|
1056
|
+
return CARD;
|
|
1057
|
+
}
|
|
1058
|
+
function twoC2pRequestRefusal(request, environment) {
|
|
1059
|
+
if (!record(request) || typeof request.method !== "string" || request.method.toUpperCase() !== "POST")
|
|
1060
|
+
return "twoc2p_url_refused";
|
|
1061
|
+
const target = twoC2pPaymentTarget(request.url);
|
|
1062
|
+
if (!target || environment !== void 0 && target.environment !== environment)
|
|
1063
|
+
return "twoc2p_url_refused";
|
|
1064
|
+
if (!target.reviewed)
|
|
1065
|
+
return "twoc2p_build_unreviewed";
|
|
1066
|
+
const keyBytes = twoC2pKeyBytes(target.environment);
|
|
1067
|
+
if (keyBytes === null)
|
|
1068
|
+
return "twoc2p_field_unreviewed";
|
|
1069
|
+
const verdict = twoC2pBodyVerdict(request.body, keyBytes, target.environment);
|
|
1070
|
+
return verdict.verdict === "card" ? null : verdict.verdict === "refuse" ? verdict.code : "twoc2p_body_unsupported";
|
|
1071
|
+
}
|
|
1072
|
+
var REFUSAL_MESSAGES = /* @__PURE__ */ Object.freeze({
|
|
1073
|
+
twoc2p_url_refused: "Refusing to pay: this request does not go to 2C2P's payment API exactly as 2C2P's payment page sends it. Nothing was sent.",
|
|
1074
|
+
twoc2p_build_unreviewed: "Refusing to pay: this 2C2P payment page is a version Agentcard has not reviewed. Nothing was sent.",
|
|
1075
|
+
twoc2p_body_unsupported: "Refusing to pay: this is not the card payment 2C2P's payment page sends. Nothing was sent.",
|
|
1076
|
+
twoc2p_channel_unsupported: "Refusing to pay: this 2C2P payment is not a card payment Agentcard can make. Nothing was sent.",
|
|
1077
|
+
twoc2p_field_unreviewed: "Refusing to pay: 2C2P's page encrypted the card in a way Agentcard has not reviewed. Nothing was sent.",
|
|
1078
|
+
twoc2p_keeps_card: "Refusing to pay: this 2C2P payment would save the card for later payments. Nothing was sent.",
|
|
1079
|
+
twoc2p_stored_card: "Refusing to pay: this 2C2P payment uses a card 2C2P saved, not the card you approve. Nothing was sent.",
|
|
1080
|
+
twoc2p_card_in_clear: "Refusing to pay: this 2C2P payment carries a card number outside 2C2P's encrypted field. Nothing was sent.",
|
|
1081
|
+
twoc2p_installments: "Refusing to pay: this 2C2P payment is an instalment plan. Nothing was sent.",
|
|
1082
|
+
twoc2p_currency_conversion: "Refusing to pay: this 2C2P payment takes a currency conversion offer, which changes what is charged. Nothing was sent.",
|
|
1083
|
+
twoc2p_loyalty_redemption: "Refusing to pay: this 2C2P payment redeems loyalty points. Nothing was sent."
|
|
1084
|
+
});
|
|
1085
|
+
function twoC2pRefusalMessage(code) {
|
|
1086
|
+
return Object.prototype.hasOwnProperty.call(REFUSAL_MESSAGES, code) ? REFUSAL_MESSAGES[code] : REFUSAL_MESSAGES.twoc2p_body_unsupported;
|
|
1087
|
+
}
|
|
1088
|
+
async function encryptTwoC2pCard(card, environment, facilities = {}) {
|
|
1089
|
+
const key = twoC2pKey(environment);
|
|
1090
|
+
if (!key)
|
|
1091
|
+
throw new TwoC2pSecurePayError("twoc2p_key_unavailable");
|
|
1092
|
+
return buildTwoC2pSecurePayToken(card, key, facilities);
|
|
1093
|
+
}
|
|
1094
|
+
function twoC2pSubstitutionOf(value, environment) {
|
|
1095
|
+
const key = twoC2pKey(environment);
|
|
1096
|
+
if (!key || !record(value) || Object.keys(value).join(",") !== TWOC2P_FIELD)
|
|
1097
|
+
return null;
|
|
1098
|
+
return isTwoC2pDeviceField(value[TWOC2P_FIELD], key) ? value[TWOC2P_FIELD] : null;
|
|
1099
|
+
}
|
|
1100
|
+
function pausedField(url, body) {
|
|
1101
|
+
const target = twoC2pPaymentTarget(url);
|
|
1102
|
+
if (!target || !target.reviewed || twoC2pRequestRefusal({ method: "POST", url, body }) !== null)
|
|
1103
|
+
return null;
|
|
1104
|
+
const field = JSON.parse(body).payment.data.securePayToken;
|
|
1105
|
+
return { target, field, copies: body.split(field).length - 1 };
|
|
1106
|
+
}
|
|
1107
|
+
function substituteTwoC2p(url, body, field) {
|
|
1108
|
+
const paused = pausedField(url, body);
|
|
1109
|
+
if (!paused)
|
|
1110
|
+
throw new SubstitutionError("the paused request is not the card payment 2C2P's page sends");
|
|
1111
|
+
if (paused.copies !== 1)
|
|
1112
|
+
throw new SubstitutionError("the paused card field is written more than once");
|
|
1113
|
+
const device = twoC2pSubstitutionOf({ [TWOC2P_FIELD]: field }, paused.target.environment);
|
|
1114
|
+
if (device === null || device === paused.field)
|
|
1115
|
+
throw new SubstitutionError("the substitution is not a new 2C2P card field in the shape the device writes");
|
|
1116
|
+
const keyBytes = twoC2pKeyBytes(paused.target.environment);
|
|
1117
|
+
const kind = Object.freeze({ accepts: (value) => isTwoC2pPageField(value, keyBytes), label: "a 2C2P card field in the library's format" });
|
|
1118
|
+
const out = swapMerchantHostedFields(body, { path: ["payment", "data"], fields: { [TWOC2P_FIELD]: device } }, kind);
|
|
1119
|
+
if (twoC2pRequestRefusal({ method: "POST", url, body: out }) !== null || JSON.parse(out).payment.data.securePayToken !== device) {
|
|
1120
|
+
throw new SubstitutionError("the substitution did not land in the page's card payment");
|
|
1121
|
+
}
|
|
1122
|
+
return out;
|
|
1123
|
+
}
|
|
1124
|
+
function twoC2pSubstitutionsRefusal(template, substitution) {
|
|
1125
|
+
const refuseWith = (message) => ({ code: "invalid_substitutions", message });
|
|
1126
|
+
const paused = record(template) ? pausedField(template.url, template.body) : null;
|
|
1127
|
+
if (!paused)
|
|
1128
|
+
return refuseWith("This authorization is not the card payment 2C2P's page sends.");
|
|
1129
|
+
const device = twoC2pSubstitutionOf(substitution, paused.target.environment);
|
|
1130
|
+
if (device === null)
|
|
1131
|
+
return refuseWith("substitutions must be exactly securePayToken, a 2C2P card field in the shape the device writes.");
|
|
1132
|
+
if (device === paused.field)
|
|
1133
|
+
return refuseWith("substitutions must be a new card field, not the one already in the paused request.");
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
function twoC2pKeyUsable(authorization) {
|
|
1137
|
+
if (!record(authorization) || !record(authorization.cse))
|
|
1138
|
+
return false;
|
|
1139
|
+
const cse = authorization.cse;
|
|
1140
|
+
if (cse.psp !== TWOC2P_PSP || cse.scheme !== TWOC2P_SCHEME)
|
|
1141
|
+
return false;
|
|
1142
|
+
if (cse.public_key != null || cse.client_key != null || Object.prototype.hasOwnProperty.call(cse, "merchant_hosted"))
|
|
1143
|
+
return false;
|
|
1144
|
+
const target = twoC2pPaymentTarget(authorization.url);
|
|
1145
|
+
if (!target || !target.reviewed || cse.environment !== target.environment || authorization.sandbox !== (target.environment === "sandbox"))
|
|
1146
|
+
return false;
|
|
1147
|
+
return twoC2pKeyBytes(target.environment) !== null;
|
|
1148
|
+
}
|
|
1149
|
+
export { TWOC2P_CAPABILITY, TWOC2P_FIELD, TWOC2P_FIELD_AT, TWOC2P_OPERATION, TWOC2P_PSP, TWOC2P_REFUSAL_CODES, TWOC2P_REVIEWED_BUILDS, TWOC2P_SCHEME, encryptTwoC2pCard, substituteTwoC2p, twoC2pBodyVerdict, twoC2pKeyUsable, twoC2pPaymentCarriesCard, twoC2pPaymentTarget, twoC2pRefusalMessage, twoC2pRequestRefusal, twoC2pSubstitutionOf, twoC2pSubstitutionsRefusal };
|