@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
package/dist/aci.d.ts CHANGED
@@ -140,15 +140,17 @@ export declare class AciGate {
140
140
  /** The card-number request's approval ended, answered or not; unanswered siblings fail. */
141
141
  end(): void;
142
142
  /**
143
- * A request on this checkout was refused before any card-number request began (a card-number
144
- * request that asks ACI to keep the card, a leg the widget never sends): the siblings waiting for
145
- * one fail now, and so does every later sibling until a card-number request begins. A running or
146
- * answered card-number request keeps its siblings.
143
+ * A request on this checkout was refused. When it names the card number (a card-number request
144
+ * that asks ACI to keep the card, or carries a field the widget never sends with it) and no
145
+ * card-number request began, the siblings waiting for one fail now, and so does every later sibling
146
+ * until a card-number request begins. A refused request that names no card number, such as a
147
+ * fingerprint longer than ACI takes, says nothing about the card-number request a later Pay sends,
148
+ * so its siblings keep waiting for it. A running or answered card-number request keeps its siblings.
147
149
  */
148
150
  refused(leg: {
149
151
  host: string;
150
152
  checkoutId: string;
151
- }): void;
153
+ }, body: string | null | undefined): void;
152
154
  /**
153
155
  * A sibling leg (the page's CVC or expiry request): the answer to hand it, once its checkout's
154
156
  * card-number request was answered, or null when that request failed, never began within
@@ -11,6 +11,7 @@ declare var ACI_ABSORBED_FIELDS: readonly string[];
11
11
  declare var ACI_PASSED_FIELDS: readonly string[];
12
12
  declare var ACI_SIBLING_KEYS: readonly string[];
13
13
  declare var ACI_REMOVED_ON_CONTINUATION: readonly string[];
14
+ declare var ACI_FINGERPRINT_FIELD: string;
14
15
  declare function aciEndpoint(url: any): {
15
16
  kind: string;
16
17
  host: string;
@@ -84,4 +85,4 @@ declare function aciResponseOutcome(status: any, text: any): {
84
85
  reason?: undefined;
85
86
  };
86
87
  declare function sanitizeAciResponse(text: any): string;
87
- export { ACI_ABSORBED_FIELDS, ACI_BRAND_MISMATCH, ACI_CARD_FIELD, ACI_CARD_ON_FILE_REFUSED, ACI_CHECKOUT_UPDATED, ACI_EXPIRY_UNSTRIPPABLE, ACI_HOSTS, ACI_PASSED_FIELDS, ACI_REMOVED_ON_CONTINUATION, ACI_RESULT_URL_REFUSED, ACI_SIBLING_KEYS, ACI_TEST_HOSTS, ACI_TEST_PANS, ACI_UNSUPPORTED_REQUEST, aciBrandForNetwork, aciBrandOfCardLeg, aciCardBrand, aciEndpoint, aciEnvironment, aciFacts, aciHostEnvironment, aciPlaceholderBrandRefusal, aciResponseOutcome, classifyAciRequest, injectAciCard, isAciCardOnFileKey, isAciCardRequest, isPreparedAciRequest, prepareAciContinuation, readAciForm, sanitizeAciResponse };
88
+ export { ACI_ABSORBED_FIELDS, ACI_BRAND_MISMATCH, ACI_CARD_FIELD, ACI_CARD_ON_FILE_REFUSED, ACI_CHECKOUT_UPDATED, ACI_EXPIRY_UNSTRIPPABLE, ACI_FINGERPRINT_FIELD, ACI_HOSTS, ACI_PASSED_FIELDS, ACI_REMOVED_ON_CONTINUATION, ACI_RESULT_URL_REFUSED, ACI_SIBLING_KEYS, ACI_TEST_HOSTS, ACI_TEST_PANS, ACI_UNSUPPORTED_REQUEST, aciBrandForNetwork, aciBrandOfCardLeg, aciCardBrand, aciEndpoint, aciEnvironment, aciFacts, aciHostEnvironment, aciPlaceholderBrandRefusal, aciResponseOutcome, classifyAciRequest, injectAciCard, isAciCardOnFileKey, isAciCardRequest, isPreparedAciRequest, prepareAciContinuation, readAciForm, sanitizeAciResponse };
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: b5b6d696111cb2906b73f99dc9ed21cbf51580bd0799183694ed5147c8a10e66
3
+ // artifact-sha256: 624ca03645e28d72156188c850b3650fd0f5162b0366475e6a124657120f7f11
4
4
  // src/aci.js
5
5
  var ACI_HOSTS = Object.freeze(["eu-prod.oppwa.com", "oppwa.com", "eu-test.oppwa.com", "test.oppwa.com"]);
6
6
  var ACI_TEST_HOSTS = Object.freeze(["eu-test.oppwa.com", "test.oppwa.com"]);
@@ -22,6 +22,9 @@ var ACI_ABSORBED_FIELDS = Object.freeze(["card.cvv", "card.expiryMonth", "card.e
22
22
  var ACI_PASSED_FIELDS = Object.freeze(["card.holder"]);
23
23
  var ACI_SIBLING_KEYS = Object.freeze(["customParameters[SHOPPER_EndToEndIdentity]"]);
24
24
  var ACI_REMOVED_ON_CONTINUATION = Object.freeze(["card.expiryMonth", "card.expiryYear"]);
25
+ var ACI_FINGERPRINT_FIELD = "customer.browserFingerprint.value";
26
+ var FINGERPRINT_LENGTH = 4096;
27
+ var LEG_BODY_LIMIT = 16384;
25
28
  var END_TO_END = ACI_SIBLING_KEYS[0];
26
29
  var CHECKOUT_ID = /^[0-9A-F]{32}\.[a-z0-9]{1,16}-vm-tx[0-9]{1,3}$/i;
27
30
  var ENDPOINT_PATH = /^\/v1\/checkouts\/([^/]+?)(\/payment|\/registration)?$/;
@@ -76,7 +79,8 @@ function readAciForm(body, limit = 8192) {
76
79
  catch {
77
80
  return null;
78
81
  }
79
- if (!key || key.length > 128 || CONTROL.test(key) || CONTROL.test(value) || value.length > 2048 || seen.has(key))
82
+ const longest = key === ACI_FINGERPRINT_FIELD ? FINGERPRINT_LENGTH : 2048;
83
+ if (!key || key.length > 128 || CONTROL.test(key) || CONTROL.test(value) || value.length > longest || seen.has(key))
80
84
  return null;
81
85
  seen.add(key);
82
86
  pairs.push({ key, value, raw });
@@ -117,11 +121,13 @@ function classifyAciRequest(request) {
117
121
  return refuse(ACI_CARD_ON_FILE_REFUSED);
118
122
  if (endpoint.kind === "payment")
119
123
  return { leg: "continuation", ...at };
120
- const pairs = readAciForm(request.body);
124
+ const pairs = readAciForm(request.body, LEG_BODY_LIMIT);
121
125
  if (!pairs)
122
126
  return refuse(ACI_UNSUPPORTED_REQUEST);
123
127
  if (pairs.some(({ key }) => isAciCardOnFileKey(key)))
124
128
  return refuse(ACI_CARD_ON_FILE_REFUSED);
129
+ if (pairs.length === 1 && pairs[0].key === ACI_FINGERPRINT_FIELD)
130
+ return { leg: "pass", ...at };
125
131
  if (!pairs.every(({ key, value }) => validField(key, value)))
126
132
  return refuse(ACI_UNSUPPORTED_REQUEST);
127
133
  const own = pairs.map(({ key }) => key).filter((key) => key !== END_TO_END);
@@ -199,6 +205,7 @@ function aciPlaceholderBrandRefusal(templateBody, card) {
199
205
  var CONTINUATION_KEYS = /* @__PURE__ */ new Set([
200
206
  "paymentBrand",
201
207
  "shopperResultUrl",
208
+ "shopperResultTarget",
202
209
  "shopOrigin",
203
210
  "forceUtf8",
204
211
  "card.holder",
@@ -250,6 +257,9 @@ function prepareAciContinuation(request, context) {
250
257
  if (!pairs.every(({ key }) => continuationKey(key)))
251
258
  return refuse(ACI_UNSUPPORTED_REQUEST);
252
259
  const value = (key) => pairs.find((pair) => pair.key === key)?.value;
260
+ const target = value("shopperResultTarget");
261
+ if (target !== void 0 && target !== "_top")
262
+ return refuse(ACI_UNSUPPORTED_REQUEST);
253
263
  if (typeof brand !== "string" || !/^[A-Z]{2,16}$/.test(brand) || value("paymentBrand") !== brand)
254
264
  return refuse(ACI_BRAND_MISMATCH);
255
265
  if (!onOrigin(value("shopperResultUrl"), merchantOrigin))
@@ -303,4 +313,4 @@ function sanitizeAciResponse(text) {
303
313
  throw new Error("Refusing to store an unconfirmed ACI answer.");
304
314
  return JSON.stringify({ result: { code: ACI_CHECKOUT_UPDATED, description: "successfully updated checkout" } });
305
315
  }
306
- export { ACI_ABSORBED_FIELDS, ACI_BRAND_MISMATCH, ACI_CARD_FIELD, ACI_CARD_ON_FILE_REFUSED, ACI_CHECKOUT_UPDATED, ACI_EXPIRY_UNSTRIPPABLE, ACI_HOSTS, ACI_PASSED_FIELDS, ACI_REMOVED_ON_CONTINUATION, ACI_RESULT_URL_REFUSED, ACI_SIBLING_KEYS, ACI_TEST_HOSTS, ACI_TEST_PANS, ACI_UNSUPPORTED_REQUEST, aciBrandForNetwork, aciBrandOfCardLeg, aciCardBrand, aciEndpoint, aciEnvironment, aciFacts, aciHostEnvironment, aciPlaceholderBrandRefusal, aciResponseOutcome, classifyAciRequest, injectAciCard, isAciCardOnFileKey, isAciCardRequest, isPreparedAciRequest, prepareAciContinuation, readAciForm, sanitizeAciResponse };
316
+ export { ACI_ABSORBED_FIELDS, ACI_BRAND_MISMATCH, ACI_CARD_FIELD, ACI_CARD_ON_FILE_REFUSED, ACI_CHECKOUT_UPDATED, ACI_EXPIRY_UNSTRIPPABLE, ACI_FINGERPRINT_FIELD, ACI_HOSTS, ACI_PASSED_FIELDS, ACI_REMOVED_ON_CONTINUATION, ACI_RESULT_URL_REFUSED, ACI_SIBLING_KEYS, ACI_TEST_HOSTS, ACI_TEST_PANS, ACI_UNSUPPORTED_REQUEST, aciBrandForNetwork, aciBrandOfCardLeg, aciCardBrand, aciEndpoint, aciEnvironment, aciFacts, aciHostEnvironment, aciPlaceholderBrandRefusal, aciResponseOutcome, classifyAciRequest, injectAciCard, isAciCardOnFileKey, isAciCardRequest, isPreparedAciRequest, prepareAciContinuation, readAciForm, sanitizeAciResponse };
package/dist/aci.js CHANGED
@@ -12,7 +12,10 @@
12
12
  // - the page's own CVC and expiry requests carry the agent's placeholder values: they are held
13
13
  // and answered with the card-number request's answer once it lands, never forwarded, and
14
14
  // failed when it does not (or when no card-number request for their checkout ever begins);
15
- // - the holder request carries nothing secret and continues;
15
+ // - the holder request and the ReD Shield device fingerprint the widget posts on load carry
16
+ // nothing secret and continue;
17
+ // - a refused request fails the checkout's waiting CVC and expiry requests only when it is that
18
+ // checkout's card-number request: a refusal that names no card number leaves them waiting;
16
19
  // - a second card-number request on a checkout whose first one is running or was answered here
17
20
  // is refused: that answer put the real card on the checkout and may already have let /payment
18
21
  // through, and ACI warns that one checkout id reused can carry a second transaction;
@@ -22,7 +25,7 @@
22
25
  // It continues only as a frame navigation, which is how the widget's form posts it into its
23
26
  // hidden iframe, and a classic-mode body only on a browser shown to send the body the SDK
24
27
  // replaced on a navigation (Chromium; see playwrightReplacesNavigationBody).
25
- import { aciBrandOfCardLeg as brandOfCardLeg, aciEndpoint, classifyAciRequest, prepareAciContinuation, sanitizeAciResponse, } from './aci.generated.js';
28
+ import { ACI_CARD_FIELD, aciBrandOfCardLeg as brandOfCardLeg, aciEndpoint, classifyAciRequest, prepareAciContinuation, sanitizeAciResponse, } from './aci.generated.js';
26
29
  const classifyLeg = classifyAciRequest;
27
30
  const judgePayment = prepareAciContinuation;
28
31
  /** The processor id, as the registry and the API name it. */
@@ -164,6 +167,20 @@ export async function playwrightReplacesNavigationBody(page, probeMs = ACI_ENGIN
164
167
  }
165
168
  }
166
169
  const groupKey = (leg) => `${leg.host}/${leg.checkoutId}`;
170
+ /** Whether a form body has a card.number key, whatever else it carries and however it broke the rules. */
171
+ function namesCardNumber(body) {
172
+ if (typeof body !== 'string')
173
+ return false;
174
+ return body.split('&').some((pair) => {
175
+ const eq = pair.indexOf('=');
176
+ try {
177
+ return decodeURIComponent((eq < 0 ? pair : pair.slice(0, eq)).replace(/\+/g, ' ')) === ACI_CARD_FIELD;
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ });
183
+ }
167
184
  /**
168
185
  * One attachment's ACI state, keyed by checkout (host and checkout id). Every sibling and every
169
186
  * /payment submission is matched against a card-number request this attachment itself paused and
@@ -280,12 +297,16 @@ export class AciGate {
280
297
  this.prune();
281
298
  }
282
299
  /**
283
- * A request on this checkout was refused before any card-number request began (a card-number
284
- * request that asks ACI to keep the card, a leg the widget never sends): the siblings waiting for
285
- * one fail now, and so does every later sibling until a card-number request begins. A running or
286
- * answered card-number request keeps its siblings.
300
+ * A request on this checkout was refused. When it names the card number (a card-number request
301
+ * that asks ACI to keep the card, or carries a field the widget never sends with it) and no
302
+ * card-number request began, the siblings waiting for one fail now, and so does every later sibling
303
+ * until a card-number request begins. A refused request that names no card number, such as a
304
+ * fingerprint longer than ACI takes, says nothing about the card-number request a later Pay sends,
305
+ * so its siblings keep waiting for it. A running or answered card-number request keeps its siblings.
287
306
  */
288
- refused(leg) {
307
+ refused(leg, body) {
308
+ if (!namesCardNumber(body))
309
+ return;
289
310
  const key = groupKey(leg);
290
311
  const known = this.groups.get(key);
291
312
  if (known && known.state !== 'failed')
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: bc8b9a0ad66eb321bec67650079b2d157a84f9f8de4ad79cc9006b937a9a5a58
3
+ // artifact-sha256: d537d04a5cd64f34af196a0f6cec7a7f5319bb396d94824131857a04632aab0a
4
4
  // src/amadeus-body.js
5
5
  var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
6
6
  var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -44,7 +44,7 @@ var AMADEUS_CARD_URLS = Object.freeze({
44
44
  })
45
45
  });
46
46
  var AMADEUS_MERCHANT_EXPIRY_POLICIES = Object.freeze(["refuse", "fill"]);
47
- var AMADEUS_MERCHANT_EXPIRY_POLICY = "refuse";
47
+ var AMADEUS_MERCHANT_EXPIRY_POLICY = "fill";
48
48
  var AMADEUS_REFUSAL_REASONS = Object.freeze([
49
49
  "unsupported",
50
50
  "card_on_file",
@@ -1 +1 @@
1
- export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "sandboxHosts": ["^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "sandboxHosts": ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "sandboxHosts": ["^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "sandboxHosts": ["^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "sandboxHosts": ["^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "sandboxHosts": ["^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [], "jsonCardFields": [["paymentOption", "card", "cardNumber"], ["cardData", "cardNumber"]], "passWithoutCard": "(?:secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:clientPayment|websdk/initPaymentWithCardTokenization)\\.do" }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "sandboxHosts": ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "sandboxHosts": ["^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "sandboxHosts": ["^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "sandboxHosts": ["^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "sandboxHosts": ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "sandboxHosts": ["^esqa\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }, { "psp": "gravy", "match": /api(\.sandbox)?\.[a-z0-9-]+\.gr4vy\.app\/checkout\/sessions\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/fields/i, "hosts": ["^api\\.[a-z0-9-]+\\.gr4vy\\.app$", "^api\\.sandbox\\.[a-z0-9-]+\\.gr4vy\\.app$"], "sandboxHosts": ["^api\\.sandbox\\.[a-z0-9-]+\\.gr4vy\\.app$"], "encoding": "json", "passthroughHeaders": [] }];
1
+ export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|sources|confirmation_tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm|payment_pages\/cs_(?:test|live)_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i], "cardFields": ["card[number]", "payment_method_data[card][number]"], "passWithoutCard": "api\\.stripe\\.com/v1/(sources|confirmation_tokens|payment_pages/cs_(?:test|live)_\\w+/confirm)" }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "sandboxHosts": ["^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "sandboxHosts": ["^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "sandboxHosts": ["^checkoutshopper-test\\.adyen\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "sandboxHosts": ["^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "sandboxHosts": ["^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "sandboxHosts": ["^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "sandboxHosts": ["^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [], "jsonCardFields": [["paymentOption", "card", "cardNumber"], ["cardData", "cardNumber"]], "passWithoutCard": "(?:secure|ppp-test)\\.safecharge\\.com/ppp/api/v1/(?:clientPayment|websdk/initPaymentWithCardTokenization)\\.do" }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "sandboxHosts": ["^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "sandboxHosts": ["^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "sandboxHosts": ["^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "sandboxHosts": ["^sandbox\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "sandboxHosts": ["^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "sandboxHosts": ["^api\\.test\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "sandboxHosts": ["^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 5c2c8becc2301811aee1b42b47148039ab07c8854932a21e80687e18090f1a93
3
+ // artifact-sha256: 35f4549be2f1addcdcbaf1c7874a5022db72c1b74f27d85119b8c7da7757bd8c
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
@@ -29,7 +29,8 @@ function jsonAt(root, path) {
29
29
  // src/elavon-converge.js
30
30
  var CONVERGE_PROCESS_URLS = /* @__PURE__ */ Object.freeze([
31
31
  "https://api.convergepay.com/hosted-payments/service/payment/hpp/process",
32
- "https://api.demo.convergepay.com/hosted-payments/service/payment/hpp/process"
32
+ "https://api.demo.convergepay.com/hosted-payments/service/payment/hpp/process",
33
+ "https://www.convergepay.com/hosted-payments/service/payment/hpp/process"
33
34
  ]);
34
35
  var TOP_LEVEL_KEYS = /* @__PURE__ */ Object.freeze(["paymentType", "paymentData", "context", "captchaToken", "shippingSameAsBilling"]);
35
36
  var WALLET_FIELDS = /* @__PURE__ */ Object.freeze([
@@ -70,15 +71,18 @@ var RECURRING_FIELDS = /* @__PURE__ */ Object.freeze([
70
71
  "ssl_recurring_batch_count",
71
72
  "ssl_installment_id"
72
73
  ]);
74
+ var ONE_TIME_RECURRING_FLAG = "false";
73
75
  var CARD_SAVING_FLAGS = /* @__PURE__ */ Object.freeze(["ssl_get_token", "ssl_add_token"]);
74
76
  var CREDENTIAL_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_account_id", "ssl_merchant_id", "ssl_user_id", "ssl_pin", "ssl_vendor_id"]);
75
77
  var AMOUNT_ADD_ON_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_tip_amount", "ssl_cashback_amount"]);
78
+ var SOLVED_CAPTCHA = /^[A-Za-z0-9_-]{1,4096}$/;
76
79
  var CARD_NUMBER = /^\d{12,19}$/;
77
80
  var EXPIRY_MMYY = /^(0[1-9]|1[0-2])\d{2}$/;
78
81
  var CVC = /^\d{3,4}$/;
79
82
  var AMOUNT = /^(\d{1,13})(?:\.(\d{1,2}))?$/;
80
83
  var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
81
84
  var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
85
+ var recurringRequested = (data) => RECURRING_FIELDS.some((key) => owns(data, key) && !(key === "recurring" && data[key] === ONE_TIME_RECURRING_FLAG));
82
86
  var refuse = (code) => Object.freeze({ verdict: "refuse", code });
83
87
  var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
84
88
  var CONTINUE = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
@@ -127,7 +131,7 @@ function convergeBodyVerdict(body) {
127
131
  return refuse("converge_credential_refused");
128
132
  if (WALLET_FIELDS.some((key) => owns(data, key)))
129
133
  return refuse("converge_wallet_refused");
130
- if (owns(parsed, "captchaToken"))
134
+ if (owns(parsed, "captchaToken") && !(typeof parsed.captchaToken === "string" && SOLVED_CAPTCHA.test(parsed.captchaToken)))
131
135
  return refuse("converge_captcha_refused");
132
136
  if (SERVICE_FEE_FIELDS.some((key) => owns(data, key)))
133
137
  return refuse("converge_service_fee_refused");
@@ -140,7 +144,7 @@ function convergeBodyVerdict(body) {
140
144
  if (owns(data, "ssl_token") || CARD_SAVING_FLAGS.some((key) => owns(data, key) && !(typeof data[key] === "string" && /^n$/i.test(data[key])))) {
141
145
  return refuse("converge_card_saving_refused");
142
146
  }
143
- if (RECURRING_FIELDS.some((key) => owns(data, key)))
147
+ if (recurringRequested(data))
144
148
  return refuse("converge_recurring_refused");
145
149
  if (owns(data, "ssl_transaction_currency") && !(typeof data.ssl_transaction_currency === "string" && /^usd$/i.test(data.ssl_transaction_currency))) {
146
150
  return refuse("converge_currency_refused");
@@ -169,11 +173,155 @@ function isConvergeProcessUrl(url) {
169
173
  }
170
174
  return parsed.href === url && CONVERGE_PROCESS_URLS.includes(parsed.href);
171
175
  }
172
- // src/amadeus-body.js
173
- var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
176
+ // src/gravy.js
177
+ var GRAVY_PSP = "gravy";
178
+ var BODY_LIMIT = 4096;
179
+ var CARD_KEYS = /* @__PURE__ */ Object.freeze(["method", "number", "expiration_date", "security_code"]);
180
+ var ROOT_KEYS = /* @__PURE__ */ Object.freeze(["payment_method", "postal_code"]);
181
+ var CARD_NUMBER2 = /^[0-9]{12,19}$/;
182
+ var EXPIRY = /^(?:0[1-9]|1[0-2])\/[0-9]{2}$/;
183
+ var SECURITY_CODE = /^[0-9]{3,4}$/;
184
+ var POSTAL_CODE = /^[A-Za-z0-9 -]{0,16}$/;
185
+ var PAN_LIKE = /(?:[0-9][ _.:-]*){12,}/;
186
+ 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;
174
187
  var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
175
188
  var owns2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
176
- var holdsPan = (mopdata) => record(mopdata) && owns2(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
189
+ function cardNamed(key) {
190
+ const name = key.slice(key.lastIndexOf(".") + 1);
191
+ return CARD_NUMBER_KEY.test(name) || /cardnumber$/i.test(name) || EXPIRY_OR_CODE.test(name);
192
+ }
193
+ function cardOutside(root) {
194
+ const pending = [];
195
+ for (const key of Object.keys(root)) {
196
+ const value = root[key];
197
+ if (key === "payment_method" && record(value)) {
198
+ for (const inner of Object.keys(value))
199
+ if (!CARD_KEYS.includes(inner))
200
+ pending.push([inner, value[inner]]);
201
+ }
202
+ else {
203
+ pending.push([key, value]);
204
+ }
205
+ }
206
+ while (pending.length > 0) {
207
+ const [key, value] = pending.pop();
208
+ if (key !== null && cardNamed(key))
209
+ return true;
210
+ if (typeof value === "string" || typeof value === "number") {
211
+ if (PAN_LIKE.test(String(value)))
212
+ return true;
213
+ }
214
+ else if (Array.isArray(value)) {
215
+ for (const item of value)
216
+ pending.push([null, item]);
217
+ }
218
+ else if (record(value)) {
219
+ for (const inner of Object.keys(value))
220
+ pending.push([inner, value[inner]]);
221
+ }
222
+ }
223
+ return false;
224
+ }
225
+ function readBody(body) {
226
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
227
+ return null;
228
+ let value;
229
+ try {
230
+ value = JSON.parse(body);
231
+ if (!record(value) || JSON.stringify(value) !== body)
232
+ return null;
233
+ }
234
+ catch {
235
+ return null;
236
+ }
237
+ return value;
238
+ }
239
+ var refuse2 = (code) => Object.freeze({ verdict: "refuse", code });
240
+ var CARD2 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
241
+ function gravyBodyVerdict(body) {
242
+ const value = readBody(body);
243
+ if (value === null)
244
+ return refuse2("gravy_body_unsupported");
245
+ if (cardOutside(value))
246
+ return refuse2("gravy_card_outside_payment_method");
247
+ const card = value.payment_method;
248
+ if (!record(card) || card.method !== "card" || !CARD_KEYS.every((key) => owns2(card, key)))
249
+ return refuse2("gravy_new_card_missing");
250
+ 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))) {
251
+ return refuse2("gravy_body_unsupported");
252
+ }
253
+ return CARD2;
254
+ }
255
+ // src/moneris.js
256
+ var MONERIS_PSP = "moneris";
257
+ var CARD_KEYS2 = /* @__PURE__ */ Object.freeze(["input_data", "input_exp", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]);
258
+ var PARTIAL_KEYS = /* @__PURE__ */ Object.freeze([
259
+ ["input_data", "hpt_id", "hpt_ticket", "doTokenize"],
260
+ ["input_data", "input_exp", "hpt_id", "hpt_ticket", "doTokenize"],
261
+ ["input_data", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]
262
+ ]);
263
+ var CARD_NUMBER3 = /^\d{12,19}$/;
264
+ var GROUPED_DIGITS = /^\d+(?: \d+)*$/;
265
+ var EXPIRY_MMYY2 = /^(0[1-9]|1[0-2])\d{2}$/;
266
+ var CVD = /^\d{3,4}$/;
267
+ var PROFILE_ID = /^ht[A-Za-z0-9]{1,64}$/;
268
+ var TICKET = /^tt[A-Za-z0-9]{1,128}$/;
269
+ var refuse3 = (code) => Object.freeze({ verdict: "refuse", code });
270
+ var CARD3 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
271
+ function readPairs(body) {
272
+ if (typeof body !== "string" || body === "" || body.length > 4096)
273
+ return null;
274
+ const pairs = [];
275
+ for (const chunk of body.split("&")) {
276
+ const eq = chunk.indexOf("=");
277
+ if (eq <= 0)
278
+ return null;
279
+ pairs.push([chunk.slice(0, eq), chunk.slice(eq + 1)]);
280
+ }
281
+ return pairs;
282
+ }
283
+ var hasKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
284
+ function monerisBodyVerdict(body) {
285
+ const pairs = readPairs(body);
286
+ if (pairs === null)
287
+ return refuse3("moneris_body_malformed");
288
+ if (PARTIAL_KEYS.some((keys) => hasKeys(pairs, keys)))
289
+ return refuse3("moneris_partial_card");
290
+ if (!hasKeys(pairs, CARD_KEYS2))
291
+ return refuse3("moneris_body_malformed");
292
+ const [[, number], [, expiry], [, cvd], [, profile], [, ticket], [, tokenize]] = pairs;
293
+ 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")
294
+ return refuse3("moneris_body_malformed");
295
+ return CARD3;
296
+ }
297
+ var MONERIS_TOKENIZE_HOSTS = /* @__PURE__ */ Object.freeze({
298
+ production: Object.freeze(["www3.moneris.com", "gateway.moneris.com"]),
299
+ sandbox: Object.freeze(["esqa.moneris.com"])
300
+ });
301
+ var MONERIS_TOKENIZE_PATH = "/HPPtoken/request.php";
302
+ function monerisTokenizeEnvironment(url) {
303
+ if (typeof url !== "string")
304
+ return null;
305
+ for (const environment of
306
+ /** @type {const} */
307
+ ["production", "sandbox"]) {
308
+ if (MONERIS_TOKENIZE_HOSTS[environment].some((host) => url === `https://${host}${MONERIS_TOKENIZE_PATH}`))
309
+ return environment;
310
+ }
311
+ return null;
312
+ }
313
+ function isMonerisTokenizeUrl(url) {
314
+ return monerisTokenizeEnvironment(url) !== null;
315
+ }
316
+ var pin = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fields.origins]), profileIds: Object.freeze([...fields.profileIds]) });
317
+ var MONERIS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
318
+ pin({ id: "jack_org_raisin", merchant: "Jack.org", origins: ["https://jack.akaraisin.com"], profileIds: ["htL7TR9H7ICYN2B"] })
319
+ ]);
320
+ // src/amadeus-body.js
321
+ var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
322
+ var record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
323
+ var owns3 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
324
+ var holdsPan = (mopdata) => record2(mopdata) && owns3(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
177
325
  function amadeusBodyKind(body) {
178
326
  let value;
179
327
  try {
@@ -182,18 +330,25 @@ function amadeusBodyKind(body) {
182
330
  catch {
183
331
  return "card";
184
332
  }
185
- if (!record(value))
333
+ if (!record2(value))
186
334
  return "card";
187
335
  if (value.action === "add") {
188
336
  const entries = Array.isArray(value.data) ? value.data : [value.data];
189
- return entries.some((entry) => record(entry) && typeof entry.mopid === "string" && AMADEUS_CARD_MOPID.test(entry.mopid) && holdsPan(entry.mopdata)) ? "card" : "other";
337
+ return entries.some((entry) => record2(entry) && typeof entry.mopid === "string" && AMADEUS_CARD_MOPID.test(entry.mopid) && holdsPan(entry.mopdata)) ? "card" : "other";
190
338
  }
191
339
  if (value.action === "store")
192
- return record(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
340
+ return record2(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
193
341
  return "other";
194
342
  }
195
343
  // src/card-fields.js
196
344
  function processorGate(recognizer, url, body) {
345
+ if (recognizer && recognizer.psp === GRAVY_PSP)
346
+ return gravyBodyVerdict(body);
347
+ if (recognizer && recognizer.psp === MONERIS_PSP) {
348
+ if (url !== void 0 && !isMonerisTokenizeUrl(url))
349
+ return { verdict: "refuse", code: "moneris_url_refused" };
350
+ return monerisBodyVerdict(body);
351
+ }
197
352
  if (!recognizer || recognizer.psp !== "elavon_converge")
198
353
  return null;
199
354
  if (url !== void 0 && !isConvergeProcessUrl(url))