@agent-cards/sdk 0.23.9 → 0.23.11

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 (65) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/PREFLIGHT.md +7 -5
  3. package/README.md +211 -102
  4. package/dist/aci.d.ts +7 -5
  5. package/dist/aci.generated.d.ts +2 -1
  6. package/dist/aci.generated.js +14 -4
  7. package/dist/aci.js +28 -7
  8. package/dist/amadeus-checkout.generated.js +2 -2
  9. package/dist/builtin-registry.generated.js +1 -1
  10. package/dist/card-fields.generated.js +165 -10
  11. package/dist/cdp.js +86 -34
  12. package/dist/client.d.ts +73 -33
  13. package/dist/client.js +136 -45
  14. package/dist/creditguard.generated.d.ts +1 -1
  15. package/dist/creditguard.generated.js +1 -3
  16. package/dist/cse-body.d.ts +7 -6
  17. package/dist/cse-body.js +8 -7
  18. package/dist/datatrans.generated.js +8 -5
  19. package/dist/elavon-converge.d.ts +5 -2
  20. package/dist/elavon-converge.generated.js +9 -5
  21. package/dist/elavon-converge.js +8 -4
  22. package/dist/fiserv.d.ts +9 -5
  23. package/dist/fiserv.generated.d.ts +3 -3
  24. package/dist/fiserv.generated.js +50 -20
  25. package/dist/fiserv.js +10 -6
  26. package/dist/lifecycle.d.ts +9 -1
  27. package/dist/lifecycle.js +34 -0
  28. package/dist/payrails.d.ts +78 -18
  29. package/dist/payrails.generated.d.ts +12 -2
  30. package/dist/payrails.generated.js +213 -18
  31. package/dist/payrails.js +165 -44
  32. package/dist/pre-claim.d.ts +7 -0
  33. package/dist/pre-claim.js +98 -36
  34. package/dist/preflight-capabilities.generated.d.ts +0 -106
  35. package/dist/preflight-capabilities.generated.js +0 -153
  36. package/dist/preflight-catalog.json +61 -181
  37. package/dist/preflight-schemas.json +6 -6
  38. package/dist/preflight.generated.d.ts +0 -106
  39. package/dist/preflight.generated.js +27 -15
  40. package/dist/preparation.d.ts +2 -0
  41. package/dist/preparation.js +4 -2
  42. package/dist/prepared-processor.d.ts +19 -5
  43. package/dist/prepared-processor.js +21 -5
  44. package/dist/primer.generated.d.ts +5 -1
  45. package/dist/primer.generated.js +202 -34
  46. package/dist/registry.d.ts +18 -12
  47. package/dist/registry.js +11 -8
  48. package/dist/shiji-sts.d.ts +24 -22
  49. package/dist/shiji-sts.generated.d.ts +1 -1
  50. package/dist/shiji-sts.generated.js +12 -8
  51. package/dist/shiji-sts.js +49 -46
  52. package/dist/stripe-checkout.generated.js +157 -49
  53. package/dist/verifone.generated.js +192 -17
  54. package/dist/verifone.js +1 -1
  55. package/dist/worldline-mycheckout.d.ts +27 -0
  56. package/dist/worldline-mycheckout.generated.d.ts +29 -0
  57. package/dist/worldline-mycheckout.generated.js +162 -0
  58. package/dist/worldline-mycheckout.js +93 -0
  59. package/dist/wspay.d.ts +17 -16
  60. package/dist/wspay.generated.d.ts +6 -1
  61. package/dist/wspay.generated.js +13 -3
  62. package/dist/wspay.js +9 -11
  63. package/examples/preflight/kernel-native/README.md +1 -1
  64. package/examples/preflight/kernel-native/inventory.json +1 -19
  65. package/package.json +3 -3
@@ -20,6 +20,22 @@ export function preparationMode(psp) {
20
20
  export function preparationRequestMethod(psp) {
21
21
  return psp === 'worldpay_eprotect' ? 'GET' : 'POST';
22
22
  }
23
+ /**
24
+ * Whether a prepared checkout's approval outlives the page's own request, as an unprepared token
25
+ * approval does (cdp.ts survivesAbandonment): the page's next request for the same purchase, from
26
+ * the card frame that sent the first, is answered from it, with no second prompt. The approval stays
27
+ * that frame's (cdp.ts sameFrame): another frame's request is refused, and the frame's removal ends it.
28
+ *
29
+ * Bambora only. Custom Checkout's card frame fails its tokenization request 5 seconds after
30
+ * sending it, and a Pay button that runs the tokenization again sends the same placeholder and
31
+ * fields (WooCommerce's Bambora plugin shows the timeout, clears its token fields and lets Pay be
32
+ * clicked again). The cardholder's device answers with a token that pays nothing until the
33
+ * merchant's server charges it. Every other prepared checkout still answers only the request it
34
+ * was bound to.
35
+ */
36
+ export function preparedApprovalAnswersRetry(psp) {
37
+ return psp === 'bambora';
38
+ }
23
39
  /**
24
40
  * Shared endpoint processors cannot attest test/live mode from their URL or key prefix.
25
41
  * Adyen can: test and live are separate hosts and key prefixes. Fiserv's are separate
@@ -356,11 +372,11 @@ function secureAcceptanceClaimRefusal(requestUrl, body, headers, merchantOrigin,
356
372
  /**
357
373
  * The reason a claim reports for a request its preparation does not match. An Amadeus preparation
358
374
  * names what payment-core's Amadeus check refused on its own environment's card URL, as
359
- * `amadeus_<reason>`: Air France's and KLM's add, whose expiry their own form keeps, is
360
- * `amadeus_merchant_held_expiry`, every Data Collection Page store is
361
- * `amadeus_dcp_field_config_unreviewed` until a merchant's field config is reviewed, and a Host
362
- * header that does not name the approved page is `amadeus_header_binding`. Everything else, another
363
- * processor's request or the other environment's host included, is `checkout_changed`.
375
+ * `amadeus_<reason>`: every Data Collection Page store is `amadeus_dcp_field_config_unreviewed`
376
+ * until a merchant's field config is reviewed, an add that keeps the card on file is
377
+ * `amadeus_card_on_file`, and a Host header that does not name the approved page is
378
+ * `amadeus_header_binding`. Everything else, another processor's request or the other
379
+ * environment's host included, is `checkout_changed`.
364
380
  */
365
381
  export function preparedRequestRefusal(psp, environment, requestUrl, body, headers, merchantOrigin, currency) {
366
382
  // A Secure Acceptance token create on the prepared environment's host names what payment-core's request
@@ -1,5 +1,7 @@
1
1
  declare var PRIMER_PCI_HOSTS: readonly string[];
2
2
  declare var PRIMER_TOKENIZE_PATH: string;
3
+ declare var PRIMER_SESSION_CARD_ON_FILE_POLICIES: readonly string[];
4
+ declare var PRIMER_SESSION_CARD_ON_FILE: string;
3
5
  declare var PRIMER_REFUSAL_REASONS: readonly string[];
4
6
  declare var PRIMER_PROVABLE_NETWORKS: readonly string[];
5
7
  declare function isPrimerTokenizeUrl(url: any, method?: string): boolean;
@@ -9,6 +11,8 @@ declare function primerKeepOnFileRequested(body: any): boolean | null;
9
11
  declare function classifyPrimerRequest(url: any, method: any, body: any, headers: any): "card" | "unsupported" | "keep_on_file" | "unsupported_sdk" | null;
10
12
  declare function primerTokenizeRefusal(request: any): "unsupported" | "keep_on_file" | "unsupported_sdk" | null;
11
13
  declare function isPrimerTokenizeRequest(request: any): boolean;
14
+ declare function primerCardOnFileDisclosure(url: any, policy?: string): boolean;
15
+ declare function primerCardOnFileRefusal(url: any, options: any, policy?: string): "unsupported" | "keep_on_file" | null;
12
16
  declare function primerRefusalMessage(reason: any): string;
13
17
  declare function primerCardNetwork(number: any): "VISA" | "AMEX" | "MASTERCARD" | null;
14
18
  declare function primerNetworkRefusal(body: any, card: any): "unsupported" | "network_mismatch" | null;
@@ -35,4 +39,4 @@ declare function primerFacts(status: any, body: any): {
35
39
  outcome: string;
36
40
  refusalCode: any;
37
41
  };
38
- export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
42
+ export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_SESSION_CARD_ON_FILE, PRIMER_SESSION_CARD_ON_FILE_POLICIES, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerCardOnFileDisclosure, primerCardOnFileRefusal, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 6dfbdbf520cb1656f338b88088dde209111190a2cbd3e7767e0e1b6d5e15e5ed
3
+ // artifact-sha256: 3f5ac5b4125cab96cc0851e9b1fe705aa96635e1df05b9216e951140fb47cd43
4
4
  // src/card-number-key.js
5
5
  var CARD_NUMBER_KEY = /^(number|card_number|cardnumber|cardNum|card_num|pan)$/i;
6
6
  // src/json-path.js
@@ -37,7 +37,8 @@ function roundTrips(parsed, body) {
37
37
  // src/elavon-converge.js
38
38
  var CONVERGE_PROCESS_URLS = /* @__PURE__ */ Object.freeze([
39
39
  "https://api.convergepay.com/hosted-payments/service/payment/hpp/process",
40
- "https://api.demo.convergepay.com/hosted-payments/service/payment/hpp/process"
40
+ "https://api.demo.convergepay.com/hosted-payments/service/payment/hpp/process",
41
+ "https://www.convergepay.com/hosted-payments/service/payment/hpp/process"
41
42
  ]);
42
43
  var TOP_LEVEL_KEYS = /* @__PURE__ */ Object.freeze(["paymentType", "paymentData", "context", "captchaToken", "shippingSameAsBilling"]);
43
44
  var WALLET_FIELDS = /* @__PURE__ */ Object.freeze([
@@ -78,15 +79,18 @@ var RECURRING_FIELDS = /* @__PURE__ */ Object.freeze([
78
79
  "ssl_recurring_batch_count",
79
80
  "ssl_installment_id"
80
81
  ]);
82
+ var ONE_TIME_RECURRING_FLAG = "false";
81
83
  var CARD_SAVING_FLAGS = /* @__PURE__ */ Object.freeze(["ssl_get_token", "ssl_add_token"]);
82
84
  var CREDENTIAL_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_account_id", "ssl_merchant_id", "ssl_user_id", "ssl_pin", "ssl_vendor_id"]);
83
85
  var AMOUNT_ADD_ON_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_tip_amount", "ssl_cashback_amount"]);
86
+ var SOLVED_CAPTCHA = /^[A-Za-z0-9_-]{1,4096}$/;
84
87
  var CARD_NUMBER = /^\d{12,19}$/;
85
88
  var EXPIRY_MMYY = /^(0[1-9]|1[0-2])\d{2}$/;
86
89
  var CVC = /^\d{3,4}$/;
87
90
  var AMOUNT = /^(\d{1,13})(?:\.(\d{1,2}))?$/;
88
91
  var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
89
92
  var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
93
+ var recurringRequested = (data) => RECURRING_FIELDS.some((key) => owns(data, key) && !(key === "recurring" && data[key] === ONE_TIME_RECURRING_FLAG));
90
94
  var refuse = (code) => Object.freeze({ verdict: "refuse", code });
91
95
  var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
92
96
  var CONTINUE = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
@@ -135,7 +139,7 @@ function convergeBodyVerdict(body) {
135
139
  return refuse("converge_credential_refused");
136
140
  if (WALLET_FIELDS.some((key) => owns(data, key)))
137
141
  return refuse("converge_wallet_refused");
138
- if (owns(parsed, "captchaToken"))
142
+ if (owns(parsed, "captchaToken") && !(typeof parsed.captchaToken === "string" && SOLVED_CAPTCHA.test(parsed.captchaToken)))
139
143
  return refuse("converge_captcha_refused");
140
144
  if (SERVICE_FEE_FIELDS.some((key) => owns(data, key)))
141
145
  return refuse("converge_service_fee_refused");
@@ -148,7 +152,7 @@ function convergeBodyVerdict(body) {
148
152
  if (owns(data, "ssl_token") || CARD_SAVING_FLAGS.some((key) => owns(data, key) && !(typeof data[key] === "string" && /^n$/i.test(data[key])))) {
149
153
  return refuse("converge_card_saving_refused");
150
154
  }
151
- if (RECURRING_FIELDS.some((key) => owns(data, key)))
155
+ if (recurringRequested(data))
152
156
  return refuse("converge_recurring_refused");
153
157
  if (owns(data, "ssl_transaction_currency") && !(typeof data.ssl_transaction_currency === "string" && /^usd$/i.test(data.ssl_transaction_currency))) {
154
158
  return refuse("converge_currency_refused");
@@ -177,11 +181,155 @@ function isConvergeProcessUrl(url) {
177
181
  }
178
182
  return parsed.href === url && CONVERGE_PROCESS_URLS.includes(parsed.href);
179
183
  }
180
- // src/amadeus-body.js
181
- var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
184
+ // src/gravy.js
185
+ var GRAVY_PSP = "gravy";
186
+ var BODY_LIMIT = 4096;
187
+ var CARD_KEYS = /* @__PURE__ */ Object.freeze(["method", "number", "expiration_date", "security_code"]);
188
+ var ROOT_KEYS = /* @__PURE__ */ Object.freeze(["payment_method", "postal_code"]);
189
+ var CARD_NUMBER2 = /^[0-9]{12,19}$/;
190
+ var EXPIRY = /^(?:0[1-9]|1[0-2])\/[0-9]{2}$/;
191
+ var SECURITY_CODE = /^[0-9]{3,4}$/;
192
+ var POSTAL_CODE = /^[A-Za-z0-9 -]{0,16}$/;
193
+ var PAN_LIKE = /(?:[0-9][ _.:-]*){12,}/;
194
+ var EXPIRY_OR_CODE = /^(?:expiration_?date|expiry(?:_?date)?|exp_?date|card_?expiry|security_?code|cvc|cvc2|cvv|cvv2|cvd|card_?cvv|card_?cvc|verification_?value)$/i;
182
195
  var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
183
196
  var owns2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
184
- var holdsPan = (mopdata) => record(mopdata) && owns2(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
197
+ function cardNamed(key) {
198
+ const name = key.slice(key.lastIndexOf(".") + 1);
199
+ return CARD_NUMBER_KEY.test(name) || /cardnumber$/i.test(name) || EXPIRY_OR_CODE.test(name);
200
+ }
201
+ function cardOutside(root) {
202
+ const pending = [];
203
+ for (const key of Object.keys(root)) {
204
+ const value = root[key];
205
+ if (key === "payment_method" && record(value)) {
206
+ for (const inner of Object.keys(value))
207
+ if (!CARD_KEYS.includes(inner))
208
+ pending.push([inner, value[inner]]);
209
+ }
210
+ else {
211
+ pending.push([key, value]);
212
+ }
213
+ }
214
+ while (pending.length > 0) {
215
+ const [key, value] = pending.pop();
216
+ if (key !== null && cardNamed(key))
217
+ return true;
218
+ if (typeof value === "string" || typeof value === "number") {
219
+ if (PAN_LIKE.test(String(value)))
220
+ return true;
221
+ }
222
+ else if (Array.isArray(value)) {
223
+ for (const item of value)
224
+ pending.push([null, item]);
225
+ }
226
+ else if (record(value)) {
227
+ for (const inner of Object.keys(value))
228
+ pending.push([inner, value[inner]]);
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+ function readBody(body) {
234
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
235
+ return null;
236
+ let value;
237
+ try {
238
+ value = JSON.parse(body);
239
+ if (!record(value) || JSON.stringify(value) !== body)
240
+ return null;
241
+ }
242
+ catch {
243
+ return null;
244
+ }
245
+ return value;
246
+ }
247
+ var refuse2 = (code) => Object.freeze({ verdict: "refuse", code });
248
+ var CARD2 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
249
+ function gravyBodyVerdict(body) {
250
+ const value = readBody(body);
251
+ if (value === null)
252
+ return refuse2("gravy_body_unsupported");
253
+ if (cardOutside(value))
254
+ return refuse2("gravy_card_outside_payment_method");
255
+ const card = value.payment_method;
256
+ if (!record(card) || card.method !== "card" || !CARD_KEYS.every((key) => owns2(card, key)))
257
+ return refuse2("gravy_new_card_missing");
258
+ if (!Object.keys(value).every((key) => ROOT_KEYS.includes(key)) || !Object.keys(card).every((key) => CARD_KEYS.includes(key)) || typeof card.number !== "string" || !CARD_NUMBER2.test(card.number) || typeof card.expiration_date !== "string" || !EXPIRY.test(card.expiration_date) || typeof card.security_code !== "string" || !SECURITY_CODE.test(card.security_code) || owns2(value, "postal_code") && (typeof value.postal_code !== "string" || !POSTAL_CODE.test(value.postal_code))) {
259
+ return refuse2("gravy_body_unsupported");
260
+ }
261
+ return CARD2;
262
+ }
263
+ // src/moneris.js
264
+ var MONERIS_PSP = "moneris";
265
+ var CARD_KEYS2 = /* @__PURE__ */ Object.freeze(["input_data", "input_exp", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]);
266
+ var PARTIAL_KEYS = /* @__PURE__ */ Object.freeze([
267
+ ["input_data", "hpt_id", "hpt_ticket", "doTokenize"],
268
+ ["input_data", "input_exp", "hpt_id", "hpt_ticket", "doTokenize"],
269
+ ["input_data", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]
270
+ ]);
271
+ var CARD_NUMBER3 = /^\d{12,19}$/;
272
+ var GROUPED_DIGITS = /^\d+(?: \d+)*$/;
273
+ var EXPIRY_MMYY2 = /^(0[1-9]|1[0-2])\d{2}$/;
274
+ var CVD = /^\d{3,4}$/;
275
+ var PROFILE_ID = /^ht[A-Za-z0-9]{1,64}$/;
276
+ var TICKET = /^tt[A-Za-z0-9]{1,128}$/;
277
+ var refuse3 = (code) => Object.freeze({ verdict: "refuse", code });
278
+ var CARD3 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
279
+ function readPairs(body) {
280
+ if (typeof body !== "string" || body === "" || body.length > 4096)
281
+ return null;
282
+ const pairs = [];
283
+ for (const chunk of body.split("&")) {
284
+ const eq = chunk.indexOf("=");
285
+ if (eq <= 0)
286
+ return null;
287
+ pairs.push([chunk.slice(0, eq), chunk.slice(eq + 1)]);
288
+ }
289
+ return pairs;
290
+ }
291
+ var hasKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
292
+ function monerisBodyVerdict(body) {
293
+ const pairs = readPairs(body);
294
+ if (pairs === null)
295
+ return refuse3("moneris_body_malformed");
296
+ if (PARTIAL_KEYS.some((keys) => hasKeys(pairs, keys)))
297
+ return refuse3("moneris_partial_card");
298
+ if (!hasKeys(pairs, CARD_KEYS2))
299
+ return refuse3("moneris_body_malformed");
300
+ const [[, number], [, expiry], [, cvd], [, profile], [, ticket], [, tokenize]] = pairs;
301
+ if (!GROUPED_DIGITS.test(number) || !CARD_NUMBER3.test(number.replace(/ /g, "")) || !EXPIRY_MMYY2.test(expiry) || !CVD.test(cvd) || !PROFILE_ID.test(profile) || !TICKET.test(ticket) || tokenize !== "true")
302
+ return refuse3("moneris_body_malformed");
303
+ return CARD3;
304
+ }
305
+ var MONERIS_TOKENIZE_HOSTS = /* @__PURE__ */ Object.freeze({
306
+ production: Object.freeze(["www3.moneris.com", "gateway.moneris.com"]),
307
+ sandbox: Object.freeze(["esqa.moneris.com"])
308
+ });
309
+ var MONERIS_TOKENIZE_PATH = "/HPPtoken/request.php";
310
+ function monerisTokenizeEnvironment(url) {
311
+ if (typeof url !== "string")
312
+ return null;
313
+ for (const environment of
314
+ /** @type {const} */
315
+ ["production", "sandbox"]) {
316
+ if (MONERIS_TOKENIZE_HOSTS[environment].some((host) => url === `https://${host}${MONERIS_TOKENIZE_PATH}`))
317
+ return environment;
318
+ }
319
+ return null;
320
+ }
321
+ function isMonerisTokenizeUrl(url) {
322
+ return monerisTokenizeEnvironment(url) !== null;
323
+ }
324
+ var pin = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fields.origins]), profileIds: Object.freeze([...fields.profileIds]) });
325
+ var MONERIS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
326
+ pin({ id: "jack_org_raisin", merchant: "Jack.org", origins: ["https://jack.akaraisin.com"], profileIds: ["htL7TR9H7ICYN2B"] })
327
+ ]);
328
+ // src/amadeus-body.js
329
+ var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
330
+ var record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
331
+ var owns3 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
332
+ var holdsPan = (mopdata) => record2(mopdata) && owns3(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
185
333
  function amadeusBodyKind(body) {
186
334
  let value;
187
335
  try {
@@ -190,18 +338,25 @@ function amadeusBodyKind(body) {
190
338
  catch {
191
339
  return "card";
192
340
  }
193
- if (!record(value))
341
+ if (!record2(value))
194
342
  return "card";
195
343
  if (value.action === "add") {
196
344
  const entries = Array.isArray(value.data) ? value.data : [value.data];
197
- return entries.some((entry) => record(entry) && typeof entry.mopid === "string" && AMADEUS_CARD_MOPID.test(entry.mopid) && holdsPan(entry.mopdata)) ? "card" : "other";
345
+ return entries.some((entry) => record2(entry) && typeof entry.mopid === "string" && AMADEUS_CARD_MOPID.test(entry.mopid) && holdsPan(entry.mopdata)) ? "card" : "other";
198
346
  }
199
347
  if (value.action === "store")
200
- return record(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
348
+ return record2(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
201
349
  return "other";
202
350
  }
203
351
  // src/card-fields.js
204
352
  function processorGate(recognizer, url, body) {
353
+ if (recognizer && recognizer.psp === GRAVY_PSP)
354
+ return gravyBodyVerdict(body);
355
+ if (recognizer && recognizer.psp === MONERIS_PSP) {
356
+ if (url !== void 0 && !isMonerisTokenizeUrl(url))
357
+ return { verdict: "refuse", code: "moneris_url_refused" };
358
+ return monerisBodyVerdict(body);
359
+ }
205
360
  if (!recognizer || recognizer.psp !== "elavon_converge")
206
361
  return null;
207
362
  if (url !== void 0 && !isConvergeProcessUrl(url))
@@ -305,6 +460,9 @@ function namesCardNumber(root, declared) {
305
460
  // src/primer.js
306
461
  var PRIMER_PCI_HOSTS = /* @__PURE__ */ Object.freeze(["sdk.api.primer.io", "sdk.api.sandbox.primer.io"]);
307
462
  var PRIMER_TOKENIZE_PATH = "/payment-instruments";
463
+ var PRIMER_SESSION_CARD_ON_FILE_POLICIES = /* @__PURE__ */ Object.freeze(["refuse", "disclose"]);
464
+ var PRIMER_SESSION_CARD_ON_FILE = "disclose";
465
+ var LIVE_PCI_HOST = "sdk.api.primer.io";
308
466
  var PRIMER_REFUSAL_REASONS = /* @__PURE__ */ Object.freeze(["unsupported", "keep_on_file", "unsupported_sdk", "network_mismatch"]);
309
467
  var PRIMER_PROVABLE_NETWORKS = /* @__PURE__ */ Object.freeze(["VISA", "MASTERCARD", "AMEX"]);
310
468
  var UNSUPPORTED_REFUSAL = "Refusing to pay: unsupported Primer card request.";
@@ -312,13 +470,13 @@ var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Primer payment would keep the
312
470
  var UNSUPPORTED_SDK_REFUSAL = "Refusing to pay: this checkout uses a Primer SDK Agentcard does not support yet. Nothing was sent.";
313
471
  var NETWORK_MISMATCH_REFUSAL = "Refusing to pay: this Primer payment names a card network that is not your card's. Nothing was sent.";
314
472
  var FILL_REFUSAL = "Could not fill the card fields for Primer. Nothing was sent.";
315
- var BODY_LIMIT = 16384;
473
+ var BODY_LIMIT2 = 16384;
316
474
  var RESPONSE_LIMIT = 32768;
317
- var ROOT_KEYS = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
475
+ var ROOT_KEYS2 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
318
476
  var INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear", "cardholderName", "preferredNetwork"];
319
477
  var REQUIRED_INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear"];
320
- var record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
321
- var owns3 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
478
+ var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
479
+ var owns4 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
322
480
  var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
323
481
  var text = (value, max) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
324
482
  var digits = (value, min, max) => typeof value === "string" && value.length >= min && value.length <= max && !/[^0-9]/.test(value);
@@ -340,7 +498,7 @@ function isPrimerTokenizeUrl(url, method = "POST") {
340
498
  return parsed.href === url && parsed.protocol === "https:" && !parsed.port && !parsed.username && !parsed.password && !parsed.search && !parsed.hash && PRIMER_PCI_HOSTS.includes(parsed.hostname) && parsed.pathname === PRIMER_TOKENIZE_PATH;
341
499
  }
342
500
  function judgeBody(body) {
343
- if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
501
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT2)
344
502
  return null;
345
503
  let value;
346
504
  try {
@@ -349,18 +507,18 @@ function judgeBody(body) {
349
507
  catch {
350
508
  return null;
351
509
  }
352
- if (!record2(value) || !roundTrips(value, body))
510
+ if (!record3(value) || !roundTrips(value, body))
353
511
  return null;
354
- if (!Object.keys(value).every((key) => ROOT_KEYS.includes(key)) || !owns3(value, "paymentInstrument"))
512
+ if (!Object.keys(value).every((key) => ROOT_KEYS2.includes(key)) || !owns4(value, "paymentInstrument"))
355
513
  return null;
356
514
  const instrument = value.paymentInstrument;
357
- if (!record2(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns3(instrument, key)))
515
+ if (!record3(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns4(instrument, key)))
358
516
  return null;
359
517
  if (!digits(instrument.number, 12, 19) || !digits(instrument.cvv, 3, 4) || !exact(MONTH, instrument.expirationMonth) || !exact(YEAR, instrument.expirationYear))
360
518
  return null;
361
- if (owns3(instrument, "cardholderName") && instrument.cardholderName !== null && !plain(instrument.cardholderName, 256) || owns3(instrument, "preferredNetwork") && instrument.preferredNetwork !== null && !exact(NETWORK, instrument.preferredNetwork) || owns3(value, "userDescription") && value.userDescription !== null && !plain(value.userDescription, 256) || owns3(value, "tokenType") && value.tokenType !== "MULTI_USE" || owns3(value, "paymentFlow") && value.paymentFlow !== "VAULT")
519
+ if (owns4(instrument, "cardholderName") && instrument.cardholderName !== null && !plain(instrument.cardholderName, 256) || owns4(instrument, "preferredNetwork") && instrument.preferredNetwork !== null && !exact(NETWORK, instrument.preferredNetwork) || owns4(value, "userDescription") && value.userDescription !== null && !plain(value.userDescription, 256) || owns4(value, "tokenType") && value.tokenType !== "MULTI_USE" || owns4(value, "paymentFlow") && value.paymentFlow !== "VAULT")
362
520
  return null;
363
- return { value, keepOnFile: owns3(value, "tokenType") || owns3(value, "paymentFlow") };
521
+ return { value, keepOnFile: owns4(value, "tokenType") || owns4(value, "paymentFlow") };
364
522
  }
365
523
  function primerBodyRefusal(body) {
366
524
  const judged = judgeBody(body);
@@ -382,7 +540,7 @@ var CLIENT_TOKEN = /^[A-Za-z0-9._~+/=-]{16,4096}$/;
382
540
  var REQUEST_ID = /^[A-Za-z0-9-]{1,64}$/;
383
541
  var API_VERSION = /^2\.[0-9]{1,2}$/;
384
542
  function readHeaders(headers) {
385
- if (!record2(headers))
543
+ if (!record3(headers))
386
544
  return null;
387
545
  const folded = /* @__PURE__ */ new Map();
388
546
  for (const [name, value] of Object.entries(headers)) {
@@ -429,13 +587,23 @@ function onTokenizePath(url) {
429
587
  }
430
588
  }
431
589
  function primerTokenizeRefusal(request) {
432
- if (!record2(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
590
+ if (!record3(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
433
591
  return "unsupported";
434
592
  return headersRefusal(request.headers) ?? primerBodyRefusal(request.body);
435
593
  }
436
594
  function isPrimerTokenizeRequest(request) {
437
595
  return primerTokenizeRefusal(request) === null;
438
596
  }
597
+ function primerCardOnFileDisclosure(url, policy = PRIMER_SESSION_CARD_ON_FILE) {
598
+ return policy === "disclose" && isPrimerTokenizeUrl(url) && new URL(url).hostname === LIVE_PCI_HOST;
599
+ }
600
+ function primerCardOnFileRefusal(url, options, policy = PRIMER_SESSION_CARD_ON_FILE) {
601
+ if (!isPrimerTokenizeUrl(url))
602
+ return "unsupported";
603
+ if (new URL(url).hostname !== LIVE_PCI_HOST)
604
+ return null;
605
+ return primerCardOnFileDisclosure(url, policy) && record3(options) && options.keepOnFile === true ? null : "keep_on_file";
606
+ }
439
607
  function primerRefusalMessage(reason) {
440
608
  if (reason === "keep_on_file")
441
609
  return KEEP_ON_FILE_REFUSAL;
@@ -518,28 +686,28 @@ function readAnswer(body) {
518
686
  return null;
519
687
  try {
520
688
  const value = JSON.parse(body);
521
- return record2(value) ? value : null;
689
+ return record3(value) ? value : null;
522
690
  }
523
691
  catch {
524
692
  return null;
525
693
  }
526
694
  }
527
695
  var tokenValue = (value) => exact(TOKEN, value) && !panLike(value);
528
- var optional = (value, key, check) => !owns3(value, key) || value[key] === null || check(value[key]);
696
+ var optional = (value, key, check) => !owns4(value, key) || value[key] === null || check(value[key]);
529
697
  function cardDetails(data) {
530
- if (!record2(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
698
+ if (!record3(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
531
699
  return null;
532
- if (!optional(data, "first6Digits", (v) => exact(/^[0-9]{6}$/, v)) || !optional(data, "expirationMonth", (v) => exact(RESPONSE_MONTH, v)) || !optional(data, "expirationYear", (v) => exact(RESPONSE_YEAR, v)) || !optional(data, "cardholderName", (v) => plain(v, 256)) || !optional(data, "network", (v) => exact(NETWORK_NAME, v)) || !optional(data, "isNetworkTokenized", (v) => typeof v === "boolean") || !optional(data, "binData", record2))
700
+ if (!optional(data, "first6Digits", (v) => exact(/^[0-9]{6}$/, v)) || !optional(data, "expirationMonth", (v) => exact(RESPONSE_MONTH, v)) || !optional(data, "expirationYear", (v) => exact(RESPONSE_YEAR, v)) || !optional(data, "cardholderName", (v) => plain(v, 256)) || !optional(data, "network", (v) => exact(NETWORK_NAME, v)) || !optional(data, "isNetworkTokenized", (v) => typeof v === "boolean") || !optional(data, "binData", record3))
533
701
  return null;
534
702
  const safe = { last4Digits: data.last4Digits };
535
703
  for (const key of ["first6Digits", "expirationMonth", "expirationYear", "cardholderName", "network", "isNetworkTokenized"]) {
536
- if (owns3(data, key) && data[key] !== null)
704
+ if (owns4(data, key) && data[key] !== null)
537
705
  safe[key] = data[key];
538
706
  }
539
- if (record2(data.binData)) {
707
+ if (record3(data.binData)) {
540
708
  const bin = {};
541
709
  for (const key of BIN_KEYS) {
542
- if (!owns3(data.binData, key) || data.binData[key] === null)
710
+ if (!owns4(data.binData, key) || data.binData[key] === null)
543
711
  continue;
544
712
  if (!plain(data.binData[key], 128))
545
713
  return null;
@@ -552,11 +720,11 @@ function cardDetails(data) {
552
720
  function threeDsSummary(value) {
553
721
  if (value === void 0 || value === null)
554
722
  return null;
555
- if (!record2(value) || !exact(CODE, value.responseCode) || !optional(value, "reasonCode", (v) => exact(CODE, v)) || !optional(value, "reasonText", (v) => plain(v, 256)) || !optional(value, "protocolVersion", (v) => v === "" || exact(PROTOCOL, v)) || !optional(value, "challengeIssued", (v) => typeof v === "boolean"))
723
+ if (!record3(value) || !exact(CODE, value.responseCode) || !optional(value, "reasonCode", (v) => exact(CODE, v)) || !optional(value, "reasonText", (v) => plain(v, 256)) || !optional(value, "protocolVersion", (v) => v === "" || exact(PROTOCOL, v)) || !optional(value, "challengeIssued", (v) => typeof v === "boolean"))
556
724
  return void 0;
557
725
  const safe = { responseCode: value.responseCode };
558
726
  for (const key of ["reasonCode", "reasonText", "protocolVersion", "challengeIssued"]) {
559
- if (owns3(value, key) && value[key] !== null)
727
+ if (owns4(value, key) && value[key] !== null)
560
728
  safe[key] = value[key];
561
729
  }
562
730
  return safe;
@@ -569,7 +737,7 @@ function rebuildToken(value) {
569
737
  if (!details || threeDs === void 0)
570
738
  return null;
571
739
  const safe = { token: value.token };
572
- if (owns3(value, "analyticsId") && value.analyticsId !== null)
740
+ if (owns4(value, "analyticsId") && value.analyticsId !== null)
573
741
  safe.analyticsId = value.analyticsId;
574
742
  safe.tokenType = "SINGLE_USE";
575
743
  safe.paymentInstrumentType = "PAYMENT_CARD";
@@ -580,7 +748,7 @@ function rebuildToken(value) {
580
748
  }
581
749
  function primerErrorId(body) {
582
750
  const value = readAnswer(body);
583
- const id = value && record2(value.error) ? value.error.errorId : null;
751
+ const id = value && record3(value.error) ? value.error.errorId : null;
584
752
  return exact(ERROR_ID, id) && !panLike(id) ? id : null;
585
753
  }
586
754
  function matchesCard(details, card) {
@@ -621,4 +789,4 @@ function primerFacts(status, body) {
621
789
  const verdict = primerResponseOutcome(status, body);
622
790
  return { outcome: verdict.outcome, refusalCode: verdict.outcome === "refused" ? verdict.code : null };
623
791
  }
624
- export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
792
+ export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_SESSION_CARD_ON_FILE, PRIMER_SESSION_CARD_ON_FILE_POLICIES, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerCardOnFileDisclosure, primerCardOnFileRefusal, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
@@ -118,7 +118,9 @@ export interface Recognizer {
118
118
  * names `authorize_net_dpm_form`, Pelecard names `pelecard_transaction`,
119
119
  * Primer names `primer_payment_instruments`, Payrails names
120
120
  * `payrails_device_encrypt`, Elavon Converge names `converge_hpp_process`,
121
- * WSPay names `wspay_confirm` and Shiji STS names `shiji_sts_pinned_key`.
121
+ * WSPay names `wspay_confirm`, Shiji STS names `shiji_sts_pinned_key`, Gravy
122
+ * names `gravy_session_fields_put` and Moneris Hosted Tokenization names
123
+ * `moneris_hosted_tokenization`.
122
124
  */
123
125
  capability?: string;
124
126
  /**
@@ -165,13 +167,15 @@ export interface Recognizer {
165
167
  /**
166
168
  * The card request's shape when it is not a POST body. Worldpay eProtect
167
169
  * declares `{ method: 'GET', cardIn: 'query', response: 'jsonp' }`: its card
168
- * rides in the query of one JSONP <script> GET. Absent means a POST. Carried
170
+ * rides in the query of one JSONP <script> GET. Gravy declares
171
+ * `{ method: 'PUT', cardIn: 'body', response: 'no_content' }`: its card rides in
172
+ * the JSON body of a PUT, as a POST's does. Absent means a POST. Carried
169
173
  * verbatim by syncRegistry; isCardRequest reads `method` through
170
- * recognizerRequestMethod, and pauses a GET only for a processor whose exact
171
- * query this build can check.
174
+ * recognizerRequestMethod, pauses a PUT as it pauses a POST, and pauses a GET
175
+ * only for a processor whose exact query this build can check.
172
176
  */
173
177
  request?: {
174
- method: 'GET' | 'POST';
178
+ method: 'GET' | 'POST' | 'PUT';
175
179
  cardIn?: string;
176
180
  response?: string;
177
181
  };
@@ -212,15 +216,17 @@ export declare function urlsOnProcessorHosts(url: string, rec: Recognizer, opts?
212
216
  export declare function findRecognizer(url: string, registry?: Recognizer[], opts?: RecognizerOptions): Recognizer | null;
213
217
  /**
214
218
  * The HTTP method of a recognizer's card request, the rule payment-core's
215
- * recognizerRequestMethod applies: GET only when the entry declares
216
- * `request.method: 'GET'` (Worldpay eProtect), POST for everything else, so a
217
- * value that rode in over the wire as anything else never widens a POST
218
- * recognizer.
219
+ * recognizerRequestMethod applies: GET or PUT only when the entry declares that
220
+ * `request.method` (Worldpay eProtect's GET, Gravy's PUT), POST for everything
221
+ * else, so a value that rode in over the wire as anything else never widens a
222
+ * POST recognizer.
219
223
  */
220
- export declare function recognizerRequestMethod(rec: Pick<Recognizer, 'request'>): 'GET' | 'POST';
224
+ export declare function recognizerRequestMethod(rec: Pick<Recognizer, 'request'>): 'GET' | 'POST' | 'PUT';
221
225
  /**
222
- * Whether `method` is this recognizer's card request method for `url`. A GET
223
- * carries the card in its URL, so a GET recognizer is a card request only for a
226
+ * Whether `method` is this recognizer's card request method for `url`. A PUT
227
+ * recognizer (Gravy) carries the card in its body as a POST one does, so its PUT
228
+ * pauses like a POST, and neither pauses in the other's method. A GET carries
229
+ * the card in its URL, so a GET recognizer is a card request only for a
224
230
  * processor whose exact query this build checks (Worldpay eProtect, through
225
231
  * payment-core's grammar); any other GET recognizer is nothing this build can
226
232
  * complete and is never paused.
package/dist/registry.js CHANGED
@@ -128,17 +128,20 @@ export function findRecognizer(url, registry = BUILTIN_REGISTRY, opts = {}) {
128
128
  }
129
129
  /**
130
130
  * The HTTP method of a recognizer's card request, the rule payment-core's
131
- * recognizerRequestMethod applies: GET only when the entry declares
132
- * `request.method: 'GET'` (Worldpay eProtect), POST for everything else, so a
133
- * value that rode in over the wire as anything else never widens a POST
134
- * recognizer.
131
+ * recognizerRequestMethod applies: GET or PUT only when the entry declares that
132
+ * `request.method` (Worldpay eProtect's GET, Gravy's PUT), POST for everything
133
+ * else, so a value that rode in over the wire as anything else never widens a
134
+ * POST recognizer.
135
135
  */
136
136
  export function recognizerRequestMethod(rec) {
137
- return rec?.request?.method === 'GET' ? 'GET' : 'POST';
137
+ const method = rec?.request?.method;
138
+ return method === 'GET' || method === 'PUT' ? method : 'POST';
138
139
  }
139
140
  /**
140
- * Whether `method` is this recognizer's card request method for `url`. A GET
141
- * carries the card in its URL, so a GET recognizer is a card request only for a
141
+ * Whether `method` is this recognizer's card request method for `url`. A PUT
142
+ * recognizer (Gravy) carries the card in its body as a POST one does, so its PUT
143
+ * pauses like a POST, and neither pauses in the other's method. A GET carries
144
+ * the card in its URL, so a GET recognizer is a card request only for a
142
145
  * processor whose exact query this build checks (Worldpay eProtect, through
143
146
  * payment-core's grammar); any other GET recognizer is nothing this build can
144
147
  * complete and is never paused.
@@ -147,7 +150,7 @@ export function isRecognizerRequest(rec, url, method) {
147
150
  const expected = recognizerRequestMethod(rec);
148
151
  if (typeof method !== 'string' || method.toUpperCase() !== expected)
149
152
  return false;
150
- return expected === 'POST' || (rec.psp === EPROTECT_PSP && isEprotectTokenRequest(url, 'GET'));
153
+ return expected !== 'GET' || (rec.psp === EPROTECT_PSP && isEprotectTokenRequest(url, 'GET'));
151
154
  }
152
155
  /**
153
156
  * True when this processor cannot be taken over on an ordinary authorization: