
Stripe Integration
- 64 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Build secure Stripe payment flows with Payment Intents, subscription billing, webhook handling, and European SCA compliance.
About
Integrates Stripe using Payment Intents, subscriptions, webhook handling, and SCA compliance for card payments. A developer uses it to add secure payment processing to a store.
- Payment Intents with SCA compliance
- Subscription billing and webhook handling
Stripe Integration by the numbers
- 64 all-time installs (skills.sh)
- Ranked #3,123 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill stripe-integrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 64 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Build secure Stripe payment flows with Payment Intents, subscription billing, webhook handling, and European SCA compliance.
Files
Stripe Integration
Overview
Stripe is the most widely supported payment processor across ecommerce platforms. On Shopify, it powers Shopify Payments natively. On WooCommerce and BigCommerce, official plugins provide full Stripe Checkout with minimal configuration. Custom code is only required for headless storefronts — and even then, Stripe's hosted Checkout page and Elements components remove most of the complexity.
When to Use This Skill
- When adding Stripe as a payment processor to an existing store
- When setting up Stripe on a new WooCommerce or BigCommerce store
- When implementing SCA-compliant checkout for European customers
- When setting up webhook handlers for order fulfillment automation
- When building a custom or headless storefront that needs Stripe payment processing
Core Instructions
Step 1: Create and configure your Stripe account
1. Sign up at stripe.com — business verification takes 1–3 business days 2. Complete Stripe Dashboard → Activate account to start accepting live payments 3. Configure your Statement descriptor (how you appear on customers' bank statements) under Settings → Public details 4. Set up Stripe Tax under Dashboard → Tax if you want Stripe to handle tax calculation automatically
Step 2: Install Stripe on your platform
---
Shopify
Shopify Payments is powered by Stripe. It is the simplest possible Stripe integration:
1. Go to Settings → Payments → Shopify Payments → Complete account setup 2. Verify your business details and banking information 3. Shopify Payments automatically handles: card processing, Apple Pay, Google Pay, Shop Pay, and (with configuration) Klarna and Afterpay 4. No additional plugins or API keys are needed — Shopify Payments handles everything
If you need to use Stripe directly (e.g., for features Shopify Payments does not support): 1. Install the Stripe payment provider under Settings → Payments → Add payment methods → Stripe 2. Note: third-party payment providers on Shopify incur an additional transaction fee (0.5%–2%); Shopify Payments does not
Configure Stripe webhooks for Shopify: Shopify handles webhook processing internally for Shopify Payments. If using Stripe directly, register your webhook endpoint under Stripe Dashboard → Developers → Webhooks.
WooCommerce
1. Install the WooCommerce Stripe Payment Gateway plugin (free, from WordPress.org — by WooCommerce) 2. Go to WooCommerce → Settings → Payments → Stripe and click Enable 3. Enter your Publishable key and Secret key from Stripe Dashboard → Developers → API Keys 4. Enable Payment Request Buttons (Apple Pay, Google Pay) — these appear automatically on product pages and checkout
Enable additional payment methods:
- Go to WooCommerce → Settings → Payments → Stripe and enable:
- Express Checkout (Apple Pay, Google Pay, Link)
- SEPA Direct Debit (for European customers)
- iDEAL (Netherlands), Bancontact (Belgium), Sofort (Germany/Austria)
- Klarna and Afterpay (if eligible)
Set up webhooks: 1. In the Stripe plugin settings, click Configure webhooks 2. The plugin automatically registers the required webhook endpoint with Stripe 3. Verify the webhook is active in Stripe Dashboard → Developers → Webhooks
Enable Stripe Radar (fraud prevention): Stripe Radar is enabled by default. Configure rules under Stripe Dashboard → Radar → Rules to block or review high-risk transactions.
BigCommerce
1. Go to Settings → Payment Methods → Online Payment Methods 2. Find Stripe and click Set Up 3. Enter your Stripe API keys (Publishable and Secret) from the Stripe Dashboard 4. Enable the payment methods you want to offer under the Stripe configuration panel 5. BigCommerce automatically registers the required Stripe webhooks
Enable Stripe Link (saved cards): In the BigCommerce Stripe settings, enable Stripe Link to allow returning customers to pay with one click using their saved payment details.
---
Custom / Headless
Install the Stripe SDK:
npm install stripe @stripe/stripe-js @stripe/react-stripe-jsOption A: Stripe Checkout (hosted page — simplest)
For the fastest integration with no payment form to build:
// Server: create a Checkout Session
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
product_data: { name: 'Order #' + orderNumber },
unit_amount: Math.round(orderTotal * 100), // cents
},
quantity: 1,
}],
mode: 'payment',
success_url: `${YOUR_DOMAIN}/orders/{CHECKOUT_SESSION_ID}/confirmation`,
cancel_url: `${YOUR_DOMAIN}/cart`,
metadata: { order_id: orderId },
});
// Redirect customer to session.url
res.redirect(session.url);Option B: Payment Intents with Stripe Elements (custom checkout form)
// Server: create a Payment Intent
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(orderTotal * 100), // cents
currency: 'usd',
automatic_payment_methods: { enabled: true }, // Enables all eligible methods
metadata: { order_id: orderId, customer_email: customerEmail },
});
res.json({ clientSecret: paymentIntent.client_secret });// Client: render the payment form using Stripe Elements
import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
function CheckoutForm({ onSuccess }) {
const stripe = useStripe();
const elements = useElements();
async function handleSubmit(e) {
e.preventDefault();
const { error } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: `${window.location.origin}/orders/confirmation` },
});
if (error) showError(error.message);
// On success, Stripe redirects to return_url automatically
}
return (
<form onSubmit={handleSubmit}>
<PaymentElement />
<button type="submit" disabled={!stripe}>Pay Now</button>
</form>
);
}
export function PaymentPage({ clientSecret }) {
return (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<CheckoutForm />
</Elements>
);
}Handle webhooks for order fulfillment:
// POST /api/webhooks/stripe
// IMPORTANT: use raw body parser — do not parse as JSON
export async function handleStripeWebhook(req, res) {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody, // raw buffer — not JSON.parse'd
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'payment_intent.succeeded':
await fulfillOrder(event.data.object);
break;
case 'payment_intent.payment_failed':
await handlePaymentFailed(event.data.object);
break;
case 'charge.refunded':
await handleRefund(event.data.object);
break;
case 'charge.dispute.created':
await handleDispute(event.data.object);
break;
}
res.json({ received: true });
}
// Always check if already fulfilled — webhooks can arrive multiple times
async function fulfillOrder(paymentIntent) {
const orderId = paymentIntent.metadata.order_id;
const order = await db.orders.findUnique({ where: { id: orderId } });
if (order.status !== 'pending') return; // Idempotency check
await db.orders.update({ where: { id: orderId }, data: { status: 'confirmed', paidAt: new Date() } });
await sendOrderConfirmationEmail(orderId);
}Local webhook testing:
# Install Stripe CLI and forward events to your local server
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# Copy the webhook signing secret printed by the CLI into your .envStep 3: PCI compliance and SCA
PCI DSS scope: Using Stripe Elements or Stripe Checkout significantly reduces your PCI DSS compliance burden:
- SAQ A (22 requirements): applicable when using Stripe Checkout (hosted page) — card data never touches your servers or your page
- SAQ A-EP (191 requirements): applicable when your checkout page is served from your own domain and Stripe.js tokenizes cards in the browser
The WooCommerce Stripe plugin and BigCommerce's Stripe integration both use Stripe.js for tokenization, qualifying most merchants for SAQ A-EP.
SCA (Strong Customer Authentication) for European customers: Using automatic_payment_methods: { enabled: true } on Payment Intents automatically handles 3D Secure challenges when required by the customer's bank. No additional configuration needed.
Best Practices
- Always use Payment Intents — the legacy Charges API does not support SCA and is not recommended for new integrations
- Never log or store raw card numbers — use Stripe Elements or Checkout to stay out of PCI scope
- Use webhook events for fulfillment — do not rely on the client-side redirect alone; customers can close the browser window
- Make all webhook handlers idempotent — Stripe may deliver the same event multiple times; always check the current order status before processing
- Attach `order_id` to every Payment Intent metadata — this enables reconciliation and dispute management
- Use Stripe Tax if selling to multiple jurisdictions — configure under Stripe Dashboard → Tax rather than building tax calculation yourself
Common Pitfalls
| Problem | Solution |
|---|---|
| Webhook signature verification fails | Pass the raw request body (not JSON-parsed) to constructEvent; configure your framework to skip JSON parsing for the webhook route |
| 3DS challenges not triggering | Use automatic_payment_methods: { enabled: true } instead of manually listing payment method types |
| WooCommerce Stripe plugin shows blank payment form | Check for JavaScript console errors; often caused by a CSP (Content Security Policy) blocking Stripe.js; add js.stripe.com to your CSP allowlist |
| Stripe payment shows as succeeded but order not confirmed | Webhooks are the authoritative signal — if the webhook handler has an error, the order will not be confirmed; check your webhook logs in Stripe Dashboard → Developers → Webhooks → [Event] |
| Test mode charges appearing in live data | Verify you are using live API keys in production; Stripe's Dashboard has a separate live/test toggle — confirm you are in the correct mode |
| Currency amount wrong | Stripe uses the smallest currency unit — $20.00 = 2000 cents; JPY ¥3,000 = 3000 (no multiplication needed) |
Related Skills
- @checkout-flow-optimization
- @subscription-billing
- @paypal-integration
- @order-processing-pipeline
- @buy-now-pay-later
{
"context": "Tests whether the agent correctly creates a Stripe Checkout Session with the required fields (line_items, mode, success_url, cancel_url, metadata) and implements a direct subscription with trial using the correct parameters (payment_behavior: 'default_incomplete', expand: ['latest_invoice.payment_intent']).",
"type": "weighted_checklist",
"checklist": [
{
"name": "checkout.sessions.create used",
"max_score": 8,
"description": "Uses stripe.checkout.sessions.create() to create the hosted checkout session"
},
{
"name": "line_items present",
"max_score": 8,
"description": "Checkout session includes a line_items array with at least one item using the price_pro_monthly price"
},
{
"name": "mode: 'subscription'",
"max_score": 10,
"description": "Checkout session uses mode: 'subscription' (not 'payment')"
},
{
"name": "success_url set",
"max_score": 7,
"description": "Checkout session includes a success_url pointing to the Luminary domain (e.g. https://app.luminary.io/success or similar)"
},
{
"name": "cancel_url set",
"max_score": 7,
"description": "Checkout session includes a cancel_url pointing to the Luminary domain (e.g. https://app.luminary.io/pricing or similar)"
},
{
"name": "Checkout metadata with order_id",
"max_score": 8,
"description": "Checkout session includes a metadata object (e.g. with order_id or customer reference)"
},
{
"name": "session.url returned",
"max_score": 7,
"description": "createSubscriptionCheckout() returns session.url (the hosted checkout redirect URL)"
},
{
"name": "subscriptions.create used",
"max_score": 8,
"description": "Uses stripe.subscriptions.create() in createDirectSubscription() to create the subscription directly"
},
{
"name": "trial_period_days: 14",
"max_score": 10,
"description": "Direct subscription sets trial_period_days to 14"
},
{
"name": "payment_behavior: 'default_incomplete'",
"max_score": 15,
"description": "Direct subscription creation uses payment_behavior: 'default_incomplete'"
},
{
"name": "expand latest_invoice.payment_intent",
"max_score": 12,
"description": "Direct subscription creation uses expand: ['latest_invoice.payment_intent']"
}
]
}
SaaS Subscription Signup with Free Trial
Problem/Feature Description
Luminary, a B2B project management SaaS, is launching a new "Pro" tier. The growth team wants to offer a 14-day free trial to reduce signup friction — no credit card charge until the trial ends, but they do want to collect payment details upfront so that billing is automatic when the trial expires. The hosted checkout experience needs to feel polished without the engineering team building a custom UI.
The existing backend has a customer creation flow: when a user registers, a Stripe customer object is created and the customerId is stored in the database. The Pro tier pricing is already configured in Stripe with a monthly price ID of price_pro_monthly. The backend team wants to implement the subscription creation in Node.js and use Stripe's hosted checkout to collect payment details and start the trial. After the user completes checkout, they should land on a success page, and if they abandon checkout, they should return to the pricing page.
Output Specification
Produce a Node.js file called subscription-checkout.js that exports two functions:
1. createSubscriptionCheckout(customerId, orderId) — Creates a Stripe Checkout Session for the Pro subscription with a 14-day trial and returns the session URL for redirect
2. createDirectSubscription(customerId) — Creates a Stripe Subscription directly (without hosted checkout) using the same price and trial period, returning the subscription object
Include comments explaining important parameter choices.
Assume the following constants are defined at the top of your file:
YOUR_DOMAIN='https://app.luminary.io'- The Pro monthly price ID is
'price_pro_monthly'
{
"context": "Tests whether the agent correctly implements the Payment Intents API (not the legacy Charges API), loads Stripe.js from CDN, uses automatic_payment_methods, attaches order metadata, converts dollar amounts to cents, returns the client secret, and confirms payment with the correct return_url.",
"type": "weighted_checklist",
"checklist": [
{
"name": "stripe npm package",
"max_score": 5,
"description": "Server-side code uses the 'stripe' npm package (import/require of 'stripe'), not any other payment library"
},
{
"name": "STRIPE_SECRET_KEY from env",
"max_score": 5,
"description": "Stripe client is initialised with process.env.STRIPE_SECRET_KEY (not a hardcoded key)"
},
{
"name": "Stripe.js loaded from CDN",
"max_score": 12,
"description": "The HTML file loads Stripe.js via a <script> tag pointing to https://js.stripe.com/v3/ rather than bundling it locally"
},
{
"name": "Payment Intents API used",
"max_score": 10,
"description": "Uses stripe.paymentIntents.create() — does NOT use the legacy stripe.charges.create()"
},
{
"name": "automatic_payment_methods enabled",
"max_score": 12,
"description": "Payment Intent is created with automatic_payment_methods: { enabled: true }"
},
{
"name": "order_id in metadata",
"max_score": 10,
"description": "Payment Intent creation includes a metadata object containing an order_id field populated from the request"
},
{
"name": "Amount converted to cents",
"max_score": 10,
"description": "Dollar amount from the request is multiplied by 100 (or equivalent conversion) before being passed as the amount to paymentIntents.create()"
},
{
"name": "clientSecret returned",
"max_score": 8,
"description": "The API endpoint returns the paymentIntent.client_secret value to the client (e.g. res.json({ clientSecret: paymentIntent.client_secret }))"
},
{
"name": "confirmPayment return_url",
"max_score": 10,
"description": "Client-side code calls stripe.confirmPayment() with a confirmParams.return_url that points to an order confirmation path (e.g. /order/confirmation)"
},
{
"name": "Error handling on client",
"max_score": 8,
"description": "Client-side code checks for an error in the result of confirmPayment and displays it to the user rather than silently failing"
},
{
"name": "No raw card data",
"max_score": 10,
"description": "Code does NOT read or transmit raw card numbers directly — uses Stripe Elements for card input"
}
]
}
Online Checkout Payment Flow
Problem/Feature Description
Hartley Books is a small independent bookshop launching an online store. They have a working cart and order system but no payment processing. The shop sells internationally and has seen customers in Germany and France, so they need their checkout to comply with European banking regulations that require additional customer authentication steps for certain cards.
The team lead has decided to use Stripe and wants a clean, maintainable checkout implementation. The backend already has a basic Express server. The frontend uses plain HTML/JavaScript. The order system already generates an order_id for each basket before payment is initiated, and the team wants to be able to trace every Stripe transaction back to its originating order in the Stripe dashboard without querying their own database.
Output Specification
Produce two files:
1. server.js — An Express endpoint at POST /api/create-payment-intent that:
- Accepts
amount(in USD dollars, e.g. 29.99),currency, andorderIdfrom the request body - Creates a Stripe Payment Intent and returns the client secret to the frontend
2. checkout.html — A minimal checkout page that:
- Loads Stripe.js
- Mounts a payment form using Stripe Elements
- On form submit, calls the
/api/create-payment-intentendpoint then confirms the payment - Handles errors (declined card, etc.) by displaying a message to the user
- On success, redirects to
/order/confirmation
Both files should include comments explaining key decisions. Include a short notes.md listing which environment variables need to be configured.
{
"context": "Tests whether the agent correctly implements Stripe webhook signature verification using the raw request body, handles the required event types, makes fulfillment logic idempotent, and documents local development workflow with the Stripe CLI.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Raw body for verification",
"max_score": 15,
"description": "Passes the raw (unparsed) request body — not a parsed JSON object — as the first argument to stripe.webhooks.constructEvent()"
},
{
"name": "constructEvent used",
"max_score": 10,
"description": "Uses stripe.webhooks.constructEvent() to verify the incoming webhook event rather than trusting the request body directly"
},
{
"name": "Webhook secret from env",
"max_score": 8,
"description": "Reads the webhook signing secret from process.env.STRIPE_WEBHOOK_SECRET (not hardcoded)"
},
{
"name": "stripe-signature header",
"max_score": 8,
"description": "Reads the signature from req.headers['stripe-signature'] and passes it to constructEvent()"
},
{
"name": "payment_intent.succeeded handled",
"max_score": 8,
"description": "Includes a handler for the 'payment_intent.succeeded' event type"
},
{
"name": "payment_intent.payment_failed handled",
"max_score": 7,
"description": "Includes a handler for the 'payment_intent.payment_failed' event type"
},
{
"name": "charge.refunded handled",
"max_score": 7,
"description": "Includes a handler for the 'charge.refunded' event type"
},
{
"name": "Idempotency check",
"max_score": 15,
"description": "Fulfillment function checks whether the order has already been fulfilled (e.g., checks a status field) before executing fulfillment side-effects, and returns early if already done"
},
{
"name": "Returns 200 on success",
"max_score": 7,
"description": "Returns a 200 JSON response (e.g., { received: true }) after successfully processing the event"
},
{
"name": "Returns 400 on bad signature",
"max_score": 7,
"description": "Returns a 400 status code when webhook signature verification fails"
},
{
"name": "stripe listen documented",
"max_score": 8,
"description": "README or comments document using 'stripe listen --forward-to' for local webhook testing during development"
}
]
}
Stripe Webhook Order Fulfillment Handler
Problem/Feature Description
FreshPrint, an on-demand print shop, processes hundreds of custom orders per day. When a customer pays, FreshPrint's backend needs to trigger print queue submission, send a confirmation email, and mark the order fulfilled in their database. Currently this logic runs on the browser redirect after payment, which means orders frequently go unfulfilled when customers close their tabs or lose internet connectivity before the redirect completes.
The engineering team wants to move fulfillment to a server-side webhook handler so that every successful payment reliably triggers the fulfillment pipeline. They also need the system to handle Stripe's delivery guarantees — Stripe may redeliver the same event multiple times, and the team has been burned before by orders being double-processed and double-emailed. In addition, they need the webhook endpoint to be hardened against spoofed requests, since a forged payment_intent.succeeded could trigger free order shipments.
Output Specification
Produce a Node.js/Express implementation of the Stripe webhook endpoint. Write the code to a file called webhook-handler.js. The handler should:
- Accept POST requests at
/api/webhooks/stripe - Validate the authenticity of incoming Stripe events
- Process the following event types: payment succeeded, payment failed, and charge refunded
- Implement fulfillment logic that is safe to call multiple times for the same order
Include a README.md explaining:
- How to configure the required environment variables
- How to test the webhook locally during development
You may stub out the database and email functions (e.g., db.orders.findById, sendConfirmationEmail) rather than implementing them fully. Focus on the structure and correctness of the webhook handler itself.
{
"name": "finsi/stripe-integration",
"version": "0.1.0",
"summary": "E-commerce Stripe payment integration skill",
"skills": {
"stripe-integration": {
"path": "SKILL.md"
}
}
}