DEV Community

Otto
Otto

Posted on

Stripe Integration for Beginners: Accept Payments in Your App in 2026

Stripe Integration for Beginners: Accept Payments in Your App in 2026

Accepting payments online sounds intimidating. But with Stripe, it's actually one of the most developer-friendly experiences out there.

This guide shows you how to integrate Stripe into your web app from scratch — no payment processing experience needed.


Why Stripe in 2026?

Stripe processes hundreds of billions in payments yearly. Developers love it because:

  • Clean, well-documented API
  • SDKs for every language
  • Built-in fraud detection
  • 135+ currencies supported
  • Webhooks for automation
  • Test mode — no real money needed to develop

Setup: 5 Minutes

  1. Create a free account at stripe.com
  2. Get your API keys (Dashboard → Developers → API Keys)
  3. Install the SDK:
npm install stripe @stripe/stripe-js
# or for Python
pip install stripe
Enter fullscreen mode Exit fullscreen mode

Accept a One-Time Payment (Node.js + React)

Backend: Create a Payment Intent

// server.js (Express)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const express = require('express');
const app = express();

app.use(express.json());

app.post('/create-payment-intent', async (req, res) => {
  const { amount, currency = 'usd' } = req.body;

  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: amount * 100, // Stripe uses cents
      currency,
      automatic_payment_methods: { enabled: true },
    });

    res.json({ clientSecret: paymentIntent.client_secret });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

app.listen(3001, () => console.log('Server running on port 3001'));
Enter fullscreen mode Exit fullscreen mode

Frontend: Show the Payment Form

// PaymentForm.jsx (React)
import { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY);

function CheckoutForm({ amount }) {
  const stripe = useStripe();
  const elements = useElements();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);

    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/payment-success`,
      },
    });

    if (error) {
      setError(error.message);
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <button disabled={!stripe || loading}>
        {loading ? 'Processing...' : `Pay $${amount}`}
      </button>
    </form>
  );
}

export default function Payment({ amount }) {
  const [clientSecret, setClientSecret] = useState('');

  // Fetch clientSecret from your backend
  useEffect(() => {
    fetch('/create-payment-intent', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ amount }),
    })
      .then(res => res.json())
      .then(data => setClientSecret(data.clientSecret));
  }, [amount]);

  return clientSecret ? (
    <Elements stripe={stripePromise} options={{ clientSecret }}>
      <CheckoutForm amount={amount} />
    </Elements>
  ) : <p>Loading...</p>;
}
Enter fullscreen mode Exit fullscreen mode

Subscriptions: The Recurring Revenue Model

// Create a subscription with Stripe Billing
const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: 'price_monthly_plan_id' }],
  payment_behavior: 'default_incomplete',
  expand: ['latest_invoice.payment_intent'],
});

const clientSecret = subscription.latest_invoice.payment_intent.client_secret;
Enter fullscreen mode Exit fullscreen mode

Webhooks: Handle Payment Events

Webhooks are how Stripe tells your server what happened:

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];

  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body, 
      sig, 
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  switch (event.type) {
    case 'payment_intent.succeeded':
      const payment = event.data.object;
      console.log(`Payment ${payment.id} succeeded!`);
      // Unlock product, send email, etc.
      break;

    case 'customer.subscription.deleted':
      // Handle subscription cancellation
      break;

    default:
      console.log(`Unhandled event type: ${event.type}`);
  }

  res.json({ received: true });
});
Enter fullscreen mode Exit fullscreen mode

Test Payments Without Real Money

Stripe's test cards are your best friend:

Scenario Card Number
Success 4242 4242 4242 4242
Declined 4000 0000 0000 0002
3D Secure 4000 0025 0000 3155

Use any future date, any CVC, any ZIP.


Python Version (for FastAPI/Flask)

import stripe
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

stripe.api_key = "sk_test_your_key"
app = FastAPI()

class PaymentRequest(BaseModel):
    amount: int  # in dollars
    currency: str = "usd"

@app.post("/create-payment-intent")
async def create_payment_intent(request: PaymentRequest):
    try:
        intent = stripe.PaymentIntent.create(
            amount=request.amount * 100,
            currency=request.currency,
            automatic_payment_methods={"enabled": True},
        )
        return {"clientSecret": intent.client_secret}
    except stripe.error.StripeError as e:
        raise HTTPException(status_code=400, detail=str(e))
Enter fullscreen mode Exit fullscreen mode

Common Mistakes to Avoid

Never expose your secret key in frontend code or public repos
✅ Use environment variables — always

Don't trust frontend payment confirmation — always verify with webhooks
✅ Server-side verification is mandatory

Don't forget idempotency keys for retry safety

await stripe.paymentIntents.create(
  { amount: 1000, currency: 'usd' },
  { idempotencyKey: 'order-123-attempt-1' }
);
Enter fullscreen mode Exit fullscreen mode

Pricing Reality Check

Stripe charges 2.9% + $0.30 per transaction in the US (EU: ~1.5% for EU cards).

On a $50 product: you keep ~$48.25. That's excellent.


Next Steps After This Tutorial

  1. Stripe Checkout — hosted payment page, zero frontend code
  2. Stripe Connect — marketplace payments, split payments
  3. Stripe Billing — full subscription management
  4. Stripe Radar — AI fraud detection

With Stripe integrated, you can start selling digital products, SaaS plans, or services immediately. The hard part isn't the code — it's building something worth paying for.


Freelance developer? Check out tools at guittet.gumroad.com — including the Freelancer OS Notion template (€19) for managing clients and invoices.

Top comments (0)