@agent-cards/sdk 0.23.9 → 0.23.10

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 (58) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/PREFLIGHT.md +7 -5
  3. package/README.md +181 -92
  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 +163 -9
  11. package/dist/cdp.js +79 -34
  12. package/dist/client.d.ts +61 -31
  13. package/dist/client.js +99 -42
  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 +1 -1
  20. package/dist/elavon-converge.generated.js +7 -4
  21. package/dist/elavon-converge.js +4 -3
  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/payrails.d.ts +78 -18
  27. package/dist/payrails.generated.d.ts +12 -2
  28. package/dist/payrails.generated.js +213 -18
  29. package/dist/payrails.js +165 -44
  30. package/dist/pre-claim.js +51 -29
  31. package/dist/preflight-capabilities.generated.d.ts +0 -106
  32. package/dist/preflight-capabilities.generated.js +0 -153
  33. package/dist/preflight-catalog.json +61 -181
  34. package/dist/preflight-schemas.json +6 -6
  35. package/dist/preflight.generated.d.ts +0 -106
  36. package/dist/preflight.generated.js +27 -15
  37. package/dist/preparation.d.ts +2 -0
  38. package/dist/preparation.js +4 -2
  39. package/dist/prepared-processor.d.ts +19 -5
  40. package/dist/prepared-processor.js +21 -5
  41. package/dist/primer.generated.d.ts +5 -1
  42. package/dist/primer.generated.js +200 -33
  43. package/dist/registry.d.ts +18 -12
  44. package/dist/registry.js +11 -8
  45. package/dist/shiji-sts.d.ts +24 -22
  46. package/dist/shiji-sts.generated.d.ts +1 -1
  47. package/dist/shiji-sts.generated.js +12 -8
  48. package/dist/shiji-sts.js +49 -46
  49. package/dist/stripe-checkout.generated.js +146 -49
  50. package/dist/verifone.generated.js +192 -17
  51. package/dist/verifone.js +1 -1
  52. package/dist/wspay.d.ts +17 -16
  53. package/dist/wspay.generated.d.ts +6 -1
  54. package/dist/wspay.generated.js +13 -3
  55. package/dist/wspay.js +9 -11
  56. package/examples/preflight/kernel-native/README.md +1 -1
  57. package/examples/preflight/kernel-native/inventory.json +1 -19
  58. 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: bbfd8946fd3963bf32fe66d5c17ba50dd69e141187c1d5896ee60c33b3aa0e41
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,6 +79,7 @@ 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"]);
@@ -87,6 +89,7 @@ var CVC = /^\d{3,4}$/;
87
89
  var AMOUNT = /^(\d{1,13})(?:\.(\d{1,2}))?$/;
88
90
  var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
89
91
  var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
92
+ var recurringRequested = (data) => RECURRING_FIELDS.some((key) => owns(data, key) && !(key === "recurring" && data[key] === ONE_TIME_RECURRING_FLAG));
90
93
  var refuse = (code) => Object.freeze({ verdict: "refuse", code });
91
94
  var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
92
95
  var CONTINUE = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
@@ -148,7 +151,7 @@ function convergeBodyVerdict(body) {
148
151
  if (owns(data, "ssl_token") || CARD_SAVING_FLAGS.some((key) => owns(data, key) && !(typeof data[key] === "string" && /^n$/i.test(data[key])))) {
149
152
  return refuse("converge_card_saving_refused");
150
153
  }
151
- if (RECURRING_FIELDS.some((key) => owns(data, key)))
154
+ if (recurringRequested(data))
152
155
  return refuse("converge_recurring_refused");
153
156
  if (owns(data, "ssl_transaction_currency") && !(typeof data.ssl_transaction_currency === "string" && /^usd$/i.test(data.ssl_transaction_currency))) {
154
157
  return refuse("converge_currency_refused");
@@ -177,11 +180,155 @@ function isConvergeProcessUrl(url) {
177
180
  }
178
181
  return parsed.href === url && CONVERGE_PROCESS_URLS.includes(parsed.href);
179
182
  }
180
- // src/amadeus-body.js
181
- var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
183
+ // src/gravy.js
184
+ var GRAVY_PSP = "gravy";
185
+ var BODY_LIMIT = 4096;
186
+ var CARD_KEYS = /* @__PURE__ */ Object.freeze(["method", "number", "expiration_date", "security_code"]);
187
+ var ROOT_KEYS = /* @__PURE__ */ Object.freeze(["payment_method", "postal_code"]);
188
+ var CARD_NUMBER2 = /^[0-9]{12,19}$/;
189
+ var EXPIRY = /^(?:0[1-9]|1[0-2])\/[0-9]{2}$/;
190
+ var SECURITY_CODE = /^[0-9]{3,4}$/;
191
+ var POSTAL_CODE = /^[A-Za-z0-9 -]{0,16}$/;
192
+ var PAN_LIKE = /(?:[0-9][ _.:-]*){12,}/;
193
+ 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
194
  var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
183
195
  var owns2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
184
- var holdsPan = (mopdata) => record(mopdata) && owns2(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
196
+ function cardNamed(key) {
197
+ const name = key.slice(key.lastIndexOf(".") + 1);
198
+ return CARD_NUMBER_KEY.test(name) || /cardnumber$/i.test(name) || EXPIRY_OR_CODE.test(name);
199
+ }
200
+ function cardOutside(root) {
201
+ const pending = [];
202
+ for (const key of Object.keys(root)) {
203
+ const value = root[key];
204
+ if (key === "payment_method" && record(value)) {
205
+ for (const inner of Object.keys(value))
206
+ if (!CARD_KEYS.includes(inner))
207
+ pending.push([inner, value[inner]]);
208
+ }
209
+ else {
210
+ pending.push([key, value]);
211
+ }
212
+ }
213
+ while (pending.length > 0) {
214
+ const [key, value] = pending.pop();
215
+ if (key !== null && cardNamed(key))
216
+ return true;
217
+ if (typeof value === "string" || typeof value === "number") {
218
+ if (PAN_LIKE.test(String(value)))
219
+ return true;
220
+ }
221
+ else if (Array.isArray(value)) {
222
+ for (const item of value)
223
+ pending.push([null, item]);
224
+ }
225
+ else if (record(value)) {
226
+ for (const inner of Object.keys(value))
227
+ pending.push([inner, value[inner]]);
228
+ }
229
+ }
230
+ return false;
231
+ }
232
+ function readBody(body) {
233
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
234
+ return null;
235
+ let value;
236
+ try {
237
+ value = JSON.parse(body);
238
+ if (!record(value) || JSON.stringify(value) !== body)
239
+ return null;
240
+ }
241
+ catch {
242
+ return null;
243
+ }
244
+ return value;
245
+ }
246
+ var refuse2 = (code) => Object.freeze({ verdict: "refuse", code });
247
+ var CARD2 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
248
+ function gravyBodyVerdict(body) {
249
+ const value = readBody(body);
250
+ if (value === null)
251
+ return refuse2("gravy_body_unsupported");
252
+ if (cardOutside(value))
253
+ return refuse2("gravy_card_outside_payment_method");
254
+ const card = value.payment_method;
255
+ if (!record(card) || card.method !== "card" || !CARD_KEYS.every((key) => owns2(card, key)))
256
+ return refuse2("gravy_new_card_missing");
257
+ 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))) {
258
+ return refuse2("gravy_body_unsupported");
259
+ }
260
+ return CARD2;
261
+ }
262
+ // src/moneris.js
263
+ var MONERIS_PSP = "moneris";
264
+ var CARD_KEYS2 = /* @__PURE__ */ Object.freeze(["input_data", "input_exp", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]);
265
+ var PARTIAL_KEYS = /* @__PURE__ */ Object.freeze([
266
+ ["input_data", "hpt_id", "hpt_ticket", "doTokenize"],
267
+ ["input_data", "input_exp", "hpt_id", "hpt_ticket", "doTokenize"],
268
+ ["input_data", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]
269
+ ]);
270
+ var CARD_NUMBER3 = /^\d{12,19}$/;
271
+ var GROUPED_DIGITS = /^\d+(?: \d+)*$/;
272
+ var EXPIRY_MMYY2 = /^(0[1-9]|1[0-2])\d{2}$/;
273
+ var CVD = /^\d{3,4}$/;
274
+ var PROFILE_ID = /^ht[A-Za-z0-9]{1,64}$/;
275
+ var TICKET = /^tt[A-Za-z0-9]{1,128}$/;
276
+ var refuse3 = (code) => Object.freeze({ verdict: "refuse", code });
277
+ var CARD3 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
278
+ function readPairs(body) {
279
+ if (typeof body !== "string" || body === "" || body.length > 4096)
280
+ return null;
281
+ const pairs = [];
282
+ for (const chunk of body.split("&")) {
283
+ const eq = chunk.indexOf("=");
284
+ if (eq <= 0)
285
+ return null;
286
+ pairs.push([chunk.slice(0, eq), chunk.slice(eq + 1)]);
287
+ }
288
+ return pairs;
289
+ }
290
+ var hasKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
291
+ function monerisBodyVerdict(body) {
292
+ const pairs = readPairs(body);
293
+ if (pairs === null)
294
+ return refuse3("moneris_body_malformed");
295
+ if (PARTIAL_KEYS.some((keys) => hasKeys(pairs, keys)))
296
+ return refuse3("moneris_partial_card");
297
+ if (!hasKeys(pairs, CARD_KEYS2))
298
+ return refuse3("moneris_body_malformed");
299
+ const [[, number], [, expiry], [, cvd], [, profile], [, ticket], [, tokenize]] = pairs;
300
+ 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")
301
+ return refuse3("moneris_body_malformed");
302
+ return CARD3;
303
+ }
304
+ var MONERIS_TOKENIZE_HOSTS = /* @__PURE__ */ Object.freeze({
305
+ production: Object.freeze(["www3.moneris.com", "gateway.moneris.com"]),
306
+ sandbox: Object.freeze(["esqa.moneris.com"])
307
+ });
308
+ var MONERIS_TOKENIZE_PATH = "/HPPtoken/request.php";
309
+ function monerisTokenizeEnvironment(url) {
310
+ if (typeof url !== "string")
311
+ return null;
312
+ for (const environment of
313
+ /** @type {const} */
314
+ ["production", "sandbox"]) {
315
+ if (MONERIS_TOKENIZE_HOSTS[environment].some((host) => url === `https://${host}${MONERIS_TOKENIZE_PATH}`))
316
+ return environment;
317
+ }
318
+ return null;
319
+ }
320
+ function isMonerisTokenizeUrl(url) {
321
+ return monerisTokenizeEnvironment(url) !== null;
322
+ }
323
+ var pin = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fields.origins]), profileIds: Object.freeze([...fields.profileIds]) });
324
+ var MONERIS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
325
+ pin({ id: "jack_org_raisin", merchant: "Jack.org", origins: ["https://jack.akaraisin.com"], profileIds: ["htL7TR9H7ICYN2B"] })
326
+ ]);
327
+ // src/amadeus-body.js
328
+ var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
329
+ var record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
330
+ var owns3 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
331
+ var holdsPan = (mopdata) => record2(mopdata) && owns3(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
185
332
  function amadeusBodyKind(body) {
186
333
  let value;
187
334
  try {
@@ -190,18 +337,25 @@ function amadeusBodyKind(body) {
190
337
  catch {
191
338
  return "card";
192
339
  }
193
- if (!record(value))
340
+ if (!record2(value))
194
341
  return "card";
195
342
  if (value.action === "add") {
196
343
  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";
344
+ return entries.some((entry) => record2(entry) && typeof entry.mopid === "string" && AMADEUS_CARD_MOPID.test(entry.mopid) && holdsPan(entry.mopdata)) ? "card" : "other";
198
345
  }
199
346
  if (value.action === "store")
200
- return record(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
347
+ return record2(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
201
348
  return "other";
202
349
  }
203
350
  // src/card-fields.js
204
351
  function processorGate(recognizer, url, body) {
352
+ if (recognizer && recognizer.psp === GRAVY_PSP)
353
+ return gravyBodyVerdict(body);
354
+ if (recognizer && recognizer.psp === MONERIS_PSP) {
355
+ if (url !== void 0 && !isMonerisTokenizeUrl(url))
356
+ return { verdict: "refuse", code: "moneris_url_refused" };
357
+ return monerisBodyVerdict(body);
358
+ }
205
359
  if (!recognizer || recognizer.psp !== "elavon_converge")
206
360
  return null;
207
361
  if (url !== void 0 && !isConvergeProcessUrl(url))
@@ -305,6 +459,9 @@ function namesCardNumber(root, declared) {
305
459
  // src/primer.js
306
460
  var PRIMER_PCI_HOSTS = /* @__PURE__ */ Object.freeze(["sdk.api.primer.io", "sdk.api.sandbox.primer.io"]);
307
461
  var PRIMER_TOKENIZE_PATH = "/payment-instruments";
462
+ var PRIMER_SESSION_CARD_ON_FILE_POLICIES = /* @__PURE__ */ Object.freeze(["refuse", "disclose"]);
463
+ var PRIMER_SESSION_CARD_ON_FILE = "disclose";
464
+ var LIVE_PCI_HOST = "sdk.api.primer.io";
308
465
  var PRIMER_REFUSAL_REASONS = /* @__PURE__ */ Object.freeze(["unsupported", "keep_on_file", "unsupported_sdk", "network_mismatch"]);
309
466
  var PRIMER_PROVABLE_NETWORKS = /* @__PURE__ */ Object.freeze(["VISA", "MASTERCARD", "AMEX"]);
310
467
  var UNSUPPORTED_REFUSAL = "Refusing to pay: unsupported Primer card request.";
@@ -312,13 +469,13 @@ var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Primer payment would keep the
312
469
  var UNSUPPORTED_SDK_REFUSAL = "Refusing to pay: this checkout uses a Primer SDK Agentcard does not support yet. Nothing was sent.";
313
470
  var NETWORK_MISMATCH_REFUSAL = "Refusing to pay: this Primer payment names a card network that is not your card's. Nothing was sent.";
314
471
  var FILL_REFUSAL = "Could not fill the card fields for Primer. Nothing was sent.";
315
- var BODY_LIMIT = 16384;
472
+ var BODY_LIMIT2 = 16384;
316
473
  var RESPONSE_LIMIT = 32768;
317
- var ROOT_KEYS = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
474
+ var ROOT_KEYS2 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
318
475
  var INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear", "cardholderName", "preferredNetwork"];
319
476
  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);
477
+ var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
478
+ var owns4 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
322
479
  var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
323
480
  var text = (value, max) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
324
481
  var digits = (value, min, max) => typeof value === "string" && value.length >= min && value.length <= max && !/[^0-9]/.test(value);
@@ -340,7 +497,7 @@ function isPrimerTokenizeUrl(url, method = "POST") {
340
497
  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
498
  }
342
499
  function judgeBody(body) {
343
- if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
500
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT2)
344
501
  return null;
345
502
  let value;
346
503
  try {
@@ -349,18 +506,18 @@ function judgeBody(body) {
349
506
  catch {
350
507
  return null;
351
508
  }
352
- if (!record2(value) || !roundTrips(value, body))
509
+ if (!record3(value) || !roundTrips(value, body))
353
510
  return null;
354
- if (!Object.keys(value).every((key) => ROOT_KEYS.includes(key)) || !owns3(value, "paymentInstrument"))
511
+ if (!Object.keys(value).every((key) => ROOT_KEYS2.includes(key)) || !owns4(value, "paymentInstrument"))
355
512
  return null;
356
513
  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)))
514
+ if (!record3(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns4(instrument, key)))
358
515
  return null;
359
516
  if (!digits(instrument.number, 12, 19) || !digits(instrument.cvv, 3, 4) || !exact(MONTH, instrument.expirationMonth) || !exact(YEAR, instrument.expirationYear))
360
517
  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")
518
+ 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
519
  return null;
363
- return { value, keepOnFile: owns3(value, "tokenType") || owns3(value, "paymentFlow") };
520
+ return { value, keepOnFile: owns4(value, "tokenType") || owns4(value, "paymentFlow") };
364
521
  }
365
522
  function primerBodyRefusal(body) {
366
523
  const judged = judgeBody(body);
@@ -382,7 +539,7 @@ var CLIENT_TOKEN = /^[A-Za-z0-9._~+/=-]{16,4096}$/;
382
539
  var REQUEST_ID = /^[A-Za-z0-9-]{1,64}$/;
383
540
  var API_VERSION = /^2\.[0-9]{1,2}$/;
384
541
  function readHeaders(headers) {
385
- if (!record2(headers))
542
+ if (!record3(headers))
386
543
  return null;
387
544
  const folded = /* @__PURE__ */ new Map();
388
545
  for (const [name, value] of Object.entries(headers)) {
@@ -429,13 +586,23 @@ function onTokenizePath(url) {
429
586
  }
430
587
  }
431
588
  function primerTokenizeRefusal(request) {
432
- if (!record2(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
589
+ if (!record3(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
433
590
  return "unsupported";
434
591
  return headersRefusal(request.headers) ?? primerBodyRefusal(request.body);
435
592
  }
436
593
  function isPrimerTokenizeRequest(request) {
437
594
  return primerTokenizeRefusal(request) === null;
438
595
  }
596
+ function primerCardOnFileDisclosure(url, policy = PRIMER_SESSION_CARD_ON_FILE) {
597
+ return policy === "disclose" && isPrimerTokenizeUrl(url) && new URL(url).hostname === LIVE_PCI_HOST;
598
+ }
599
+ function primerCardOnFileRefusal(url, options, policy = PRIMER_SESSION_CARD_ON_FILE) {
600
+ if (!isPrimerTokenizeUrl(url))
601
+ return "unsupported";
602
+ if (new URL(url).hostname !== LIVE_PCI_HOST)
603
+ return null;
604
+ return primerCardOnFileDisclosure(url, policy) && record3(options) && options.keepOnFile === true ? null : "keep_on_file";
605
+ }
439
606
  function primerRefusalMessage(reason) {
440
607
  if (reason === "keep_on_file")
441
608
  return KEEP_ON_FILE_REFUSAL;
@@ -518,28 +685,28 @@ function readAnswer(body) {
518
685
  return null;
519
686
  try {
520
687
  const value = JSON.parse(body);
521
- return record2(value) ? value : null;
688
+ return record3(value) ? value : null;
522
689
  }
523
690
  catch {
524
691
  return null;
525
692
  }
526
693
  }
527
694
  var tokenValue = (value) => exact(TOKEN, value) && !panLike(value);
528
- var optional = (value, key, check) => !owns3(value, key) || value[key] === null || check(value[key]);
695
+ var optional = (value, key, check) => !owns4(value, key) || value[key] === null || check(value[key]);
529
696
  function cardDetails(data) {
530
- if (!record2(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
697
+ if (!record3(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
531
698
  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))
699
+ 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
700
  return null;
534
701
  const safe = { last4Digits: data.last4Digits };
535
702
  for (const key of ["first6Digits", "expirationMonth", "expirationYear", "cardholderName", "network", "isNetworkTokenized"]) {
536
- if (owns3(data, key) && data[key] !== null)
703
+ if (owns4(data, key) && data[key] !== null)
537
704
  safe[key] = data[key];
538
705
  }
539
- if (record2(data.binData)) {
706
+ if (record3(data.binData)) {
540
707
  const bin = {};
541
708
  for (const key of BIN_KEYS) {
542
- if (!owns3(data.binData, key) || data.binData[key] === null)
709
+ if (!owns4(data.binData, key) || data.binData[key] === null)
543
710
  continue;
544
711
  if (!plain(data.binData[key], 128))
545
712
  return null;
@@ -552,11 +719,11 @@ function cardDetails(data) {
552
719
  function threeDsSummary(value) {
553
720
  if (value === void 0 || value === null)
554
721
  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"))
722
+ 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
723
  return void 0;
557
724
  const safe = { responseCode: value.responseCode };
558
725
  for (const key of ["reasonCode", "reasonText", "protocolVersion", "challengeIssued"]) {
559
- if (owns3(value, key) && value[key] !== null)
726
+ if (owns4(value, key) && value[key] !== null)
560
727
  safe[key] = value[key];
561
728
  }
562
729
  return safe;
@@ -569,7 +736,7 @@ function rebuildToken(value) {
569
736
  if (!details || threeDs === void 0)
570
737
  return null;
571
738
  const safe = { token: value.token };
572
- if (owns3(value, "analyticsId") && value.analyticsId !== null)
739
+ if (owns4(value, "analyticsId") && value.analyticsId !== null)
573
740
  safe.analyticsId = value.analyticsId;
574
741
  safe.tokenType = "SINGLE_USE";
575
742
  safe.paymentInstrumentType = "PAYMENT_CARD";
@@ -580,7 +747,7 @@ function rebuildToken(value) {
580
747
  }
581
748
  function primerErrorId(body) {
582
749
  const value = readAnswer(body);
583
- const id = value && record2(value.error) ? value.error.errorId : null;
750
+ const id = value && record3(value.error) ? value.error.errorId : null;
584
751
  return exact(ERROR_ID, id) && !panLike(id) ? id : null;
585
752
  }
586
753
  function matchesCard(details, card) {
@@ -621,4 +788,4 @@ function primerFacts(status, body) {
621
788
  const verdict = primerResponseOutcome(status, body);
622
789
  return { outcome: verdict.outcome, refusalCode: verdict.outcome === "refused" ? verdict.code : null };
623
790
  }
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 };
791
+ 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: