@agent-cards/sdk 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/PREFLIGHT.md +32 -0
  3. package/README.md +1177 -23
  4. package/dist/aci.d.ts +173 -0
  5. package/dist/aci.generated.d.ts +87 -0
  6. package/dist/aci.generated.js +306 -0
  7. package/dist/aci.js +371 -0
  8. package/dist/adyen-merchant-hosted.generated.d.ts +3 -3
  9. package/dist/adyen-merchant-hosted.generated.js +302 -56
  10. package/dist/agentcard.d.ts +9 -2
  11. package/dist/agentcard.js +4 -2
  12. package/dist/amadeus-checkout.generated.d.ts +57 -0
  13. package/dist/amadeus-checkout.generated.js +549 -0
  14. package/dist/amadeus.d.ts +41 -0
  15. package/dist/amadeus.js +81 -0
  16. package/dist/builtin-registry.generated.js +1 -1
  17. package/dist/card-endpoint-facts.d.ts +96 -0
  18. package/dist/card-endpoint-facts.js +275 -0
  19. package/dist/card-endpoint-page.d.ts +54 -0
  20. package/dist/card-endpoint-page.js +69 -0
  21. package/dist/card-endpoint.d.ts +298 -0
  22. package/dist/card-endpoint.js +438 -0
  23. package/dist/card-fields.generated.d.ts +5 -3
  24. package/dist/card-fields.generated.js +284 -7
  25. package/dist/cdp.d.ts +5 -1
  26. package/dist/cdp.js +1155 -141
  27. package/dist/client.d.ts +456 -20
  28. package/dist/client.js +1072 -104
  29. package/dist/creditguard.generated.d.ts +79 -0
  30. package/dist/creditguard.generated.js +662 -0
  31. package/dist/cse-body.js +11 -1
  32. package/dist/cybersource-flex.d.ts +30 -0
  33. package/dist/cybersource-flex.generated.d.ts +27 -1
  34. package/dist/cybersource-flex.generated.js +42 -2
  35. package/dist/cybersource-flex.js +46 -0
  36. package/dist/datatrans.d.ts +96 -0
  37. package/dist/datatrans.generated.d.ts +64 -0
  38. package/dist/datatrans.generated.js +426 -0
  39. package/dist/datatrans.js +178 -0
  40. package/dist/ekashu.d.ts +35 -0
  41. package/dist/ekashu.js +55 -0
  42. package/dist/elavon-converge.d.ts +37 -0
  43. package/dist/elavon-converge.generated.d.ts +49 -0
  44. package/dist/elavon-converge.generated.js +439 -0
  45. package/dist/elavon-converge.js +84 -0
  46. package/dist/eprotect.d.ts +152 -0
  47. package/dist/eprotect.generated.d.ts +77 -0
  48. package/dist/eprotect.generated.js +498 -0
  49. package/dist/eprotect.js +222 -0
  50. package/dist/hosted-form.d.ts +3 -2
  51. package/dist/hosted-form.js +3 -2
  52. package/dist/index.d.ts +17 -3
  53. package/dist/index.js +6 -1
  54. package/dist/juspay-gate.d.ts +82 -0
  55. package/dist/juspay-gate.js +126 -0
  56. package/dist/juspay.generated.d.ts +89 -0
  57. package/dist/juspay.generated.js +705 -0
  58. package/dist/lifecycle.d.ts +60 -6
  59. package/dist/lifecycle.js +122 -8
  60. package/dist/merchant-card-endpoints.generated.d.ts +122 -0
  61. package/dist/merchant-card-endpoints.generated.js +4261 -0
  62. package/dist/merchant-hosted.d.ts +4 -1
  63. package/dist/merchant-hosted.js +5 -2
  64. package/dist/merchant-session-probe.d.ts +48 -0
  65. package/dist/merchant-session-probe.js +53 -0
  66. package/dist/merchant-session.generated.d.ts +69 -0
  67. package/dist/merchant-session.generated.js +919 -0
  68. package/dist/payrails.d.ts +133 -0
  69. package/dist/payrails.generated.d.ts +73 -0
  70. package/dist/payrails.generated.js +596 -0
  71. package/dist/payrails.js +305 -0
  72. package/dist/pelecard.d.ts +32 -0
  73. package/dist/pelecard.generated.d.ts +32 -0
  74. package/dist/pelecard.generated.js +283 -0
  75. package/dist/pelecard.js +42 -0
  76. package/dist/pre-claim.d.ts +81 -2
  77. package/dist/pre-claim.js +1023 -5
  78. package/dist/preflight-catalog.json +2267 -288
  79. package/dist/preflight-schemas.json +183 -6
  80. package/dist/preflight.generated.d.ts +1 -1
  81. package/dist/preflight.generated.js +243 -4
  82. package/dist/preparation.d.ts +12 -3
  83. package/dist/preparation.js +63 -12
  84. package/dist/prepared-processor.d.ts +84 -5
  85. package/dist/prepared-processor.js +147 -6
  86. package/dist/primer.d.ts +53 -0
  87. package/dist/primer.generated.d.ts +38 -0
  88. package/dist/primer.generated.js +624 -0
  89. package/dist/primer.js +82 -0
  90. package/dist/registry.d.ts +100 -8
  91. package/dist/registry.js +85 -11
  92. package/dist/shiji-sts.d.ts +144 -0
  93. package/dist/shiji-sts.generated.d.ts +72 -0
  94. package/dist/shiji-sts.generated.js +913 -0
  95. package/dist/shiji-sts.js +222 -0
  96. package/dist/stripe-checkout.generated.d.ts +1 -1
  97. package/dist/stripe-checkout.generated.js +764 -9
  98. package/dist/stripe-setup.d.ts +16 -0
  99. package/dist/stripe-setup.js +61 -0
  100. package/dist/substitutions.generated.js +187 -29
  101. package/dist/verifone.d.ts +68 -0
  102. package/dist/verifone.generated.d.ts +51 -0
  103. package/dist/verifone.generated.js +1413 -0
  104. package/dist/verifone.js +119 -0
  105. package/dist/voice-types.d.ts +27 -0
  106. package/dist/voice.d.ts +6 -1
  107. package/dist/voice.js +6 -1
  108. package/dist/wspay.d.ts +41 -0
  109. package/dist/wspay.generated.d.ts +41 -0
  110. package/dist/wspay.generated.js +370 -0
  111. package/dist/wspay.js +65 -0
  112. package/examples/preflight/kernel-native/inventory.json +1 -1
  113. package/package.json +3 -3
@@ -0,0 +1,705 @@
1
+ // @ts-nocheck
2
+ // Generated from @agent-cards/payment-core. Do not edit.
3
+ // artifact-sha256: c771bc4608bb5f6fd8db2cd15a071bec7cfc12c8d753d1e38403790599ddf300
4
+ // src/juspay.js
5
+ var ENDPOINTS = Object.freeze({
6
+ production: Object.freeze({ host: "api.juspay.in", txns: "https://api.juspay.in/txns", tokenize: "https://api.juspay.in/card/tokenize" }),
7
+ sandbox: Object.freeze({ host: "sandbox.juspay.in", txns: "https://sandbox.juspay.in/txns", tokenize: "https://sandbox.juspay.in/card/tokenize" })
8
+ });
9
+ var JUSPAY_API_HOSTS = Object.freeze([ENDPOINTS.production.host, ENDPOINTS.sandbox.host]);
10
+ var JUSPAY_SIBLING_HOSTS = Object.freeze(["api.juspay.io", "api-ns1.juspay.in"]);
11
+ var JUSPAY_SYNTHETIC_TOKEN = /^ctkn_agentcard[0-9a-f]{32}$/;
12
+ var REAL_TOKEN = /^ctkn_[A-Za-z0-9_-]{1,64}$/;
13
+ var TOKEN_TEXT = /ctkn_/i;
14
+ var TOKEN_TAIL = 8;
15
+ var JUSPAY_APPROVED_STATUSES = Object.freeze(["PENDING_VBV", "AUTHORIZING", "NEW", "CHARGED"]);
16
+ var JUSPAY_REFUSED_STATUSES = Object.freeze(["AUTHORIZATION_FAILED", "JUSPAY_DECLINED", "AUTHENTICATION_FAILED"]);
17
+ var JUSPAY_TOKENIZE_NETWORK = "juspay_tokenize_network";
18
+ var TOKENIZE_REFUSAL = /^juspay_tokenize_[A-Za-z0-9_]{1,48}$/;
19
+ var MAX_BODY = 8192;
20
+ var MAX_RESPONSE = 16384;
21
+ var KEY = /^[a-z][a-z0-9_]{0,63}$/;
22
+ var ORDER_ID = /^[A-Za-z0-9_-]{1,64}$/;
23
+ var MERCHANT_ID = /^[A-Za-z0-9._-]{1,64}$/;
24
+ var ROUTING_ID = /^[A-Za-z0-9._:-]{1,128}$/;
25
+ var ERROR_CODE = /^[A-Za-z][A-Za-z0-9_]{0,39}$/;
26
+ var STATUS = /^[A-Z][A-Z_]{0,63}$/;
27
+ var CONTROL = /[\u0000-\u001f\u007f]/;
28
+ var FORM_TYPE = /^application\/x-www-form-urlencoded(?:;\s*charset=utf-8)?$/i;
29
+ var CARD_KEY = /card|cvv|cvc|cvn|secur(?:ity)?_?code|expir|exp_?(?:month|year|date)|ccnum|(?:^|[^a-z])pan(?:[^a-z]|$)/i;
30
+ var PAY_START_V1 = /^\/pay\/start\/[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)*$/;
31
+ var PAY_START_V2 = /^\/v2\/pay\/start\/([A-Za-z0-9._-]{1,64})\/[A-Za-z0-9_.-]{1,128}$/;
32
+ var PAY_START_QUERY = Object.freeze({
33
+ cardIssuerBankName: /^(?:[A-Za-z][A-Za-z .,'()-]{0,79})?$/,
34
+ cardType: /^[A-Z_]{0,20}$/,
35
+ paymentMethod: /^[A-Z_]{0,32}$/,
36
+ paymentMethodType: /^[A-Z_]{0,20}$/
37
+ });
38
+ var TXNS_LEGACY = Object.freeze([
39
+ "order_id",
40
+ "merchant_id",
41
+ "payment_method_type",
42
+ "card_token",
43
+ "redirect_after_payment",
44
+ "format",
45
+ "card_security_code",
46
+ "save_to_locker"
47
+ ]);
48
+ var TXNS_WEB = Object.freeze([...TXNS_LEGACY, "tokenize", "card_nickname"]);
49
+ var TOKENIZE_WEB = Object.freeze([
50
+ "card_number",
51
+ "card_exp_year",
52
+ "card_exp_month",
53
+ "card_security_code",
54
+ "merchant_id",
55
+ "name_on_card",
56
+ "card_nickname"
57
+ ]);
58
+ var TOKENIZE_LEGACY = Object.freeze(TOKENIZE_WEB.filter((key) => key !== "card_nickname"));
59
+ var SAVED_TOKENIZE = Object.freeze(["stored_card_token", "merchant_id", "card_security_code"]);
60
+ var TXNS_CARD_KEYS = Object.freeze([
61
+ "card_number",
62
+ "card_token",
63
+ "card_security_code",
64
+ "card_exp_month",
65
+ "card_exp_year",
66
+ "name_on_card",
67
+ "token"
68
+ ]);
69
+ var CARD_FIELD_TEXT = /card_?(?:number|token|security_?code|exp)|security_?code|cvv|cvc|ctkn_/i;
70
+ var CARD_NAME = /cvn|ccnum|expir|exp_?(?:month|year|date)|(?:^|[^a-z])pan(?:[^a-z]|$)/i;
71
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
72
+ var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
73
+ var sameSet = (keys, allowed) => keys.length === allowed.length && allowed.every((key) => keys.includes(key));
74
+ function fieldNames(text) {
75
+ const names = [];
76
+ for (const pair of text.replace(/^\?/, "").split("&")) {
77
+ const eq = pair.indexOf("=");
78
+ if (eq > 0)
79
+ names.push(pair.slice(0, eq));
80
+ }
81
+ for (const match of text.matchAll(/"((?:[^"\\]|\\.){1,128})"\s*:/g))
82
+ names.push(match[1]);
83
+ return names;
84
+ }
85
+ function namesCardField(text) {
86
+ return fieldNames(text).some((name) => {
87
+ let decoded = name;
88
+ try {
89
+ decoded = decodeURIComponent(name.replace(/\+/g, " "));
90
+ }
91
+ catch {
92
+ }
93
+ return CARD_NAME.test(name) || CARD_NAME.test(decoded);
94
+ });
95
+ }
96
+ function cardBearing(text) {
97
+ if (!text)
98
+ return false;
99
+ let decoded = text;
100
+ try {
101
+ decoded = decodeURIComponent(text.replace(/\+/g, " "));
102
+ }
103
+ catch {
104
+ }
105
+ return CARD_FIELD_TEXT.test(decoded) || namesCardField(text) || namesCardField(decoded);
106
+ }
107
+ function decode(raw) {
108
+ if (!/^(?:[A-Za-z0-9\-_.!~*'()+]|%[0-9A-Fa-f]{2})*$/.test(raw))
109
+ return null;
110
+ let value;
111
+ try {
112
+ value = decodeURIComponent(raw.replace(/\+/g, " "));
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ return CONTROL.test(value) ? null : value;
118
+ }
119
+ function parseJuspayForm(body) {
120
+ if (typeof body !== "string" || body.length === 0 || body.length > MAX_BODY)
121
+ return null;
122
+ const entries = [];
123
+ const seen = /* @__PURE__ */ new Set();
124
+ let start = 0;
125
+ for (;;) {
126
+ const amp = body.indexOf("&", start);
127
+ const end = amp === -1 ? body.length : amp;
128
+ const eq = body.indexOf("=", start);
129
+ if (eq === -1 || eq >= end || eq === start)
130
+ return null;
131
+ const key = body.slice(start, eq);
132
+ const value = decode(body.slice(eq + 1, end));
133
+ if (!KEY.test(key) || value === null || seen.has(key))
134
+ return null;
135
+ seen.add(key);
136
+ entries.push({ key, value, valueStart: eq + 1, valueEnd: end });
137
+ if (amp === -1)
138
+ return entries;
139
+ start = amp + 1;
140
+ }
141
+ }
142
+ function formMap(body) {
143
+ const entries = parseJuspayForm(body);
144
+ return entries ? new Map(entries.map((entry) => [entry.key, entry.value])) : null;
145
+ }
146
+ function luhn(digits) {
147
+ let sum = 0;
148
+ for (let i = 0; i < digits.length; i++) {
149
+ let digit = digits.charCodeAt(digits.length - 1 - i) - 48;
150
+ if (i % 2 === 1) {
151
+ digit *= 2;
152
+ if (digit > 9)
153
+ digit -= 9;
154
+ }
155
+ sum += digit;
156
+ }
157
+ return sum % 10 === 0;
158
+ }
159
+ function cardNumberIn(text) {
160
+ for (const match of String(text).matchAll(/\d(?:[ -]?\d)*/g)) {
161
+ const digits = match[0].replace(/[ -]/g, "");
162
+ if (digits.length >= 12 && digits.length <= 19 && luhn(digits))
163
+ return true;
164
+ }
165
+ return false;
166
+ }
167
+ function header(headers, name) {
168
+ if (headers === void 0 || headers === null)
169
+ return void 0;
170
+ if (!record(headers))
171
+ return null;
172
+ let found;
173
+ for (const [key, value] of Object.entries(headers)) {
174
+ if (key.toLowerCase() !== name)
175
+ continue;
176
+ if (found !== void 0 || typeof value !== "string")
177
+ return null;
178
+ found = value;
179
+ }
180
+ return found;
181
+ }
182
+ function juspayEnvironment(url) {
183
+ if (url === ENDPOINTS.production.txns)
184
+ return "production";
185
+ if (url === ENDPOINTS.sandbox.txns)
186
+ return "sandbox";
187
+ return void 0;
188
+ }
189
+ function isJuspaySyntheticToken(value) {
190
+ return typeof value === "string" && JUSPAY_SYNTHETIC_TOKEN.test(value);
191
+ }
192
+ function juspaySyntheticToken(bytes) {
193
+ if (Object.prototype.toString.call(bytes) !== "[object Uint8Array]" || bytes.length !== 16) {
194
+ throw new Error("A synthetic Juspay token needs 16 random bytes.");
195
+ }
196
+ return "ctkn_agentcard" + Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
197
+ }
198
+ function juspayTokenizeAnswer(token) {
199
+ if (!isJuspaySyntheticToken(token))
200
+ throw new Error("Only a synthetic Juspay token may answer the page.");
201
+ return { status: 200, headers: { "content-type": "application/json" }, body: JSON.stringify({ token }) };
202
+ }
203
+ function juspayTxnsTemplateRefusal(url, headers, body) {
204
+ if (!juspayEnvironment(url))
205
+ return "juspay_endpoint";
206
+ const contentType = header(headers, "content-type");
207
+ if (typeof contentType !== "string" || !FORM_TYPE.test(contentType.trim()))
208
+ return "content_type";
209
+ const values = formMap(body);
210
+ if (!values)
211
+ return "unreadable_body";
212
+ const keys = [...values.keys()];
213
+ if (keys.some((key) => !TXNS_WEB.includes(key)))
214
+ return "unsupported_field";
215
+ if (!sameSet(keys, TXNS_WEB) && !sameSet(keys, TXNS_LEGACY))
216
+ return "missing_field";
217
+ if (!ORDER_ID.test(values.get("order_id")))
218
+ return "order_id";
219
+ if (!MERCHANT_ID.test(values.get("merchant_id")))
220
+ return "merchant_id";
221
+ const merchantHeader = header(headers, "x-merchantid");
222
+ if (merchantHeader === null || merchantHeader !== void 0 && merchantHeader !== values.get("merchant_id"))
223
+ return "merchant_mismatch";
224
+ const routing = header(headers, "x-routing-id");
225
+ if (routing === null || routing !== void 0 && !ROUTING_ID.test(routing))
226
+ return "routing_id";
227
+ if (values.get("payment_method_type") !== "CARD")
228
+ return "payment_method_type";
229
+ if (!isJuspaySyntheticToken(values.get("card_token")))
230
+ return "card_token";
231
+ if (values.get("format") !== "json")
232
+ return "format";
233
+ if (values.get("redirect_after_payment") !== "true")
234
+ return "redirect_after_payment";
235
+ if (values.get("save_to_locker") !== "false")
236
+ return "save_to_locker";
237
+ if (values.has("tokenize") && !["false", "null"].includes(values.get("tokenize")))
238
+ return "tokenize";
239
+ if (!/^\d{3,4}$/.test(values.get("card_security_code")))
240
+ return "card_security_code";
241
+ if (values.has("card_nickname")) {
242
+ const nickname = values.get("card_nickname");
243
+ if (nickname.length > 32 || /\d{6}/.test(nickname))
244
+ return "card_nickname";
245
+ }
246
+ return null;
247
+ }
248
+ function assertJuspayTxnsTemplate(url, headers, body) {
249
+ const refusal = juspayTxnsTemplateRefusal(url, headers, body);
250
+ if (refusal)
251
+ throw new Error(`Refusing to pay: this Juspay card request is not the reviewed payment (${refusal}).`);
252
+ const environment = juspayEnvironment(url);
253
+ const values = formMap(body);
254
+ const routing = header(headers, "x-routing-id");
255
+ return Object.freeze({
256
+ environment,
257
+ host: ENDPOINTS[environment].host,
258
+ txnsUrl: ENDPOINTS[environment].txns,
259
+ tokenizeUrl: ENDPOINTS[environment].tokenize,
260
+ orderId: values.get("order_id"),
261
+ merchantId: values.get("merchant_id"),
262
+ routingId: routing ?? null,
263
+ contentType: header(headers, "content-type").trim()
264
+ });
265
+ }
266
+ var decide = (decision, reason) => ({ decision, reason });
267
+ function classifyTokenize(values, headers, host) {
268
+ const keys = [...values.keys()];
269
+ if (values.has("stored_card_token")) {
270
+ const saved = keys.every((key) => SAVED_TOKENIZE.includes(key)) && !cardNumberIn(values.get("stored_card_token")) && (!values.has("card_security_code") || /^\d{3,4}$/.test(values.get("card_security_code")));
271
+ return saved ? decide("continue", "saved_card_tokenize") : decide("abort", "card_on_saved_card_tokenize");
272
+ }
273
+ if (!sameSet(keys, TOKENIZE_WEB) && !sameSet(keys, TOKENIZE_LEGACY))
274
+ return decide("abort", "unsupported_tokenize");
275
+ const merchantId = values.get("merchant_id");
276
+ if (!MERCHANT_ID.test(merchantId))
277
+ return decide("abort", "merchant_id");
278
+ const merchantHeader = header(headers, "x-merchantid");
279
+ if (merchantHeader === null || merchantHeader !== void 0 && merchantHeader !== merchantId)
280
+ return decide("abort", "merchant_mismatch");
281
+ return { decision: "answer_tokenize", reason: "placeholder_tokenize", host, merchantId };
282
+ }
283
+ function issuedFor(issued, token) {
284
+ if (!issued || typeof issued.get !== "function")
285
+ return null;
286
+ const entry = issued.get(token);
287
+ return record(entry) ? entry : null;
288
+ }
289
+ function issuedEntries(issued) {
290
+ if (!issued || typeof issued.values !== "function")
291
+ return [];
292
+ try {
293
+ return Array.from(issued.values()).filter(record);
294
+ }
295
+ catch {
296
+ return [];
297
+ }
298
+ }
299
+ function payStartCarriesCard(text) {
300
+ if (!text)
301
+ return false;
302
+ let decoded = text;
303
+ try {
304
+ decoded = decodeURIComponent(text.replace(/\+/g, " "));
305
+ }
306
+ catch {
307
+ }
308
+ return cardBearing(text) || cardNumberIn(text) || cardNumberIn(decoded);
309
+ }
310
+ function payStartNamesCard(body) {
311
+ if (!body)
312
+ return false;
313
+ return body.split("&").some((pair) => {
314
+ const eq = pair.indexOf("=");
315
+ const name = eq === -1 ? pair : pair.slice(0, eq);
316
+ let decoded = name;
317
+ try {
318
+ decoded = decodeURIComponent(name.replace(/\+/g, " "));
319
+ }
320
+ catch {
321
+ }
322
+ return CARD_KEY.test(name) || CARD_KEY.test(decoded);
323
+ });
324
+ }
325
+ function classifyTxns(url, values, headers, body, host, issued) {
326
+ if (values.get("payment_method_type") !== "CARD") {
327
+ return TXNS_CARD_KEYS.some((key) => values.has(key)) || [...values.keys()].some((key) => CARD_NAME.test(key)) ? decide("abort", "card_on_non_card_payment") : decide("continue", "non_card_payment");
328
+ }
329
+ if (values.has("card_number"))
330
+ return decide("abort", "direct_card_payment");
331
+ const token = values.get("card_token");
332
+ if (!isJuspaySyntheticToken(token)) {
333
+ if (typeof token !== "string" || !token)
334
+ return decide("abort", "unsupported_card_payment");
335
+ const cvv = values.get("card_security_code");
336
+ const saved = !cardNumberIn(token) && (cvv === void 0 || /^\d{3,4}$/.test(cvv)) && !["card_exp_month", "card_exp_year", "name_on_card"].some((key) => values.has(key)) && ![...values].some(([key, value]) => (CARD_KEY.test(key) || CARD_NAME.test(key)) && cardNumberIn(value));
337
+ return saved ? decide("continue", "saved_card_payment") : decide("abort", "card_on_saved_card_payment");
338
+ }
339
+ const known = issuedFor(issued, token);
340
+ if (!known || known.host !== host || known.merchantId !== values.get("merchant_id"))
341
+ return decide("abort", "unknown_synthetic_token");
342
+ const refusal = juspayTxnsTemplateRefusal(url, headers, body);
343
+ return refusal ? decide("abort", refusal) : decide("pause", "synthetic_token_payment");
344
+ }
345
+ function classifyJuspayRequest(request, issued) {
346
+ if (!record(request) || typeof request.url !== "string")
347
+ return null;
348
+ let url;
349
+ try {
350
+ url = new URL(request.url);
351
+ }
352
+ catch {
353
+ return null;
354
+ }
355
+ const trailingDot = url.hostname.endsWith(".");
356
+ const host = url.hostname.toLowerCase().replace(/\.+$/, "");
357
+ const api = JUSPAY_API_HOSTS.some((candidate) => candidate === host);
358
+ if (!api && !JUSPAY_SIBLING_HOSTS.some((candidate) => candidate === host))
359
+ return null;
360
+ const method = typeof request.method === "string" ? request.method.toUpperCase() : "GET";
361
+ const body = typeof request.body === "string" ? request.body : "";
362
+ if (method !== "POST") {
363
+ if (cardBearing(url.search))
364
+ return decide("abort", "card_in_query");
365
+ return cardBearing(body) ? decide("abort", "card_on_non_post") : decide("continue", "not_a_post");
366
+ }
367
+ const headers = request.headers;
368
+ if (url.protocol !== "https:" || url.port || url.username || url.password)
369
+ return decide("abort", "noncanonical_url");
370
+ if (trailingDot)
371
+ return decide("abort", "noncanonical_host");
372
+ if (!api)
373
+ return cardBearing(url.search) || cardBearing(body) ? decide("abort", "sibling_host") : decide("continue", "sibling_without_card");
374
+ const bound = issuedEntries(issued).filter((entry) => entry.host === host);
375
+ if (bound.length && payStartLink(url, host, (merchant) => bound.some((entry) => entry.merchantId === merchant))) {
376
+ return payStartCarriesCard(url.search) || payStartCarriesCard(body) || payStartNamesCard(body) ? decide("abort", "card_on_pay_start") : decide("continue", "pay_start");
377
+ }
378
+ if (url.pathname === "/v2/card/fingerprint")
379
+ return decide("abort", "placeholder_fingerprint");
380
+ const exact = request.url === `https://${host}${url.pathname}`;
381
+ if (url.pathname === "/card/tokenize") {
382
+ const values = exact ? formMap(body) : null;
383
+ return values ? classifyTokenize(values, headers, host) : decide("abort", "unreadable_tokenize");
384
+ }
385
+ if (url.pathname === "/txns") {
386
+ const values = exact ? formMap(body) : null;
387
+ return values ? classifyTxns(url.href, values, headers, body, host, issued) : decide("abort", "unreadable_payment");
388
+ }
389
+ return cardBearing(url.search) || cardBearing(body) ? decide("abort", "card_on_unreviewed_path") : decide("continue", "not_a_card_request");
390
+ }
391
+ function realCard(card) {
392
+ const number = card && typeof card.number === "string" ? card.number.replace(/[ -]/g, "") : "";
393
+ const cvc = card && card.cvc !== void 0 && card.cvc !== null ? String(card.cvc) : "";
394
+ const month = card && card.month !== void 0 && card.month !== null ? String(card.month) : "";
395
+ const year = card && card.year !== void 0 && card.year !== null ? String(card.year) : "";
396
+ if (!/^\d{12,19}$/.test(number) || !/^\d{3,4}$/.test(cvc) || !/^\d{1,2}$/.test(month) || Number(month) < 1 || Number(month) > 12 || !/^(?:\d{2}|\d{4})$/.test(year)) {
397
+ throw new Error("Could not fill the card fields for Juspay. Nothing was sent.");
398
+ }
399
+ const name = card && typeof card.name === "string" ? card.name.trim() : "";
400
+ return {
401
+ number,
402
+ cvc,
403
+ month: month.padStart(2, "0"),
404
+ year: year.length === 2 ? "20" + year : year,
405
+ name: name && name.length <= 100 && !CONTROL.test(name) ? name : ""
406
+ };
407
+ }
408
+ function legHeaders(template) {
409
+ return {
410
+ "content-type": template.contentType,
411
+ "x-merchantid": template.merchantId,
412
+ ...template.routingId ? { "x-routing-id": template.routingId } : {}
413
+ };
414
+ }
415
+ function juspayTokenizeRequest(request, card) {
416
+ const template = assertJuspayTxnsTemplate(request && request.url, request && request.headers, request && request.body);
417
+ const real = realCard(card);
418
+ const pairs = [
419
+ ["card_number", real.number],
420
+ ["card_exp_year", real.year],
421
+ ["card_exp_month", real.month],
422
+ ["card_security_code", real.cvc],
423
+ ["merchant_id", template.merchantId],
424
+ ...real.name ? [["name_on_card", real.name]] : []
425
+ ];
426
+ return {
427
+ url: template.tokenizeUrl,
428
+ method: "POST",
429
+ headers: legHeaders(template),
430
+ body: pairs.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")
431
+ };
432
+ }
433
+ function juspayTxnsRequest(request, token, cvc) {
434
+ const template = assertJuspayTxnsTemplate(request && request.url, request && request.headers, request && request.body);
435
+ if (typeof token !== "string" || !REAL_TOKEN.test(token) || isJuspaySyntheticToken(token)) {
436
+ throw new Error("Refusing to pay: Juspay did not return a card token for this card.");
437
+ }
438
+ if (!/^\d{3,4}$/.test(String(cvc ?? "")))
439
+ throw new Error("Could not fill the card fields for Juspay. Nothing was sent.");
440
+ const replacements = { card_token: token, card_security_code: String(cvc) };
441
+ let body = request.body;
442
+ const entries = parseJuspayForm(body).filter((entry) => owns(replacements, entry.key)).sort((a, b) => b.valueStart - a.valueStart);
443
+ for (const entry of entries) {
444
+ body = body.slice(0, entry.valueStart) + encodeURIComponent(replacements[entry.key]) + body.slice(entry.valueEnd);
445
+ }
446
+ return { url: template.txnsUrl, method: "POST", headers: legHeaders(template), body };
447
+ }
448
+ function readJson(text, limit) {
449
+ if (typeof text !== "string" || !text || text.length > limit)
450
+ return null;
451
+ let at = 0, nodes = 0;
452
+ const fail = () => {
453
+ throw new Error("invalid_juspay_json");
454
+ };
455
+ const space = () => {
456
+ while (" \r\n".includes(text[at] ?? "\0"))
457
+ at++;
458
+ };
459
+ const string = () => {
460
+ space();
461
+ const start = at;
462
+ if (text[at++] !== '"')
463
+ fail();
464
+ while (at < text.length) {
465
+ const char = text[at++];
466
+ if (char === "\\") {
467
+ at++;
468
+ continue;
469
+ }
470
+ if (char === '"')
471
+ return JSON.parse(text.slice(start, at));
472
+ }
473
+ return fail();
474
+ };
475
+ const value = (depth) => {
476
+ if (depth > 8 || ++nodes > 512)
477
+ fail();
478
+ space();
479
+ const char = text[at];
480
+ if (char === '"')
481
+ return string();
482
+ if (char === "{") {
483
+ at++;
484
+ const result = /* @__PURE__ */ Object.create(null), seen = /* @__PURE__ */ new Set();
485
+ space();
486
+ if (text[at] === "}") {
487
+ at++;
488
+ return result;
489
+ }
490
+ for (;;) {
491
+ const key = string();
492
+ if (seen.has(key) || ["__proto__", "prototype", "constructor"].includes(key))
493
+ fail();
494
+ seen.add(key);
495
+ space();
496
+ if (text[at++] !== ":")
497
+ fail();
498
+ result[key] = value(depth + 1);
499
+ space();
500
+ const next = text[at++];
501
+ if (next === "}")
502
+ return result;
503
+ if (next !== ",")
504
+ fail();
505
+ }
506
+ }
507
+ if (char === "[") {
508
+ at++;
509
+ const result = [];
510
+ space();
511
+ if (text[at] === "]") {
512
+ at++;
513
+ return result;
514
+ }
515
+ for (;;) {
516
+ result.push(value(depth + 1));
517
+ space();
518
+ const next = text[at++];
519
+ if (next === "]")
520
+ return result;
521
+ if (next !== ",")
522
+ fail();
523
+ }
524
+ }
525
+ for (const literal of ["true", "false", "null"]) {
526
+ if (text.startsWith(literal, at)) {
527
+ at += literal.length;
528
+ return JSON.parse(literal);
529
+ }
530
+ }
531
+ const number = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/.exec(text.slice(at));
532
+ if (!number)
533
+ fail();
534
+ at += number[0].length;
535
+ return Number(number[0]);
536
+ };
537
+ try {
538
+ const parsed = value(0);
539
+ space();
540
+ return at === text.length && record(parsed) ? parsed : null;
541
+ }
542
+ catch {
543
+ return null;
544
+ }
545
+ }
546
+ function identifier(value, pattern) {
547
+ if (typeof value !== "string" || !pattern.test(value))
548
+ return null;
549
+ if (TOKEN_TEXT.test(value) || /\d{12}/.test(value))
550
+ return null;
551
+ return value;
552
+ }
553
+ function juspayResponseFacts(text) {
554
+ const value = readJson(text, MAX_RESPONSE);
555
+ if (!value)
556
+ return { errorCode: null, status: null };
557
+ const info = record(value.error_info) ? value.error_info : null;
558
+ const code = identifier(info && info.code, ERROR_CODE) ?? identifier(value.error_code, ERROR_CODE);
559
+ return { errorCode: code, status: identifier(value.status, STATUS) };
560
+ }
561
+ function readJuspayTokenizeReply(status, text) {
562
+ if (status === 200) {
563
+ const value = readJson(text, MAX_RESPONSE);
564
+ const token = value && !owns(value, "error_info") && !owns(value, "error") ? value.token : void 0;
565
+ return typeof token === "string" && REAL_TOKEN.test(token) && !isJuspaySyntheticToken(token) ? { ok: true, token } : { ok: false, code: "juspay_tokenize_unreadable" };
566
+ }
567
+ if (!Number.isInteger(status) || status < 100 || status > 599)
568
+ return { ok: false, code: JUSPAY_TOKENIZE_NETWORK };
569
+ const facts = status >= 400 && status < 500 ? juspayResponseFacts(text) : { errorCode: null };
570
+ return { ok: false, code: facts.errorCode ? `juspay_tokenize_${facts.errorCode}` : `juspay_tokenize_http_${status}` };
571
+ }
572
+ function isJuspayTokenizeRefusal(code) {
573
+ if (typeof code !== "string" || !TOKENIZE_REFUSAL.test(code))
574
+ return false;
575
+ return !TOKEN_TEXT.test(code) && !/\d{12}/.test(code);
576
+ }
577
+ function templateOf(template) {
578
+ if (!record(template))
579
+ return null;
580
+ try {
581
+ return assertJuspayTxnsTemplate(template.url, template.headers, template.body);
582
+ }
583
+ catch {
584
+ return null;
585
+ }
586
+ }
587
+ function tokenForms(token) {
588
+ if (token === void 0)
589
+ return [];
590
+ if (typeof token !== "string" || !REAL_TOKEN.test(token) || isJuspaySyntheticToken(token))
591
+ return null;
592
+ const tail = token.slice("ctkn_".length).toLowerCase();
593
+ return [token.toLowerCase(), ...tail.length >= TOKEN_TAIL ? [tail] : []];
594
+ }
595
+ function namesToken(text, forms) {
596
+ const texts = [String(text)];
597
+ try {
598
+ texts.push(decodeURIComponent(texts[0].replace(/\+/g, " ")));
599
+ }
600
+ catch {
601
+ }
602
+ return texts.some((candidate) => TOKEN_TEXT.test(candidate) || forms.some((form) => candidate.toLowerCase().includes(form)));
603
+ }
604
+ function reviewedPayStartQuery(search) {
605
+ let text;
606
+ try {
607
+ text = decodeURIComponent(search.slice(1));
608
+ }
609
+ catch {
610
+ return false;
611
+ }
612
+ if (!text || text.includes("%") || CONTROL.test(text))
613
+ return false;
614
+ const seen = /* @__PURE__ */ new Set();
615
+ for (const pair of text.split("&")) {
616
+ const eq = pair.indexOf("=");
617
+ const key = eq > 0 ? pair.slice(0, eq) : "";
618
+ if (!owns(PAY_START_QUERY, key) || seen.has(key) || !PAY_START_QUERY[key].test(pair.slice(eq + 1)))
619
+ return false;
620
+ seen.add(key);
621
+ }
622
+ return true;
623
+ }
624
+ function payStartLink(url, host, merchantOk) {
625
+ if (url.protocol !== "https:" || url.hostname !== host || url.port || url.username || url.password || url.hash)
626
+ return false;
627
+ const v2 = PAY_START_V2.exec(url.pathname);
628
+ if (v2 ? !merchantOk(v2[1]) : !PAY_START_V1.test(url.pathname))
629
+ return false;
630
+ if (url.search && !(v2 && reviewedPayStartQuery(url.search)))
631
+ return false;
632
+ return !url.pathname.split("/").some((segment) => /^\d{12,19}$/.test(segment) && luhn(segment));
633
+ }
634
+ function sanitizeJuspayTxnsResponse(text, template, token) {
635
+ const paused = templateOf(template);
636
+ const value = readJson(text, MAX_RESPONSE);
637
+ const forms = tokenForms(token);
638
+ const invalid = () => {
639
+ throw new Error("Invalid Juspay payment response.");
640
+ };
641
+ if (!paused || !value || !forms)
642
+ return invalid();
643
+ if (forms.some((form) => text.toLowerCase().includes(form)))
644
+ return invalid();
645
+ const { order_id: orderId, txn_id: txnId, status, payment } = value;
646
+ if (orderId !== paused.orderId || typeof txnId !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(txnId) || !JUSPAY_APPROVED_STATUSES.includes(status) || !record(payment) || !record(payment.authentication))
647
+ return invalid();
648
+ if (namesToken(txnId, forms))
649
+ return invalid();
650
+ const { method, url: raw, params } = payment.authentication;
651
+ if (method !== "GET" && method !== "POST" || typeof raw !== "string" || raw.length > 2048 || method === "POST" && params === void 0)
652
+ return invalid();
653
+ if (namesToken(raw, forms))
654
+ return invalid();
655
+ let url;
656
+ try {
657
+ url = new URL(raw);
658
+ }
659
+ catch {
660
+ return invalid();
661
+ }
662
+ if (url.href !== raw || !payStartLink(url, paused.host, (merchant) => merchant === paused.merchantId))
663
+ return invalid();
664
+ const authentication = { method, url: `${url.origin}${url.pathname}` };
665
+ if (params !== void 0) {
666
+ if (!record(params) || Object.keys(params).length > 32)
667
+ return invalid();
668
+ const safe = {};
669
+ for (const [key, item] of Object.entries(params)) {
670
+ if (!/^[A-Za-z0-9_.-]{1,64}$/.test(key) || CARD_KEY.test(key) || typeof item !== "string" || item.length > 4096 || CONTROL.test(item) || cardNumberIn(item))
671
+ return invalid();
672
+ if (namesToken(key, forms) || namesToken(item, forms))
673
+ return invalid();
674
+ safe[key] = item;
675
+ }
676
+ authentication.params = safe;
677
+ }
678
+ return JSON.stringify({ order_id: orderId, txn_id: txnId, status, payment: { authentication } });
679
+ }
680
+ function juspayTxnsOutcome(status, text, template, token) {
681
+ if (!Number.isInteger(status))
682
+ return { outcome: "unknown", reason: "juspay_unconfirmed" };
683
+ if (status >= 500)
684
+ return { outcome: "unknown", reason: "server_error" };
685
+ if (status >= 400) {
686
+ const { errorCode } = juspayResponseFacts(text);
687
+ return errorCode ? { outcome: "refused", code: errorCode } : { outcome: "unknown", reason: "juspay_refusal_unreadable" };
688
+ }
689
+ if (status !== 200)
690
+ return { outcome: "unknown", reason: "juspay_unconfirmed" };
691
+ const value = readJson(text, MAX_RESPONSE);
692
+ if (!value)
693
+ return { outcome: "unknown", reason: "juspay_unreadable" };
694
+ if (JUSPAY_REFUSED_STATUSES.includes(value.status))
695
+ return { outcome: "refused", code: value.status };
696
+ if (!JUSPAY_APPROVED_STATUSES.includes(value.status))
697
+ return { outcome: "unknown", reason: "juspay_status_unknown" };
698
+ try {
699
+ return { outcome: "approved", body: sanitizeJuspayTxnsResponse(text, template, token) };
700
+ }
701
+ catch {
702
+ return { outcome: "unknown", reason: "juspay_unsafe_response" };
703
+ }
704
+ }
705
+ export { JUSPAY_API_HOSTS, JUSPAY_APPROVED_STATUSES, JUSPAY_REFUSED_STATUSES, JUSPAY_SIBLING_HOSTS, JUSPAY_SYNTHETIC_TOKEN, JUSPAY_TOKENIZE_NETWORK, assertJuspayTxnsTemplate, classifyJuspayRequest, isJuspaySyntheticToken, isJuspayTokenizeRefusal, juspayEnvironment, juspayResponseFacts, juspaySyntheticToken, juspayTokenizeAnswer, juspayTokenizeRequest, juspayTxnsOutcome, juspayTxnsRequest, juspayTxnsTemplateRefusal, parseJuspayForm, readJuspayTokenizeReply, sanitizeJuspayTxnsResponse };