@agent-cards/sdk 0.23.28 → 0.24.1
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 +84 -0
- package/PREFLIGHT.md +7 -1
- package/README.md +197 -102
- package/dist/aci.d.ts +52 -22
- package/dist/aci.js +79 -15
- package/dist/adyen-merchant-hosted.generated.d.ts +2 -2
- package/dist/adyen-merchant-hosted.generated.js +586 -55
- package/dist/agent-amount.generated.js +1 -1
- package/dist/agentcard.d.ts +4 -5
- package/dist/agentcard.js +4 -5
- package/dist/amadeus.d.ts +9 -3
- package/dist/amadeus.js +33 -4
- package/dist/api-module.d.ts +1 -7
- package/dist/api-module.js +3 -5
- package/dist/{voice-retry.js → api-retry.js} +1 -1
- package/dist/api-types.d.ts +53 -0
- package/dist/{voice-types.js → api-types.js} +7 -2
- package/dist/approvals.d.ts +166 -0
- package/dist/approvals.js +213 -0
- package/dist/aurus.d.ts +33 -0
- package/dist/aurus.js +75 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/buy.d.ts +20 -101
- package/dist/buy.js +19 -74
- package/dist/capabilities.generated.js +1 -1
- package/dist/card-fields.generated.js +2864 -249
- package/dist/cardlink-vpos.d.ts +43 -0
- package/dist/cardlink-vpos.js +90 -0
- package/dist/cdp.d.ts +3 -13
- package/dist/cdp.js +244 -175
- package/dist/checkout-com.generated.js +5 -1
- package/dist/client.d.ts +170 -22
- package/dist/client.js +281 -40
- package/dist/creditguard.generated.d.ts +4 -4
- package/dist/cybersource-flex.generated.d.ts +39 -1
- package/dist/cybersource-flex.generated.js +100 -11
- package/dist/datatrans.generated.js +22 -1
- package/dist/eghl.d.ts +40 -0
- package/dist/eghl.js +72 -0
- package/dist/elavon-converge.generated.js +5 -3
- package/dist/gmo.generated.js +7 -2
- package/dist/hold.d.ts +8 -0
- package/dist/hold.js +43 -2
- package/dist/hosted-entry.js +4 -0
- package/dist/index.d.ts +9 -15
- package/dist/index.js +6 -10
- package/dist/lifecycle.d.ts +9 -61
- package/dist/lifecycle.js +24 -10
- package/dist/merchant-card-endpoints.generated.d.ts +6 -1
- package/dist/merchant-card-endpoints.generated.js +344 -19
- package/dist/merchant-hosted.d.ts +8 -1
- package/dist/nike-order.d.ts +51 -0
- package/dist/nike-order.js +81 -0
- package/dist/oceanpayment.d.ts +38 -0
- package/dist/oceanpayment.js +70 -0
- package/dist/paddle-tokenex.generated.js +111 -11
- package/dist/payplug.d.ts +41 -0
- package/dist/payplug.js +71 -0
- package/dist/payrails.generated.js +33 -8
- package/dist/pre-claim.d.ts +11 -0
- package/dist/pre-claim.js +130 -7
- package/dist/preflight-capabilities.generated.d.ts +0 -4
- package/dist/preflight-capabilities.generated.js +0 -5
- package/dist/preflight-catalog.json +200 -6
- package/dist/preflight-playwright.d.ts +2 -2
- package/dist/preflight-playwright.js +63 -14
- package/dist/preflight-schemas.json +18 -0
- package/dist/preflight.generated.d.ts +5 -4
- package/dist/preflight.generated.js +63 -3
- package/dist/preparation.d.ts +7 -0
- package/dist/preparation.js +21 -0
- package/dist/prepared-processor.d.ts +18 -5
- package/dist/prepared-processor.js +48 -8
- package/dist/primer.generated.js +2889 -268
- package/dist/processout.generated.d.ts +61 -0
- package/dist/processout.generated.js +577 -0
- package/dist/requests.js +1 -1
- package/dist/shiji-sts.generated.js +111 -11
- package/dist/stripe-checkout.generated.js +2761 -319
- package/dist/substitutions.generated.js +553 -26
- package/dist/tap-payments.generated.js +111 -11
- package/dist/twoc2p.generated.js +111 -11
- package/dist/url-masks/bluesnap.d.ts +2 -0
- package/dist/url-masks/bluesnap.js +15 -0
- package/dist/url-masks/oceanpayment.d.ts +2 -0
- package/dist/url-masks/oceanpayment.js +13 -0
- package/dist/url-masks/payplug.d.ts +2 -0
- package/dist/url-masks/payplug.js +13 -0
- package/dist/url-masks/vtex_payments.d.ts +2 -0
- package/dist/url-masks/vtex_payments.js +12 -0
- package/dist/url-masks.generated.d.ts +1 -1
- package/dist/url-masks.generated.js +6 -2
- package/dist/verifone.generated.js +42 -2
- package/dist/vtex-payments.generated.d.ts +127 -0
- package/dist/vtex-payments.generated.js +544 -0
- package/dist/webhooks.d.ts +2 -2
- package/dist/webhooks.js +2 -2
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +6 -8
- package/dist/languages.d.ts +0 -23
- package/dist/languages.js +0 -76
- package/dist/voice-pkce.d.ts +0 -49
- package/dist/voice-pkce.js +0 -147
- package/dist/voice-stream.generated.d.ts +0 -118
- package/dist/voice-stream.generated.js +0 -170
- package/dist/voice-types.d.ts +0 -735
- package/dist/voice.d.ts +0 -373
- package/dist/voice.js +0 -815
- package/readme.test.mjs +0 -53
- /package/dist/{voice-retry.d.ts → api-retry.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# @agent-cards/sdk
|
|
2
2
|
|
|
3
|
-
Agentcard's SDK: one client, `Agentcard`, with
|
|
3
|
+
Agentcard's SDK: one client, `Agentcard`, with three modules. The directory in the monorepo is still `packages/checkout`; the package on npm is `@agent-cards/sdk`. `@agent-cards/checkout` is its older name and re-exports this package, entry for entry.
|
|
4
4
|
|
|
5
5
|
| Module | What it does |
|
|
6
6
|
|---|---|
|
|
7
7
|
| `agentcard.checkout` | Pauses a browser purchase's card request, has the cardholder approve it, and replays the processor's answer; creates a purchase directly; the recognisers and the request builders |
|
|
8
|
-
| `agentcard.
|
|
8
|
+
| `agentcard.buy` | Asks the Purchase API for a cart for a person you connected, places it by its hash, and reads the conversation |
|
|
9
|
+
| `agentcard.approvals` | Creates a purchase for a person you connected, which they approve on Agentcard's page, and reads or waits for their decision |
|
|
9
10
|
|
|
10
11
|
Card data never enters your process, your logs or your network: your server holds a placeholder card and a token, and the person's device supplies the real card.
|
|
11
12
|
|
|
@@ -15,73 +16,9 @@ Card data never enters your process, your logs or your network: your server hold
|
|
|
15
16
|
npm i @agent-cards/sdk
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
Node 22 or any runtime with `fetch
|
|
19
|
+
Node 22 or any runtime with `fetch` and WebCrypto. Zero runtime dependencies. One base URL, `https://api.agentcard.sh`; sandbox and live are decided by your credentials.
|
|
19
20
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
A person connects their card to your app once, then approves a purchase by voice. Six lines, against a sandbox client.
|
|
23
|
-
|
|
24
|
-
```js
|
|
25
|
-
import { Agentcard } from '@agent-cards/sdk';
|
|
26
|
-
|
|
27
|
-
const agentcard = new Agentcard({ clientId: process.env.AGENTCARD_CLIENT_ID, clientSecret: process.env.AGENTCARD_CLIENT_SECRET, voice: { approval: 'sheet', returnUrl: 'yourapp://connected' } });
|
|
28
|
-
|
|
29
|
-
const link = await agentcard.voice.connect(); // the app opens link.url in its sheet
|
|
30
|
-
const person = await agentcard.voice.completeConnect(returnLink); // the link the sheet came back with
|
|
31
|
-
const purchase = await agentcard.voice.authorizations.create({ user_id: person.user_id, merchant: 'Coffee', amount: 1250, currency: 'usd', request: 'sandbox' });
|
|
32
|
-
const { card, cards } = purchase; // the card that pays and why; cards are the others, for authorizations.setCard(purchase.id, id)
|
|
33
|
-
const result = await agentcard.voice.awaitResult(purchase.id); // { status: 'approved', approved_with: 'voice' }
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
The app opens `purchase.approval_url` in its sheet for the approval (see `@agent-cards/voice`). Sandbox and live are decided by the credentials: the token exchange names the client's mode and the client reads it (`agentcard.sandbox`), so nothing here says which one this is. `request: 'sandbox'` is the first-run form: it names no processor, the sandbox completes it as a sandbox charge, and only a sandbox client may send it. At go-live the credentials change and the request becomes the processor's own card request: one a browser purchase paused on, or one `agentcard.checkout.requests.build(...)` made for your order. `voice.returnUrl` is exactly one of the redirect links registered on your client. `readme.test.mjs` runs this block against a stub on every test run.
|
|
37
|
-
|
|
38
|
-
### A returning person
|
|
39
|
-
|
|
40
|
-
Store `person.user_id`. On a later launch ask what they already have, and open the Vault only for what is missing.
|
|
41
|
-
|
|
42
|
-
```js
|
|
43
|
-
const who = await agentcard.voice.people.get(storedUserId); // { user_id, card, voice: ['en'], rule, passkey }
|
|
44
|
-
if (!who.card || !who.voice.includes('en')) {
|
|
45
|
-
const link = await agentcard.voice.connect({ voice: 'required' }); // the Vault shows only the missing step
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
`connection_not_found` means the person never connected through your client (or disconnected): treat them as new.
|
|
50
|
-
|
|
51
|
-
### Approving inside your app
|
|
52
|
-
|
|
53
|
-
`voice: { approval: 'sheet' }`: the person approves on Agentcard's page, opened in your app for one moment; `purchase.approval_url` (or `voice.approvals.approvalUrl(id)`) is the link. `voice: { approval: 'in_app' }`: hold-to-talk in your app, judged on the phone and by Agentcard, signed by a key in your app. The server then mints the key word as one sealed bundle the app forwards unread:
|
|
54
|
-
|
|
55
|
-
```js
|
|
56
|
-
const bundle = await agentcard.voice.challengeForHost(purchase.id, { lang: 'es' }); // to the app's VoiceHost.approve, unread
|
|
57
|
-
const reg = await agentcard.voice.appKeys.register(key, { user_id: person.user_id }); // the key the app's frame minted; reg.credential goes back to the phone
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
One choice per business, not per purchase. Leave it out and the client approves on Agentcard's page, as with `approval: 'sheet'`; a value that is neither `'sheet'` nor `'in_app'` throws `missing_approval_posture` at construction.
|
|
61
|
-
|
|
62
|
-
### The card that pays
|
|
63
|
-
|
|
64
|
-
Under the person's smart routing (on unless they turned it off in the Vault) the card that earns the most for the purchase pays when your create pins none, and every read names it: `purchase.card` is `{ id, name, last4, brand, reason }`, the reason the recommendation's sentence for it, null when your create pinned the card (`card_id`) or the person chose another. `purchase.cards` lists every card the person could pay with instead, each with `recommended` and the recommendation's reason where it scored the card. The key word line names the card and the reason before the word ("Paying 34 dollars and 99 cents to Epicdrops with your Sapphire, three times the points on shopping. Please say your secure passphrase, …"), and the bundle carries `card` so the app shows it beside the word: hearing the card, then speaking the passphrase, is the confirmation. "Another card", spoken or tapped, is `voice.authorizations.setCard(purchase.id, card.id)` before the approval; ask for the key word after it, so the line names the card that will pay. Once the purchase is decided the call answers `already_finalized`. A prepared checkout pays only with the card the cardholder approved for it: there the call answers `card_not_stored` for a card a company added and `preparation_card_locked` for any other card.
|
|
65
|
-
|
|
66
|
-
### Where the key word goes
|
|
67
|
-
|
|
68
|
-
`challengeForHost(id, { delivery })` names where the key word goes, and the bundle carries both of Agentcard's lines either way: `say` with the word in it, `say_private` pointing at the screen. `delivery: 'screen'`: the app shows `word`, speaks `say_private`, and the word never leaves the phone as sound. `delivery: 'spoken'`: the app speaks `say`, or plays `audio_url` (`audio_delivery` says which form it carries), so a purchase completes with no look at the screen; the word stays on the screen as well. `voice.challenge` takes the same option.
|
|
69
|
-
|
|
70
|
-
The choice is yours, and it has a cost. Spoken through the loudspeaker, a nearby recorder hears the fresh word before the person has said it. Over headphones or the earpiece it stays with them. The app half reads the audio route (this package cannot) and names it on the streamed answer as `channel`; the `@agent-cards/voice` README says how the reference app reads it.
|
|
71
|
-
|
|
72
|
-
### A bot with no screen
|
|
73
|
-
|
|
74
|
-
A messaging bot or an agent with no screen in front of the person sends `link.url` or `purchase.approval_url` as a text itself; the person opens it on their phone. Leave `return_url` out on such a connect: the Vault ends on "Connected", the link polls (`grant: 'poll'`), and `voice.awaitConnected(link.session_id)` reads the result.
|
|
75
|
-
|
|
76
|
-
### Languages
|
|
77
|
-
|
|
78
|
-
A language is a BCP 47 tag, and the list Agentcard speaks is data: `await agentcard.voice.languages()` reads it (`[{ tag: 'en', name: 'English' }, { tag: 'es', name: 'Español' }]`), and every call that names a `lang` checks the tag against it first, refusing one not on the list with `unsupported_language` before anything is sent. A language added on Agentcard's side needs no new release of this package.
|
|
79
|
-
|
|
80
|
-
### Reading, without waiting
|
|
81
|
-
|
|
82
|
-
`voice.awaitResult`, `voice.awaitConnected` and `voice.actions.awaitDone` wait. For one read with no wait, from a handler that answers a poll: `voice.authorizations.get(id)`, `voice.sessions.get(session_id)`, `voice.actions.get(session_id)`. Every read waits out the API's `slow_down` for you. Passing `timeout_ms: 0` to an await verb is not a read; use the read verbs.
|
|
83
|
-
|
|
84
|
-
### Webhooks
|
|
21
|
+
## Webhooks
|
|
85
22
|
|
|
86
23
|
Agentcard signs every delivery. Verify against the raw bytes and branch on `type`:
|
|
87
24
|
|
|
@@ -94,35 +31,40 @@ if (isCheckoutAuthorizationEvent(event) && event.type === 'checkout_authorizatio
|
|
|
94
31
|
|
|
95
32
|
More than one secret is accepted, so a secret rotates without a missed event; a stale timestamp, a malformed header and a wrong signature each throw an `AgentcardError` with its own code. The event unions are `CheckoutAuthorizationEvent` (`checkout_authorization.*`) and `VaultEvent` (`vault.*`); an event of a type this release does not name arrives as `OtherEvent`, so branch on `type` and read `data` yourself.
|
|
96
33
|
|
|
97
|
-
##
|
|
34
|
+
## Connect a person
|
|
35
|
+
|
|
36
|
+
Every purchase names the person who pays: `user_id` on `agentcard.buy`, `user` on `agentcard.checkout`. A person gets that id when they store a card in the Vault from a link your server mints:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
curl -X POST https://api.agentcard.sh/api/v2/vault_sessions -H "Authorization: Bearer $ORG_TOKEN" -H "Content-Type: application/json" -d '{}'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Send the person the answer's `url`. When they finish, the `vault.session_linked` webhook carries their `user_id`, and `GET /api/v2/vault_sessions/{id}` returns it once `status` is `linked`. Store it, because every call below names it. `$ORG_TOKEN` is your client's token from `POST /api/v2/oauth/token`. The [Vault quickstart](https://docs.agentcard.sh/vault/quickstart) covers the whole flow, test cards included.
|
|
43
|
+
|
|
44
|
+
## Buy through the Purchase API
|
|
98
45
|
|
|
99
|
-
`agentcard.buy` is Agentcard's Purchase API from the same client: it finds the merchant, builds the cart and places the order for a person you connected.
|
|
46
|
+
`agentcard.buy` is Agentcard's Purchase API from the same client: it finds the merchant, builds the cart and places the order for a person you connected. `user_id` is the id from [Connect a person](#connect-a-person).
|
|
100
47
|
|
|
101
48
|
```js
|
|
102
|
-
const purchase = await agentcard.voice.authorizations.create({ user_id, merchant: 'Coffee', amount: 650, currency: 'usd', request: 'sandbox' });
|
|
103
|
-
const result = await agentcard.voice.awaitResult(purchase.id); // { status: 'approved', approved_with: 'voice' }
|
|
104
49
|
const turn = await agentcard.buy.ask({ user_id, ask: 'a flat white from Coffee to my desk' }); // { status: 'needs_input', cart: { hash, totalCents, items } }
|
|
105
|
-
const placed = await agentcard.buy.confirm({ user_id, conversation_id: turn.conversation_id, confirm: turn.cart.hash
|
|
50
|
+
const placed = await agentcard.buy.confirm({ user_id, conversation_id: turn.conversation_id, confirm: turn.cart.hash });
|
|
106
51
|
const state = await agentcard.buy.conversation({ user_id, conversation_id: turn.conversation_id }); // { turn_in_progress: false, orders: [{ order_id }] }
|
|
107
52
|
if (placed.status !== 'order_placed') console.log(placed.status, placed.decline_code); // 'declined' names why; 'needs_input' is a question, not a failure
|
|
108
53
|
```
|
|
109
54
|
|
|
110
55
|
Every call names `user_id`; a person not connected to your client is refused with `user_not_connected`. `buy.ask` answers one turn: `status` is `needs_input` (a question, or a cart waiting for its confirm), `order_placed`, `partially_placed` (several carts, some placed; each one's outcome is in `placements`) or `declined` (with `decline_code`). Read `turn.cart.hash` and never the prose; a cart that changed since its hash was issued refuses the confirm with `cart_changed`, and the fresh carts ride on the error. `buy.confirm` with `authorization_id` places against the approval the person gave: one that is not approved, is for another purchase, or already paid an order is refused with `authorization_not_approved`, `authorization_mismatch` or `authorization_spent`, and nothing is charged; the answer carries `authorization_id` back. Without it the confirm runs as before, with `payment_source: 'vault'` for the person's own card. A turn runs against live merchants, so this call waits up to 130 seconds and is sent once, never retried; a turn still running past the API's own budget answers `error_code: 'turn_timeout'` with the carts it had shown, and `buy.conversation` reads whether it is done (`turn_in_progress`) and every order it placed. Refusals are a `BuyError`, an `AgentcardError` with the API's code; `isBuyDeclined(turn)` narrows a declined answer. `buy.test.mjs` runs this block against a stub on every test run.
|
|
111
56
|
|
|
112
|
-
## Approve a purchase
|
|
57
|
+
## Approve a purchase on Agentcard's page
|
|
113
58
|
|
|
114
|
-
|
|
59
|
+
`agentcard.approvals` creates a purchase for a person you connected and has them approve it on Agentcard's page. `user_id` is the id from [Connect a person](#connect-a-person); `request` is the processor's card request the purchase pays, or `'sandbox'` under a sandbox client.
|
|
115
60
|
|
|
116
61
|
```js
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}); // 'order_placed', 'declined', or 'voice_execution_unsupported'
|
|
121
|
-
if (placed.status === 'declined') console.log(placed.decline_code); // 'voice_declined' | 'approval_expired'
|
|
122
|
-
if (placed.status === 'voice_execution_unsupported') console.log(placed.error.message); // take your own path; nothing was charged
|
|
62
|
+
const purchase = await agentcard.approvals.create({ user_id, merchant: 'Coffee', amount: 1250, currency: 'usd', request: 'sandbox' });
|
|
63
|
+
// Send the person purchase.approval_url: they approve with Face ID, Touch ID or their master password.
|
|
64
|
+
const result = await agentcard.approvals.awaitResult(purchase.id); // { status: 'approved', approved_with: 'passkey' }
|
|
123
65
|
```
|
|
124
66
|
|
|
125
|
-
`
|
|
67
|
+
`approvals.get(id)` reads where a purchase stands without waiting, and `approvals.cancel(id, { reason })` withdraws one the person has not decided, with `person_closed_sheet`, `person_changed_mind` or `business_cancelled`. `awaitResult` polls no faster than the interval the purchase names, waits out `slow_down`, and answers `{ status: 'timeout' }` after 60 seconds unless `timeout_ms` names another deadline. A live client refuses `request: 'sandbox'` with `sandbox_request_in_live` before any call.
|
|
126
68
|
|
|
127
69
|
## Browser purchases
|
|
128
70
|
|
|
@@ -176,10 +118,6 @@ await agentcard.checkout.attachToCdp(cdp, pageSessionId, { user: 'usr_123', merc
|
|
|
176
118
|
|
|
177
119
|
Merchant timeouts still apply to the request itself: Square's observed tokenization deadline is about 10 seconds, Braintree's native request timeout is 60 seconds, and Adyen Web's own request timeout abandons its Sessions payment call 60 seconds after Pay. These are the processors' limits; the SDK's own authorization wait stays 15 minutes. The approval outlives the page's own request: when the page's script gives up on its paused card request while the cardholder is still deciding, the SDK keeps the approval pending (state `awaiting_approval`, reason `merchant_request_lost`) and answers the page's next request for the same purchase from it; when the approval lands first, the controller reads `ready_to_submit` with reason `awaiting_merchant_retry`, and one more Pay click completes the purchase. The wait for that request is bounded (`merchantRetryWaitMs`, two minutes by default); an approval the page never asks for again is retired as `merchant_never_retried`. For a short-deadline processor, `controller.prepare()` before the first Pay finishes the request inside its own deadline. A Shiji STS getTokenId is the exception: its approval answers only the request it was created from, and the SDK cancels it as `merchant_request_aborted` when the page abandons that request.
|
|
178
120
|
|
|
179
|
-
### Pause only, and let your server create
|
|
180
|
-
|
|
181
|
-
`attachToCdp(…, { pauseOnly: true, onPaused })` pauses the card request without creating the purchase and hands you `{ request, recognizer, page_origin, controller }`. Create it with `voice.authorizations.create` as on any other purchase, read the result, and `controller.resume(result)` replays the processor's answer into the page. `recognizer` names the processor that claimed the request (`stripe`), and so does the `card_request_paused` event's detail, so nothing has to re-match the URL.
|
|
182
|
-
|
|
183
121
|
### Hold the card request until the person confirms the total
|
|
184
122
|
|
|
185
123
|
`attachToCdp(…, { hold: true })` pauses the first card request and holds it with nothing created. `controller.hold.whenHeld()` resolves with what was held (the processor and the endpoint, never a body). Once the person confirmed the total and the page still shows it, `controller.release({ amount, currency, pageTotal })` creates the authorization at that amount: a live client answers the page with the device's replay, a sandbox client never sends the request on and resolves `approved_not_charged`. A page total that moved, or one you could not read (`pageTotal: null`), cancels the request instead. `upTo: true` releases at a ceiling while the page total stays at or under it. `controller.hold.cancel()` fails the request. A held request is cancelled before its processor's own wait runs out (Braintree 60 s, Bolt 20 s, 30 s for the rest), and every other card request, and every one after a decision, fails at once.
|
|
@@ -204,13 +142,7 @@ The three shipped builders are the Stripe shapes Agentcard completes from a dire
|
|
|
204
142
|
|
|
205
143
|
### Moving from `@agent-cards/checkout` 0.x
|
|
206
144
|
|
|
207
|
-
`@agent-cards/checkout` is this package's older name and re-exports it for one release.
|
|
208
|
-
|
|
209
|
-
## The paused request
|
|
210
|
-
|
|
211
|
-
A purchase's `status` is one of `awaiting_approval`, `approved`, `declined` (the person said no, a rule or the processor refused: `terminal_reason` says which), `cancelled` (withdrawn before a decision, by you or by the checkout runtime), `expired`, or, from `awaitResult` alone, `timeout` at your deadline. `voice.authorizations.cancel(id, { reason })` withdraws a purchase the person has not decided yet, in your words (`person_closed_sheet`, `person_changed_mind`, `business_cancelled`). `checkout_origin` on the create (or the paused request's `origin` header) is what an auto-approval rule needs to take the purchase; without it the create answers `execution_reason: 'unverified_origin'`. On a finished purchase `awaitResult` and `authorizations.get` carry `settlement`: whether the charge settled, as Agentcard read it back from the processor (`status: 'settled' | 'not_settled' | 'unknown'`, `final`, `settled_amount`, `message`).
|
|
212
|
-
|
|
213
|
-
The connect link is an OAuth authorization grant: `voice.connect` mints `state` and a PKCE challenge, the sheet's return link carries `code` and `state`, and `voice.completeConnect(url)` exchanges it once, within 60 seconds, refusing a `state` it did not mint before any call. When another server instance minted the link, pass the `code_verifier` and `expected_state` that `connect` returned there. A mint with no return link polls (`grant: 'poll'`, then `awaitConnected`). `readReturnLink(url)` reads a return link's `code`, `state` and `voice_reset`. `voice.connect({ landing: 'voice_options' })` mints a link that lands the signed-in person on the Vault's Voice options page (the voice key, its reset, the language, the passkey it rests on) for an app's own settings row; it collects nothing and polls.
|
|
145
|
+
`@agent-cards/checkout` is this package's older name and re-exports it for one release. `VaultClient` constructs the same client as `Agentcard`; every top-level checkout verb (`syncRegistry`, `authorize`, `isCardRequest`, …) is also under `agentcard.checkout`.
|
|
214
146
|
|
|
215
147
|
## Credentials
|
|
216
148
|
|
|
@@ -235,8 +167,16 @@ what this package encapsulates.
|
|
|
235
167
|
| | Sees the real card |
|
|
236
168
|
|---|---|
|
|
237
169
|
| Your agent / browser | **no**, only a dummy PAN and a token |
|
|
238
|
-
| Agentcard servers |
|
|
239
|
-
| Cardholder's device | yes, decrypts locally, calls the merchant directly |
|
|
170
|
+
| Agentcard servers | no for a card the cardholder stored, only a request template and a token; yes for a card your company issued, for the one payment it pays |
|
|
171
|
+
| Cardholder's device | yes, decrypts a stored card locally or receives your company's card from Agentcard, and calls the merchant directly |
|
|
172
|
+
|
|
173
|
+
The cardholder's Vault keeps no copy of a card your company issued, only a link
|
|
174
|
+
to it, so their device has nothing to decrypt. When the cardholder approves a
|
|
175
|
+
payment with that card, Agentcard's servers fetch its number and CVC from the
|
|
176
|
+
card's issuer and send them to the cardholder's device in the response to that
|
|
177
|
+
approval. The servers hold the card in memory for that one response and never
|
|
178
|
+
log or store it. Your agent and browser still see only the dummy card and the
|
|
179
|
+
token.
|
|
240
180
|
|
|
241
181
|
On a reviewed merchant's own card endpoint (mode `card_endpoint`, below) the
|
|
242
182
|
device sends the card to the merchant through Agentcard's checkout relay, which
|
|
@@ -244,6 +184,18 @@ carries end-to-end TLS it cannot read. Agentcard's servers hold the paused
|
|
|
244
184
|
request's session and the merchant's answer encrypted, for minutes, and hand
|
|
245
185
|
each over once.
|
|
246
186
|
|
|
187
|
+
Agentcard's own shopping agent (/buy) has one more lane, built and switched
|
|
188
|
+
off, the typed card lane, which this package never runs in your process. Once
|
|
189
|
+
it is on, at a store whose card request no swap can reach, the shopper approves
|
|
190
|
+
Agentcard typing their card into the store's page, and Agentcard's card typer
|
|
191
|
+
types it there and presses the store's Pay, in Agentcard's own hosted browser.
|
|
192
|
+
The card reaches the typer sealed by the shopper's device, and your agent never
|
|
193
|
+
sees it. Agentcard's backend and its checkout agent let go of that page before
|
|
194
|
+
the card goes in and connect again only once the store's page settled after
|
|
195
|
+
Pay, so the requests that carry the card reach the typer's connection alone.
|
|
196
|
+
Until then, /buy answers such a store as it does today: the card form is not
|
|
197
|
+
one it pays at.
|
|
198
|
+
|
|
247
199
|
Because your process only ever handles a dummy card and an opaque token, this
|
|
248
200
|
integration is designed to keep you out of PCI scope. Get your own QSA's read
|
|
249
201
|
before you put that in writing.
|
|
@@ -294,6 +246,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
|
|
|
294
246
|
| Amadeus Checkout (Air France, KLM) | dark: only for an org Agentcard enabled, and only through `prepare({ psp: 'amadeus_checkout', cardId })`. The Checkout Web SDK's card add pauses; every other action on its URL continues. Air France and KLM keep the expiry in their own form, so the agent types the stored card's real expiry there and the checkout pays only with that card. Every Data Collection Page store is refused as `amadeus_dcp_field_config_unreviewed`. No live purchase yet. See "Prepare an Amadeus Checkout card step" |
|
|
295
247
|
| Paddle checkout (Paddle's card vault or TokenEx; any Paddle seller) | dark launch: after `vault.syncRegistry()`, and only for an org Agentcard has enabled. On a Paddle checkout whose card method names VGS, pay only through `prepare({ psp: 'paddle_vgs', environment: 'production' })` (`'sandbox'` for Paddle's sandbox) on an attachment whose `cardId` names the paying card; without one, `prepare()` fails with `card_required`. The frame's post to `vault.paddle.com/cards` pauses, the cardholder's device sends the card itself, and the frame receives the stored card's id; its second request after its 10-second timeout gets the same answer. Type `placeholderCard()`'s facts for that card into Paddle's form, and pass `amount` and `currency`, the total the cardholder approves. Only Visa, Mastercard and American Express cards are paid (`paddle_vgs_card_unsupported` otherwise), and a typed expiry or placeholder that is not the card's fails the create with `paddle_vgs_card_mismatch`. On the live vault, Agentcard pays a checkout at any Paddle seller's own https page; from any other page, such as `http://localhost:4000`, `prepare()` fails with `unsupported_checkout`. Paddle's vault keeps the card, so Paddle can charge it again without another approval, at a subscription's renewal for example. Paddle's `/pay` that follows names the approved card's first six and last four digits, expiry and brand: the SDK writes them over the ones Paddle's form read from the placeholder, and aborts a `/pay` that names another stored card or comes more than a minute later. On a Paddle checkout whose card method names TokenEx, call no `prepare()`: the SDK pauses Paddle's `/pay`, and once the cardholder approves it, writes the card the cardholder's device encrypted over the placeholder's. With a `paddle_vgs` preparation on the attachment, the SDK refuses that `/pay` with `checkout_changed`. A Paddle checkout that sends the card through Spreedly is not supported |
|
|
296
248
|
| Paygent (`PaygentToken.js`; Fundex donation forms) | dark launch: only for an org Agentcard enabled, after `vault.syncRegistry()`, and only through `prepare({ psp: 'paygent', environment: 'production' })` (`'sandbox'` for Paygent's test host) before the press that sends the card: the script gives up on its token request 35 seconds after sending it. The request to `token.paygent.co.jp/n/token/request` pauses, the cardholder's device sends the card itself, and the page receives Paygent's single-use token. One approval sends one card request, so the SDK refuses a second press, and a request for a stored card's security code too. Pass `amount` and `currency`, the total the cardholder approves. On the live host the API accepts only a reviewed merchant at its reviewed page, under its reviewed name, merchant id and token key, and in its currency (Tokyo Chinese School at `https://fundexapp.jp`, in yen), and refuses any other before anyone is asked. The merchant runs 3-D Secure after the token in the agent's browser. No live payment has gone through this route yet |
|
|
249
|
+
| ProcessOut (ProcessOut.js's card frame on `js.processout.com`) | dark launch: only for the company Agentcard turned ProcessOut on for, after `vault.syncRegistry()`, and only through `prepare({ psp: 'processout', environment: 'production' })` (`'sandbox'` for a `test-proj_` project) before the press that asks the frame for a token: the frame calls the page's error callback 20 seconds after that press. The frame's post to `api.processout.com/cards` pauses, the cardholder's device sends the card to ProcessOut itself, and the frame receives ProcessOut's card token, rebuilt from the card facts the page reads. A refusal inside a 2xx answer never reaches the frame as a token, and a request still unsent 12 seconds after the pause fails before the card leaves (`processout_send_window_closed`). Pass `amount` and `currency` above zero, the total the cardholder approves. A live project pays only a store Agentcard reviewed, at its reviewed page and under its reviewed name and project id, and Agentcard has reviewed none yet, so a live `prepare()` fails with `unsupported_checkout`. No ProcessOut payment has gone through this route yet |
|
|
297
250
|
|
|
298
251
|
The recognizer list, and the status of each reviewed Adyen merchant profile on
|
|
299
252
|
your client, are fetched from the API at runtime (`agentcard.checkout.syncRegistry()`),
|
|
@@ -301,7 +254,7 @@ so new processors work without you shipping a release. `attachToCdp` derives the
|
|
|
301
254
|
`Fetch.enable` url patterns from that same list rather than a constant, which is
|
|
302
255
|
why the sync call belongs before the attach. `agentcard.checkout.cardUrlPatterns()` returns
|
|
303
256
|
those patterns if you arm a CDP connection yourself. Call
|
|
304
|
-
`GET /v2/checkout/recognizers?modes=token,cse,hosted_form,device_encrypt&capabilities=fiserv_card_capture,gmo_shared_pk,capture_context,juspay_txns.v1,datatrans_secure_fields_tokenize,authorize_net_dpm_form,pelecard_transaction,aci_split_card.v1,creditguard_pps_process,primer_payment_instruments,payrails_device_encrypt,converge_hpp_process,worldpay_eprotect_jsonp,wspay_confirm,shiji_sts_pinned_key,amadeus_checkout_actions.v1,worldline_mycheckout_form,verifone_card_capture,ekashu_hosted_payment_page,cybersource_sa_token_create,gravy_session_fields_put,moneris_hosted_tokenization,paddle_vgs_cards,paddle_tokenex_pay,redsys_hosted_payment_page,lyra_krypton_payment,payfast_card_pay,wix_payments_tokenize,paymongo_checkout_pay,paygent_token,pay_nl_card_token,hipay_vault_token,twoc2p_secure_pay,quickpay_hosted_payment_page,revolut_card_field,paypal_card_fields,gopay_card_form,payrexx_card_frame,comgate_hosted_payment_page,przelewy24_hosted_payment_page,makecommerce_card_frame,poynt_collect_nonce,tokenex_iframe_tokenize,transbank_webpay_hosted_payment_page,sumup_checkout_process,clover_card_token,eximbay_hosted_payment_page,toast_pinned_key,xendit_card_token,omise_card_token,paytrail_hosted_payment_page,fastspring_card_payment,openpay_tokenizer,barion_hosted_payment_page,paymob_checkout_pay,payu_latam_webcheckout_card,nexi_checkout_pay,pin_payments_card_token`
|
|
257
|
+
`GET /v2/checkout/recognizers?modes=token,cse,hosted_form,device_encrypt&capabilities=fiserv_card_capture,gmo_shared_pk,capture_context,juspay_txns.v1,datatrans_secure_fields_tokenize,authorize_net_dpm_form,pelecard_transaction,aci_split_card.v1,creditguard_pps_process,primer_payment_instruments,payrails_device_encrypt,converge_hpp_process,worldpay_eprotect_jsonp,wspay_confirm,shiji_sts_pinned_key,amadeus_checkout_actions.v1,worldline_mycheckout_form,verifone_card_capture,ekashu_hosted_payment_page,cybersource_sa_token_create,gravy_session_fields_put,moneris_hosted_tokenization,paddle_vgs_cards,paddle_tokenex_pay,redsys_hosted_payment_page,lyra_krypton_payment,payfast_card_pay,wix_payments_tokenize,paymongo_checkout_pay,paygent_token,pay_nl_card_token,hipay_vault_token,twoc2p_secure_pay,quickpay_hosted_payment_page,revolut_card_field,paypal_card_fields,gopay_card_form,payrexx_card_frame,comgate_hosted_payment_page,przelewy24_hosted_payment_page,makecommerce_card_frame,poynt_collect_nonce,tokenex_iframe_tokenize,transbank_webpay_hosted_payment_page,sumup_checkout_process,clover_card_token,eximbay_hosted_payment_page,toast_pinned_key,xendit_card_token,omise_card_token,paytrail_hosted_payment_page,fastspring_card_payment,openpay_tokenizer,barion_hosted_payment_page,paymob_checkout_pay,payu_latam_webcheckout_card,nexi_checkout_pay,pin_payments_card_token,paymentwall_card_token,oceanpayment_hosted_payment_page,payplug_hosted_payment_page,processout_card_token,aps_custom_merchant_page,bluesnap_hosted_payment_fields,iyzico_checkout_form,midtrans_card_token,sbps_token_jsonp,tap_card_sdk_token,usaepay_payment_key,vtex_payments_card_payment,viva_smart_checkout_chargetoken,cardlink_vpos_hosted_payment_page,eghl_hosted_payment_page`
|
|
305
258
|
for the list this build syncs. The API serves Fiserv's card capture only to a
|
|
306
259
|
build that lists `fiserv_card_capture`, and only for a company Agentcard turned
|
|
307
260
|
Fiserv on for. An older SDK never pauses a Fiserv request it could not finish,
|
|
@@ -380,6 +333,42 @@ this build pauses the gateway link `secure.barion.com/Pay?Id=<payment id>` for
|
|
|
380
333
|
the cardholder's device and fails every post a page it watches makes to the
|
|
381
334
|
gateway. A merchant's server often sends the buyer to that link with a
|
|
382
335
|
redirect, and only `attachToCdp` pauses a redirect.
|
|
336
|
+
PayPlug's payment page (`payplug_hosted_payment_page`) is served the same way:
|
|
337
|
+
this build pauses the page link `secure.payplug.com/pay/<payment id>` (or
|
|
338
|
+
`/pay/test/<payment id>`) for the cardholder's device and fails every post a
|
|
339
|
+
page it watches makes to PayPlug's page host. The link names no amount, so
|
|
340
|
+
`authorize()` needs your `amount`, above zero, and the API takes it in euros
|
|
341
|
+
alone (`currency: 'eur'`), the one currency PayPlug's page charges. One PayPlug
|
|
342
|
+
payment takes one authorization across your company: a second one while the
|
|
343
|
+
first awaits answers `duplicate_submission`, and one for a payment a card may
|
|
344
|
+
already have gone to answers `payplug_payment_already_sent`. The API serves
|
|
345
|
+
PayPlug to Agentcard's own company alone for now, and takes no PayPlug payment
|
|
346
|
+
until Agentcard has reviewed the name and the PayPlug account a merchant's
|
|
347
|
+
PayPlug page shows: until then it refuses every PayPlug link with
|
|
348
|
+
`seller_unreviewed`, which is expected and not a setup error.
|
|
349
|
+
Cardlink's VPOS (`cardlink_vpos_hosted_payment_page`), the payment page Greek
|
|
350
|
+
banks give their merchants on `vpos.eurocommerce.gr`, `ecommerce.cardlink.gr`
|
|
351
|
+
and `www.alphaecommerce.gr`, is served the same way: this build pauses the
|
|
352
|
+
merchant's signed entry, a form post to `/vpos/shophandlermpi`, for the
|
|
353
|
+
cardholder's device and fails every other post a page it watches makes to those
|
|
354
|
+
hosts, the card page's own post to the same URL included. The entry names its
|
|
355
|
+
amount in euros, and the API holds your `amount` to it exactly. The API serves
|
|
356
|
+
the lane to Agentcard's own company alone for now. While the relay route stays
|
|
357
|
+
closed, every Cardlink create answers `503 cardlink_vpos_relay_unavailable`
|
|
358
|
+
before any prompt. Once the route opens, the API takes an entry only for a
|
|
359
|
+
merchant Agentcard has reviewed by its mid on its host, one so far, and refuses
|
|
360
|
+
every other entry with `seller_unreviewed`, whose answer names the `mid`,
|
|
361
|
+
`vpos_host` and `result_host` a review needs. Both refusals are expected and not
|
|
362
|
+
a setup error.
|
|
363
|
+
eGHL's payment page (`eghl_hosted_payment_page`, sold as ADAPTIS) on
|
|
364
|
+
`securepay.e-ghl.com` is served the same way: this build pauses the merchant's
|
|
365
|
+
entry, a `GET` of `/IPG/payment.aspx` whose query names the payment, for the
|
|
366
|
+
cardholder's device and fails every post a page it watches makes to eGHL's
|
|
367
|
+
hosts, the card page's own post to the same address included. The entry names
|
|
368
|
+
its amount, and the API holds your `amount` to it exactly. The API serves the
|
|
369
|
+
lane to Agentcard's own company alone for now. While the relay route stays
|
|
370
|
+
closed, every eGHL create answers `503 eghl_relay_unavailable` before any
|
|
371
|
+
prompt, which is expected and not a setup error.
|
|
383
372
|
Revolut Business's card field (`revolut_card_field`) is served on the same
|
|
384
373
|
terms: this build pauses the frame's card payment on `merchant.revolut.com`,
|
|
385
374
|
the cardholder's device sends it to Revolut itself, and the frame gets
|
|
@@ -429,6 +418,22 @@ page, the cardholder's device sends it to Omise itself with the real card, and
|
|
|
429
418
|
the page gets Omise's token. The API pays only a merchant Agentcard reviewed
|
|
430
419
|
for the page the request comes from. Omise's request names no price, so pass
|
|
431
420
|
`amount` and `currency`, above zero: that is the amount the cardholder approves.
|
|
421
|
+
VTEX Payments' card post (`vtex_payments_card_payment`) is served on the same
|
|
422
|
+
terms, and only through `prepare({ psp: 'vtex_payments', environment: 'production' })`
|
|
423
|
+
before Pay: Ollie's headless checkout, which some VTEX stores run, posts the
|
|
424
|
+
card to the store's own VTEX vault, `<account>.vtexvault.com`, and gives the
|
|
425
|
+
post 12 seconds. This build pauses that post, binds it to the approval, and
|
|
426
|
+
refuses each later post on the same approval, Ollie's resends and its post to
|
|
427
|
+
`api.vtexvault.com` included, so one approval sends the card once. The post
|
|
428
|
+
names its price, which the cardholder approves; VTEX answers it with 201 and no
|
|
429
|
+
body, so the payment's result comes from the checkout's next step, not from
|
|
430
|
+
this answer. The API pays only a store Agentcard reviewed for the page. A paused
|
|
431
|
+
post is read every 500 ms while Ollie waits, and one the cardholder's device has
|
|
432
|
+
not sent 8 seconds after the pause fails in your browser with nothing sent
|
|
433
|
+
(`CheckoutPreparationError`, `vtex_payments_send_window_closed`). A direct
|
|
434
|
+
`authorize()` of that post names `pausedAt`, the moment it paused in epoch
|
|
435
|
+
milliseconds. The adapters fill it, and a call without it throws before
|
|
436
|
+
anything is created.
|
|
432
437
|
Pin Payments' card request (`pin_payments_card_token`) is served on the same
|
|
433
438
|
terms: this build pauses the card request Pin's Hosted Fields or Pin.js sends to
|
|
434
439
|
`https://api.pinpayments.com/1/cards`, the cardholder's device sends it to Pin
|
|
@@ -437,6 +442,24 @@ merchant's server charges afterwards. The API pays only a Pin account Agentcard
|
|
|
437
442
|
reviewed for the page the request comes from. Pin's request names no price, so
|
|
438
443
|
pass `amount` and `currency`, above zero: that is the amount the cardholder
|
|
439
444
|
approves.
|
|
445
|
+
Paymentwall's card request (`paymentwall_card_token`) is served on the same
|
|
446
|
+
terms: this build pauses the card request Paymentwall's card form sends to
|
|
447
|
+
`https://pwgateway.com/api/token` (`https://api.paymentwall.com/api/brick/token`
|
|
448
|
+
under a test key), from Paymentwall's hosted checkout or a merchant's own page,
|
|
449
|
+
the cardholder's device sends it to Paymentwall itself with
|
|
450
|
+
the real card, and the page gets Paymentwall's one-time token, which the
|
|
451
|
+
checkout charges afterwards. The API pays only a Paymentwall merchant Agentcard
|
|
452
|
+
reviewed for the page the request comes from. Paymentwall's request names no
|
|
453
|
+
price, so pass `amount` and `currency`, above zero: that is the amount the
|
|
454
|
+
cardholder approves.
|
|
455
|
+
ProcessOut's card request (`processout_card_token`) is served on the same
|
|
456
|
+
terms, through a prepared checkout only: this build pauses the card request
|
|
457
|
+
ProcessOut.js's card frame sends to `https://api.processout.com/cards`, the
|
|
458
|
+
cardholder's device sends it to ProcessOut itself with the real card, and the
|
|
459
|
+
frame gets ProcessOut's card token, which the store's server charges
|
|
460
|
+
afterwards. The API pays only a store Agentcard reviewed for the page the
|
|
461
|
+
request comes from. ProcessOut's request names no price, so pass `amount` and
|
|
462
|
+
`currency`, above zero: that is the amount the cardholder approves.
|
|
440
463
|
FastSpring's checkout (`fastspring_card_payment`) is served on the same terms:
|
|
441
464
|
this build pauses the card payment the checkout posts to
|
|
442
465
|
`<store>.onfastspring.com/.../session/<id>/payment`, the cardholder's device
|
|
@@ -472,6 +495,14 @@ checkout shows, which the API holds to the payment's own amount and the device
|
|
|
472
495
|
holds to Nexi's record. The SDK sends the checkout page's origin as
|
|
473
496
|
`checkout_origin`, which for Nexi's hosted page is `https://checkout.dibspayment.eu`,
|
|
474
497
|
and the API pays only a merchant reviewed for that page.
|
|
498
|
+
Oceanpayment's hosted payment page (`oceanpayment_hosted_payment_page`) is served
|
|
499
|
+
on the same terms: this build pauses the card post the page submits to
|
|
500
|
+
`https://secure.oceanpayment.com/gateway/sendPay;jsessionid=<session>`, aborts every
|
|
501
|
+
other post to Oceanpayment's hosts, and the cardholder's device writes the real card
|
|
502
|
+
into the paused post and sends it through Agentcard's checkout relay. The post
|
|
503
|
+
names no amount, so pass `amount` and `currency`, above zero: the total the page
|
|
504
|
+
shows. The API takes only a test payment on Oceanpayment's sandbox today, because
|
|
505
|
+
the post names neither the merchant nor the amount.
|
|
475
506
|
CyberSource Secure Acceptance's token
|
|
476
507
|
create (`cybersource_sa_token_create`) is served on the same terms too; every
|
|
477
508
|
other company keeps the stop every build applies to a card posted to Secure
|
|
@@ -636,7 +667,8 @@ mode; the difference matters if you drive `authorize()` yourself.
|
|
|
636
667
|
sends the card request to the merchant itself over Agentcard's checkout
|
|
637
668
|
relay and reports the merchant's answer; `authorize()` resolves with
|
|
638
669
|
`{ mode: 'card_endpoint', profile, status, headers, setCookies, body,
|
|
639
|
-
withheld }
|
|
670
|
+
withheld, approved }`, where `approved` is the amount the cardholder approved,
|
|
671
|
+
in the currency's smallest unit. The adapters write each `Set-Cookie` into your browser
|
|
640
672
|
host-only on the endpoint host and Secure, so none of them travels over
|
|
641
673
|
plain http. A `Set-Cookie` whose `Domain` names a parent of the endpoint
|
|
642
674
|
host that your browser already holds a cookie of that name on is written
|
|
@@ -689,6 +721,44 @@ mode; the difference matters if you drive `authorize()` yourself.
|
|
|
689
721
|
`profile` and the `endpoint` origin, never the request's URL, body or
|
|
690
722
|
session: the URL carries the merchant's cart and booking ids.
|
|
691
723
|
|
|
724
|
+
Nike's card frame stores the card under the checkout's id, and Nike charges
|
|
725
|
+
nothing until its page sends the order, `PUT
|
|
726
|
+
https://api.nike.com/buy/checkouts/v3/<checkoutId>`. So once an adapter asks
|
|
727
|
+
the cardholder to pay through Nike's profile, it holds every request to
|
|
728
|
+
`api.nike.com`. `attachToCdp` pauses it in every frame the attachment arms,
|
|
729
|
+
from the attach on, so a document already loaded when the cardholder is
|
|
730
|
+
asked is held too. At the ask it also arms every other page of the browser
|
|
731
|
+
context then open, and every frame in one, and the browser target, which
|
|
732
|
+
reaches a tab, frame, worker or shared worker this checkout did not open
|
|
733
|
+
once it loads after the ask: Chrome applies the browser-level pause only to
|
|
734
|
+
what loads after it. `attachToPlaywright` routes the page's browser context,
|
|
735
|
+
which reaches its pages, those already loaded included, but no shared or
|
|
736
|
+
service worker. A write there whose path a server may read as
|
|
737
|
+
under `/buy/checkouts/`, however it is spelled, is the order. One order goes
|
|
738
|
+
unchanged, only when its `request.totals.total`, a JSON number, and its
|
|
739
|
+
`request.currency` are the `approved` amount and the checkout still names
|
|
740
|
+
that approval as under way, and after it only a byte-identical retry. An
|
|
741
|
+
order that fails the amount rule, or comes while no approved amount was read,
|
|
742
|
+
fails in the browser, so Nike never charges the card: a checkout still under
|
|
743
|
+
way reads `failed` with a `merchant_order_` reason such as
|
|
744
|
+
`merchant_order_amount_mismatch`, and every later order under that approval
|
|
745
|
+
fails too. A second order, or one after the checkout was cancelled,
|
|
746
|
+
reconciled or retried, fails and leaves the state as it is. The events
|
|
747
|
+
`merchant_order_released` and `merchant_order_refused` name the
|
|
748
|
+
authorization, never the order. Every other request, and the order on an
|
|
749
|
+
attachment that never asked to pay through Nike's profile, continues
|
|
750
|
+
untouched. A browser that refuses the browser-level pause, a page or frame
|
|
751
|
+
`attachToCdp` cannot arm at the ask, or a context that refuses the route,
|
|
752
|
+
refuses the payment to Nike before anything is created. At the browser
|
|
753
|
+
target, in the pages armed at the ask and on Playwright's context route,
|
|
754
|
+
only the attachment that asked through Nike's profile last in that browser
|
|
755
|
+
context, for any attempt, judges Nike's order; in the frames it arms from
|
|
756
|
+
the attach on, each `attachToCdp` attachment judges every order itself. A
|
|
757
|
+
shared or service worker that started before the ask, which `attachToCdp`
|
|
758
|
+
lets run only when its `sharedWorkers` or `serviceWorkers` option allows
|
|
759
|
+
one, is not held. An attachment that has not asked holds nothing, and an
|
|
760
|
+
order sent from outside the browser never reaches the SDK.
|
|
761
|
+
|
|
692
762
|
Some of these merchants' pages submit the card form as a page load rather
|
|
693
763
|
than a fetch: the profile's `navigation` is `top`, or `frame` for a form
|
|
694
764
|
inside an iframe, which is then judged by the iframe's own document. The
|
|
@@ -742,7 +812,7 @@ mode; the difference matters if you drive `authorize()` yourself.
|
|
|
742
812
|
cannot finish is never paused; the API serves `hosted_form` and `device_encrypt`
|
|
743
813
|
entries only to callers that ask.
|
|
744
814
|
`syncRegistry()` also sends `SUPPORTED_CAPABILITIES`
|
|
745
|
-
(`capabilities
|
|
815
|
+
(the `capabilities=` list under "Supported processors"),
|
|
746
816
|
the dark-launch half of the same negotiation: the API serves a processor gated
|
|
747
817
|
behind a capability only to a build that declares it, for a company Agentcard
|
|
748
818
|
turned that processor on for, and the built-in list never holds one, so the SDK
|
|
@@ -1085,8 +1155,7 @@ It declares **no dependencies**, so the workspace
|
|
|
1085
1155
|
lockfile needs no importer entry for it (a new package with its own deps cannot
|
|
1086
1156
|
be installed here without regenerating the lockfile, and a full regen drifts
|
|
1087
1157
|
unrelated transitive versions). The build writes the SDK's copies of
|
|
1088
|
-
payment-core's rules (`src/*.generated.ts`)
|
|
1089
|
-
from `packages/voice/src/core/stream.ts`, and the Kernel kit's
|
|
1158
|
+
payment-core's rules (`src/*.generated.ts`) and the Kernel kit's
|
|
1090
1159
|
`examples/preflight/kernel-native/inventory.json`. Git ignores those files, so
|
|
1091
1160
|
build before you test, with the workspace TypeScript:
|
|
1092
1161
|
|
|
@@ -1280,7 +1349,7 @@ with the Card Element after `createPaymentMethod`) has it sent with every
|
|
|
1280
1349
|
`payment_method_data` field removed and the approved `payment_method` in their
|
|
1281
1350
|
place: the SDK asks the API with exactly that body and continues the page's
|
|
1282
1351
|
request with it. The API allows it only for an approval the cardholder gave on
|
|
1283
|
-
the Vault page,
|
|
1352
|
+
the Vault page, with no `amount` (a SetupIntent charges
|
|
1284
1353
|
nothing), paid with exactly the approved payment method on the same Stripe
|
|
1285
1354
|
account, and a SetupIntent Stripe reports still confirmable. Allowed, the SDK
|
|
1286
1355
|
emits `stripe_setup_continued` with
|
|
@@ -1358,7 +1427,15 @@ Prepare a Checkout.com checkout before the agent clicks Pay:
|
|
|
1358
1427
|
await controller.prepare({ psp: 'checkout_com', environment: 'production' });
|
|
1359
1428
|
```
|
|
1360
1429
|
|
|
1361
|
-
Keep the phone approval page open, then click Pay once after readiness. Checkout.com Flow 1.228.0 gives its card iframe 12 seconds to return a token; approval before Pay leaves that interval for request binding and tokenization.
|
|
1430
|
+
Keep the phone approval page open, then click Pay once after readiness. Checkout.com Flow 1.228.0 gives its card iframe 12 seconds to return a token; approval before Pay leaves that interval for request binding and tokenization.
|
|
1431
|
+
|
|
1432
|
+
Prepared requests must use an exact URL that Checkout.com's card fields send the card to:
|
|
1433
|
+
|
|
1434
|
+
- `/tokens` on `api.checkout.com` or `card-acquisition-gateway.checkout.com`.
|
|
1435
|
+
- `/tokens` on your account's own API host, named by its base URL prefix, such as `4k2njisl.api.checkout.com`. Frames sends the card there when you set `baseURLPrefix`.
|
|
1436
|
+
- `/cag/tokens`, or `/tokens` after a failed gateway call, on `devices.api.checkout.com` or under your prefix, such as `4k2njisl.devices.api.checkout.com`. Flow sends the card there when its payment session turns on Checkout.com's devices API, as the hosted payments page does.
|
|
1437
|
+
|
|
1438
|
+
Any alphanumeric prefix works. For `sandbox`, each host is its `.sandbox.checkout.com` counterpart, such as `4k2njisl.api.sandbox.checkout.com`. The request must describe a fresh card with a CVV and public-key authentication. Wallet, saved-card, reusable-token and recurring variants are refused. A native API fallback after the first CAG request cannot reuse approval. Match the actual merchant charge to the approved amount separately; a token does not enforce that amount.
|
|
1362
1439
|
|
|
1363
1440
|
Prepare an Adyen Sessions checkout before the agent clicks Pay:
|
|
1364
1441
|
|
|
@@ -1462,6 +1539,24 @@ An attachment keeps no memory of an earlier one. The API reads each checkout fro
|
|
|
1462
1539
|
|
|
1463
1540
|
Once the card-number request is answered, ACI holds the real card on that checkout for up to 30 minutes, and the checkout id is what lets anyone post `/payment` against it, so event URLs show it as `{checkoutId}`. The `/payment` check runs in your browser: it holds an honest agent to the approved card, and the merchant's order state is the outcome to confirm. Autopilot is unavailable for ACI.
|
|
1464
1541
|
|
|
1542
|
+
### Pay a BlueSnap Hosted Payment Fields checkout
|
|
1543
|
+
|
|
1544
|
+
BlueSnap's Hosted Payment Fields send the card in three requests too: the card number frame, the expiry frame and the code frame each send their own field as a JSON `PUT` to the merchant's token, `https://ws1.bluesnap.com/services/2/payment-fields-tokens/<token>` (`ws2` for a token ending `_2`, `sandpay` for BlueSnap's sandbox). BlueSnap launches dark: `syncRegistry()` declares `bluesnap_hosted_payment_fields`, the API serves BlueSnap only to an SDK that declares it, and only for Agentcard's own company.
|
|
1545
|
+
|
|
1546
|
+
Pass `amount` and `currency`, above zero: the request names no price, so that is the amount the cardholder approves. Type a placeholder card of the paying card's network. The SDK:
|
|
1547
|
+
|
|
1548
|
+
- pauses the card number request, which the number frame sends when its field loses focus. The cardholder's device sends it to BlueSnap with the real number, expiry and code, and the page receives BlueSnap's card data.
|
|
1549
|
+
- holds the page's own expiry and code requests, which the frames send when the page submits, and answers them with that answer, so the placeholder values never reach BlueSnap (`split_card_leg_absorbed`). They fail when the card number request fails, or when none begins within 5 seconds (`split_card_leg_unanswered`).
|
|
1550
|
+
- refuses a second card number request on the same token (`split_card_reused`), a saved card's request (`bluesnap_saved_card`), and anything else on the token's URL. The page's own retry of the card number request, sent again while the cardholder is still deciding, gets the approval's answer.
|
|
1551
|
+
|
|
1552
|
+
The store's server charges the token afterwards, and its charge can ask BlueSnap to keep the card. So a live BlueSnap payment takes only a card the person stored, never one a company added (`card_not_stored`), and the approval says the store can keep it.
|
|
1553
|
+
|
|
1554
|
+
### Pay a Paystack checkout
|
|
1555
|
+
|
|
1556
|
+
Paystack's hosted checkout (`checkout.paystack.com`) sends the card in one form `POST` to `https://standard.paystack.co/charge/`, as `clientdata`: the card number, code and expiry encrypted under the one RSA key Paystack's page carries, with the card's last four digits beside it. Paystack launches dark: `syncRegistry()` declares `paystack_pinned_key`, the API serves Paystack only to an SDK that declares it, and only for Agentcard's own company.
|
|
1557
|
+
|
|
1558
|
+
Pass `amount` and `currency`, above zero: the charge names no price, so that is the amount the cardholder approves. Type a placeholder card of the paying card's network, since Paystack's page reads the card's first six digits before the charge. The SDK pauses the charge, and the cardholder's device encrypts the real card under the same pinned key and sends the charge to Paystack itself. The page receives Paystack's answer: paid, declined, or the issuer's 3-D Secure step, which the page runs in the agent's browser. A charge that keeps the card (`paystack_saved_card`), starts a plan (`paystack_subscription`) or carries a PIN is refused before anything is created. Autopilot is unavailable for Paystack.
|
|
1559
|
+
|
|
1465
1560
|
### Use Checkout Pro in Mexico
|
|
1466
1561
|
|
|
1467
1562
|
Attach the SDK before entering card fields on `www.mercadopago.com.mx`, then prepare the guest card checkout:
|