@agent-cards/sdk 0.24.1 → 0.24.2
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 +6 -0
- package/dist/capabilities.generated.js +1 -1
- package/dist/card-fields.generated.js +354 -263
- package/dist/cdp.js +9 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +9 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.js +25 -0
- package/dist/pre-claim.d.ts +3 -0
- package/dist/pre-claim.js +45 -1
- package/dist/preflight-catalog.json +25 -0
- package/dist/preflight-schemas.json +2 -0
- package/dist/preflight.generated.js +10 -1
- package/dist/primer.generated.js +364 -273
- package/dist/stripe-checkout.generated.js +333 -242
- package/dist/token-repeats.d.ts +32 -0
- package/dist/token-repeats.js +64 -0
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ReplayResponse } from './client.js';
|
|
2
|
+
/** How long an approved answer may answer a re-send: the approval's own lifetime (cdp.ts HOSTED_FORM_REPEAT_QUIET_MS). */
|
|
3
|
+
export declare const TOKEN_REPEAT_WINDOW_MS: number;
|
|
4
|
+
/** How many re-sends one approved answer answers. Shopify's frame sent 11 in a row; past this the re-send is held. */
|
|
5
|
+
export declare const MAX_TOKEN_REPEATS = 3;
|
|
6
|
+
export interface TokenRepeatAnswer {
|
|
7
|
+
authorizationId: string;
|
|
8
|
+
response: {
|
|
9
|
+
status: number;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
body: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare class TokenRepeats {
|
|
15
|
+
private readonly now;
|
|
16
|
+
private last;
|
|
17
|
+
constructor(now?: () => number);
|
|
18
|
+
/**
|
|
19
|
+
* Keep the answer a token replay handed to a card request, once it reached the page. Only a plain token answer the
|
|
20
|
+
* processor accepted (a 2xx): a hosted form, a card endpoint, a client-side encryption or a device's own send is
|
|
21
|
+
* never answered again from here. A new answer replaces the last, so an earlier approval answers nothing.
|
|
22
|
+
*/
|
|
23
|
+
remember(frame: unknown, url: string, body: string | null, replay: ReplayResponse): void;
|
|
24
|
+
/**
|
|
25
|
+
* The stored answer for a byte-identical re-send from the same frame under `authorizationId`, inside the window and
|
|
26
|
+
* the count, or null. Handing one out reserves a place in the count; the caller gives it back (released) when the
|
|
27
|
+
* answer never reached the page.
|
|
28
|
+
*/
|
|
29
|
+
answerFor(frame: unknown, url: string, body: string | null, authorizationId: string | null): TokenRepeatAnswer | null;
|
|
30
|
+
/** Give back a place answerFor reserved for an answer that never reached the page. */
|
|
31
|
+
released(authorizationId: string): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// The page's own re-send of a card request this attachment already answered with an approved token.
|
|
2
|
+
//
|
|
3
|
+
// Once the cardholder's device replayed the card and the processor's token reached the page, a checkout can send the
|
|
4
|
+
// very same card request again before the merchant confirms the order: Shopify's card frame did, after a checkout that
|
|
5
|
+
// sat a few minutes between its total and Pay (lexmod.com, expedusa.com and four more stores, 2026-10-27 to 2026-11-05).
|
|
6
|
+
// The SDK held every such re-send while the merchant resolver read the order as pending (lifecycle.ts resolve), so the
|
|
7
|
+
// page never got a token it would submit and the order stayed unconfirmed with the card already out of the device.
|
|
8
|
+
//
|
|
9
|
+
// TokenRepeats keeps the one answer this attachment last handed a plain token request, and hands it out again only for
|
|
10
|
+
// a byte-identical request from the same frame to the same URL under the same approval: the same card fields, the same
|
|
11
|
+
// amount the approval was given for, and no new question for the cardholder. The pre-claim gate (pre-claim.ts tokenRepeatPreClaim) also
|
|
12
|
+
// asks the merchant resolver first and answers only while the order reads pending, so the stored token never reaches a
|
|
13
|
+
// later purchase. A token the processor takes once is refused by the processor the second time, and nothing charges
|
|
14
|
+
// twice: the page holds the same token it already had, never a second payment.
|
|
15
|
+
/** How long an approved answer may answer a re-send: the approval's own lifetime (cdp.ts HOSTED_FORM_REPEAT_QUIET_MS). */
|
|
16
|
+
export const TOKEN_REPEAT_WINDOW_MS = 15 * 60_000;
|
|
17
|
+
/** How many re-sends one approved answer answers. Shopify's frame sent 11 in a row; past this the re-send is held. */
|
|
18
|
+
export const MAX_TOKEN_REPEATS = 3;
|
|
19
|
+
export class TokenRepeats {
|
|
20
|
+
now;
|
|
21
|
+
last = null;
|
|
22
|
+
constructor(now = Date.now) {
|
|
23
|
+
this.now = now;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Keep the answer a token replay handed to a card request, once it reached the page. Only a plain token answer the
|
|
27
|
+
* processor accepted (a 2xx): a hosted form, a card endpoint, a client-side encryption or a device's own send is
|
|
28
|
+
* never answered again from here. A new answer replaces the last, so an earlier approval answers nothing.
|
|
29
|
+
*/
|
|
30
|
+
remember(frame, url, body, replay) {
|
|
31
|
+
try {
|
|
32
|
+
if (frame === undefined || frame === null || frame === '' || body === null || (replay.mode !== undefined && replay.mode !== 'token'))
|
|
33
|
+
return;
|
|
34
|
+
const { status, headers } = replay;
|
|
35
|
+
if (!(status >= 200 && status < 300) || typeof replay.body !== 'string')
|
|
36
|
+
return;
|
|
37
|
+
this.last = { frame, url, body, authorizationId: replay.authorizationId, response: { status, headers: { ...headers }, body: replay.body }, at: this.now(), answered: 0 };
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
this.last = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The stored answer for a byte-identical re-send from the same frame under `authorizationId`, inside the window and
|
|
45
|
+
* the count, or null. Handing one out reserves a place in the count; the caller gives it back (released) when the
|
|
46
|
+
* answer never reached the page.
|
|
47
|
+
*/
|
|
48
|
+
answerFor(frame, url, body, authorizationId) {
|
|
49
|
+
const last = this.last;
|
|
50
|
+
if (!last || !authorizationId || body === null || frame === undefined || frame === null || frame === '')
|
|
51
|
+
return null;
|
|
52
|
+
if (last.authorizationId !== authorizationId || last.frame !== frame || last.url !== url || last.body !== body)
|
|
53
|
+
return null;
|
|
54
|
+
if (this.now() - last.at >= TOKEN_REPEAT_WINDOW_MS || last.answered >= MAX_TOKEN_REPEATS)
|
|
55
|
+
return null;
|
|
56
|
+
last.answered += 1;
|
|
57
|
+
return { authorizationId, response: { ...last.response, headers: { ...last.response.headers } } };
|
|
58
|
+
}
|
|
59
|
+
/** Give back a place answerFor reserved for an answer that never reached the page. */
|
|
60
|
+
released(authorizationId) {
|
|
61
|
+
if (this.last?.authorizationId === authorizationId && this.last.answered > 0)
|
|
62
|
+
this.last.answered -= 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"catalog_version": "2026-09-15.1",
|
|
4
|
-
"catalog_sha256": "
|
|
4
|
+
"catalog_sha256": "87d4e75b981d7186a2cd39b6f382717acf5b757d053ed389cb8e181f261f0a49",
|
|
5
5
|
"baseline_version": "kernel-native-docs-2026-09-11.1",
|
|
6
6
|
"baseline_sha256": "d86722f49633d8223fccae54cd3d485df4375aee06aa156a273cc689b7c8507c",
|
|
7
7
|
"native_build": null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-cards/sdk",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.2",
|
|
4
4
|
"description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; buy asks the Purchase API for a cart and places it. Card data never enters your process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"_comment_test": "check:built runs first and names the command when a file the build generates (the copies in src/, the preflight contracts in dist/, the Kernel inventory) is missing or stale; verify:pack runs last, on the same dist.",
|
|
31
31
|
"check:built": "node scripts/generate-payment-core.mjs --check && node scripts/generate-url-masks.mjs --check && node scripts/generate-capabilities.mjs --check && node scripts/generate-agent-amount.mjs --check && node scripts/generate-preflight-contract.mjs --check && node scripts/generate-kernel-native-inventory.mjs --check",
|
|
32
32
|
"verify:pack": "node scripts/verify-pack.mjs",
|
|
33
|
-
"test": "npm run check:built && node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs ekashu.test.mjs redsys.test.mjs quickpay.test.mjs paytrail.test.mjs paypal-hosted-fields.test.mjs paypal-card-forms.test.mjs cybersource-secure-acceptance.test.mjs ebanx.test.mjs payrexx.test.mjs payrexx-follow-ups.test.mjs payrexx-adapters.test.mjs nike-order.test.mjs nike-order-adapters.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs wix-payments.test.mjs hipay.test.mjs openpay.test.mjs processout.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs agentcard.test.mjs buy.test.mjs clean-dist.test.mjs approvals.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.test.mjs gravy.test.mjs paddle-vgs.test.mjs sbps.test.mjs hold.test.mjs paddle-tokenex.test.mjs lyra.test.mjs gopay.test.mjs paygent.test.mjs twoc2p.test.mjs tap-payments.test.mjs pay-nl.test.mjs toast.test.mjs redact-url.test.mjs hosted-entry.test.mjs comgate.test.mjs przelewy24.test.mjs tokenex-iframe.test.mjs transbank.test.mjs sumup.test.mjs clover.test.mjs eximbay.test.mjs xendit.test.mjs omise.test.mjs fastspring.test.mjs amazon-payment-services.test.mjs barion.test.mjs paymob.test.mjs payu-latam.test.mjs nexi-checkout.test.mjs oceanpayment.test.mjs pin-payments.test.mjs payplug.test.mjs cardlink-vpos.test.mjs eghl.test.mjs paymentwall.test.mjs vtex-payments.test.mjs vtex-payments-window.test.mjs iyzico.test.mjs usaepay.test.mjs viva-wallet.test.mjs aurus.test.mjs monei.test.mjs paystack.test.mjs frisbii.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs browser-tests.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs && node scripts/verify-pack.mjs",
|
|
33
|
+
"test": "npm run check:built && node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs ekashu.test.mjs redsys.test.mjs quickpay.test.mjs paytrail.test.mjs paypal-hosted-fields.test.mjs paypal-card-forms.test.mjs cybersource-secure-acceptance.test.mjs ebanx.test.mjs payrexx.test.mjs payrexx-follow-ups.test.mjs payrexx-adapters.test.mjs nike-order.test.mjs nike-order-adapters.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs wix-payments.test.mjs hipay.test.mjs openpay.test.mjs processout.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs agentcard.test.mjs buy.test.mjs clean-dist.test.mjs approvals.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.test.mjs gravy.test.mjs paddle-vgs.test.mjs sbps.test.mjs hold.test.mjs paddle-tokenex.test.mjs lyra.test.mjs gopay.test.mjs paygent.test.mjs twoc2p.test.mjs tap-payments.test.mjs pay-nl.test.mjs toast.test.mjs redact-url.test.mjs hosted-entry.test.mjs comgate.test.mjs przelewy24.test.mjs tokenex-iframe.test.mjs transbank.test.mjs sumup.test.mjs clover.test.mjs eximbay.test.mjs xendit.test.mjs omise.test.mjs fastspring.test.mjs amazon-payment-services.test.mjs barion.test.mjs paymob.test.mjs payu-latam.test.mjs nexi-checkout.test.mjs oceanpayment.test.mjs pin-payments.test.mjs payplug.test.mjs cardlink-vpos.test.mjs eghl.test.mjs paymentwall.test.mjs vtex-payments.test.mjs vtex-payments-window.test.mjs iyzico.test.mjs usaepay.test.mjs viva-wallet.test.mjs aurus.test.mjs monei.test.mjs paystack.test.mjs frisbii.test.mjs token-repeat.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs browser-tests.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs && node scripts/verify-pack.mjs",
|
|
34
34
|
"test:browser": "node browser-tests.mjs",
|
|
35
35
|
"check:payment-core": "node scripts/generate-payment-core.mjs --check",
|
|
36
36
|
"test:preflight": "node --test preflight-package.test.mjs preflight-collector.test.mjs kernel-native-qualification.test.mjs",
|