Zero-Code AI App BuilderZero-Code AI App Builder
Vibe Coding

Payments

Take payments safely with Stripe

Many apps need to take money: an online shop, a paid course, a booking page, a monthly subscription. In this lesson, you'll learn how online payments work and how to add them safely with Stripe, using test mode so no real money moves.

How online payments work

Building your own card form is like building your own bank vault. It is hard, risky, and comes with strict security rules. Instead, almost every small app hands the payment to a payment provider, a company that handles cards safely for you.

Stripe is a popular choice. Every payment has the same three parts:

  • Your app decides what is being bought and how much it costs
  • The payment provider shows a secure payment page and takes the card
  • Your server confirms the payment really happened, then saves the order

Your app never sees or stores card numbers. That is the whole point.

What is Stripe Checkout?

Stripe Checkout is a ready-made payment page run by Stripe. Your app sends the customer there, Stripe takes the card, and then sends the customer back to your app.

A checkout session is one visit to that page. Your server creates it with the items and prices, and Stripe gives back a link.

Your app
Stripe
Create checkout session (items + real prices)
→
←
Here is a secure payment page link
Customer enters their card on Stripe's page
→
←
Payment OK, send customer to /success
←
Webhook: "this payment was completed"
Confirm and save the order
→
How a payment flows between your app and Stripe.

Test mode and test keys

Stripe has a test mode. It works exactly like the real thing, but no real money moves. Always build and practice in test mode.

  1. Create a free Stripe account and make sure the dashboard shows Test mode
  2. Find your test API keys (look for Developers, then API keys)
  3. Put them in your environment file, never in your code
.env.local
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_SITE_URL=http://localhost:3000

Let's break this down:

  • The publishable key (pk_test_) is safe in the browser
  • The secret key (sk_test_) can create charges, so it stays on the server
  • The site URL is used to build the success and cancel links
The secret key must only be used in server-side code. In a Next.js app, it must never start with NEXT_PUBLIC_, because those values are visible in the browser.

Paying with a test card

Stripe gives you fake cards for testing. The most common one is:

text
Card number:  4242 4242 4242 4242
Expiry:       any future date (for example 12/34)
CVC:          any 3 digits (for example 123)
Name / ZIP:   anything

Adding checkout with a prompt

Here is a reusable prompt for any app that sells something. Use plan mode first, then let the AI build it.

Prompt
Add Stripe Checkout (test mode) to my [type of app].

- Add a "Buy" / "Checkout" button on [page].
- The button calls a server-side API route that looks up the real
  prices from the database and creates a Stripe Checkout Session.
  Use STRIPE_SECRET_KEY from the environment file.
- Success URL: /checkout/success. Cancel URL: [page to go back to].
  Build both from NEXT_PUBLIC_SITE_URL.
- Never trust prices or totals sent from the browser.

Show me the plan first. Do not edit files yet.

Success and cancel pages

After paying, Stripe sends the customer to your success page. If they click back or close the form, Stripe sends them to your cancel page.

  • The success page says thank you and shows an order number
  • The cancel page says "No problem, nothing was charged" and links back

Here's the catch. Anyone can type /checkout/success into the address bar. So reaching the success page does not prove that anyone paid.

Confirming payment on the server

Your server must ask Stripe "did this payment really go through?" before saving an order. There are two common ways:

  • Check on the success page: the server looks up the checkout session with Stripe and checks it is paid
  • Webhooks: Stripe sends your server a message when a payment completes

A webhook is like a delivery company texting you "your parcel arrived". You don't keep checking. Stripe calls a special address on your server (for example /api/stripe-webhook) and tells you what happened. Webhooks still work if the customer closes the tab before the success page loads, so they are the most reliable choice.

Prompt
Confirm payments on the server.
- Add a Stripe webhook route that listens for completed checkout sessions.
- Verify the webhook signature with STRIPE_WEBHOOK_SECRET.
- When a payment is confirmed, save the order and its items in the
  database, linked to the user. Make sure the same order is never
  saved twice.
- Explain how to test the webhook on my computer in test mode.
A webhook signing secret proves the message really came from Stripe. Without that check, anyone could send your server a fake "payment completed" message.

Saving orders

Once a payment is confirmed, save it. Most apps use two tables:

  • orders: one row per purchase (who, total, status, date)
  • order_items: the items inside each order, with the price actually paid

Then add a "My orders" page so customers can see their history. With Row Level Security on, each user only sees their own orders.

Example feature: carts and wishlists

Many shops let people collect items before paying. A cart is the basket you carry around the shop. A wishlist is a note of things you might buy later. Both are just database tables linked to a user.

Prompt
Add a cart and a wishlist, stored per user in the database
(cart_items and wishlist_items tables, with Row Level Security).

- Item page: "Add to cart" with a quantity picker, and a heart
  button to save to the wishlist.
- Header: a cart icon showing the number of items.
- /cart page: items, quantity + and -, Remove, and the total.
- /wishlist page: saved items with "Move to cart".
- Logged-out users who click "Add to cart" go to /login.
- Empty the cart after a confirmed payment.

Test it: add two items, change a quantity, refresh the page, then pay with the 4242 card. The order should appear in your database and in the Stripe dashboard.

Common mistakes

Trusting the price from the browser

text
Wrong: "Send the cart total from the page to Stripe."
Right: "On the server, look up each price in the database and build the total there."

Saving the order too early

text
Wrong: "Save the order when the customer clicks Checkout."
Right: "Save the order only after the server confirms the payment with Stripe."
  • Using live keys while building. Test keys start with sk_test_ and pk_test_.
  • Skipping the webhook signature check
  • Forgetting the cancel page, so customers land on an error

Practice

Try these exercises to lock in what you just learned.

Quiz
Not started
You're testing Stripe Checkout in test mode. Which card number should you use?
Quiz
Not started
Where should the total sent to Stripe come from?
Quiz
Not started
A customer lands on your /checkout/success page. When should your app save the order?

What's next?

Your app can now take (test) payments. Next, you'll learn how to give some users special powers with admin roles, so you can manage your content without touching the database by hand.