@agent-cards/sdk 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/PREFLIGHT.md +30 -0
- package/README.md +1070 -21
- package/dist/aci.d.ts +173 -0
- package/dist/aci.generated.d.ts +87 -0
- package/dist/aci.generated.js +306 -0
- package/dist/aci.js +371 -0
- package/dist/adyen-merchant-hosted.generated.d.ts +3 -3
- package/dist/adyen-merchant-hosted.generated.js +283 -49
- package/dist/agentcard.d.ts +9 -2
- package/dist/agentcard.js +4 -2
- package/dist/amadeus-checkout.generated.d.ts +57 -0
- package/dist/amadeus-checkout.generated.js +549 -0
- package/dist/amadeus.d.ts +41 -0
- package/dist/amadeus.js +81 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/card-endpoint.d.ts +177 -0
- package/dist/card-endpoint.js +286 -0
- package/dist/card-fields.generated.d.ts +5 -3
- package/dist/card-fields.generated.js +284 -7
- package/dist/cdp.d.ts +5 -1
- package/dist/cdp.js +841 -135
- package/dist/client.d.ts +403 -19
- package/dist/client.js +949 -98
- package/dist/creditguard.generated.d.ts +79 -0
- package/dist/creditguard.generated.js +662 -0
- package/dist/cse-body.js +11 -1
- package/dist/datatrans.d.ts +96 -0
- package/dist/datatrans.generated.d.ts +64 -0
- package/dist/datatrans.generated.js +426 -0
- package/dist/datatrans.js +178 -0
- package/dist/elavon-converge.d.ts +37 -0
- package/dist/elavon-converge.generated.d.ts +49 -0
- package/dist/elavon-converge.generated.js +439 -0
- package/dist/elavon-converge.js +84 -0
- package/dist/eprotect.d.ts +152 -0
- package/dist/eprotect.generated.d.ts +77 -0
- package/dist/eprotect.generated.js +498 -0
- package/dist/eprotect.js +222 -0
- package/dist/hosted-form.d.ts +3 -2
- package/dist/hosted-form.js +3 -2
- package/dist/index.d.ts +17 -3
- package/dist/index.js +6 -1
- package/dist/juspay-gate.d.ts +82 -0
- package/dist/juspay-gate.js +126 -0
- package/dist/juspay.generated.d.ts +89 -0
- package/dist/juspay.generated.js +705 -0
- package/dist/lifecycle.d.ts +57 -6
- package/dist/lifecycle.js +119 -8
- package/dist/merchant-card-endpoints.generated.d.ts +63 -0
- package/dist/merchant-card-endpoints.generated.js +1103 -0
- package/dist/merchant-hosted.d.ts +4 -1
- package/dist/merchant-hosted.js +5 -2
- package/dist/merchant-session-probe.d.ts +48 -0
- package/dist/merchant-session-probe.js +53 -0
- package/dist/merchant-session.generated.d.ts +69 -0
- package/dist/merchant-session.generated.js +919 -0
- package/dist/payrails.d.ts +133 -0
- package/dist/payrails.generated.d.ts +73 -0
- package/dist/payrails.generated.js +596 -0
- package/dist/payrails.js +305 -0
- package/dist/pelecard.d.ts +32 -0
- package/dist/pelecard.generated.d.ts +32 -0
- package/dist/pelecard.generated.js +283 -0
- package/dist/pelecard.js +42 -0
- package/dist/pre-claim.d.ts +72 -2
- package/dist/pre-claim.js +857 -5
- package/dist/preflight-catalog.json +2218 -287
- package/dist/preflight-schemas.json +179 -6
- package/dist/preflight.generated.d.ts +1 -1
- package/dist/preflight.generated.js +233 -4
- package/dist/preparation.d.ts +5 -2
- package/dist/preparation.js +36 -10
- package/dist/prepared-processor.d.ts +84 -5
- package/dist/prepared-processor.js +147 -6
- package/dist/primer.d.ts +53 -0
- package/dist/primer.generated.d.ts +38 -0
- package/dist/primer.generated.js +624 -0
- package/dist/primer.js +82 -0
- package/dist/registry.d.ts +100 -8
- package/dist/registry.js +85 -11
- package/dist/shiji-sts.d.ts +144 -0
- package/dist/shiji-sts.generated.d.ts +72 -0
- package/dist/shiji-sts.generated.js +913 -0
- package/dist/shiji-sts.js +222 -0
- package/dist/stripe-checkout.generated.d.ts +1 -1
- package/dist/stripe-checkout.generated.js +699 -9
- package/dist/stripe-setup.d.ts +16 -0
- package/dist/stripe-setup.js +61 -0
- package/dist/substitutions.generated.js +187 -29
- package/dist/verifone.d.ts +68 -0
- package/dist/verifone.generated.d.ts +51 -0
- package/dist/verifone.generated.js +1401 -0
- package/dist/verifone.js +119 -0
- package/dist/voice-types.d.ts +27 -0
- package/dist/voice.d.ts +6 -1
- package/dist/voice.js +6 -1
- package/dist/wspay.d.ts +41 -0
- package/dist/wspay.generated.d.ts +41 -0
- package/dist/wspay.generated.js +370 -0
- package/dist/wspay.js +65 -0
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** A SetupIntent confirm's path, as Stripe.js sends it. */
|
|
2
|
+
export declare const STRIPE_SETUP_INTENT_CONFIRM: RegExp;
|
|
3
|
+
/**
|
|
4
|
+
* The page's SetupIntent confirm, paying with `paymentMethod` instead of the
|
|
5
|
+
* card it carries: every `payment_method_data[...]` field goes (the card, and
|
|
6
|
+
* the billing details and telemetry Stripe.js sends beside it; the payment
|
|
7
|
+
* method already carries the billing details of the approved request), and
|
|
8
|
+
* `payment_method=<id>` takes the place of the first one. Every other field
|
|
9
|
+
* stays byte for byte as the page wrote it (client_secret, key,
|
|
10
|
+
* expected_payment_method_type, use_stripe_sdk, return_url, mandate and option
|
|
11
|
+
* fields). Null, and nothing is rewritten, when the id is not a payment
|
|
12
|
+
* method's, when a key cannot be decoded, when the body already names another
|
|
13
|
+
* way to pay (payment_method, source, confirmation_token), or when it carries
|
|
14
|
+
* no payment_method_data at all.
|
|
15
|
+
*/
|
|
16
|
+
export declare function setupConfirmPayingWith(body: string, paymentMethod: string): string | null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The SetupIntent confirm a Stripe card save continues with, shared by both
|
|
2
|
+
// adapters (cdp.ts claimStripeContinuation). Kept in its own module, outside the
|
|
3
|
+
// package's published entries, so its tests can reach it.
|
|
4
|
+
//
|
|
5
|
+
// A page that creates the payment method first (createPaymentMethod, which the
|
|
6
|
+
// cardholder approves) and then confirms a SetupIntent usually pays with the
|
|
7
|
+
// pm_ it got back: Twilio's Payment Element page with paymentMethodCreation
|
|
8
|
+
// 'manual' does, on basil and v3 Stripe.js (flows harness F12). Some pages send
|
|
9
|
+
// the card again inline instead, as payment_method_data[card][number] in
|
|
10
|
+
// POST /v1/setup_intents/{seti}/confirm: confirmCardSetup with the Card
|
|
11
|
+
// Element after createPaymentMethod does (F12b). The card there is whatever the
|
|
12
|
+
// agent typed; the approved payment method holds the cardholder's card. So that
|
|
13
|
+
// confirm continues paying with the approved payment method instead, once the
|
|
14
|
+
// API allows it.
|
|
15
|
+
/** A SetupIntent confirm's path, as Stripe.js sends it. */
|
|
16
|
+
export const STRIPE_SETUP_INTENT_CONFIRM = /^\/v1\/setup_intents\/seti_[A-Za-z0-9]+\/confirm$/;
|
|
17
|
+
const PAYMENT_METHOD_ID = /^pm_[A-Za-z0-9]{1,250}$/;
|
|
18
|
+
// The other ways a confirm can say what it pays with. A body that already names
|
|
19
|
+
// one is not the shape rewritten here.
|
|
20
|
+
const OTHER_WAYS_TO_PAY = new Set(['payment_method', 'source', 'confirmation_token']);
|
|
21
|
+
/**
|
|
22
|
+
* The page's SetupIntent confirm, paying with `paymentMethod` instead of the
|
|
23
|
+
* card it carries: every `payment_method_data[...]` field goes (the card, and
|
|
24
|
+
* the billing details and telemetry Stripe.js sends beside it; the payment
|
|
25
|
+
* method already carries the billing details of the approved request), and
|
|
26
|
+
* `payment_method=<id>` takes the place of the first one. Every other field
|
|
27
|
+
* stays byte for byte as the page wrote it (client_secret, key,
|
|
28
|
+
* expected_payment_method_type, use_stripe_sdk, return_url, mandate and option
|
|
29
|
+
* fields). Null, and nothing is rewritten, when the id is not a payment
|
|
30
|
+
* method's, when a key cannot be decoded, when the body already names another
|
|
31
|
+
* way to pay (payment_method, source, confirmation_token), or when it carries
|
|
32
|
+
* no payment_method_data at all.
|
|
33
|
+
*/
|
|
34
|
+
export function setupConfirmPayingWith(body, paymentMethod) {
|
|
35
|
+
if (!PAYMENT_METHOD_ID.test(paymentMethod) || typeof body !== 'string' || body === '')
|
|
36
|
+
return null;
|
|
37
|
+
const kept = [];
|
|
38
|
+
let replaced = false;
|
|
39
|
+
for (const chunk of body.split('&')) {
|
|
40
|
+
if (chunk === '')
|
|
41
|
+
continue;
|
|
42
|
+
const eq = chunk.indexOf('=');
|
|
43
|
+
let key;
|
|
44
|
+
try {
|
|
45
|
+
key = decodeURIComponent((eq < 0 ? chunk : chunk.slice(0, eq)).replace(/\+/g, ' '));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (OTHER_WAYS_TO_PAY.has(key))
|
|
51
|
+
return null;
|
|
52
|
+
if (key.startsWith('payment_method_data[')) {
|
|
53
|
+
if (!replaced)
|
|
54
|
+
kept.push(`payment_method=${paymentMethod}`);
|
|
55
|
+
replaced = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
kept.push(chunk);
|
|
59
|
+
}
|
|
60
|
+
return replaced ? kept.join('&') : null;
|
|
61
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 6209fa4a22c349d03eed24ea4ff9e626334c5975ec515a4b7f6a83592343274f
|
|
4
4
|
// src/adyen-jwe.js
|
|
5
5
|
var ADYEN_JWE_HEADER = { alg: "RSA-OAEP-256", enc: "A256GCM", version: "1" };
|
|
6
6
|
var BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
@@ -78,6 +78,28 @@ function pathsOverlap(a, b) {
|
|
|
78
78
|
return false;
|
|
79
79
|
return true;
|
|
80
80
|
}
|
|
81
|
+
function sameKeyIgnoringCase(key, step) {
|
|
82
|
+
return !FORBIDDEN_KEYS.has(key) && (key.toLowerCase() === step.toLowerCase() || key.toUpperCase() === step.toUpperCase());
|
|
83
|
+
}
|
|
84
|
+
function valuesIgnoringCase(root, path) {
|
|
85
|
+
let nodes = [root];
|
|
86
|
+
for (const step of path) {
|
|
87
|
+
const next = [];
|
|
88
|
+
for (const node of nodes) {
|
|
89
|
+
if (typeof step === "number") {
|
|
90
|
+
if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
|
|
91
|
+
next.push(node[step]);
|
|
92
|
+
}
|
|
93
|
+
else if (isPlainObject(node)) {
|
|
94
|
+
for (const key of Object.keys(node))
|
|
95
|
+
if (sameKeyIgnoringCase(key, step))
|
|
96
|
+
next.push(node[key]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
nodes = next;
|
|
100
|
+
}
|
|
101
|
+
return nodes;
|
|
102
|
+
}
|
|
81
103
|
function describePath(path) {
|
|
82
104
|
return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : `.${step}`).join("").replace(/^\./, "") : "(root)";
|
|
83
105
|
}
|
|
@@ -117,7 +139,8 @@ function checkedSet(set, path, names, remove) {
|
|
|
117
139
|
}
|
|
118
140
|
return entries;
|
|
119
141
|
}
|
|
120
|
-
|
|
142
|
+
var ADYEN_JWE = /* @__PURE__ */ Object.freeze({ accepts: isAdyenJweCompact, label: "an Adyen JWE" });
|
|
143
|
+
function swapMerchantHostedFields(body, spec, ciphertext = ADYEN_JWE) {
|
|
121
144
|
if (!isPlainObject(spec))
|
|
122
145
|
throw new SubstitutionError("refusing a substitution that is not an object");
|
|
123
146
|
for (const member of Object.keys(spec)) {
|
|
@@ -136,8 +159,8 @@ function swapMerchantHostedFields(body, spec) {
|
|
|
136
159
|
for (const name of names) {
|
|
137
160
|
if (FORBIDDEN_KEYS.has(name))
|
|
138
161
|
throw new SubstitutionError(`refusing substitution field ${name}`);
|
|
139
|
-
if (!
|
|
140
|
-
throw new SubstitutionError(`substitution for ${name} is not
|
|
162
|
+
if (!ciphertext.accepts(fields[name]))
|
|
163
|
+
throw new SubstitutionError(`substitution for ${name} is not ${ciphertext.label}`);
|
|
141
164
|
}
|
|
142
165
|
if (!Array.isArray(remove))
|
|
143
166
|
throw new SubstitutionError("substitutions.remove is not a list");
|
|
@@ -169,8 +192,8 @@ function swapMerchantHostedFields(body, spec) {
|
|
|
169
192
|
const dummy = Object.prototype.hasOwnProperty.call(holder.value, name) ? holder.value[name] : void 0;
|
|
170
193
|
if (typeof dummy !== "string")
|
|
171
194
|
throw new SubstitutionError(`the paused body has no string field ${name} at ${describePath(path)}`);
|
|
172
|
-
if (!
|
|
173
|
-
throw new SubstitutionError(`the paused field ${name} is not
|
|
195
|
+
if (!ciphertext.accepts(dummy))
|
|
196
|
+
throw new SubstitutionError(`the paused field ${name} is not ${ciphertext.label}`);
|
|
174
197
|
if (swaps.has(dummy))
|
|
175
198
|
throw new SubstitutionError("two paused fields carry the same ciphertext");
|
|
176
199
|
swaps.set(dummy, fields[name]);
|
|
@@ -247,6 +270,12 @@ function merchantAmountCardPaths(profile) {
|
|
|
247
270
|
const rules = [source, isPlainObject(source.afterPayment) ? source.afterPayment : {}].flatMap((stage) => [...Array.isArray(stage.bind) ? stage.bind : [], ...Array.isArray(stage.match) ? stage.match : []]);
|
|
248
271
|
return rules.flatMap((rule) => isPlainObject(rule) ? [rule.card, rule.whenCard] : []).filter(Array.isArray);
|
|
249
272
|
}
|
|
273
|
+
function merchantBodyAmountPaths(profile) {
|
|
274
|
+
const amount = isPlainObject(profile) ? profile.amount : void 0;
|
|
275
|
+
if (Array.isArray(amount))
|
|
276
|
+
return [amount];
|
|
277
|
+
return isPlainObject(amount) ? [amount.value, amount.currency].filter(Array.isArray) : [];
|
|
278
|
+
}
|
|
250
279
|
// src/merchant-profiles.js
|
|
251
280
|
var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
252
281
|
{
|
|
@@ -549,6 +578,154 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
549
578
|
clientKey: "live_YJLFKT47JJG2RI7QIYX6HTKKDEDOEGOH",
|
|
550
579
|
publicKeySha256: "da33611149dff3ab715be8671c0550bb8aec6112983b2473db9ee7afb829c110"
|
|
551
580
|
}
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
// Read Sep 25, 2026, from public code only: the WooCommerce plugin super-payments 1.40.1 and
|
|
584
|
+
// cdn.superpayments.com/js/v2/payment.webcomponents.script.js as it stood on Sep 19.
|
|
585
|
+
// WePrescribe's page runs Super's <super-checkout> component, which picks the card leg per
|
|
586
|
+
// checkout session: ADYEN, under Super's own Adyen account (adyen-web 6.36.0 in Super's frame
|
|
587
|
+
// web-checkout.superpayments.com/adyen-card-host, which hands state.data to the page by
|
|
588
|
+
// postMessage), or STRIPE, a Payment Element the stripe definition already pays. On the ADYEN
|
|
589
|
+
// leg handleAdyenSubmit posts, with the session's Bearer token, {amount (the gross, minor
|
|
590
|
+
// units), currency, fundingMethod: {amount (the card's net share), type CARD, providerArgs:
|
|
591
|
+
// {type ADYEN, paymentMethod: {type CARD, the four JWEs, holderName, postalCode},
|
|
592
|
+
// browserInfo}}, rewardsToUse} from the page to api.superpayments.com. The card is charged
|
|
593
|
+
// fundingMethod.amount, so that is what the agent's amount must equal. No brand rides in the
|
|
594
|
+
// body. A saved card sends type SAVED_CARD with storedPaymentMethodId and no JWEs. Super's
|
|
595
|
+
// save-card component posts ciphertext to client.superpayments.com/setup-intent/confirm,
|
|
596
|
+
// which the WooCommerce checkout never calls. Public key read from checkoutshopper-live.adyen.com
|
|
597
|
+
// on 2026-09-25 and again on 2026-09-30 (UTC), unchanged. The path carries the checkout
|
|
598
|
+
// session id, which no preparation can name before the card request exists, so the review
|
|
599
|
+
// rules hold this profile in observe. A HAR still has to confirm the leg Super picks for a
|
|
600
|
+
// WePrescribe order and the body that leg posts.
|
|
601
|
+
id: "super_payments_checkout",
|
|
602
|
+
merchant: "Super Payments",
|
|
603
|
+
status: "observe",
|
|
604
|
+
origins: [{ checkout: "https://weprescribe.co.uk", endpoint: "https://api.superpayments.com" }],
|
|
605
|
+
method: "POST",
|
|
606
|
+
path: "/v3/checkout-sessions/:checkoutSessionId/payment",
|
|
607
|
+
query: {},
|
|
608
|
+
requires: [
|
|
609
|
+
{ path: ["fundingMethod", "type"], equals: "CARD" },
|
|
610
|
+
{ path: ["fundingMethod", "providerArgs", "type"], equals: "ADYEN" }
|
|
611
|
+
],
|
|
612
|
+
holder: ["fundingMethod", "providerArgs", "paymentMethod"],
|
|
613
|
+
holderType: "CARD",
|
|
614
|
+
fields: {
|
|
615
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
616
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
617
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
618
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
619
|
+
},
|
|
620
|
+
remove: [],
|
|
621
|
+
refuse: [
|
|
622
|
+
{ path: ["fundingMethod", "providerArgs", "paymentMethod", "storedPaymentMethodId"], when: "present" }
|
|
623
|
+
],
|
|
624
|
+
amount: { value: ["fundingMethod", "amount"], currency: ["currency"], format: "number", unit: "minor" },
|
|
625
|
+
otherCardRequests: [{ origin: "https://client.superpayments.com", path: "/setup-intent/confirm" }],
|
|
626
|
+
adyen: {
|
|
627
|
+
environment: "live",
|
|
628
|
+
clientKey: "live_5MO5NXLPV5HPFJZWKYBAGA5RRMHEYWSD",
|
|
629
|
+
publicKeySha256: "b1cd67db43491625b433c9b864beea8a0f0351378bcd3e9e1e0f19e3aad38fcf"
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
// Read Sep 25, 2026, from public code only.
|
|
634
|
+
// www.corepoweryoga.com asks its API for a hosted payment page (the card-free POST
|
|
635
|
+
// api2.corepoweryoga.com/stored-payment answers hosted_payment_uri) and frames it: Zenoti
|
|
636
|
+
// Payments, Zenoti's Adyen platform, at corepoweryoga.zenoti.com/OnlinePayments/AdyentHPP.aspx,
|
|
637
|
+
// a cross-origin frame running adyen-web v6 Drop-in in the Advanced flow. Its makePayment
|
|
638
|
+
// (fintech-cdn.zenoti.com .../AdyentHPP.js, SHA-256 95e14da8...5df39) posts {processorId,
|
|
639
|
+
// subscriberId, transactionSource, paymentRequest: {paymentMethod: adyen-web's
|
|
640
|
+
// state.data.paymentMethod, type: that method's type, reference, BrowserInformation,
|
|
641
|
+
// shopperLocale, signature, channel, amount: {currency, value}, brand: onBrand's code for the
|
|
642
|
+
// typed card, FirstRecurringCharge}} to AdyentHPP.aspx/ProcessTransaction on the frame's own
|
|
643
|
+
// origin, and Zenoti's server calls Adyen /payments. The pair names the top page, the checkout
|
|
644
|
+
// the SDK prepares on. paymentRequest.brand describes the agent's dummy, so the approved card's
|
|
645
|
+
// brand is written there, beside the holder, and adyen-web's own brand at the holder is dropped.
|
|
646
|
+
// This request saves the card on file: CorePower then charges the stored card on its server
|
|
647
|
+
// (api2.corepoweryoga.com/purchase-invoice-cc-on-file) and memberships bill it monthly. The
|
|
648
|
+
// client key and environment arrive with the frame's per-session token, which only a signed-in
|
|
649
|
+
// shopper's page reads, so the key is a draft until a HAR pins it.
|
|
650
|
+
id: "zenoti_corepoweryoga_hpp",
|
|
651
|
+
merchant: "CorePower Yoga",
|
|
652
|
+
status: "observe",
|
|
653
|
+
origins: [{ checkout: "https://www.corepoweryoga.com", endpoint: "https://corepoweryoga.zenoti.com" }],
|
|
654
|
+
method: "POST",
|
|
655
|
+
path: "/OnlinePayments/AdyentHPP.aspx/ProcessTransaction",
|
|
656
|
+
query: {},
|
|
657
|
+
requires: [{ path: ["paymentRequest", "type"], equals: "scheme" }],
|
|
658
|
+
holder: ["paymentRequest", "paymentMethod"],
|
|
659
|
+
fields: {
|
|
660
|
+
encryptedCardNumber: "encryptedCardNumber",
|
|
661
|
+
encryptedExpiryMonth: "encryptedExpiryMonth",
|
|
662
|
+
encryptedExpiryYear: "encryptedExpiryYear",
|
|
663
|
+
encryptedSecurityCode: "encryptedSecurityCode"
|
|
664
|
+
},
|
|
665
|
+
remove: ["brand"],
|
|
666
|
+
refuse: [
|
|
667
|
+
{ path: ["paymentRequest", "storePaymentMethod"], when: "true" },
|
|
668
|
+
{ path: ["paymentRequest", "paymentMethod", "storedPaymentMethodId"], when: "present" },
|
|
669
|
+
{ path: ["paymentRequest", "paymentMethod", "recurringDetailReference"], when: "present" }
|
|
670
|
+
],
|
|
671
|
+
amount: ["paymentRequest", "amount"],
|
|
672
|
+
facts: [
|
|
673
|
+
// adyen-web's brand codes, as onBrand reports them.
|
|
674
|
+
{
|
|
675
|
+
path: ["paymentRequest", "brand"],
|
|
676
|
+
fact: "brand",
|
|
677
|
+
map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover", diners: "diners", jcb: "jcb" }
|
|
678
|
+
}
|
|
679
|
+
],
|
|
680
|
+
adyen: { environment: "live", clientKey: null, publicKeySha256: null }
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
// Read Sep 25, 2026, from public code only: the Cathay Payment Page bundle on
|
|
684
|
+
// payment.cathaypacific.com (main.14d368386ca2a9041a823b758307b3c2.js, adyen-web 5.71.5
|
|
685
|
+
// securedfields, environment live), where flight bookings and manage-booking pay. The page
|
|
686
|
+
// builds one outgoing request: amount and currency from the booking (a decimal
|
|
687
|
+
// number in major units; the cash part when miles pay the rest), the four JWEs at its root as
|
|
688
|
+
// encrypted_card_number, encrypted_expiry_month, encrypted_expiry_year and
|
|
689
|
+
// encrypted_security_code, raw_card_bin (the typed card's BIN, as adyen-web's issuerBin or
|
|
690
|
+
// binValue reports it), store_a_card, pay_with_stored_card, stored_card_token and
|
|
691
|
+
// pay_with_adyen_web true. signAndAuthorise posts {sign_request: that request, hpp_signature}
|
|
692
|
+
// to /cpp-paypage/v1/public/sign (header signapi: authorise, or its discount, currency, split,
|
|
693
|
+
// full-miles or bank-form variant), then a copy made before signing, plus the returned
|
|
694
|
+
// cpp_signature, to /cpp-paypage/v1/miles-plus-cash-authorise, which charges it. The signature
|
|
695
|
+
// covers the JWEs, so the card request is the signing step and the charge is its then request.
|
|
696
|
+
// raw_card_bin describes the dummy, so the approved card's BIN6 is written there, never more
|
|
697
|
+
// (adyen-web can report eight digits). The client key comes from document.cppData per
|
|
698
|
+
// session, so the key is a draft until a HAR pins it. The page's other card form sends a card
|
|
699
|
+
// alias (alias_card_number) in place of the JWEs, and a refuse rule below turns it away.
|
|
700
|
+
id: "cathay_cpp_sign",
|
|
701
|
+
merchant: "Cathay Pacific",
|
|
702
|
+
status: "observe",
|
|
703
|
+
origins: [{ checkout: "https://payment.cathaypacific.com", endpoint: "https://api.cathaypacific.com" }],
|
|
704
|
+
method: "POST",
|
|
705
|
+
path: "/cpp-paypage/v1/public/sign",
|
|
706
|
+
query: {},
|
|
707
|
+
requires: [
|
|
708
|
+
{ path: ["sign_request", "pay_with_adyen_web"], equals: true },
|
|
709
|
+
{ path: ["sign_request", "pay_with_stored_card"], equals: false },
|
|
710
|
+
{ path: ["sign_request", "stored_card_token"], equals: "" }
|
|
711
|
+
],
|
|
712
|
+
holder: ["sign_request"],
|
|
713
|
+
fields: {
|
|
714
|
+
encryptedCardNumber: "encrypted_card_number",
|
|
715
|
+
encryptedExpiryMonth: "encrypted_expiry_month",
|
|
716
|
+
encryptedExpiryYear: "encrypted_expiry_year",
|
|
717
|
+
encryptedSecurityCode: "encrypted_security_code"
|
|
718
|
+
},
|
|
719
|
+
remove: [],
|
|
720
|
+
refuse: [
|
|
721
|
+
{ path: ["sign_request", "store_a_card"], when: "true" },
|
|
722
|
+
{ path: ["sign_request", "stored_card_id"], when: "present" },
|
|
723
|
+
{ path: ["sign_request", "alias_card_number"], when: "present" }
|
|
724
|
+
],
|
|
725
|
+
amount: { value: ["sign_request", "amount"], currency: ["sign_request", "currency"], format: "number", unit: "major" },
|
|
726
|
+
facts: [{ path: ["sign_request", "raw_card_bin"], fact: "bin6" }],
|
|
727
|
+
then: { method: "POST", path: "/cpp-paypage/v1/miles-plus-cash-authorise", query: {}, holder: [] },
|
|
728
|
+
adyen: { environment: "live", clientKey: null, publicKeySha256: null }
|
|
552
729
|
}
|
|
553
730
|
]);
|
|
554
731
|
function adyenKeyHost(environment) {
|
|
@@ -627,7 +804,7 @@ function merchantProfileFactsRefusal(profile) {
|
|
|
627
804
|
const list = (value) => Array.isArray(value) ? value : [];
|
|
628
805
|
const reviewed = {
|
|
629
806
|
written: [...isPlainObject(profile.fields) ? Object.values(profile.fields) : [], ...list(profile.remove)],
|
|
630
|
-
read: [...list(profile.requires), ...list(profile.refuse)].map((rule) => rule?.path).concat(
|
|
807
|
+
read: [...list(profile.requires), ...list(profile.refuse)].map((rule) => rule?.path).concat(merchantBodyAmountPaths(profile), merchantAmountCardPaths(profile)).filter(Array.isArray)
|
|
631
808
|
};
|
|
632
809
|
for (const [i, entry] of facts.entries()) {
|
|
633
810
|
const refusal = factRefusal(entry, i, holder, besideHolder, reviewed);
|
|
@@ -642,28 +819,6 @@ function merchantProfileFactsRefusal(profile) {
|
|
|
642
819
|
function merchantProfileRecognizerById(id) {
|
|
643
820
|
return typeof id === "string" ? MERCHANT_PROFILE_RECOGNIZERS.find((entry) => entry.id === id) ?? null : null;
|
|
644
821
|
}
|
|
645
|
-
function sameKeyIgnoringCase(key, step) {
|
|
646
|
-
return !FORBIDDEN_KEYS.has(key) && (key.toLowerCase() === step.toLowerCase() || key.toUpperCase() === step.toUpperCase());
|
|
647
|
-
}
|
|
648
|
-
function valuesIgnoringCase(root, path) {
|
|
649
|
-
let nodes = [root];
|
|
650
|
-
for (const step of path) {
|
|
651
|
-
const next = [];
|
|
652
|
-
for (const node of nodes) {
|
|
653
|
-
if (typeof step === "number") {
|
|
654
|
-
if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
|
|
655
|
-
next.push(node[step]);
|
|
656
|
-
}
|
|
657
|
-
else if (isPlainObject(node)) {
|
|
658
|
-
for (const key of Object.keys(node))
|
|
659
|
-
if (sameKeyIgnoringCase(key, step))
|
|
660
|
-
next.push(node[key]);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
nodes = next;
|
|
664
|
-
}
|
|
665
|
-
return nodes;
|
|
666
|
-
}
|
|
667
822
|
function merchantBodyRefusal(profile, body) {
|
|
668
823
|
const p = merchantProfileRecognizerById(typeof profile === "string" ? profile : profile?.id);
|
|
669
824
|
if (!p)
|
|
@@ -785,6 +940,9 @@ function substituteMerchantHosted(body, sub, opts) {
|
|
|
785
940
|
const profile = merchantProfileRecognizerById(sub.profile);
|
|
786
941
|
if (!profile)
|
|
787
942
|
throw new SubstitutionError("the substitution names no merchant profile Agentcard reviewed");
|
|
943
|
+
if (profile.then !== void 0) {
|
|
944
|
+
throw new SubstitutionError(`${profile.merchant} takes the card in a second request too, and Agentcard does not write it there yet`);
|
|
945
|
+
}
|
|
788
946
|
if (!sameSteps(sub.path, profile.holder)) {
|
|
789
947
|
throw new SubstitutionError(`the substitution path is not where ${profile.merchant}'s card request holds the card`);
|
|
790
948
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** 'observe': paused, reported and aborted, never paid. 'enabled': a prepared checkout may pay its reviewed leg. */
|
|
2
|
+
export type VerifoneProfileStatus = 'observe' | 'enabled';
|
|
3
|
+
/** A reviewed Verifone profile this client armed: its id, the merchant it names, its environment and its effective status. */
|
|
4
|
+
export interface VerifoneProfile {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly merchant: string;
|
|
7
|
+
readonly environment: 'production' | 'sandbox';
|
|
8
|
+
readonly status: VerifoneProfileStatus;
|
|
9
|
+
}
|
|
10
|
+
/** A profile's verdict on a paused request; see classifyVerifoneProfileRequest. */
|
|
11
|
+
export type VerifoneRequestVerdict = {
|
|
12
|
+
verdict: 'pause';
|
|
13
|
+
leg: string;
|
|
14
|
+
} | {
|
|
15
|
+
verdict: 'abort';
|
|
16
|
+
reason: string;
|
|
17
|
+
} | {
|
|
18
|
+
verdict: 'pass';
|
|
19
|
+
};
|
|
20
|
+
/** The one substitution a Verifone approval writes: the device's blob at the leg's holder and field. */
|
|
21
|
+
export interface VerifoneSubstitutions {
|
|
22
|
+
encoding: 'json';
|
|
23
|
+
kind: 'verifone';
|
|
24
|
+
profile: string;
|
|
25
|
+
blob: string;
|
|
26
|
+
}
|
|
27
|
+
/** Whether an entry the API served is a Verifone profile (`{ verifone_profile }`), never a recognizer. */
|
|
28
|
+
export declare function isVerifoneProfileEntry(entry: unknown): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* The profiles this client arms: only the ones the API served, each once, in this
|
|
31
|
+
* build's order. A served entry is `{ verifone_profile: <the profile's wire
|
|
32
|
+
* projection> }`, and the first one that names a profile with exactly this build's
|
|
33
|
+
* rules (origins, legs, body rules, key subkey: everything but `status`) and a status
|
|
34
|
+
* this build knows decides it: 'enabled' only when both that entry and this build say
|
|
35
|
+
* so. A profile the API did not serve, or served with other rules, is not armed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function armServedVerifoneProfiles(entries?: readonly unknown[]): VerifoneProfile[];
|
|
38
|
+
/** The armed profile whose endpoint origin a URL is on, or null. */
|
|
39
|
+
export declare function verifoneProfileFor(profiles: readonly VerifoneProfile[], url: string): VerifoneProfile | null;
|
|
40
|
+
/** The Fetch.enable globs that pause every request to every armed profile's endpoint origins. */
|
|
41
|
+
export declare function verifoneProfileUrlPatterns(profiles: readonly VerifoneProfile[]): string[];
|
|
42
|
+
/** The verdict payment-core gives a request at an armed profile: pause (its reviewed leg), abort (a blob it cannot pay, with the reason) or pass. */
|
|
43
|
+
export declare function classifyVerifoneProfileRequest(profile: VerifoneProfile, url: string, method: string, body: string | null | undefined): VerifoneRequestVerdict;
|
|
44
|
+
/** The URL an event names for a request at an armed profile: its origin and, for a leg, the leg's path template; never a query or an id. */
|
|
45
|
+
export declare function verifoneTemplatedUrl(profileId: string, url: string): string;
|
|
46
|
+
/** The endpoint a Verifone preparation pays from a checkout page: the profile's leg URL, or null. */
|
|
47
|
+
export declare function verifoneProfileEndpointFor(profileId: string, checkoutOrigin: string): string | null;
|
|
48
|
+
/** Whether a paused request is the one a Verifone preparation for this profile consents to. */
|
|
49
|
+
export declare function isPreparedVerifoneRequest(url: string, method: string, body: string | null, profileId: string): boolean;
|
|
50
|
+
/** Whether an approval's substitution is exactly a Verifone blob for this profile. */
|
|
51
|
+
export declare function isVerifoneSubstitution(sub: unknown, profileId: string): sub is VerifoneSubstitutions;
|
|
52
|
+
/**
|
|
53
|
+
* The continuation of a Verifone approval: the live paused body with the device's blob in place
|
|
54
|
+
* of the page's placeholder blob. The API keeps no merchant body, so the live body must first
|
|
55
|
+
* hash to the one the API checked when the authorization was created (`bodySha256`); a page that
|
|
56
|
+
* rewrote its body while the cardholder decided is refused rather than paid. payment-core's
|
|
57
|
+
* substituteVerifoneBlob then reads the leg's rules on the live body again, writes only the blob,
|
|
58
|
+
* and refuses a profile that is not enabled or a blob under any other key. Throws
|
|
59
|
+
* SubstitutionError; nothing leaves the browser when it does.
|
|
60
|
+
*/
|
|
61
|
+
export declare function substituteVerifoneBody(request: {
|
|
62
|
+
url: string;
|
|
63
|
+
method: string;
|
|
64
|
+
body: string;
|
|
65
|
+
}, replay: {
|
|
66
|
+
substitutions: VerifoneSubstitutions;
|
|
67
|
+
bodySha256: string;
|
|
68
|
+
}): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare var VERIFONE_KEY_PINS: any;
|
|
2
|
+
declare function verifoneKeyPinShapeRefusal(entry: any): "verifone_key_pin_invalid" | "verifone_key_pin_mismatch" | null;
|
|
3
|
+
declare var VERIFONE_MERCHANT_PROFILES: any;
|
|
4
|
+
declare function verifoneProfileRefusal(profile: any, pins?: any): string | null;
|
|
5
|
+
declare var VERIFONE_PROFILE_RECOGNIZERS: any;
|
|
6
|
+
declare var VERIFONE_BLOB_PREFIX: string;
|
|
7
|
+
declare function verifoneBlobInfo(value: any): Readonly<{
|
|
8
|
+
recipientKeyId: string;
|
|
9
|
+
wrappedKeyLength: any;
|
|
10
|
+
armorHeaders: readonly any[];
|
|
11
|
+
}> | null;
|
|
12
|
+
declare function isVerifoneBlob(value: any): boolean;
|
|
13
|
+
declare var VERIFONE_TEST_PANS: readonly string[];
|
|
14
|
+
declare var VERIFONE_CLOCK_SKEW_MS: number;
|
|
15
|
+
declare var VERIFONE_BLOB_MAX: number;
|
|
16
|
+
declare function carriesVerifoneBlob(body: any): any;
|
|
17
|
+
declare function verifoneCardPlaintext(card: any, nowMs: any): string;
|
|
18
|
+
declare function verifoneClockSkewRefusal(deviceNowMs: any, serverNowMs: any): "verifone_clock_unknown" | "verifone_clock_skew" | null;
|
|
19
|
+
declare var VERIFONE_SUBSTITUTION: Readonly<{
|
|
20
|
+
at: "merchant_profile";
|
|
21
|
+
fields: readonly string[];
|
|
22
|
+
}>;
|
|
23
|
+
declare function classifyVerifoneRequest(profileId: any, url: any, method: any, body: any): Readonly<{
|
|
24
|
+
verdict: "pass";
|
|
25
|
+
}> | Readonly<{
|
|
26
|
+
verdict: "abort";
|
|
27
|
+
reason: any;
|
|
28
|
+
}> | Readonly<{
|
|
29
|
+
verdict: "pause";
|
|
30
|
+
leg: any;
|
|
31
|
+
}>;
|
|
32
|
+
declare function verifoneLegName(profileId: any, url: any, method: any, checkoutOrigin: any): any;
|
|
33
|
+
declare function verifonePlaceholderBlob(profileId: any, request: any): string | null;
|
|
34
|
+
declare function encryptVerifoneCard(card: any, request: any, facilities: any): Promise<string>;
|
|
35
|
+
declare function verifoneSubstitution(profileId: any, blob: any): {
|
|
36
|
+
encoding: string;
|
|
37
|
+
kind: string;
|
|
38
|
+
profile: any;
|
|
39
|
+
blob: string;
|
|
40
|
+
};
|
|
41
|
+
declare function substituteVerifoneBlob(request: any, sub: any): string;
|
|
42
|
+
declare function verifoneApprovedBlobRefusal(blob: any, context: any): "verifone_substitution_invalid" | "verifone_approval_context_invalid" | "verifone_profile_unknown" | "verifone_request_invalid" | "verifone_key_unpinned" | "verifone_environment_mismatch" | "verifone_key_mismatch" | "verifone_blob_unchanged" | null;
|
|
43
|
+
declare function isPreparedVerifoneRequest(url: any, method: any, body: any, profileId: any): boolean;
|
|
44
|
+
declare function verifoneProfileEndpointFor(profileId: any, checkoutOrigin: any): string | null;
|
|
45
|
+
declare function verifoneProfileTemplatedUrl(profileId: any, url: any): string;
|
|
46
|
+
declare function verifoneProfileUrlGlobs(profileId: any): unknown[];
|
|
47
|
+
declare function verifoneProfileAmountPath(profileId: any): any[] | null;
|
|
48
|
+
declare function verifoneProfileEnvironment(profileId: any): any;
|
|
49
|
+
declare function verifoneMerchantProfile(id: any): any;
|
|
50
|
+
declare function verifoneProfileRulesMatch(profileId: any, served: any): boolean;
|
|
51
|
+
export { VERIFONE_BLOB_MAX, VERIFONE_BLOB_PREFIX, VERIFONE_CLOCK_SKEW_MS, VERIFONE_KEY_PINS, VERIFONE_MERCHANT_PROFILES, VERIFONE_PROFILE_RECOGNIZERS, VERIFONE_SUBSTITUTION, VERIFONE_TEST_PANS, carriesVerifoneBlob, classifyVerifoneRequest, encryptVerifoneCard, isPreparedVerifoneRequest, isVerifoneBlob, substituteVerifoneBlob, verifoneApprovedBlobRefusal, verifoneBlobInfo, verifoneCardPlaintext, verifoneClockSkewRefusal, verifoneKeyPinShapeRefusal, verifoneLegName, verifoneMerchantProfile, verifonePlaceholderBlob, verifoneProfileAmountPath, verifoneProfileEndpointFor, verifoneProfileEnvironment, verifoneProfileRefusal, verifoneProfileRulesMatch, verifoneProfileTemplatedUrl, verifoneProfileUrlGlobs, verifoneSubstitution };
|