Skip to main content

tai42-tools-stripe

License: Apache 2.0

Stripe payment tools for the TAI ecosystem — manifest-loaded Checkout Session tools that mint a hosted payment link, answer a paid ask from a Stripe webhook, reconcile payments the webhook path lost, and mint a flexible-amount non-blocking payment link.

All Stripe traffic is direct REST through tai42-kit's curl client (no stripe SDK); the Stripe-Version header is pinned in code. The package registers its tools through the tai42_app handle from tai42_contract.app and never imports the skeleton.

The TAI ecosystem

TAI is an open-source runtime for MCP tools, agents, and workflows. A tool is a callable the host loads from a plugin manifest; this package supplies the Stripe payment tools. The ecosystem is open-ended, so this repo is these tools' own full doc home, and the documentation site covers the platform-level story:

Install

Requires Python 3.13+. Install from PyPI into the environment that runs the server:

uv add tai42-tools-stripe

Or from source — clone this repo and add it as an editable dependency; the tai42-* dependencies resolve in-tree from the workspace.

git clone https://github.com/tai42ai/tai42   # next to your app checkout
cd /path/to/your/app
uv add --editable ../tai42/plugins/tools-stripe

Catalog

Tool Description
create_stripe_checkout Creates a Stripe Checkout Session and returns its hosted payment URL, usable directly as the link builder for an external ask.
confirm_stripe_payment Webhook bridge: answers a paid checkout ask from a projected Stripe event. Not a user/agent tool — it holds the bridge secret.
reconcile_stripe_payments Recovery layer: re-answers paid sessions the webhook path lost, re-derived from Stripe's own session list. Not a user/agent tool.
create_stripe_payment_link Flexible-amount, non-blocking: mints a hosted payment link with no callback and returns its URL and session id. Deployment-fenced to deterministic flow callers.

The payment ask (checkout / confirm / reconcile)

The first three tools implement a payment ask: an author asks a customer to pay, the customer pays on a Stripe-hosted page, and a webhook answers the ask.

Wiring the ask. create_stripe_checkout is never called directly — it is the payment-link builder behind the ask_external extension. Attaching ask_external to it in the manifest binds a per-question shared_secret verifier (which authenticates the bridge to the callback door and turns a browser GET into a hard 404) and composes a single tool, create_stripe_checkout_ask_external. The verifier rides the attachment's author-bound config, so an agent can never supply or change it:

tools:
  - title: stripe-checkout
    module: tai42_tools_stripe.tools.create_stripe_checkout
    extensions:
      create_stripe_checkout:
        - - name: ask_external
            config:
              verifier:
                name: shared_secret
                config:
                  header: X-TAI-Bridge-Secret
                  secret_env: TAI_BRIDGE_CALLBACK_SECRET
  - title: stripe-confirm
    module: tai42_tools_stripe.tools.confirm_stripe_payment
  - title: stripe-reconcile
    module: tai42_tools_stripe.tools.reconcile_stripe_payments

On the composed tool the builder's own parameters — amount, currency, product_name, success_url, cancel_url — plus answer_schema, question and timeout are all call-time arguments. The answer_schema const-pins the expected payment and is the amount binding: the door validates the delivered answer against the stored schema and a mismatch 400s without consuming the ticket. Its consts are typed on purpose — amount_total is an integer and currency a lowercase string, because Stripe returns lowercase and the query-param answer path would otherwise deliver strings. Every one of those money and payer-facing arguments is LLM-facing on the composed tool, so they are pinned by a preset (next) and agents are given only the preset.

Pin the money with a preset — agents get only the preset. Under the ask_external composition, amount, currency, product_name, success_url, cancel_url and the answer schema are all LLM-facing arguments, so a prompt injection could charge any amount, redirect the payer, or drop the pin. Bake all six as fixed constants a caller can neither supply nor override, with the schema's amount_total const written from the same literal as amount so price and pin cannot drift:

{
  "name": "buy_pro_licence",
  "base_tool": "create_stripe_checkout_ask_external",
  "description": "Ask the customer to pay for a Pro licence.",
  "fixed_kwargs": {
    "amount": 50000,
    "currency": "usd",
    "product_name": "Pro licence",
    "success_url": "https://acme.example/thanks",
    "cancel_url": "https://acme.example/cancelled",
    "answer_schema": {
      "type": "object",
      "required": ["amount_total", "currency"],
      "properties": {"amount_total": {"const": 50000},
                     "currency": {"const": "usd"}}
    }
  }
}

The preset's exposed schema is then question and timeout and nothing else. Do not add an extensions key: an explicit empty "extensions": [] is rejected — omit the field.

Exposure. confirm_stripe_payment, reconcile_stripe_payments and the raw create_stripe_checkout_ask_external composed tool must never be exposed in user_tools or any agent toolset — they hold the bridge secret and answer payment asks. Agents get the money-pinned preset over the composed tool, never the tool itself.

Livemode. Both bridge tools assert a session's livemode against the configured STRIPE_SECRET_KEY's mode (sk_live_/rk_live_ → live, sk_test_/rk_test_ → test) and refuse a mismatch loudly. Never point a test-mode webhook endpoint at a production payments topic: a test-mode session is free to mint for any amount and satisfies a const pin exactly as a live one.

Recovery is the reconciliation schedule. A single webhook delivery is not a fulfillment guarantee (the ingress ACKs before the hook runs), so reconcile_stripe_payments re-derives paid sessions from Stripe's own list and re-answers anything the hook lost. Its default 26-hour lookback_hours covers a Checkout link's full ~24h lifetime plus slack, and answers are paced by STRIPE_RECONCILE_ANSWER_INTERVAL_SECONDS (default 1.2s) to stay off the callback door's rate limiter. Cadence and lookback are a coupled pair — state and tune them together, never one number alone: at a 15-minute cadence a 26-hour lookback re-scans each paid session on ~104 consecutive runs (every re-answer is an idempotent already_answered 200, but it is standing load). After an outage LONGER than the lookback, run reconcile_stripe_payments once by hand with a lookback_hours covering the outage — that is what the 168-hour ceiling exists to allow.

Operator prerequisites. Register the account webhook endpoint at the same API version the tools pin (the endpoint's own API version governs the event payload shape); register the topic hook with the canonical projection expr and a checkout.session.completed condition; set STRIPE_SECRET_KEY, TAI_BRIDGE_CALLBACK_SECRET and INTERACTIONS_PUBLIC_BASE_URL; and schedule reconcile_stripe_payments. STRIPE_API_BASE is settings-overridable and is a privileged capability — a host it points at receives the Authorization: Bearer <key> header, so treat env write as privileged.

The non-blocking payment link (create_stripe_payment_link)

create_stripe_payment_link mints a flexible-amount Checkout Session through the same client seam and Stripe-Version pin, but carries no callback and no ask machinery — it returns the hosted payment URL and session id and does not wait for or answer anything:

{ "link": "https://checkout.stripe.com/c/pay/cs_...", "session_id": "cs_..." }

It takes amount (minor units, ≥ 1), currency (3-letter lowercase), product_name, success_url, cancel_url, and optional wa_id / offer_uuid. The session is stamped with tai_amount and tai_currency, plus tai_wa_id / tai_offer_uuid when supplied. The created session's livemode is asserted against the configured key's mode. Its Idempotency-Key is a canonical hash of every argument, so two calls with identical arguments return the same session — distinct payments must differ in at least one argument (wa_id or offer_uuid are the natural discriminators).

Exposure. This tool is deployment-fenced to deterministic flow callers only — its money-facing arguments are all caller-supplied, so it must never be exposed on any agent or user toolset.

Configuration

Setting Env var Default Purpose
Secret key STRIPE_SECRET_KEY The Stripe secret/restricted key (sk_…/rk_…). Required.
API base STRIPE_API_BASE https://api.stripe.com Stripe REST base. Privileged — receives the Authorization header.
Request timeout STRIPE_REQUEST_TIMEOUT_SECONDS 20 Per-request timeout.
Reconcile pacing STRIPE_RECONCILE_ANSWER_INTERVAL_SECONDS 1.2 Delay between reconcile answers.
Bridge secret TAI_BRIDGE_CALLBACK_SECRET Authenticates the bridge to the callback door. Required for confirm/reconcile.
Public base INTERACTIONS_PUBLIC_BASE_URL The origin the callback POST is pinned to. Required for confirm/reconcile.

Secrets live only in the environment. A missing or empty required secret raises loudly (fails closed) — never a silently-unauthenticated request.

Development

uv venv --python 3.13
uv pip install --no-sources --extra dev --editable .
uv run --no-sync pytest --cov --cov-report=term-missing
uv run --no-sync ruff check .
uv run --no-sync ruff format --check .
uv run --no-sync pyright

License

Apache-2.0. See LICENSE and NOTICE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tai42_tools_stripe-0.4.0.tar.gz (29.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tai42_tools_stripe-0.4.0-py3-none-any.whl (29.7 kB view details)

Uploaded Python 3

File details

Details for the file tai42_tools_stripe-0.4.0.tar.gz.

File metadata

  • Download URL: tai42_tools_stripe-0.4.0.tar.gz
  • Upload date:
  • Size: 29.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for tai42_tools_stripe-0.4.0.tar.gz
Algorithm Hash digest
SHA256 62b5090d07108a9d001983398b17e9b8e4db032fa84bcc1fc234386abf4a77ff
MD5 ce8a4a03597cf57a1faece7f22dcb782
BLAKE2b-256 2a8879d0d3c6f11dab35c9dcc2bf79b62d4f8f4f81dda46afb82f2779780ef6a

See more details on using hashes here.

File details

Details for the file tai42_tools_stripe-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for tai42_tools_stripe-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f433f0af623417d406fd141f99bedbf67fbbcc834358d1c311437c62eb74d1de
MD5 9c5d72e3bf9a541a17f2421fa0d80bea
BLAKE2b-256 4aa4a24924850943b849e362a6df46ce5908e3b5b4d018f6e76065ea684d6346

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.7

2 files

1.0.6

2 files

1.0.5

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

This release

0.4.0 This release

2 files

0.3.8

2 files

0.3.7

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page