Accept a Payment Fresh
Build a payment form or use a prebuilt checkout page to start accepting online payments.
Integration options
Choose the integration that fits your needs:
| Option | Hosting | Customization | Complexity |
|---|---|---|---|
| Stripe-hosted Checkout | Stripe's domain | Limited | Lowest |
| Embedded Checkout | Your domain | Moderate (Appearance API) | Low |
| Payment Element (custom UI) | Your domain | Full CSS control | Medium |
Stripe-hosted Checkout
Redirect customers to a Stripe-hosted payment page. Stripe handles the entire payment flow.
Pros: No UI code needed, Stripe handles 3DS, all payment methods included
Cons: Customers leave your domain during checkout
javascript
// Server: create a Checkout Session
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'usd',
product_data: { name: 'T-shirt' },
unit_amount: 2000, // $20.00
},
quantity: 1,
},
],
mode: 'payment',
success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/cancel',
});
// Redirect to session.urlhtml
<!-- Client: simple checkout button -->
<form action="/create-checkout-session" method="POST">
<button type="submit">Checkout</button>
</form>A Checkout Session represents what your customer sees when redirected to the payment form. Configure it with:
- Line items with pricing
- Supported payment methods
- Success and cancel redirect URLs
- Optional: customer email, tax ID collection, shipping addresses
Checkout Session with existing price
If you have a pre-created Price in the Dashboard or API:
javascript
const session = await stripe.checkout.sessions.create({
line_items: [{ price: 'price_1234', quantity: 1 }],
mode: 'payment',
success_url: 'https://example.com/success',
cancel_url: 'https://example.com/cancel',
});Handle the success redirect
After payment, Stripe redirects to your success_url. Retrieve the session to get order details:
javascript
// GET /success?session_id=cs_...
const session = await stripe.checkout.sessions.retrieve(req.query.session_id);
console.log(session.payment_status); // 'paid'
console.log(session.customer_details.email);Handle fulfillment with webhooks
Don't rely solely on the redirect URL — customers may close the browser. Use the checkout.session.completed webhook for reliable fulfillment:
javascript
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
await fulfillOrder(session);
}
res.json({ received: true });
});Payment Element (custom UI)
Build your own checkout form using Stripe's Payment Element component. Customers stay on your domain.
javascript
// 1. Server: create a PaymentIntent
const paymentIntent = await stripe.paymentIntents.create({
amount: 2000,
currency: 'usd',
});
// Return paymentIntent.client_secret to clientjavascript
// 2. Client: initialize Stripe and mount Payment Element
const stripe = Stripe('pk_test_YOUR_KEY');
const elements = stripe.elements({ clientSecret });
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
// 3. Handle form submission
const { error } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: 'https://example.com/success' },
});Existing customers
Pre-fill checkout fields for known customers:
javascript
const session = await stripe.checkout.sessions.create({
customer: 'cus_existing_id', // existing Customer ID
// OR
customer_email: 'customer@example.com',
line_items: [{ price: 'price_1234', quantity: 1 }],
mode: 'payment',
success_url: 'https://example.com/success',
cancel_url: 'https://example.com/cancel',
});Testing
Use test card 4242 4242 4242 4242 with any future expiry and any CVC to test a successful payment. See Test Cards for decline scenarios and 3DS testing.