> ## Documentation Index
> Fetch the complete documentation index at: https://tbd-6fc993ce-hypeship-clarify-payment-guide.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Enable Payments in a Browser Agent

> connect a wallet, approve a purchase, and complete checkout with your browser agent

add payments to your existing browser agent without passing card numbers or cvc
through your application's code or model context. connect a wallet once, then
verify and approve each purchase before completing checkout.

## Before you start

* have an existing browser agent. this guide adds payment handling, not navigation or reasoning.
* choose a wallet integration from the [wallet overview](/integrations/wallets/overview), then follow its tab below.
* install a KERNEL sdk with the `vaults` resource. set `KERNEL_API_KEY` and `KERNEL_PROJECT_ID` in your controller.
* use a low-value checkout you control. link is live-only and requires an https merchant origin and verified field selectors. agentcard requires a [native processor adapter](/integrations/wallets/overview#checkout-and-processor-coverage); the processor doesn't need to be stripe.
* for agentcard, set `AGENTCARD_MODE` in your controller and verify it against the credential's mode. customer-owned configs expose `test_mode` (`true` means sandbox); for KERNEL-managed credentials, confirm the deployment's mode. stop if the mode is unknown or mismatched.
* identify how you'll read the merchant's trusted order record. you need it to confirm a matching paid order. deterministic checkout data can verify purchase details before submission, but without the order record afterward, the result remains indeterminate.

use KERNEL-managed credentials by default. optional client setup belongs in your
controller: see [link](/integrations/wallets/stripe-link#bring-your-own-link-oauth-client)
or [agentcard](/integrations/wallets/agentcard#bring-your-own-agentcard-oauth-client).

### Roles and resources

* **controller:** your trusted application code. it calls KERNEL, verifies purchases, authorizes field bindings, presents user actions, and observes payment state.
* **agent:** your browser automation. it proposes purchase details and selectors, then completes the approved checkout.
* **user:** the person who connects a payment method, confirms the purchase, and completes provider approval.

a **vault** groups KERNEL items. a **wallet item** represents a provider connection;
a **card item** references that wallet in the same vault and tracks payment input
and authorization state. neither item is the user's underlying wallet or card.

### Shared safety rules

<Warning>
  * keep wallet enrollment and provider approval outside the agent and its browser. never put action urls, oauth codes, provider responses, api keys, or browser connection urls in model context.
  * independently verify purchase details in your controller before creating, updating, or authorizing a card item. user confirmation alone doesn't validate an agent's proposal.
  * submit checkout once through the merchant's normal form. don't retry submission, automatically retry `fill`, or fall back from `fill` to aliases after failure or uncertainty.
  * report success only after the merchant's trusted order record confirms the matching paid order. a timeout, missing event, or browser deletion doesn't cancel a payment.
</Warning>

## One-time setup

### 1. Create a vault

scope the client and vault to the project that will own the browser session.
these clients disable automatic sdk retries.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Kernel from "@onkernel/sdk";

  const projectID = process.env.KERNEL_PROJECT_ID;
  if (!projectID) throw new Error("set KERNEL_PROJECT_ID");

  const kernel = new Kernel({ projectID, maxRetries: 0 });
  const vault = await kernel.vaults.upsert({ name: "user-12345" });
  ```

  ```python Python theme={null}
  import os

  from kernel import Kernel

  kernel = Kernel(
      project_id=os.environ["KERNEL_PROJECT_ID"],
      max_retries=0,
  )
  vault = kernel.vaults.upsert(name="user-12345")
  ```

  ```bash CLI theme={null}
  kernel vaults create --name user-12345
  ```
</CodeGroup>

`vaults.upsert` creates the vault or retrieves one with the same name. vault names
are immutable within the project.

### 2. Connect a wallet

allow at most one wallet item per provider in a vault. list the vault's items and
group wallets by `spec.provider` before rendering your payment settings:

| existing wallet for the provider | ui behavior                                                                                            |
| -------------------------------- | ------------------------------------------------------------------------------------------------------ |
| none                             | show the option to connect that provider                                                               |
| `pending_authorization`          | hide the add option and resume the existing hosted action                                              |
| `connected`                      | hide the add option, show the provider as connected, and reuse the existing wallet                     |
| any other state                  | hide the add option and show the existing state; recover it or use an explicit remove-and-replace flow |

recheck in your controller immediately before creating a wallet. the api makes
item keys unique, not providers, so a different key can create a duplicate wallet.
deleting a wallet invalidates its dependent card items; use an explicit
remove-and-replace flow instead of adding a second wallet.

1. let the user select a provider that doesn't already have a wallet in the vault.
2. create the wallet item through your controller, following the [link setup](/integrations/wallets/stripe-link) or [agentcard setup](/integrations/wallets/agentcard).
3. present the returned action using the authenticated flow below. the user connects or enrolls their real payment method with the provider.
4. wait for the wallet's status to become `connected` before preparing a purchase.

### Present hosted actions in your application

provider action urls are bearer-like handoffs to enrollment or approval. route
them through your controller:

1. your controller retrieves the item and keeps the raw action url out of logs, analytics, and model context.
2. store the action server-side under an opaque id bound to the authenticated user, vault id, item key, and action name.
3. render a link to your own authenticated action endpoint. before redirecting, verify the session owns that binding and the item still returns the same action.
4. send the redirect with `Cache-Control: no-store` and `Referrer-Policy: no-referrer`.
5. apply a short application ttl capped by `item.expires_at` or `state.authorization.expires_at` when present. invalidate the record immediately when the action changes, disappears, or reaches a terminal state.

the `presentProviderAction` functions later in this guide represent this
application-owned flow. the checkout agent and its browser must never receive
the raw provider url.

## For each purchase

<a id="3-attach-the-vault-to-the-browser" />

### 1. Attach the vault to a browser

vault attachments are fixed at browser creation. use the same project-scoped client that created the vault.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const browser = await kernel.browsers.create({
    vaults: [{ id: vault.id }],
    headless: false,
    timeout_seconds: 1800,
  });

  if (!browser.browser_live_view_url) {
    throw new Error("headful browser did not return a live view url");
  }
  await presentLiveView({
    userID: authenticatedUser.id,
    sessionID: browser.session_id,
    url: browser.browser_live_view_url,
  });
  ```

  ```python Python theme={null}
  browser = kernel.browsers.create(
      vaults=[{"id": vault.id}],
      headless=False,
      timeout_seconds=1800,
  )

  if browser.browser_live_view_url is None:
      raise RuntimeError("headful browser did not return a live view url")
  present_live_view(
      user_id=authenticated_user.id,
      session_id=browser.session_id,
      url=browser.browser_live_view_url,
  )
  ```

  ```bash CLI theme={null}
  kernel browsers create --vault user-12345 -o json
  ```
</CodeGroup>

`browser_live_view_url` lets the user watch the checkout during confirmation
and agentcard approval pauses. `presentLiveView` represents your authenticated
user-facing page: keep the url server-side with the user and browser session
binding, render or embed it only after checking that session, and remove it when
you delete or time out the browser. don't log the url or put it in model context.
see [live view](/browsers/live-view#embedding-in-an-iframe) for iframe and csp
requirements.

connect your existing agent to `browser.cdp_ws_url`. see [Controlling a Browser](/introduction/control) for supported connection options.

navigate to checkout in this session before verifying the purchase. use the same
session to inspect and submit it; the vault attachment includes items created later.

### 2. Verify and confirm the purchase

1. let the agent propose the merchant, amount, currency, and item or cart contents. treat every proposed value as untrusted.
2. independently obtain the expected values from a trusted source. prefer your order or cart backend. when no backend exists, use deterministic page extraction with fixed selectors or structured page data, not another model response.
3. normalize the values in your controller and compare the proposal with the trusted result. compare the amount in minor currency units and require the merchant, currency, and item or cart contents to match.
4. stop when any value is missing, cannot be verified, or disagrees. do not create or update a card item and do not invoke authorization.
5. show the independently verified values to the user and wait for explicit confirmation.
6. freeze that verified, confirmed purchase object. derive the card specification and any authorization request from that same object. do not accept replacement values from the agent after confirmation.

for stripe payment links, see [checkout-specific notes](#checkout-specific-notes)
for structured response fields and adaptive pricing.

### 3. Collect other checkout fields

collect required email, billing, shipping, phone, and other customer fields from
the user or their previously approved backend data. pass them separately from
payment input; don't ask the agent to invent missing values.

answer any agent disclosure truthfully in the merchant's form and verify the
control is selected before submission. if a required field or disclosure can't
be completed and verified, stop. see [checkout-specific notes](#checkout-specific-notes)
for stripe's disclosure controls.

<a id="4-fill-link-cards-or-pass-agentcard-aliases" />

<a id="4-fill-payment-fields" />

### 4. Prepare payment input and submit once

follow the tab for your provider. each path uses the frozen purchase object from
step 2, a connected wallet in the same vault, and this browser session.

<Tabs>
  <Tab title="Link by Stripe">
    #### Prepare and authorize the card

    1. list the wallet's payment methods and let the user select one.
    2. [create a link card item](/integrations/wallets/stripe-link) named `notebook-order` from the verified purchase and selected method.
    3. require `authorize` in `available_operations`, invoke it with that same purchase object, and present any returned action through your controller.
    4. wait for the card to become `ready`, then require `fill` immediately before use. link cards never expose `state.aliases`.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const card = await kernel.vaults.items.retrieve("notebook-order", {
        id_or_name: vault.id,
        wait: 60,
      });

      if (card.type !== "card" || card.state.status !== "ready") {
        throw new Error(`payment item is ${card.state.status}`);
      }
      ```

      ```python Python theme={null}
      card = kernel.vaults.items.retrieve(
          "notebook-order",
          id_or_name=vault.id,
          wait=60,
      )

      if card.type != "card" or card.state.status != "ready":
          raise RuntimeError(f"payment item is {card.state.status}")
      ```

      ```bash CLI theme={null}
      kernel vaults items get user-12345 notebook-order --wait 60 -o json
      ```
    </CodeGroup>

    <a id="link-invoke-fill" />

    #### Fill the approved fields

    your controller must authorize the destination and selectors before disclosure.
    `checkoutURL` / `checkout_url` is the browser's exact current top-level https url,
    including query and fragment. it must share the origin of `spec.merchant_url`.
    no playwright page object is required. see [field requirements](/integrations/wallets/stripe-link#fill-the-checkout).

    `fill` supports stored [billing fields](/integrations/wallets/stripe-link#map-card-fields-to-inputs)
    as well as card fields. requesting an absent value returns `field_unavailable`
    before any writes.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const linkCard = await kernel.vaults.items.retrieve(card.key, {
        id_or_name: vault.id,
      });
      if (
        linkCard.type !== "card" ||
        linkCard.spec.provider !== "link" ||
        !linkCard.available_operations.some((operation) => operation.type === "fill")
      ) {
        throw new Error("fill is unavailable for this card");
      }
      const result = await kernel.vaults.items.performOperation(linkCard.key, {
        id_or_name: vault.id,
        type: "fill",
        browser_id: browser.session_id,
        page_url: checkoutURL,
        fields: [
          { field: "number", selector: "#card-number" },
          { field: "expiration", selector: "#expiry", format: "MM/YY" },
          { field: "cvc", selector: "#security-code" },
        ],
        timeout_ms: 10000,
      });
      if (result.type !== "fill") throw new Error("unexpected operation response");
      console.log(result.status, result.fields);
      if (result.status !== "completed") {
        throw new Error("stop and reconcile per-field outcomes; do not retry");
      }
      ```

      ```python Python theme={null}
      link_card = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id)
      if (
          link_card.type != "card"
          or link_card.spec.provider != "link"
          or not any(operation.type == "fill" for operation in link_card.available_operations)
      ):
          raise RuntimeError("fill is unavailable for this card")
      result = kernel.vaults.items.perform_operation(
          link_card.key,
          id_or_name=vault.id,
          type="fill",
          browser_id=browser.session_id,
          page_url=checkout_url,
          fields=[
              {"field": "number", "selector": "#card-number"},
              {"field": "expiration", "selector": "#expiry", "format": "MM/YY"},
              {"field": "cvc", "selector": "#security-code"},
          ],
          timeout_ms=10000,
      )
      if result.type != "fill":
          raise RuntimeError("unexpected operation response")
      print(result.status, result.fields)
      if result.status != "completed":
          raise RuntimeError("stop and reconcile per-field outcomes; do not retry")
      ```
    </CodeGroup>

    retain each result's zero-based `index`, `status`, and optional `error_code`.
    `failed` may leave earlier writes in place; `unknown` or a transport error means
    the outcome is uncertain. stop without retrying or switching to aliases. see the
    [`fill` outcome contract](/vaults/fill#handle-the-outcome) for details and [link checkout](/integrations/wallets/stripe-link#fill-the-checkout)
    for the cli equivalent.

    only continue on `completed`. inspect the checkout without reading card values
    back, complete the remaining customer fields and disclosures, and submit once.
    then [verify the outcome](#verify-the-outcome). filling is not payment success
    and doesn't consume the item or clear its encrypted material; the provider's
    single-use card semantics are separate from item state.
  </Tab>

  <Tab title="Agentcard">
    #### Prepare the card and aliases

    aliases are placeholder card values resolved during provider handoff. the agent
    enters them instead of real card details, which stay outside the browser.

    [create an agentcard item](/integrations/wallets/agentcard) named `notebook-order`
    from the verified purchase, or reuse a ready item and update its specification
    when the api permits. pin an enrolled card with `card_id`, or let the user choose
    during approval. don't invoke `authorize`: authorization starts after the browser
    submits a recognized processor request containing aliases.

    retrieve the item and require `ready` before reading aliases:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const card = await kernel.vaults.items.retrieve("notebook-order", {
        id_or_name: vault.id,
        wait: 60,
      });

      if (card.type !== "card" || card.state.status !== "ready") {
        throw new Error(`payment item is ${card.state.status}`);
      }
      ```

      ```python Python theme={null}
      card = kernel.vaults.items.retrieve(
          "notebook-order",
          id_or_name=vault.id,
          wait=60,
      )

      if card.type != "card" or card.state.status != "ready":
          raise RuntimeError(f"payment item is {card.state.status}")
      ```

      ```bash CLI theme={null}
      kernel vaults items get user-12345 notebook-order --wait 60 -o json
      ```
    </CodeGroup>

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const agentcard = await kernel.vaults.items.retrieve(card.key, {
        id_or_name: vault.id,
      });
      if (
        agentcard.type !== "card" ||
        agentcard.state.provider !== "agentcard" ||
        agentcard.state.status !== "ready" ||
        !agentcard.state.aliases
      ) {
        throw new Error("agentcard aliases are unavailable");
      }
      const aliases = agentcard.state.aliases;
      ```

      ```python Python theme={null}
      agentcard = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id)
      if (
          agentcard.type != "card"
          or agentcard.state.provider != "agentcard"
          or agentcard.state.status != "ready"
          or agentcard.state.aliases is None
      ):
          raise RuntimeError("agentcard aliases are unavailable")
      aliases = agentcard.state.aliases
      ```
    </CodeGroup>

    aliases cover `number`, `cvc`, `exp_month`, and `exp_year`. recheck them immediately
    before use; don't cache them across expiry, state changes, or deletion.

    #### Observe approval before submitting

    <a id="5-keep-agentcard-approval-and-observation-outside-the-agent" />

    <a id="5-agentcard-only-observe-checkout-approval" />

    start this observer before the agent submits checkout. KERNEL holds the recognized
    request while the user approves it, then replays the provider's response.
    `presentProviderAction` implements the [authenticated hosted-action flow](#present-hosted-actions-in-your-application)
    above; it must never send the action url to the agent.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      let after: string | undefined;

      async function observePayment(stop: AbortSignal): Promise<void> {
        while (!stop.aborted) {
          const current = await kernel.vaults.items.retrieve(card.key, {
            id_or_name: vault.id,
            wait: 5,
          });

          if (current.action && "url" in current.action) {
            await presentProviderAction({
              userID: authenticatedUser.id,
              vaultID: vault.id,
              item: current,
            });
          } else if (current.action?.name === "push_approval") {
            console.log("complete the approval in your wallet");
          }

          const events = await kernel.vaults.items.events(card.key, {
            id_or_name: vault.id,
            after,
            wait: 5,
          });
          for (const event of events) {
            console.log(event.id, event.name, event.browser_id, event.data);
            after = event.id;
          }
        }
      }
      ```

      ```python Python theme={null}
      from threading import Event


      def observe_payment(stop: Event) -> None:
          after = None

          while not stop.is_set():
              current = kernel.vaults.items.retrieve(
                  card.key,
                  id_or_name=vault.id,
                  wait=5,
              )

              if current.action is not None and hasattr(current.action, "url"):
                  present_provider_action(
                      user_id=authenticated_user.id,
                      vault_id=vault.id,
                      item=current,
                  )
              elif current.action is not None and current.action.name == "push_approval":
                  print("complete the approval in your wallet")

              if after is None:
                  events = kernel.vaults.items.events(card.key, id_or_name=vault.id, wait=5)
              else:
                  events = kernel.vaults.items.events(
                      card.key,
                      id_or_name=vault.id,
                      after=after,
                      wait=5,
                  )

              for event in events:
                  print(event.id, event.name, event.browser_id, event.data)
                  after = event.id

      ```

      ```bash CLI theme={null}
      kernel vaults items get user-12345 notebook-order --wait 5 --open
      kernel vaults items events user-12345 notebook-order --wait 60 -o json
      ```
    </CodeGroup>

    each cli `--wait` performs one bounded observation; rerun it to observe the existing
    attempt, not to submit or authorize again. run `--open` only in your controller
    or a human-operated terminal, and keep its output outside the agent.

    `events` returns an ordered array. an empty array means no new observation, not
    failure. correlate `event.browser_id` when a vault is attached to multiple sessions.

    #### Run the checkout

    pass aliases and separately approved customer data as structured task input.
    replace the angle-bracketed fields in this prompt in your controller:

    ```text theme={null}
    Complete the checkout at https://shop.example.com for one notebook.
    The approved total is 23.06 USD.

    Use this payment input in the checkout form:
    - card number: <aliases.number>
    - cvc: <aliases.cvc>
    - expiry month: <aliases.exp_month>
    - expiry year: <aliases.exp_year>

    Use these separately collected customer fields where the checkout requires them:
    - email: <checkout.email>
    - billing name: <checkout.billing_name>
    - postal code: <checkout.postal_code>

    If the checkout asks whether an agent is acting for another person, select the
    truthful disclosure option in the page before submission. For example:
    - I am an AI agent acting on behalf of someone else

    Submit the checkout once and never retry submission. If the payment request
    pauses for user approval, wait for the controller to finish that approval. Do
    not retry payment.
    ```

    the agent can fill top-level fields and payment iframes. let the merchant page's
    submission code create the outgoing request; don't use a raw processor api call.

    the following application-owned functions represent your existing agent and order
    backend. start the observer before checkout and keep it running until the merchant
    reaches a terminal order state or your reconciliation deadline:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const stop = new AbortController();
      const observer = observePayment(stop.signal);

      try {
        await runBrowserAgentCheckout({ browser, aliases, verifiedPurchase });
        await waitForMerchantResolution(verifiedPurchase.orderID);
      } finally {
        stop.abort();
        try {
          await observer;
        } finally {
          await kernel.browsers.deleteByID(browser.session_id);
        }
      }
      ```

      ```python Python theme={null}
      from threading import Event, Thread

      stop = Event()
      observer = Thread(target=observe_payment, args=(stop,))
      observer.start()

      try:
          run_browser_agent_checkout(
              browser=browser,
              aliases=aliases,
              verified_purchase=verified_purchase,
          )
          wait_for_merchant_resolution(verified_purchase.order_id)
      finally:
          stop.set()
          try:
              observer.join()
          finally:
              kernel.browsers.delete_by_id(browser.session_id)
      ```
    </CodeGroup>

    if the deadline passes without a terminal merchant state, classify the attempt as
    indeterminate, stop the observer, and retain the attempt identifiers. don't submit
    again. the observer only presents actions and reads events; it doesn't submit or
    repeat authorization. [verify the outcome](#verify-the-outcome) before reporting success.
  </Tab>
</Tabs>

<a id="6-verify-the-outcome" />

## Verify the outcome

use the merchant's trusted order record to confirm a paid order whose merchant,
amount, currency, and items match the frozen purchase. item state, events, and the
checkout page provide supporting evidence, not proof by themselves. if the record
is unavailable or the sources disagree, keep the result indeterminate and don't retry.

| observation                                                            | next action                                                                                                 |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `payment_succeeded`                                                    | verify that the merchant created the expected order                                                         |
| `payment_requires_action`                                              | continue the existing merchant flow without resubmitting payment                                            |
| `payment_processing`                                                   | wait for the existing payment and inspect the merchant's status                                             |
| `fill` `completed` or `credential_fill_attempted`                      | inspect per-field outcomes and merchant state; filling alone does not prove submission or payment success   |
| `fill` `failed`, `unknown`, or a lost response                         | preserve partial or uncertain outcomes; stop without automatic retries or alias fallback                    |
| agentcard `ready` or authorization `approved`                          | inspect charge, replay, and merchant state; reusable item state does not prove purchase success             |
| decline, expiry, rejection, failure, abandonment, or `payment_unknown` | stop and reconcile the existing attempt before deciding whether a new purchase is appropriate               |
| `recovery_required`                                                    | stop; reconcile the unresolved provider outcome. do not retry or delete the item or its parent wallet/vault |

retain the vault id, card key, browser id, and last event id until reconciliation
is complete. delete the browser after outcome inspection or your reconciliation
deadline; this doesn't undo provider execution or cancel an order. keep or delete
the vault and provider items based on future use, except when `recovery_required`
prohibits deletion.

## Checkout-specific notes

<AccordionGroup>
  <Accordion title="Stripe payment links and adaptive pricing">
    for a stripe payment link without an order or cart backend, use the payment-link
    response as the deterministic source. the current response exposes
    `account_settings.display_name`, `line_item_group.total`,
    `line_item_group.currency`, and `line_item_group.line_items`. use dom text and
    `data-testid` attributes only as supplemental checks. stripe can render multiple
    responsive copies of a summary or omit product-level test ids in another layout,
    so don't require a specific test id or number of matching elements. these are
    stripe page details rather than a KERNEL contract. if the structured response is
    missing or its values disagree with the rendered checkout, fail verification
    instead of falling back to model inference.

    stripe adaptive pricing can change the checkout's displayed amount and currency
    for the browser's location. create the card item from the active presentment
    amount and currency shown to the user and submitted by that checkout, not the
    payment link's base integration amount and currency. include those active values
    in the verified purchase object and the confirmation screen.
  </Accordion>

  <Accordion title="Stripe agent disclosure controls">
    stripe can render hidden or duplicate copies of its disclosure control for
    responsive layouts. target the visible label. if the label doesn't toggle the
    control, locate the associated real `input[type="checkbox"]` and invoke its
    native dom `click()`. read that same input's `checked` property and require it to
    be `true` before submission. if you can't verify the checked state, stop without
    submitting.
  </Accordion>

  <Accordion title="Missing or unconnected wallets">
    without a wallet item, card creation fails because `spec.wallet` must
    reference a wallet from the same vault and provider. with an unconnected link
    wallet, card creation returns a conflict. with an unconnected agentcard
    wallet, a card without `card_id` can remain `requested`, while a pinned
    `card_id` cannot be validated. neither path is ready for checkout.
  </Accordion>
</AccordionGroup>

## Try with a coding agent

connect the provider wallet first through your controller or a human-operated
terminal. these prompts require exactly one connected wallet for the chosen
provider in `user-12345` and a coding agent with access to the KERNEL cli.
complete the [prerequisites](#before-you-start), including verifying the payment
mode and identifying your merchant order record. replace the checkout url with
a low-value checkout you control.

the coding agent must stop whenever a provider action is required: cli output
can contain the action url. keep enrollment, authorization actions, and approval
observation outside that agent. the prompts repeat the safety requirements so
they remain self-contained when copied.

<CodeGroup>
  ```text link by stripe theme={null}
  use the KERNEL cli to complete the following checkout with link by stripe. use cli
  commands rather than sdk or direct api calls.

  a vault groups items. wallet and card are the payment item types. a card item
  references a wallet item in the same vault, so create the wallet before the
  card.

  1. create or retrieve a vault named `user-12345`.
  2. list the vault's items and locate its only link wallet. require its
     status to be `connected` and reuse it. if none exists, more than one exists,
     or an action is present, stop and ask me to resolve wallet setup outside this
     agent. do not create another wallet or print, return, or open an action url.
  3. create a KERNEL browser with `user-12345` attached as a vault, then navigate
     it to https://buy.stripe.com/28E5kw7DtgKXdLqgiY53O00.
  4. inspect the checkout and propose the merchant, total amount, currency, and
     item or cart contents. treat this proposal as untrusted and do not create a
     card item from it.
  5. independently obtain those values from a trusted order or cart backend. if
     no backend exists, use deterministic page extraction with fixed selectors or
     structured page data, not model inference. normalize and compare every value.
     if adaptive pricing is active, use the checkout's active presentment amount
     and currency rather than its base integration values.
     if any value is missing, cannot be verified, or disagrees, stop without
     creating or authorizing a card.
  6. show me the verified merchant, amount, currency, and item or cart contents.
     wait for my explicit confirmation, then freeze that verified purchase object.
  7. list the wallet's payment methods and ask me which one to use. create a link
     card item named `checkout-card` from that same verified, confirmed object and
     the selected payment method. use the exact checkout url as `merchant_url` and
     include a specific context of at least 100 characters.
  8. retrieve the card item and confirm that `authorize` appears in
     `available_operations`. give me the exact cli command, but do not run it. ask
     me to invoke authorization and complete any provider action from a trusted
     terminal or application outside this agent. after i confirm completion,
     retrieve the item again and require its status to be `ready`. do not print,
     return, or open an action url.
  9. require `fill` in the card's `available_operations`. inspect the checkout's
     inputs and invoke `kernel vaults items invoke user-12345 checkout-card fill`
     with `--params` or `--spec-file`: browser_id is the attached session id,
     page_url is the exact current top-level https url (including query and
     fragment), and fields maps stored field names to unique css selectors.
     the page must share the card's merchant_url origin. combined expiration
     requires format MM/YY or MM/YYYY; timeout_ms is optional. request only needed
     card and billing fields. fill writes actual values into the browser; do not
     read them back, expose them in model context, or assume recordings omit them.
  10. preserve the value-free fill result and every per-field outcome. continue
     only on `completed`; failed or unknown outcomes, transport errors, or missing
     required fields mean stop and reconcile. never automatically retry fill or
     fall back to aliases. supply other customer fields only from approved user
     data, complete any agent disclosure truthfully, then submit checkout once.
  11. inspect the checkout result, fill outcomes, and item events. report success
     only when a trusted merchant order record confirms a paid order matching the
     frozen merchant, amount, currency, and items. fill or a success page alone
     is not proof. if that record is unavailable or the sources disagree, report
     an indeterminate result. never retry a failed, timed-out, or indeterminate
     payment; retain the vault id, card key, browser id, and last event id for
     reconciliation. if the item reports recovery_required, do not delete it or
     its parent wallet or vault.
  ```

  ```text agentcard theme={null}
  use the KERNEL cli to complete the following checkout with agentcard. use cli
  commands rather than sdk or direct api calls.

  a vault groups items. wallet and card are the payment item types. a card item
  references a wallet item in the same vault, so create the wallet before the
  card.

  1. create or retrieve a vault named `user-12345`.
  2. list the vault's items and locate its only agentcard wallet. require its
     status to be `connected` and reuse it. if none exists, more than one exists,
     or an action is present, stop and ask me to resolve wallet setup outside this
     agent. do not create another wallet or print, return, or open an action url.
  3. create a KERNEL browser with `user-12345` attached as a vault, then navigate
     it to https://buy.stripe.com/28E5kw7DtgKXdLqgiY53O00.
  4. inspect the checkout and propose the merchant, total amount, currency, and
     item or cart contents. treat this proposal as untrusted and do not create a
     card item from it.
  5. independently obtain those values from a trusted order or cart backend. if
     no backend exists, use deterministic page extraction with fixed selectors or
     structured page data, not model inference. normalize and compare every value.
     if adaptive pricing is active, use the checkout's active presentment amount
     and currency rather than its base integration values.
     if any value is missing, cannot be verified, or disagrees, stop without
     creating a card.
  6. show me the verified merchant, amount, currency, and item or cart contents.
     wait for my explicit confirmation, then freeze that verified purchase object.
  7. list the wallet's payment methods and ask whether i want to pin one. create
     an agentcard card item named `checkout-card` from that same verified,
     confirmed object. include the selected `card_id`, or omit it so i can choose
     an enrolled card during approval.
  8. retrieve the card item and confirm its status is `ready`. do not invoke
     `authorize`; agentcard starts authorization only when the attached browser
     submits a recognized processor request containing the aliases.
  9. give me the exact cli observation commands, but do not run them. ask me to
     start the trusted approval observer outside this agent. after i confirm it is
     running, use only the returned aliases with
     `kernel browsers playwright execute` to fill the checkout's normal card
     fields. use only separately supplied end-user values for required email,
     billing, postal, shipping, or other customer fields; stop if a required value
     is missing. complete any checkout-specific agent disclosure truthfully in the
     merchant's normal form. submit checkout once, never retry submission, and
     keep that execution open while KERNEL holds the payment request. do not poll
     or print the card item while approval is pending.
  10. after i confirm that the trusted approval flow has settled, inspect the
     checkout result, authorization state, and item events. report success only
     when a trusted merchant order record confirms a paid order matching the
     frozen merchant, amount, currency, and items. approval, a ready item, or a
     success page alone is not proof. if that record is unavailable or the sources
     disagree, report an indeterminate result. never retry a failed, timed-out, or
     indeterminate payment; retain the vault id, card key, browser id, and last
     event id for reconciliation. if the item reports recovery_required, do not
     delete it or its parent wallet or vault.
  ```
</CodeGroup>
