Stripe Integration Tutorial: From Registration to First Payment
Stripe is one of the most popular online payment platforms, known for excellent developer experience and rich APIs. This tutorial walks you through "register an account → configure keys → create a PaymentIntent → collect card details on the frontend → get notified via webhook", taking you from zero to a first real charge in about 30 minutes, with no dependency on other payment services.
1. Register Stripe Account
1.1 Account Modes
- Test mode: Development, no real transactions
- Live mode: Production, real payments
1.2 API Keys
Get from Dashboard → Developers → API Keys:
- Publishable Key (pk_...): Frontend
- Secret Key (sk_...): Backend, keep secret!
2. Payment Flow Architecture
Stripe recommends Payment Intents API:
1. User clicks "Pay"
2. Frontend requests PaymentIntent from backend
3. Backend creates PaymentIntent → returns client_secret
4. Frontend collects card info via Stripe Elements
5. Stripe.js submits encrypted card to Stripe
6. Stripe processes payment, returns result
7. Webhook notifies backend
Why Payment Intents instead of the legacy Charges API? European SCA (Strong Customer Authentication) regulations require most transactions to go through 3D Secure, and Payment Intents natively supports this "async confirmation" flow: create the intent, let the user authenticate, then confirm. It separates authorization from capture, and also makes refunds, partial refunds and later subscription charges easier. In the flow above, steps 4 and 5 collect and submit the card entirely in the browser, so your server never touches raw card numbers and the PCI scope stays minimal.
3. Integration
3.1 Backend: Create PaymentIntent
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.post('/create-payment-intent', async (req, res) => {
const paymentIntent = await stripe.paymentIntents.create({
amount: 1999, // $19.99 in cents
currency: 'usd',
automatic_payment_methods: { enabled: true },
});
res.json({ clientSecret: paymentIntent.client_secret });
});
3.2 Frontend: Stripe Elements
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('pk_test_...');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
4. Webhook Configuration
app.post('/stripe/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
switch (event.type) {
case 'payment_intent.succeeded':
// Update order status
break;
case 'payment_intent.payment_failed':
// Handle failure
break;
}
res.json({received: true});
});
4.1 Webhook Best Practices
- Always verify the signature: use
stripe.webhooks.constructEventto validatestripe-signature; never trust the POST body directly, or anyone can trigger your fulfillment logic with a forged request. - Make the endpoint idempotent: Stripe retries failed webhooks, so the same event can arrive multiple times. Check order state first and return 200 if it was already handled.
- Respond fast: acknowledge with 200 first, then do the business logic asynchronously, so timeouts don't cause Stripe to retry repeatedly.
- Debug locally:
stripe listen --forward-to localhost:3000/stripe/webhookforwards live events to your local server, which is very handy during development.
5. Supported Payment Methods
- Credit/debit cards (global): 2.9% + $0.30
- Apple Pay / Google Pay: 2.9% + $0.30
- Alipay / WeChat Pay: 2.9% + $0.30
- SEPA Direct Debit (EU): 1.5% + €0.25
6. Fees & Settlement
Standard rate: 2.9% + $0.30/transaction
Settlement: 7 days standard (2 days for US users)
6.1 Test Cards and the Test Environment
Stripe ships fixed test card numbers: 4242 4242 4242 4242 always succeeds, 4000 0000 0000 0002 triggers a decline, and 4000 0027 6000 3184 requires 3DS. Bake these three into your automated tests to cover the main paths — success, decline, and authentication. Nothing in test mode moves real money, so iterate freely.
7. Choosing an Integration Mode: Checkout vs Payment Element
Stripe pushes two main integration modes, each with its own use case:
| Mode | Characteristics | Best For |
|---|---|---|
| Checkout | Hosted payment page, live in a few lines | Fast launch, full payment-method coverage |
| Payment Elements | Embedded in your page, deeply customizable | Developers who want full control of the flow |
A common pattern for a new subscription SaaS is to launch quickly with Checkout, then migrate to a custom Payment Elements page as users grow and you want to tune conversion. Both work with Subscriptions and Invoices, so the business code barely changes during the move.
8. Summary
Stripe's clean API and documentation make it the developer's first choice for payments. Test thoroughly before going live. The Payment Intents API is the recommended integration method.
Reference: Stripe Payment Intents docs https://docs.stripe.com/payments/payment-intents; Accept a payment guide https://docs.stripe.com/payments/accept-a-payment; Stripe CLI https://docs.stripe.com/stripe-cli