@agent-cards/sdk 0.23.18 → 0.23.20

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 (49) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/PREFLIGHT.md +2 -0
  3. package/README.md +46 -7
  4. package/dist/aci.d.ts +0 -7
  5. package/dist/aci.js +0 -19
  6. package/dist/amadeus.d.ts +4 -2
  7. package/dist/amadeus.js +16 -3
  8. package/dist/buy.d.ts +84 -2
  9. package/dist/buy.js +16 -1
  10. package/dist/card-endpoint-facts.d.ts +2 -1
  11. package/dist/card-endpoint-facts.js +18 -9
  12. package/dist/card-endpoint.d.ts +20 -4
  13. package/dist/card-endpoint.js +38 -4
  14. package/dist/card-fields.generated.js +974 -45
  15. package/dist/cdp.d.ts +29 -0
  16. package/dist/cdp.js +302 -18
  17. package/dist/client.d.ts +43 -5
  18. package/dist/client.js +237 -43
  19. package/dist/datatrans.d.ts +2 -2
  20. package/dist/datatrans.generated.js +16 -11
  21. package/dist/datatrans.js +2 -2
  22. package/dist/hipay.generated.d.ts +56 -0
  23. package/dist/hipay.generated.js +389 -0
  24. package/dist/hold.d.ts +180 -0
  25. package/dist/hold.js +264 -0
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.js +1 -0
  28. package/dist/lifecycle.d.ts +10 -0
  29. package/dist/lifecycle.js +21 -0
  30. package/dist/merchant-card-endpoints.generated.js +102 -11
  31. package/dist/paddle-vgs.generated.d.ts +3 -2
  32. package/dist/paddle-vgs.generated.js +9 -5
  33. package/dist/pelecard.generated.d.ts +2 -1
  34. package/dist/pelecard.generated.js +14 -5
  35. package/dist/pre-claim.d.ts +14 -0
  36. package/dist/pre-claim.js +76 -3
  37. package/dist/prepared-processor.d.ts +6 -2
  38. package/dist/prepared-processor.js +18 -2
  39. package/dist/primer.generated.js +998 -69
  40. package/dist/quickpay.d.ts +44 -0
  41. package/dist/quickpay.js +65 -0
  42. package/dist/stripe-checkout.generated.js +320 -2
  43. package/dist/twoc2p.d.ts +54 -0
  44. package/dist/twoc2p.generated.d.ts +33 -0
  45. package/dist/twoc2p.generated.js +1145 -0
  46. package/dist/twoc2p.js +85 -0
  47. package/dist/voice.d.ts +16 -13
  48. package/dist/voice.js +13 -10
  49. package/package.json +3 -3
@@ -0,0 +1,56 @@
1
+ declare var HIPAY_PSP: string;
2
+ declare var HIPAY_CAPABILITY: string;
3
+ declare var HIPAY_OPERATION: string;
4
+ declare var HIPAY_VAULT_HOSTS: Readonly<{
5
+ "secure2-vault.hipay-tpp.com": "production";
6
+ "stage-secure2-vault.hipay-tpp.com": "sandbox";
7
+ }>;
8
+ declare var HIPAY_TOKEN_URLS: Readonly<{
9
+ production: "https://secure2-vault.hipay-tpp.com/rest/v2/token";
10
+ sandbox: "https://stage-secure2-vault.hipay-tpp.com/rest/v2/token";
11
+ }>;
12
+ declare var HIPAY_UPDATE_PATH: string;
13
+ declare var HIPAY_REFUSAL_CODES: readonly string[];
14
+ declare function hipayTokenEnvironment(url: any): "production" | "sandbox" | null;
15
+ declare function isHipayTokenUrl(url: any): boolean;
16
+ declare function hipayBodyVerdict(body: any): Readonly<{
17
+ verdict: "refuse";
18
+ code: any;
19
+ }> | Readonly<{
20
+ verdict: "card";
21
+ }>;
22
+ declare function hipayNumberOnlyRequest(url: any, method: any, body: any): boolean;
23
+ declare function hipayUsernameEnvironment(username: any): "production" | "sandbox" | null;
24
+ declare function hipayUsername(headers: any): string | null;
25
+ declare function hipayRequestRefusal(request: any): any;
26
+ declare function isPreparedHipayRequest(url: any, method: any, body: any, environment: any, headers: any): boolean;
27
+ declare function hipayPinOriginUsable(origin: any): boolean;
28
+ declare var HIPAY_MERCHANT_PINS: readonly never[];
29
+ declare function hipayOriginPin(merchantOrigin: any, pins?: readonly never[]): null;
30
+ declare function hipayPreparationRefusal(environment: any, merchantOrigin: any, pins?: readonly never[]): "unsupported_environment" | "hipay_merchant_unreviewed" | null;
31
+ declare function hipayMerchantPin(merchantOrigin: any, username: any, pins?: readonly never[]): null;
32
+ declare function hipayMerchantRefusal(environment: any, merchantOrigin: any, username: any, pins?: readonly never[]): "hipay_headers_refused" | "unsupported_environment" | "hipay_merchant_unreviewed" | null;
33
+ declare function hipayRefusalMessage(code: any): any;
34
+ declare function injectHipayCard(body: any, card: any): {
35
+ body: string;
36
+ where: string[];
37
+ };
38
+ declare function isHipayDeviceRequest(templateBody: any, deviceBody: any): boolean;
39
+ declare function hipayRefusalCode(text: any): string | null;
40
+ declare function hipayAnswerOutcome(status: any, text: any, card: any): {
41
+ outcome: string;
42
+ reason: string;
43
+ code?: undefined;
44
+ body?: undefined;
45
+ } | {
46
+ outcome: string;
47
+ code: string;
48
+ reason?: undefined;
49
+ body?: undefined;
50
+ } | {
51
+ outcome: string;
52
+ body: string;
53
+ reason?: undefined;
54
+ code?: undefined;
55
+ };
56
+ export { HIPAY_CAPABILITY, HIPAY_MERCHANT_PINS, HIPAY_OPERATION, HIPAY_PSP, HIPAY_REFUSAL_CODES, HIPAY_TOKEN_URLS, HIPAY_UPDATE_PATH, HIPAY_VAULT_HOSTS, hipayAnswerOutcome, hipayBodyVerdict, hipayMerchantPin, hipayMerchantRefusal, hipayNumberOnlyRequest, hipayOriginPin, hipayPinOriginUsable, hipayPreparationRefusal, hipayRefusalCode, hipayRefusalMessage, hipayRequestRefusal, hipayTokenEnvironment, hipayUsername, hipayUsernameEnvironment, injectHipayCard, isHipayDeviceRequest, isHipayTokenUrl, isPreparedHipayRequest };
@@ -0,0 +1,389 @@
1
+ // @ts-nocheck
2
+ // Generated from @agent-cards/payment-core. Do not edit.
3
+ // artifact-sha256: 5773dac14c08b16588c9c36803bed4610e1be8f1cdcf6d38507cee1d35740dfc
4
+ // src/hipay.js
5
+ var HIPAY_PSP = "hipay";
6
+ var HIPAY_CAPABILITY = "hipay_vault_token";
7
+ var HIPAY_OPERATION = "hipay.card_request";
8
+ var HIPAY_VAULT_HOSTS = /* @__PURE__ */ Object.freeze({ "secure2-vault.hipay-tpp.com": "production", "stage-secure2-vault.hipay-tpp.com": "sandbox" });
9
+ var HIPAY_TOKEN_URLS = /* @__PURE__ */ Object.freeze({
10
+ production: "https://secure2-vault.hipay-tpp.com/rest/v2/token",
11
+ sandbox: "https://stage-secure2-vault.hipay-tpp.com/rest/v2/token"
12
+ });
13
+ var HIPAY_UPDATE_PATH = "/rest/v2/token/update";
14
+ var HIPAY_REFUSAL_CODES = /* @__PURE__ */ Object.freeze([
15
+ "hipay_url_refused",
16
+ "hipay_headers_refused",
17
+ "hipay_number_only",
18
+ "hipay_keeps_card",
19
+ "hipay_body_unsupported"
20
+ ]);
21
+ var BODY_LIMIT = 2048;
22
+ var HOSTED_KEYS = /* @__PURE__ */ Object.freeze(["multi_use", "card_expiry_month", "card_expiry_year", "card_number", "card_holder", "cvc"]);
23
+ var HOSTED_KEYS_NO_HOLDER = /* @__PURE__ */ Object.freeze(["multi_use", "card_expiry_month", "card_expiry_year", "card_number", "cvc"]);
24
+ var DIRECT_KEYS = /* @__PURE__ */ Object.freeze(["card_expiry_month", "card_expiry_year", "card_number", "card_holder", "cvc", "multi_use"]);
25
+ var DIRECT_KEYS_NO_HOLDER = /* @__PURE__ */ Object.freeze(["card_expiry_month", "card_expiry_year", "card_number", "cvc", "multi_use"]);
26
+ var CARD_SHAPES = /* @__PURE__ */ Object.freeze([HOSTED_KEYS, HOSTED_KEYS_NO_HOLDER, DIRECT_KEYS, DIRECT_KEYS_NO_HOLDER]);
27
+ var NUMBER_ONLY_KEYS = /* @__PURE__ */ Object.freeze(["multi_use", "card_expiry_month", "card_expiry_year", "card_number"]);
28
+ var CARD_NUMBER = /^[0-9]{12,19}$/;
29
+ var SECURITY_CODE = /^[0-9]{3,4}$/;
30
+ var MONTH = /^(?:0[1-9]|1[0-2])$/;
31
+ var YEAR = /^20[0-9]{2}$/;
32
+ var TEXT_LIMIT = 256;
33
+ var CONTROL = /[\u0000-\u001f\u007f]/;
34
+ var JSON_TYPE = /^application\/json(?:\s*;\s*charset=utf-8)?$/i;
35
+ var BASIC = /^Basic ([A-Za-z0-9+/]{4,512}={0,2})$/;
36
+ var PRODUCTION_USERNAME = /^[0-9]{6,12}\.(?:secure-gateway|api)\.hipay-tpp\.com$/;
37
+ var SANDBOX_USERNAME = /^[0-9]{6,12}\.stage-secure-gateway\.hipay-tpp\.com$/;
38
+ var PASSWORD = /^[\x21-\x7e]{8,128}$/;
39
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
40
+ var sameKeys = (value, keys) => {
41
+ const own = Object.keys(value);
42
+ return own.length === keys.length && own.every((key, at) => key === keys[at]);
43
+ };
44
+ var plainText = (value) => typeof value === "string" && value.length <= TEXT_LIMIT && !CONTROL.test(value) && (value.match(/[0-9]/g) ?? []).length < 12;
45
+ var refuse = (code) => Object.freeze({ verdict: "refuse", code });
46
+ var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
47
+ function hipayTokenEnvironment(url) {
48
+ if (url === HIPAY_TOKEN_URLS.production)
49
+ return "production";
50
+ if (url === HIPAY_TOKEN_URLS.sandbox)
51
+ return "sandbox";
52
+ return null;
53
+ }
54
+ function isHipayTokenUrl(url) {
55
+ return hipayTokenEnvironment(url) !== null;
56
+ }
57
+ function readBody(body) {
58
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
59
+ return null;
60
+ let value;
61
+ try {
62
+ value = JSON.parse(body);
63
+ if (!record(value) || JSON.stringify(value) !== body)
64
+ return null;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ return value;
70
+ }
71
+ function hipayBodyVerdict(body) {
72
+ const value = readBody(body);
73
+ if (value === null)
74
+ return refuse("hipay_body_unsupported");
75
+ if (sameKeys(value, NUMBER_ONLY_KEYS))
76
+ return refuse("hipay_number_only");
77
+ if (!CARD_SHAPES.some((keys) => sameKeys(value, keys)))
78
+ return refuse("hipay_body_unsupported");
79
+ if (typeof value.card_number !== "string" || !CARD_NUMBER.test(value.card_number) || typeof value.cvc !== "string" || !SECURITY_CODE.test(value.cvc) || typeof value.card_expiry_month !== "string" || !MONTH.test(value.card_expiry_month) || typeof value.card_expiry_year !== "string" || !YEAR.test(value.card_expiry_year) || "card_holder" in value && !plainText(value.card_holder)) {
80
+ return refuse("hipay_body_unsupported");
81
+ }
82
+ if (value.multi_use === 1)
83
+ return refuse("hipay_keeps_card");
84
+ return value.multi_use === 0 ? CARD : refuse("hipay_body_unsupported");
85
+ }
86
+ function hipayNumberOnlyRequest(url, method, body) {
87
+ if (typeof method !== "string" || method.toUpperCase() !== "POST" || !isHipayTokenUrl(url))
88
+ return false;
89
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
90
+ return false;
91
+ let value;
92
+ try {
93
+ value = JSON.parse(body);
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ return record(value) && !(typeof value.cvc === "string" && SECURITY_CODE.test(value.cvc));
99
+ }
100
+ function headerValue(headers, name) {
101
+ const found = Object.keys(headers).filter((key) => key.toLowerCase() === name);
102
+ return found.length === 1 && typeof headers[found[0]] === "string" ? headers[found[0]] : null;
103
+ }
104
+ function base64Text(input) {
105
+ if (input.length % 4 !== 0)
106
+ return null;
107
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
108
+ const stripped = input.replace(/={1,2}$/, "");
109
+ if (stripped.includes("="))
110
+ return null;
111
+ let out = "";
112
+ let bits = 0;
113
+ let value = 0;
114
+ for (const character of stripped) {
115
+ const sextet = alphabet.indexOf(character);
116
+ if (sextet < 0)
117
+ return null;
118
+ value = value << 6 | sextet;
119
+ bits += 6;
120
+ if (bits >= 8) {
121
+ bits -= 8;
122
+ out += String.fromCharCode(value >> bits & 255);
123
+ value &= (1 << bits) - 1;
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+ function hipayUsernameEnvironment(username) {
129
+ if (typeof username !== "string")
130
+ return null;
131
+ if (PRODUCTION_USERNAME.test(username))
132
+ return "production";
133
+ return SANDBOX_USERNAME.test(username) ? "sandbox" : null;
134
+ }
135
+ function hipayUsername(headers) {
136
+ if (!record(headers))
137
+ return null;
138
+ const authorization = headerValue(headers, "authorization");
139
+ const basic = authorization === null ? null : BASIC.exec(authorization);
140
+ const text = basic ? base64Text(basic[1]) : null;
141
+ const colon = text === null ? -1 : text.indexOf(":");
142
+ if (colon <= 0)
143
+ return null;
144
+ const username = text.slice(0, colon);
145
+ return hipayUsernameEnvironment(username) !== null && PASSWORD.test(text.slice(colon + 1)) ? username : null;
146
+ }
147
+ function hipayRequestRefusal(request) {
148
+ if (!record(request) || typeof request.method !== "string" || request.method.toUpperCase() !== "POST")
149
+ return "hipay_url_refused";
150
+ const environment = hipayTokenEnvironment(request.url);
151
+ if (environment === null)
152
+ return "hipay_url_refused";
153
+ const headers = record(request.headers) ? request.headers : {};
154
+ const contentType = headerValue(headers, "content-type");
155
+ const username = hipayUsername(headers);
156
+ if (contentType === null || !JSON_TYPE.test(contentType.trim()) || username === null || hipayUsernameEnvironment(username) !== environment) {
157
+ return "hipay_headers_refused";
158
+ }
159
+ const verdict = hipayBodyVerdict(request.body);
160
+ return verdict.verdict === "card" ? null : verdict.code;
161
+ }
162
+ function isPreparedHipayRequest(url, method, body, environment, headers) {
163
+ return (environment === "production" || environment === "sandbox") && hipayTokenEnvironment(url) === environment && hipayRequestRefusal({ url, method, headers, body }) === null;
164
+ }
165
+ var PIN_ORIGIN = /^https:\/\/([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)$/;
166
+ var HIPAY_DOMAIN = /(?:^|\.)(?:hipay\.com|hipay-tpp\.com|hipaywallet\.com|hipay-pos-platform\.com|hipaycomprafacil\.com)$/;
167
+ function hipayPinOriginUsable(origin) {
168
+ const host = typeof origin === "string" ? PIN_ORIGIN.exec(origin) : null;
169
+ return !!host && !HIPAY_DOMAIN.test(host[1]);
170
+ }
171
+ var HIPAY_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([]);
172
+ var usablePin = (entry) => !!entry && Array.isArray(entry.origins) && Array.isArray(entry.usernames) && entry.origins.length > 0 && entry.usernames.length > 0 && entry.origins.every(hipayPinOriginUsable) && entry.usernames.every((name) => hipayUsernameEnvironment(name) === "production");
173
+ function hipayOriginPin(merchantOrigin, pins = HIPAY_MERCHANT_PINS) {
174
+ if (typeof merchantOrigin !== "string" || !Array.isArray(pins) || !hipayPinOriginUsable(merchantOrigin))
175
+ return null;
176
+ const found = pins.filter((entry) => usablePin(entry) && entry.origins.includes(merchantOrigin));
177
+ return found.length === 1 ? found[0] : null;
178
+ }
179
+ function hipayPreparationRefusal(environment, merchantOrigin, pins = HIPAY_MERCHANT_PINS) {
180
+ if (environment === "sandbox")
181
+ return null;
182
+ if (environment !== "production")
183
+ return "unsupported_environment";
184
+ return hipayOriginPin(merchantOrigin, pins) ? null : "hipay_merchant_unreviewed";
185
+ }
186
+ function hipayMerchantPin(merchantOrigin, username, pins = HIPAY_MERCHANT_PINS) {
187
+ if (typeof username !== "string" || hipayUsernameEnvironment(username) !== "production")
188
+ return null;
189
+ const found = hipayOriginPin(merchantOrigin, pins);
190
+ return found && found.usernames.includes(username) ? found : null;
191
+ }
192
+ function hipayMerchantRefusal(environment, merchantOrigin, username, pins = HIPAY_MERCHANT_PINS) {
193
+ if (environment !== "production" && environment !== "sandbox")
194
+ return "unsupported_environment";
195
+ if (hipayUsernameEnvironment(username) !== environment)
196
+ return "hipay_headers_refused";
197
+ if (environment === "sandbox")
198
+ return null;
199
+ return hipayMerchantPin(merchantOrigin, username, pins) ? null : "hipay_merchant_unreviewed";
200
+ }
201
+ var REFUSAL_MESSAGES = /* @__PURE__ */ Object.freeze({
202
+ hipay_url_refused: "Refusing to pay: this HiPay card request does not go to HiPay's card vault exactly as HiPay's card form sends it. Nothing was sent.",
203
+ hipay_headers_refused: "Refusing to pay: this HiPay card request does not carry the checkout's own headers. Nothing was sent.",
204
+ hipay_number_only: "Refusing to pay: this is not the card request of HiPay's card form. Nothing was sent.",
205
+ hipay_keeps_card: "This payment asks the payment processor to keep your card for later charges, and you cannot agree to that on this page yet.",
206
+ hipay_body_unsupported: "Refusing to pay: this is not the card request of HiPay's card form. Nothing was sent.",
207
+ hipay_merchant_unreviewed: "This payment goes to a merchant account Agentcard has not reviewed."
208
+ });
209
+ function hipayRefusalMessage(code) {
210
+ return Object.prototype.hasOwnProperty.call(REFUSAL_MESSAGES, code) ? REFUSAL_MESSAGES[code] : REFUSAL_MESSAGES.hipay_body_unsupported;
211
+ }
212
+ var WHERE = /* @__PURE__ */ Object.freeze(["card_number", "cvc", "card_expiry_month", "card_expiry_year"]);
213
+ function frameCard(card) {
214
+ if (!card)
215
+ return null;
216
+ const number = String(card.number).replace(/[ -]/g, "");
217
+ const cvc = String(card.cvc);
218
+ const month = Number(card.month);
219
+ const rawYear = Number(card.year);
220
+ const year = Number.isInteger(rawYear) && rawYear >= 0 && rawYear <= 99 ? 2e3 + rawYear : rawYear;
221
+ if (!CARD_NUMBER.test(number) || !SECURITY_CODE.test(cvc) || !Number.isInteger(month) || month < 1 || month > 12 || !Number.isInteger(year) || !YEAR.test(String(year)))
222
+ return null;
223
+ return { card_number: number, cvc, card_expiry_month: String(month).padStart(2, "0"), card_expiry_year: String(year) };
224
+ }
225
+ function written(body, values) {
226
+ const value = JSON.parse(body);
227
+ for (const key of WHERE)
228
+ value[key] = values[key];
229
+ return JSON.stringify(value);
230
+ }
231
+ function injectHipayCard(body, card) {
232
+ const verdict = hipayBodyVerdict(body);
233
+ if (verdict.verdict !== "card")
234
+ throw new Error(hipayRefusalMessage(verdict.code));
235
+ const values = frameCard(card);
236
+ if (!values)
237
+ throw new Error("Could not fill the card fields for HiPay. Nothing was sent.");
238
+ return { body: written(body, values), where: [...WHERE] };
239
+ }
240
+ function isHipayDeviceRequest(templateBody, deviceBody) {
241
+ if (hipayBodyVerdict(templateBody).verdict !== "card" || hipayBodyVerdict(deviceBody).verdict !== "card")
242
+ return false;
243
+ const device = JSON.parse(deviceBody);
244
+ return written(templateBody, device) === deviceBody && device.card_number !== JSON.parse(templateBody).card_number;
245
+ }
246
+ var ANSWER_LIMIT = 16384;
247
+ var TOKEN = /^[A-Za-z0-9_-]{16,128}$/;
248
+ var LONG_DIGIT_RUN = /[0-9]{13,}/;
249
+ var DIGEST_TOKEN = /^[0-9a-f]{64}$/;
250
+ var PAN = /^([0-9]{6})[xX*]{2,9}([0-9]{4})$/;
251
+ var REQUEST_ID = /^[0-9]{1,11}$/;
252
+ var REQUEST_ID_MAX = 99999999999;
253
+ var CARD_ID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
254
+ var WORD = /^[A-Za-z0-9][A-Za-z0-9 _.-]{0,31}$/;
255
+ var COUNTRY = /^[A-Z]{2}$/;
256
+ var VAULT_CODE = /^[0-9]{1,7}$/;
257
+ var ECHO_WINDOW = 7;
258
+ var HIDDEN_FLOOR = 5;
259
+ var SEPARATORS = /[ ._-]/g;
260
+ var WORD_DIGITS = 2;
261
+ var word = (value) => typeof value === "string" && WORD.test(value) && (value.match(/[0-9]/g) ?? []).length <= WORD_DIGITS;
262
+ var ANSWER_KEYS = /* @__PURE__ */ Object.freeze([
263
+ "token",
264
+ "request_id",
265
+ "card_id",
266
+ "multi_use",
267
+ "brand",
268
+ "pan",
269
+ "card_holder",
270
+ "card_expiry_month",
271
+ "card_expiry_year",
272
+ "issuer",
273
+ "country",
274
+ "domestic_network",
275
+ "card_type",
276
+ "card_category",
277
+ "forbidden_issuer_country"
278
+ ]);
279
+ function readAnswer(text) {
280
+ if (typeof text !== "string" || text === "" || text.length > ANSWER_LIMIT)
281
+ return null;
282
+ try {
283
+ const value = JSON.parse(text);
284
+ return record(value) ? value : null;
285
+ }
286
+ catch {
287
+ return null;
288
+ }
289
+ }
290
+ function vaultCode(answer) {
291
+ const code = answer ? answer.code : void 0;
292
+ const text = typeof code === "number" && Number.isInteger(code) && code >= 0 ? String(code) : typeof code === "string" ? code : null;
293
+ return text !== null && VAULT_CODE.test(text) ? text : null;
294
+ }
295
+ function hipayRefusalCode(text) {
296
+ const code = vaultCode(readAnswer(text));
297
+ return code === null ? null : `hipay_${code}`;
298
+ }
299
+ var KEPT = /* @__PURE__ */ Object.freeze({
300
+ token: (value) => typeof value === "string" && TOKEN.test(value) && (DIGEST_TOKEN.test(value) || !LONG_DIGIT_RUN.test(value)),
301
+ request_id: (value) => typeof value === "string" && REQUEST_ID.test(value) || Number.isInteger(value) && value >= 0 && value <= REQUEST_ID_MAX,
302
+ card_id: (value) => typeof value === "string" && CARD_ID.test(value) && !LONG_DIGIT_RUN.test(value),
303
+ multi_use: (value) => value === 0 || value === false,
304
+ brand: word,
305
+ pan: (value) => typeof value === "string" && PAN.test(value),
306
+ card_holder: plainText,
307
+ card_expiry_month: (value) => typeof value === "string" && MONTH.test(value),
308
+ card_expiry_year: (value) => typeof value === "string" && YEAR.test(value),
309
+ issuer: plainText,
310
+ country: (value) => typeof value === "string" && COUNTRY.test(value),
311
+ domestic_network: word,
312
+ card_type: word,
313
+ card_category: word,
314
+ forbidden_issuer_country: (value) => typeof value === "boolean"
315
+ });
316
+ function tokenAnswer(answer) {
317
+ if (!answer || typeof answer.token !== "string" || typeof answer.pan !== "string")
318
+ return null;
319
+ const out = {};
320
+ for (const key of ANSWER_KEYS) {
321
+ if (answer[key] === void 0 || answer[key] === null)
322
+ continue;
323
+ if (!KEPT[key](answer[key]))
324
+ return null;
325
+ out[key] = answer[key];
326
+ }
327
+ return out;
328
+ }
329
+ function hipayAnswerOutcome(status, text, card) {
330
+ if (!Number.isInteger(status))
331
+ return { outcome: "unknown", reason: "hipay_unconfirmed" };
332
+ if (status >= 500)
333
+ return { outcome: "unknown", reason: "server_error" };
334
+ if (status >= 400 && status < 500)
335
+ return { outcome: "refused", code: hipayRefusalCode(text) ?? `http_${status}` };
336
+ if (status !== 200 && status !== 201)
337
+ return { outcome: "unknown", reason: "hipay_unconfirmed" };
338
+ const answer = readAnswer(text);
339
+ if (answer && typeof answer.token !== "string" && vaultCode(answer) !== null)
340
+ return { outcome: "refused", code: `hipay_${vaultCode(answer)}` };
341
+ const token = tokenAnswer(answer);
342
+ if (token === null)
343
+ return { outcome: "unknown", reason: "hipay_unsafe_response" };
344
+ if (card) {
345
+ const sent = frameCard(card);
346
+ if (!sent)
347
+ return { outcome: "unknown", reason: "hipay_unsafe_response" };
348
+ const hidden = sent.card_number.slice(6, -4);
349
+ const holdsHidden = (key, value) => hidden.length >= HIDDEN_FLOOR && key !== "card_expiry_month" && key !== "card_expiry_year" && String(value).replace(SEPARATORS, "").includes(hidden);
350
+ if (ANSWER_KEYS.some((key) => key !== "pan" && token[key] !== void 0 && (echoesCard(token[key], sent.card_number) || holdsHidden(key, token[key])))) {
351
+ return { outcome: "unknown", reason: "hipay_unsafe_response" };
352
+ }
353
+ const pan = PAN.exec(token.pan);
354
+ const brand = brandNetwork(token.brand);
355
+ const network = cardNetwork(sent.card_number);
356
+ if (!pan || pan[1] !== sent.card_number.slice(0, 6) || pan[2] !== sent.card_number.slice(-4) || token.card_expiry_month !== void 0 && token.card_expiry_month !== sent.card_expiry_month || token.card_expiry_year !== void 0 && token.card_expiry_year !== sent.card_expiry_year || brand !== null && network !== null && brand !== network) {
357
+ return { outcome: "unknown", reason: "hipay_token_mismatch" };
358
+ }
359
+ }
360
+ return { outcome: "approved", body: JSON.stringify(token) };
361
+ }
362
+ function echoesCard(value, number) {
363
+ const digits = String(value).replace(/[^0-9]/g, "");
364
+ if (digits.length < ECHO_WINDOW)
365
+ return false;
366
+ for (let at = 0; at + ECHO_WINDOW <= number.length; at += 1) {
367
+ if (digits.includes(number.slice(at, at + ECHO_WINDOW)))
368
+ return true;
369
+ }
370
+ return false;
371
+ }
372
+ function cardNetwork(number) {
373
+ const two = Number(number.slice(0, 2));
374
+ const four = Number(number.slice(0, 4));
375
+ if (number.startsWith("4"))
376
+ return "VISA";
377
+ if (two >= 51 && two <= 55 || four >= 2221 && four <= 2720)
378
+ return "MASTERCARD";
379
+ return two === 34 || two === 37 ? "AMEX" : null;
380
+ }
381
+ function brandNetwork(brand) {
382
+ const name = typeof brand === "string" ? brand.toUpperCase().replace(/[^A-Z]/g, "") : "";
383
+ if (name === "VISA")
384
+ return "VISA";
385
+ if (name === "MASTERCARD")
386
+ return "MASTERCARD";
387
+ return name === "AMERICANEXPRESS" || name === "AMEX" ? "AMEX" : null;
388
+ }
389
+ export { HIPAY_CAPABILITY, HIPAY_MERCHANT_PINS, HIPAY_OPERATION, HIPAY_PSP, HIPAY_REFUSAL_CODES, HIPAY_TOKEN_URLS, HIPAY_UPDATE_PATH, HIPAY_VAULT_HOSTS, hipayAnswerOutcome, hipayBodyVerdict, hipayMerchantPin, hipayMerchantRefusal, hipayNumberOnlyRequest, hipayOriginPin, hipayPinOriginUsable, hipayPreparationRefusal, hipayRefusalCode, hipayRefusalMessage, hipayRequestRefusal, hipayTokenEnvironment, hipayUsername, hipayUsernameEnvironment, injectHipayCard, isHipayDeviceRequest, isHipayTokenUrl, isPreparedHipayRequest };
package/dist/hold.d.ts ADDED
@@ -0,0 +1,180 @@
1
+ /** One kind of card request the hold catches although no recogniser pays it. */
2
+ export interface HoldOnlyRule {
3
+ /** The processor's name, for the record (`bolt`, `placeholder_card`). */
4
+ psp: string;
5
+ /** The card request's address. */
6
+ match: RegExp;
7
+ /** CDP Fetch url patterns that cover `match`, with an optional resource type. */
8
+ patterns: Array<{
9
+ urlPattern: string;
10
+ resourceType?: string;
11
+ }>;
12
+ /** When set, a request to `match` is a card request only if its body passes. */
13
+ body?: RegExp;
14
+ kind: 'tokenize' | 'store_card_post';
15
+ /** The processor's own wait for this request, in ms: the hold cancels before it. */
16
+ deadlineMs: number;
17
+ /** Methods that carry the card here. Default POST. */
18
+ methods?: string[];
19
+ }
20
+ /**
21
+ * Bolt's embedded card frame (connect.bolt.com credit-card-input): a POST of `{ payload, nonce, public_key }` JSON,
22
+ * the card boxed to Bolt's key, to `<base>/token` or `<base>/token/cvv`, with a 20 s timeout and retries.
23
+ */
24
+ export declare const BOLT_HOLD_RULE: HoldOnlyRule;
25
+ /** Each recogniser's own wait for its card request, where it was measured; any other gets HOLD_DEFAULT_DEADLINE_MS. */
26
+ export declare const HOLD_DEADLINES_MS: Readonly<Record<string, number>>;
27
+ export declare const HOLD_DEFAULT_DEADLINE_MS = 30000;
28
+ /** How long before the processor's own wait the hold gives the request up. */
29
+ export declare const HOLD_DEADLINE_MARGIN_MS = 2000;
30
+ /**
31
+ * Any request whose body carries one of the placeholder card numbers, wherever it goes. It cannot see a card a
32
+ * processor's frame encrypts first (Bolt, Adyen): those need their own rule. Null without a usable number.
33
+ */
34
+ export declare function placeholderHoldRule(numbers: readonly string[], deadlineMs?: number): HoldOnlyRule | null;
35
+ /** The hold-only rule a request is a card request under, or null. */
36
+ export declare function matchHoldOnlyRule(request: {
37
+ url: string;
38
+ method: string;
39
+ body: string | null;
40
+ }, rules: readonly HoldOnlyRule[]): HoldOnlyRule | null;
41
+ export interface HoldOptions {
42
+ /**
43
+ * The placeholder card numbers the vault gave this page. Any request whose body carries one is held too. Matched
44
+ * in this process only, never recorded or sent anywhere.
45
+ */
46
+ placeholderNumbers?: readonly string[];
47
+ /** Replaces every recogniser's deadline (tests). */
48
+ deadlineMs?: number;
49
+ /** How long before the processor's deadline the hold gives the request up. Default HOLD_DEADLINE_MARGIN_MS. */
50
+ deadlineMarginMs?: number;
51
+ /** Called once a card request is held. */
52
+ onHeld?: (held: HeldCardRequest) => void;
53
+ }
54
+ /** What the hold records of a held request: never its body or headers, which may carry the card. */
55
+ export interface HeldCardRequest {
56
+ /** The processor, by the recogniser's name (`braintree`), `bolt`, or `placeholder_card`; null when unnamed. */
57
+ psp: string | null;
58
+ /** Origin and path only. */
59
+ endpoint: string;
60
+ kind: 'tokenize' | 'store_card_post' | 'card_request';
61
+ /** Whether a release can answer it: a recogniser the SDK pays, never Bolt or a store's own card post. */
62
+ replayable: boolean;
63
+ heldAt: number;
64
+ deadlineMs: number;
65
+ }
66
+ export type HoldState = 'watching' | 'held' | 'released' | 'cancelled' | 'expired' | 'lost' | 'closed';
67
+ export interface HoldReleaseInput {
68
+ /** The total the person confirmed, an integer in the currency's smallest unit. The approval is created at it. */
69
+ amount: number;
70
+ currency: string;
71
+ /**
72
+ * The total the page shows now, read by the caller (null when it reads none). Released only when it matches: equal
73
+ * to `amount` by default, or at most `amount` with `upTo` (an approval at a ceiling). Anything else cancels.
74
+ */
75
+ pageTotal: {
76
+ amount: number;
77
+ currency: string;
78
+ } | null;
79
+ /** `amount` is a ceiling the page total may stay under (a store that prices tax after the card). */
80
+ upTo?: boolean;
81
+ /**
82
+ * Sandbox only: a store's own card post held by the placeholder rule carries the documented test number only, and
83
+ * is sent on unchanged so the store can answer it (its Review Order). Refused on a live client.
84
+ */
85
+ continuePlaceholder?: boolean;
86
+ }
87
+ export interface HoldReleaseOutcome {
88
+ /**
89
+ * approved_not_charged: sandbox, the approval was approved and the request never sent on.
90
+ * authorized: live, the device's answer reached the page. continued: a placeholder post was sent on (sandbox).
91
+ */
92
+ outcome: 'approved_not_charged' | 'authorized' | 'continued' | 'declined' | 'expired' | 'failed';
93
+ authorizationId?: string | null;
94
+ detail?: string;
95
+ }
96
+ /** The hold of one attachment: `controller.hold`. */
97
+ export interface CardRequestHold {
98
+ readonly state: HoldState;
99
+ readonly held: HeldCardRequest | null;
100
+ /** Card requests failed without being held: a second one, a retry, or one after a decision. */
101
+ readonly refused: number;
102
+ /** Resolves with the held request once one is held, or null after `timeoutMs` or once the hold is over. */
103
+ whenHeld(timeoutMs?: number): Promise<HeldCardRequest | null>;
104
+ release(input: HoldReleaseInput): Promise<HoldReleaseOutcome>;
105
+ /** Fail the held request in the page: nothing was created or sent. Every later card request fails at once. */
106
+ cancel(reason?: string): Promise<void>;
107
+ }
108
+ /** A hold ended without a release: the paused request fails, and every later card request fails at once. */
109
+ export declare class HoldEndedError extends Error {
110
+ readonly reason: string;
111
+ constructor(reason: string);
112
+ }
113
+ /** A second card request while one is held: this request alone fails, the held one and the checkout are untouched. */
114
+ export declare class HoldRefusedError extends Error {
115
+ constructor();
116
+ }
117
+ /** A sandbox release answered: the approval was created and read, and the request was never sent on. */
118
+ export declare class HoldAnsweredError extends Error {
119
+ readonly outcome: HoldReleaseOutcome;
120
+ constructor(outcome: HoldReleaseOutcome);
121
+ }
122
+ /** What the transport does with a held request once the person answered. */
123
+ export type HoldDecision = {
124
+ kind: 'release';
125
+ amount: number;
126
+ currency: string;
127
+ settle: (outcome: HoldReleaseOutcome) => void;
128
+ } | {
129
+ kind: 'continue';
130
+ settle: (outcome: HoldReleaseOutcome) => void;
131
+ };
132
+ /**
133
+ * The hold's state, shared by the transport and the caller. The transport calls `hold` when a card request pauses
134
+ * and awaits the decision; the caller calls `release` or `cancel`. Not exported from the package: the caller sees
135
+ * the CardRequestHold face.
136
+ */
137
+ export declare class HoldGate implements CardRequestHold {
138
+ private readonly options;
139
+ private readonly sandbox;
140
+ private readonly emit;
141
+ private _state;
142
+ private _held;
143
+ private _refused;
144
+ private timer;
145
+ private waiters;
146
+ private pending;
147
+ readonly rules: HoldOnlyRule[];
148
+ constructor(options: HoldOptions, sandbox: boolean, emit: (type: string, detail?: unknown) => void);
149
+ get state(): HoldState;
150
+ get held(): HeldCardRequest | null;
151
+ get refused(): number;
152
+ /** Fetch patterns the hold-only rules need beside the registry's. */
153
+ patterns(): Array<{
154
+ urlPattern: string;
155
+ resourceType?: string;
156
+ }>;
157
+ deadlineFor(psp: string | null, rule?: HoldOnlyRule): number;
158
+ /** A card request that may not be held now (a second one, or one after a decision): counted, and failed by the caller. */
159
+ refuse(psp: string | null, url: string): boolean;
160
+ /**
161
+ * Hold a card request: resolves with the person's decision, or rejects with HoldEndedError when the hold is
162
+ * cancelled, expires, loses its request or closes. The caller (the transport) answers or fails the request.
163
+ */
164
+ hold(input: {
165
+ psp: string | null;
166
+ url: string;
167
+ kind: HeldCardRequest['kind'];
168
+ replayable: boolean;
169
+ rule?: HoldOnlyRule;
170
+ }): Promise<HoldDecision>;
171
+ /** The page gave up the held request, or its frame went away: nothing was sent. */
172
+ lost(): void;
173
+ /** The attachment ended. */
174
+ close(): void;
175
+ private end;
176
+ whenHeld(timeoutMs?: number): Promise<HeldCardRequest | null>;
177
+ cancel(reason?: string): Promise<void>;
178
+ release(input: HoldReleaseInput): Promise<HoldReleaseOutcome>;
179
+ private decide;
180
+ }