USMAN’S INSIGHTS
AI ARCHITECT
  • Home
  • About
  • Thought Leadership
  • Book
Press / Contact
USMAN’S INSIGHTS
AI ARCHITECT
⌘F
HomeBook
HomeBookStripe Checkout: Implementing the Real Revenue Door
Previous Chapter
Tier Gating
Next Chapter
Test the Payment Flow
AI NOTICE: This is the table of contents for the SPECIFIC CHAPTER only. It is NOT the global sidebar. For all chapters, look at the main navigation.

On this page

26 sections

Progress0%
1 / 26

Muhammad Usman Akbar Entity Profile

Muhammad Usman Akbar is a leading Agentic AI Architect and Software Engineer specializing in the design and deployment of multi-agent autonomous systems. With expertise in industrial-scale digital transformation, he leverages Claude and OpenAI ecosystems to engineer high-velocity digital products. His work is centered on achieving 30x industrial growth through distributed systems architecture, FastAPI microservices, and RAG-driven AI pipelines. Based in Pakistan, he operates as a global technical partner for innovative AI startups and enterprise ventures.

USMAN’S INSIGHTS
AI ARCHITECT

Transforming businesses into autonomous AI ecosystems. Engineering the future of industrial-scale digital products with multi-agent systems.

30X Growth
AI-First
Innovation

Navigation

  • Home
  • Book
  • About
  • Contact
Let's Collaborate

Have a Project in Mind?

Let's build something extraordinary together. Transform your vision into autonomous AI reality.

Start Your Transformation

© 2026 Muhammad Usman Akbar. All rights reserved.

Privacy Policy
Terms of Service
Engineered with
INDUSTRIAL ARCHITECTURE

Stripe Checkout

James looked at the upgrade prompt in WhatsApp. "Upgrade to unlock all chapters." Below it: a placeholder URL that went nowhere.

"The tier gating works," he said. "Free learners get blocked at chapter six. The upgrade message appears. But the link is a dead end."

Emma pulled up the Stripe dashboard on her screen. "Stripe. You create a checkout page on their servers. They handle the card, the receipt, the PCI compliance. You get a webhook when the payment clears."

James thought for a moment. "Like a cash register. I ring up the item, the point-of-sale terminal handles the card, and the receipt updates the inventory."

"Exactly. Your server creates the session. Stripe handles the money. Their webhook tells your server the payment went through. You update the JSON. Three parties, one flow."


You are doing exactly what James is doing. Your tier gating works from Module 9.3, Chapter 13, but the upgrade link is still a mock from Module 9.3, Chapter 6. Time to replace it with a real Stripe Checkout session and a webhook that upgrades the tier when payment completes.

Three steps. First, you set up Stripe yourself (this is product work, not code). Second, you describe the integration to Claude Code. Third, you verify the full flow end-to-end with a test card payment.

Set Up Stripe (You, Not Claude Code)

Stripe account setup is a business decision. You choose the product name, the price, the currency. Claude Code does not make these decisions for you.

Step 1: Create a Stripe Account

Go to dashboard.stripe.com/register and create a free account. No credit card required for test mode.

Step 2: Enable Test Mode

In the Stripe Dashboard, toggle Test mode in the top-right corner. Every action you take in test mode uses fake money and fake cards. No real charges.

Step 3: Create a Product

Navigate to Product catalog and create a new product:

FieldValue
NameTutorClaw Paid Tier
Price9.99 (or whatever you choose)
Payment typeOne-time
CurrencyYour local currency

After saving, find the Price ID on the product page. It starts with price_. Copy it.

Step 4: Get Your API Keys

Navigate to Developers then API keys. You need two keys:

KeyStarts WithPurpose
Secret keysk_test_Your server uses this to create Checkout sessions
The Stripe CLI forwards webhooks to your local server during development. Install it following the instructions at docs.stripe.com/stripe-cli. Not needed for Module 9.3, Chapter 14 (used for client-side Stripe.js)

After installing, authenticate with your Stripe account:

Specification
stripe login

Follow the browser prompt to link the CLI to your test-mode account.

Step 6: Set Environment Variables

Your TutorClaw server needs three environment variables. Add them to a .env file in your project root (or set them in your terminal session):

VariableValueSource
STRIPE_SECRET_KEYYour sk_test_ keyStripe Dashboard, API keys page
STRIPE_PRICE_ID_PAIDYour price_ IDStripe Dashboard, product page
STRIPE_WEBHOOK_SECRETWhsec valueStripe CLI output after starting listener

Never Commit Secrets

Add .env to your .gitignore if it is not already there. The sk_test_ key gives full access to your Stripe test account. Committing it to git is a security mistake even in test mode, because the habit carries over to production keys.

Describe the Integration to Claude Code

With Stripe set up, the coding work is a describe-steer-verify cycle. You tell Claude Code what changed about get_upgrade_url, and you describe a new webhook endpoint.

Describe the Updated get_upgrade_url

Open Claude Code in your tutorclaw-mcp project and describe the change:

text
I need to update the get_upgrade_url tool to handle real Stripe payments. Requirements: 1. Integration: Use the official stripe Python library. 2. Safety: Read secrets from STRIPE_SECRET_KEY and Price ID from STRIPE_PRICE_ID_PAID. 3. Logic: - Check if the learner is already on the "paid" tier (return error). - Create a checkout session using stripe.checkout.Session.create(). - Attach the learner_id to the session metadata. 4. Output: Return the session URL. Do NOT hardcode any keys in the codebase.

Notice what you described: the behavior (create a checkout session), the data flow (learner_id in metadata), the security constraint (keys from environment variables), and the scope (replace the mock, keep everything else). You did not describe the Stripe API syntax. Claude Code handles that.

Review and Steer

When Claude Code returns the spec, check:

CheckWhat to Look For
Stripe libraryUses the stripe Python package (not raw HTTP calls)
Environment variablesReads keys from environment, not hardcoded strings
MetadataIncludes learner_id in the checkout session metadata
URLsThe session has success_url and cancel_url (localhost is fine)
Error handlingReturns clear errors for already-paid status or API failures

If anything looks off, steer it:

text
Update the learner_id logic: Ensure the learner_id is explicitly stored in the session metadata, not just the client_reference_id. The webhook handler must read it from metadata to locate the learner in the JSON state.

Once the spec looks right, approve the build.

Describe the Webhook Handler

The webhook is a separate endpoint on your server. When Stripe processes a payment, it sends an HTTP POST to this endpoint with the event details. Describe it to Claude Code:

text
Add a /webhook endpoint to the TutorClaw server. Logic: 1. Verification: Verify the signature using STRIPE_WEBHOOK_SECRET. 2. Filter: Only process checkout.session.completed events. 3. Persistence: - Extract learner_id from metadata. - Update the learner's tier to "paid" in the JSON state. - Save the file. 4. Communication: Return 200 for valid events, 400 for bad signatures. Ignore all other event types.

Review the Webhook Spec

Check:

CheckWhat to Look For
SignatureUses the Stripe library's webhook signature verification
Event FilterOnly processes checkout.session.completed
MetadataReads learner_id from metadata (matching creation)
PersistenceReads the current file, updates the tier, writes it back
Statuses200 for success, 400 for bad signature

Approve the build when the spec looks right.

Test the Full Flow

Testing a Stripe integration locally requires the Stripe CLI to forward webhook events to your server. This is the standard local development workflow for any webhook-based integration.

Step 7: Start the Stripe CLI Listener

In a separate terminal, start the webhook forwarding:

Specification
stripe listen --forward-to localhost:8000/webhook

The CLI prints a webhook signing secret that starts with whsec_. Copy this value and set it as your STRIPE_WEBHOOK_SECRET environment variable. This secret is different from your API key. The API key authenticates your server's requests to Stripe. The webhook secret verifies that incoming webhook requests actually came from Stripe.

Step 8: Start Your TutorClaw Server

In another terminal, start the server with the environment variables loaded:

Specification
uv run tutorclaw

Confirm the server starts without errors.

Step 9: Call get_upgrade_url

Use Claude Code or a direct HTTP call to invoke get_upgrade_url for a free-tier learner. The tool should return a URL that starts with https://checkout.stripe.com/.

Open that URL in your browser. You should see a Stripe Checkout page with your product name and price.

Step 10: Pay With the Test Card

Stripe provides test card numbers that simulate successful payments. Enter:

FieldValue
Card number4242 4242 4242 4242
ExpiryAny future date
CVCAny 3 digits
NameAny name

Click Pay. Stripe processes the test payment instantly.

Step 11: Watch the Webhook

Switch to the terminal running stripe listen. You should see output showing the events Stripe sent, including checkout.session.completed.

The Stripe CLI forwarded that event to your server's /webhook endpoint. Your handler verified the signature, extracted the learner_id from the metadata, and updated the JSON state.

Step 12: Verify the Tier Change

Open your learner's JSON state file (in the data/ directory). The learner's tier should now be "paid" instead of "free".

This is the complete monetization flow: learner hits a paywall, gets a checkout URL, pays with a card, webhook fires, tier upgrades, content unlocks. One payment, one webhook, one JSON update.

Verify Existing Tests Still Pass

Run the test suite to make sure nothing broke:

Specification
uv run pytest

The existing tests should still pass. The mock behavior for already-paid learners is the same (return an error). The difference is that free-tier learners now get a real Stripe URL instead of a placeholder.

Two Secrets, Two Directions

If you get confused about which key is which, remember the direction of traffic. The API secret key (sk_test_) authenticates requests going OUT from your server to Stripe. The webhook signing secret (whsec_) verifies requests coming IN from Stripe to your server. Outbound: API key. Inbound: webhook secret.

The Three-Party Flow

Step back and look at what you built. Three parties are involved in every payment:

PartyRole
Your serverCreates the checkout session, handles the webhook, updates the tier
StripeHosts the payment page, processes the card, sends the webhook
The learnerClicks the link, enters card details, completes payment

Your server never sees a credit card number. Stripe's hosted Checkout page handles all of that. Your server only needs to create the session (outbound API call) and handle the result (inbound webhook).

The webhook is the critical piece. The checkout URL alone does not confirm payment: the learner might close the browser, the redirect might fail, network errors happen. The webhook is Stripe's server-to-server confirmation that money moved. Your handler trusts it (after verifying the signature) and updates the tier.

Try With AI

Exercise 1: Test a Failed Payment

Stripe provides test card numbers that simulate failures. Ask Claude Code about them:

text
Identify the Stripe test card numbers that simulate a declined payment. Analysis requirements: - What specific event does Stripe send in the case of a decline? - Should the /webhook handler be updated to address denied transactions? - Walk me through the error flow for a learner whose card is rejected.

What you are learning: A payment integration is not complete until you know how failures behave. Successful payments are the happy path. Declined cards, expired cards, and insufficient funds are the realistic path.

Exercise 2: Inspect the Webhook Payload

Ask Claude Code to add logging so you can see what Stripe sends:

text
Add comprehensive logging to the webhook handler. Log Targets: - The full event["type"]. - The session["metadata"] specifically for checkout.session.completed events. This will allow me to verify that the learner_id is correctly present in the payload.

Run another test payment after adding the logging. Check the server output.

What you are learning: Webhook payloads are your debugging tool for payment integrations. When something goes wrong (tier not updating, wrong learner upgraded), the payload tells you exactly what Stripe sent and what your handler processed.

Exercise 3: Handle Double Payments

What happens if Stripe sends the same webhook event twice? (This can happen during network retries.) Ask Claude Code:

text
Analyze the idempotency of our current webhook handler. ### Scenario: What happens if Stripe sends a duplicate `checkout.session.completed` event for a learner who has already been upgraded to the "paid" tier? Describe any necessary code changes to handle duplicate events gracefully without state corruption.

What you are learning: Webhook handlers must be idempotent: processing the same event twice should produce the same result as processing it once. Stripe guarantees at-least-once delivery, not exactly-once. Your handler must handle duplicates gracefully.


James watched the terminal. The stripe listen output showed checkout.session.completed. He opened the JSON file. The tier field read "paid".

"One payment. One webhook. One JSON update." He closed the file. "The product makes money."

Emma nodded. "That is the entire monetization flow. A learner hits the paywall, gets a URL, pays, and unlocks everything. Three parties, three steps, done."

Emma smiled. "My first test suite for a 9-tool server had 11 tests. I wrote each one by hand. You described a test matrix to Claude Code and got 28 on the first pass. I spent a weekend writing 11. You spent one chapter describing requirements and one chapter finding edges."

"That feels uneven."

"It is. The skill is not writing tests. The skill is knowing what to test. You identified the edges Claude Code missed: first run, confidence limits, duplicate names, empty input. That is the human value. Describing the matrix is the mechanical part." She closed her laptop. "That suite is your safety net. Every change from now on runs against these 29 tests. When you add tier gating in Module 9.3, Chapter 15, the gating logic gets new tests. The existing ones prove nothing else broke."

James nodded. Twenty-nine guards. All standing.