Guides / Prompting and shipping

How to Add Payments to Your App: Checkout, Subscriptions and Webhooks

How to take payments in an app you built: Stripe Payment Links vs Checkout vs subscriptions, webhooks, test mode, going live, and what to know about tax.

Mythex Team · 2026-09-29 · 8 min read

The simplest way to add payments to your app is Stripe: use a Payment Link if you just need a "Buy" button and can handle orders by hand, Stripe Checkout if your app needs to know who paid and react automatically, and Checkout in subscription mode plus Stripe's customer portal for recurring billing. In every case except the simplest, you also need a webhook — a server endpoint Stripe calls when a payment succeeds — and you should test everything with Stripe's test keys and test cards before switching to live keys.

This guide explains each option, the webhook part most tutorials skip over, and what to check before taking real money. Stripe details are from Stripe's own documentation as of September 2026.

Why use a payment provider at all

Never collect card numbers in your own forms. Handling raw card data brings you into scope for PCI DSS (the card industry's security standard) and is a serious liability. A payment provider like Stripe hosts the payment form, stores the card, deals with bank authentication, and tells your app the result. Your app only ever sees "customer X paid for Y".

This guide uses Stripe because it's widely used, well documented and supported by most AI app builders. Other providers exist, and some act as a "merchant of record" that takes on sales-tax handling for you; if tax compliance worries you, that model is worth comparing.

The three ways to take payments with Stripe

OptionCode neededYour app knows who paid?Best for
Payment LinksNoneOnly if you add a webhookSelling a few fixed products, donations, testing demand
Checkout (one-time)Server code to create a sessionYes, via webhookStores, paid downloads, one-off purchases
Checkout (subscription)Server code plus webhook handlingYes, via webhookSaaS, memberships, anything recurring

Payment Links

A Payment Link is a Stripe-hosted payment page you create in the Stripe Dashboard without code. You get a URL you can put behind a button in your app, share in an email, or show as a QR code. Stripe's docs say Payment Links can sell a product, a subscription, or accept a donation, and you can let customers choose what to pay or limit the number of purchases.

The catch: your app doesn't automatically know that a particular logged-in user paid. For a donation page or a small shop where you fulfil orders by hand from Stripe's email notifications, that's fine. For "pay to unlock the Pro features", it isn't.

Stripe Checkout

Checkout is a payment page your server creates on demand for a specific purchase. Your server tells Stripe what's being bought (and optionally which customer is buying), Stripe returns a URL, and you send the customer there. Stripe's docs describe it as supporting both one-time and subscription payments, as a full Stripe-hosted page, a page embedded in your site, or an embedded form.

The flow:

  1. The customer clicks Buy in your app.
  2. Your server creates a Checkout Session using your secret key, including an ID that links it to your user or order.
  3. The customer is redirected to Stripe and pays.
  4. Stripe redirects them back to your success page.
  5. Stripe sends a checkout.session.completed event to your webhook, and your server marks the order paid or unlocks access.

Step 2 must happen on the server, because it uses your secret key. That's the main reason Checkout needs a backend.

Subscriptions

Subscriptions use Checkout in subscription mode with a recurring price you set up in Stripe. The first payment works like the flow above. After that, renewals, failed cards, upgrades and cancellations happen over time — often when the customer isn't in your app at all — so your app learns about them only through webhooks.

For letting customers manage their own subscription, Stripe offers a hosted customer portal. According to Stripe's docs, customers can update payment methods and billing details, change or cancel subscriptions, and view and download invoices there. You configure it in the Stripe Dashboard and add a "Manage billing" button that sends the customer to it. This saves you building those screens yourself.

Webhooks: the part you can't skip

A webhook is an HTTP request Stripe sends to your server when something happens. (See what a webhook is for the general idea.) Stripe's fulfilment guide is explicit: you can't rely on the success page alone to fulfil orders, because a customer can pay and then lose their connection before that page loads. For subscriptions and delayed payment methods, webhooks are required.

Events worth handling

EventWhat it meansWhat your app does
checkout.session.completedCheckout finishedMark the order paid or start access
checkout.session.async_payment_succeededA delayed method (such as a bank debit) cleared laterFulfil now
checkout.session.async_payment_failedThat delayed payment failedTell the customer; don't fulfil
invoice.paidA subscription payment succeeded, including renewalsExtend access
invoice.payment_failedA renewal failedWarn the user; Stripe may retry
customer.subscription.updatedPlan changed, or cancellation scheduledUpdate the user's plan
customer.subscription.deletedSubscription endedRemove paid access

Rules for a correct webhook handler

These come straight from Stripe's webhook documentation, and AI-generated handlers sometimes miss one:

  • Verify the signature. Each event includes a Stripe-Signature header. Your handler checks it using the endpoint's signing secret (it starts with whsec_). Without this, anyone could send a fake "payment succeeded" to your server.
  • Use the raw request body. Signature checks fail if your framework parses or modifies the body first.
  • Return a 2xx response quickly, then do slower work. Timeouts count as failures.
  • Handle duplicates. Stripe may deliver the same event more than once. Record processed event IDs, or make fulfilment safe to run twice.
  • Don't assume order. Stripe doesn't guarantee events arrive in the order they happened.
  • Expect retries. In live mode Stripe retries failed deliveries for up to three days, so a short outage doesn't lose payments — but your handler must cope with late events.

Webhooks need a public HTTPS address, so they usually only work once your app is published (Stripe's CLI can forward events to a local machine for developers).

A prompt that covers the whole flow

Add a one-time purchase with Stripe Checkout for the "Pro pack" (price ID in STRIPE_PRICE_ID). Create the Checkout Session on the server using STRIPE_SECRET_KEY from secrets, and include the logged-in user's ID in the session. Add a POST /api/stripe/webhook endpoint that verifies the Stripe signature with STRIPE_WEBHOOK_SECRET using the raw request body, handles checkout.session.completed, and marks that user as having the Pro pack. Make it safe if the same event arrives twice. Success and cancel pages. Never expose the secret key to the browser.

For subscriptions, add: "Use subscription mode with STRIPE_PRICE_ID. Handle invoice.paid, invoice.payment_failed, customer.subscription.updated and customer.subscription.deleted. Add a Manage billing button that opens the Stripe customer portal."

Test mode

Stripe has separate sandbox (test) and live environments with separate keys. Sandbox keys start with pk_test_, sk_test_ or rk_test_; live keys start with pk_live_, sk_live_ or rk_live_. Test transactions don't move money.

Useful test cards from Stripe's testing docs (any future expiry date, any three-digit CVC):

Card numberResult
4242 4242 4242 4242Payment succeeds
4000 0000 0000 0002Card is declined
4000 0000 0000 3220Requires 3D Secure authentication, then succeeds

Test at least:

  • A successful payment unlocks the right thing for the right user
  • A declined card shows a clear message and unlocks nothing
  • Closing the browser tab straight after paying still results in access (this proves the webhook works, not just the success page)
  • Cancelling on the Stripe page returns the user to your app with nothing charged
  • For subscriptions: cancel in the customer portal and confirm access ends when it should

Going live

  1. Activate your Stripe account with your business and bank details.
  2. Swap in live keys in your app's secrets. Stripe recommends restricted keys with only the permissions your integration needs, rather than an unrestricted secret key.
  3. Create a live webhook endpoint pointing at your published app. It has its own signing secret, different from the test one — update it.
  4. Recreate products and prices in live mode; test-mode IDs don't exist there.
  5. Make one real small purchase and refund it.

Keep every key in your project's secrets, never in code or chat. Our security checklist covers keys and webhook verification in more detail.

Taxes, refunds and the paperwork

Payments bring obligations that code doesn't solve. This isn't tax advice; check with an accountant.

  • Sales tax, VAT and GST. Depending on where you and your customers are, you may need to register, charge and file tax. Stripe Tax can calculate and collect tax at checkout, monitor where your sales may create an obligation, and help with registrations and filing — but responsibility stays with you. Merchant-of-record providers are an alternative that takes on more of this.
  • Refunds and disputes. Decide your refund policy before launch and put it on your site. Disputes (chargebacks) happen; keep records of what was bought and delivered.
  • Receipts and invoices. Stripe can email receipts; businesses may need proper invoices.
  • Fees. Every provider charges per transaction. Check the provider's current pricing page rather than relying on figures in articles.

Which setup fits your app

  • Donations or a handful of products: Payment Links. See how to build a nonprofit donation website.
  • A store with a cart: Checkout with line items and a webhook. See how to build an online store.
  • SaaS or memberships: Checkout in subscription mode, webhooks and the customer portal. See how to build a SaaS MVP and how to price a SaaS product.
  • A marketplace paying out to sellers: a different, more regulated setup (Stripe Connect or similar). Plan for it separately.

Adding payments in Mythex

Payments in apps you build on Mythex use your own Stripe account and keys; there's no one-click "Connect Stripe" for your app. The usual flow:

  1. Get Stripe test keys.
  2. Add STRIPE_SECRET_KEY (and the webhook secret later) to project secrets — through /secrets, project settings, or the secrets card the agent shows when it needs a key.
  3. Ask the agent for Checkout using a prompt like the one above.
  4. Test in Preview with test cards. Publish before registering the webhook, since it needs your public URL.
  5. After swapping to live keys, publish again so the live app picks them up.

See Accept payments with Stripe and Handle webhooks in your backend in the docs.

Questions

What is the easiest way to accept payments in an app?

A Stripe Payment Link needs no code: you create it in the Stripe Dashboard and link to it from your app. If your app needs to know who paid and unlock something automatically, use Stripe Checkout with a webhook instead.

Do I need a webhook to take payments?

If your app has to react to a payment — unlock an account, mark an order paid, handle a renewal — yes. Stripe's documentation says you can't rely on the success page alone, because a customer may pay and never reach it. Webhooks are also required for subscriptions.

How do I test payments without real money?

Use Stripe's sandbox (test) API keys and its test card numbers, such as 4242 4242 4242 4242 with any future expiry date and any three-digit CVC. Test transactions don't move real money.

Does Stripe handle sales tax and VAT for me?

Stripe Tax can calculate and collect tax at checkout and monitor where you may need to register, but you remain responsible for your tax obligations. Check with an accountant for your situation.

Keep reading

  • How to Add a Blog to Your Website: Options, SEO and Setup — How to add a blog to your website: Markdown files vs a built-in editor vs a CMS, subfolder vs subdomain, SEO basics, and prompts to build it with AI.
  • How to Add a Contact Form to Your Website (That Actually Reaches You) — How to add a contact form that works: save messages, get email alerts, stop spam, and avoid the mistakes that silently lose enquiries. Prompts included.
  • How to Add a Database to Your App (Without Losing Data Later) — How to add a database to an app you built: when you need one, Postgres vs hosted options, designing tables, prompts to use, and mistakes that lose data.
  • How to Add AI Features to Your App — Add summaries, chat, data extraction and classification to your app with an LLM API — keeping keys safe, costs under control and output trustworthy.
  • How to Add Analytics to Your App: GA4, Privacy-First Tools and Product Analytics — How to add analytics to your website or app: Google Analytics 4 vs privacy-first vs product analytics, what to track, cookie consent, and prompts to use.
  • How to Add Cookie Consent to Your Website — Add a cookie banner that actually blocks scripts until people agree: what needs consent, CMP vs custom, Google consent mode and a checklist. Not legal advice.

Start building free · Templates · Docs