Skip to main content
This guide runs one real purchase end to end: an agent in a Kernel browser puts a $0.99 PDF from a Shopify store in the cart, reaches the checkout, and pays with a card the user stored in the Vault. The user approves on their phone with Face ID, Shopify’s thank-you page shows the confirmation number, and the browser is deleted. Every screenshot and every line of output below comes from that run. Kernel covers the two ways to connect a Kernel browser to the Vault and the code that creates the browser and attaches the SDK. This guide does not repeat it. It takes the SDK path and shows everything around that snippet: the shopping leg a Shopify store needs, what to pass when you attach, what the SDK reports while the user decides, how to read Shopify’s confirmation number, and how to leave nothing running. What you end up with: a script you run once with a handful of environment variables that prints completed order #5CQFGUXJX, and a session you can watch live in Kernel’s dashboard while it happens.

Before you start

A real purchase needs production credentials. The sandbox recognises the card request and pauses it, but no approval link is sent and nothing is charged. Run the sandbox first to see the pause, then switch the credentials.
  • The Kernel setup from Kernel: a Kernel API key, and @onkernel/sdk, @agent-cards/sdk and playwright-core installed. @agent-cards/sdk is the SDK’s current name; @agent-cards/checkout still installs and re-exports it.
  • An Agentcard organization with production credentials (client_id and client_secret), and a user of that organization with a card in the Vault. Adding a card gets you both; the user’s user_id is what you pass to the SDK.
  • A Shopify store with guest checkout and something cheap. The run below uses The Good and the Beautiful, a homeschool publisher, and its $0.99 High School Biology Answer Key (PDF): a digital product, so nothing ships and the checkout asks for an email and a billing address only.
  • The user’s phone within reach. The approval link is theirs to open.

Tools

kernel

Kernel

agentcard

Agentcard Vault

shopify

Shopify checkout

How it works

1. Open the checkout

Kernel gives you a Chromium with a CDP address; Playwright drives it like a local browser. Two things matter before the agent types anything. Create the context with service workers blocked, because a service worker owns requests Playwright cannot see, and the card request is one of them. And dismiss the store’s cookie notice yourself, because it sits over the checkout button.
The product page in the Kernel browser: High School Biology Answer Key (PDF), 99 cents, Add to Cart The cart: one item, 99 cents, with the store's cookie notice over the checkout button Shopify’s checkout URL is /checkouts/cn/<token>/en-us. A store with Shop Pay sends the cart permalink to shop.app first; the cart page’s own checkout button lands on the store’s checkout, which is where the SDK works.

2. Fill the checkout and read the total

The checkout is one page. Fill the contact email, choose the credit card payment method, and fill the billing address. Shopify recomputes tax for the address, so read the total after the address settles, not before: the estimate before the address said $1.06, and California charges no tax on a digital product, so the total the user approved was $0.99. The amount you pass is the amount the approval shows the user. Shopify’s card request carries no total of its own for the SDK to compare it against, so wait until the order summary has stopped changing before you read it, and read it again right before Pay now if anything on the page changed.
The checkout filled in: email, credit card selected, billing address, and the order summary with the total Use an email with no Shop Pay account for the buyer. Shopify recognises an email that has one and opens a “Confirm it’s you” code prompt over the page; the agent cannot answer it, and it covers Pay now.

3. Attach before the card form

Attach once the billing address is in and before anything touches the card fields. The call is the one on the Kernel page, with the parts a real purchase needs: the total you just read, the time the user gets, and a merchant result reader.
sendToUser is yours: a text, a push, a message in the thread your product already has with the user. The link is a bearer link. Never hand it to the agent or log it where the agent reads. resolveMerchantResult reads the merchant’s answer, not the processor’s. Shopify’s thank-you page says Confirmation # and the number, then “You’ve paid for your order.” The resolver reports completed only with that number in hand; the thank-you URL alone, or the page before the number paints, is pending. The SDK never clicks Pay for you, and neither should a retry of yours. Leave requireMerchantResult off for a Shopify checkout. That option holds every further card request until you reconcile, and Shopify sends more of them right after the real card goes through; with the hold on, the SDK blocked eleven of them in two seconds and the page sat on “Processing…” until the browser was deleted. Nothing was charged, and nothing was bought either.

4. Pay with a placeholder card

Shopify renders each card field in its own iframe on checkout.pci.shopifyinc.com. The iframes are named card-fields-number-…, card-fields-expiry-…, card-fields-verification_value-… and card-fields-name-…; the suffix changes per page load, so match on the prefix. Each iframe holds a hidden honeypot input ahead of the real field; take the visible one, or the number lands in a field Shopify ignores. Type any of Stripe’s published test cards; the docs keep the number out of the page and the script reads it from PLACEHOLDER_CARD_NUMBER. The real card never enters the browser.
Shopify's card fields filled with the placeholder card, Pay now below Click Pay now once. Shopify posts the card to checkout.pci.shopifyinc.com/sessions, the SDK pauses that request, and onApprovalUrl fires with the link. The button reads “Processing…” and stays that way until the user decides; they have 15 minutes. The checkout after Pay now: the button reads Processing while the card request is held

5. Watch the approval

onStateChange and onEvent are how your code follows along. This is what the run printed, from the click to the merchant’s answer:
The user sees the merchant and the amount on the approval page and confirms with Face ID. Their device sends the real card to Shopify’s card session endpoint and reports the response; the SDK replays it into the paused request, and the checkout continues as if the browser had sent the real card. Eighteen seconds passed between the click and the approval here; the three minutes after it were Shopify finishing the order. Kernel's live view of the session while the approval is pending: the checkout page on Processing, the total 99 cents Kernel’s browser_live_view_url opens the session in a tab. During the pause it shows the checkout exactly as the agent left it, which is what you show a user who asks what their agent is doing. The approval page is bound to the user the purchase was made for. If the user’s phone is signed in to the Vault as a different account, the page says “That approval link is not valid for your account.” The user signs out there, opens a Vault link your code made for their user_id, adds the card, and the approval link works.

6. Read the confirmation number and delete the browser

Once the state turns to awaiting_merchant, wait for the thank-you page and reconcile. Delete the Kernel session only when the result is completed, failed or declined. A pending or outcome_unknown result means the merchant has not answered yet, and the page is the only place to read that answer from, so the session stays up (it ends on its own at timeout_seconds of inactivity) and you reconcile again before you decide anything.
Shopify's thank-you page: Confirmation #5CQFGUXJX, You've paid for your order, and the PDF ready to download
completed with a confirmation number is the only outcome you report as a purchase. pending and outcome_unknown mean keep the browser and look again; a missing receipt is not a failed payment, and clicking Pay again can charge twice. declined means the user said no or the processor refused; nothing was charged.

Run it

The whole run is one file in tiny-agent-company/kernel-shopify-purchase, the script this guide was captured from.
The script prints the approval link; send it to the user however you reach them. Everything else is automatic, and a screenshot of every step lands in shots/.

Handle a run that stalls

The states in the table are the status values onStateChange prints. This is the one a stalled run printed, captured with requireMerchantResult on, after the user had approved and the page sat on “Processing…” until the wait ran out:
From the run alone, whether Shopify charged that card is unknown: merchant_not_confirmed means the order was never confirmed, and the authorization’s settlement read says no_processor_reference, which means Agentcard holds nothing at the processor to read back. Only the merchant’s own record settles it. Treat such a run as unknown, keep the session, and do not start another purchase until the store confirms there is no order; the first order can still complete after your wait ran out. On this run the store’s side confirmed it afterwards: no order, no receipt email. The other rows name states this run did not reach; their output has the same shape, state=<status> reason=<reason>.

Where to go next

  • Completing a purchase for the webhooks that tell your server the same story.
  • Kernel for Kernel’s own vault integration, where Kernel intercepts the card at its network edge and no SDK is attached.