stripe-test-cards-and-webhooks
Wraps Stripe API testing patterns: test-mode initialization, the canonical test cards (4242 success; 4000 0000 0000 0002 declined; 4000 0027 6000 3184 3DS challenge per 3ds-test-flow-reference), the Stripe CLI webhook flow (`stripe listen --forward-to`), the Stripe CLI fixture commands (`stripe trigger payment_intent.succeeded`), and the webhook signature verification (Stripe-Signature header + HMAC-SHA256). Use when testing Stripe-integrated code.
Install with skills.sh (any agent)
npx skills add testland/qa --skill stripe-test-cards-and-webhooksstripe-test-cards-and-webhooks
Overview
Stripe's test-mode is feature-complete: every API call works against test data, no real money moves. Per docs.stripe.com/testing (opens in new window), test keys (sk_test_* / pk_test_*) accept canonical test cards that deterministically produce success / decline / 3DS challenge.
When to use
Authoring
Install
npm install stripe
pip install stripeTest keys come from the Stripe Dashboard (Developers → API keys → toggle to "View test data").
Initialize
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_TEST_KEY!);Canonical test cards
Per docs.stripe.com/testing (opens in new window):
| Card | Behaviour |
|---|---|
| 4242 4242 4242 4242 | Success (Visa) |
| 5555 5555 5555 4444 | Success (Mastercard) |
| 4000 0000 0000 0002 | Decline (generic_decline) |
| 4000 0000 0000 9995 | Decline (insufficient_funds) |
| 4000 0000 0000 9987 | Decline (lost_card) |
| 4000 0000 0000 0069 | Expired card |
| 4000 0027 6000 3184 | 3DS authentication required (challenge) |
| 4000 0000 0000 3055 | 3DS supported but frictionless |
| 4100 0000 0000 0019 | Fraud-prevention block |
Test PAN any future expiry; any 3-digit CVC.
PaymentIntent end-to-end
test('successful payment via 4242', async () => {
const intent = await stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
payment_method: 'pm_card_visa',
confirm: true,
return_url: 'https://example.com/return',
});
expect(intent.status).toBe('succeeded');
});
test('declined payment', async () => {
await expect(
stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
payment_method: 'pm_card_chargeDeclined',
confirm: true,
return_url: 'https://example.com/return',
})
).rejects.toThrow(/card was declined/);
});Webhook handler test
import { buffer } from 'micro';
import handler from './stripe-webhook';
test('webhook handler validates signature', async () => {
const payload = JSON.stringify({ type: 'payment_intent.succeeded', data: {...} });
const signature = stripe.webhooks.generateTestHeaderString({
payload,
secret: process.env.STRIPE_WEBHOOK_SECRET!,
});
const req = { headers: { 'stripe-signature': signature }, body: Buffer.from(payload) };
const res = await handler(req as any);
expect(res.statusCode).toBe(200);
});Per docs.stripe.com/webhooks/signatures (opens in new window): the Stripe CLI's generateTestHeaderString produces a valid HMAC-SHA256 signature for testing.
Stripe CLI local forwarding
Per docs.stripe.com/stripe-cli (opens in new window):
stripe listen --forward-to http://localhost:3000/webhooks/stripe
# Forwards real Stripe webhook events to your local endpointstripe trigger payment_intent.succeeded
# Sends a synthetic event to test your handlerThe CLI also exposes a signing secret (different from your prod webhook secret) for local testing.
Idempotency
test('idempotency key prevents duplicate charges', async () => {
const key = 'order-12345';
const r1 = await stripe.paymentIntents.create({...}, { idempotencyKey: key });
const r2 = await stripe.paymentIntents.create({...}, { idempotencyKey: key });
expect(r1.id).toBe(r2.id); // Same PaymentIntent
});Running
npm testFor webhook-integration tests with stripe listen running:
stripe listen --forward-to http://localhost:3000/webhooks/stripe &
npm testCI integration
jobs:
stripe-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
- run: npm ci && npm test
env:
STRIPE_TEST_KEY: ${{ secrets.STRIPE_TEST_KEY }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Mocking Stripe SDK directly | Loses signature verification, error mapping | Test against real test-mode API |
| Hardcoded test cards in many tests | Update breakage when Stripe changes | Per 3ds-test-flow-reference, use named constants |
| Skip webhook signature verification | Webhook replay attack | Always verify |
| Tests without idempotency key | Retried tests duplicate-create | Always set idempotency |
| Mix prod + test keys | Real money risk | Strict per-env key separation |
| Test webhook handler without raw body | body-parser strips bytes; signature mismatch | Use raw body middleware |
| One-off webhook event tests | Per-event-type integration coverage missed | Test the full event matrix relevant to your flow |
| Long-running stripe listen in CI | Hangs builds | Use stripe listen --print-secret then background |
Limitations
References
Related skills
3ds-test-flow-reference
Cross-gateway, protocol-level reference for 3-D Secure (3DS 2.x) test coverage. Covers the EMVCo frictionless / challenge / not-applicable flow paths, SCA under EU PSD2, and the per-PAN test cards for Stripe, Adyen, and Braintree. The gateway-specific wrappers (adyen-test-mode / stripe-test-cards-and-webhooks / braintree-test-cards) compose this skill for single-gateway work, so use one of those for a single-gateway query. Use this skill when designing multi-gateway 3DS test coverage, auditing a 3DS redirect round-trip, or investigating a challenge-flow regression that is not gateway-specific.
adyen-test-mode
Wraps Adyen test-mode patterns: test-API-key initialization, the canonical Adyen test cards (Visa 4111 1111 1111 1111; 5454 5454 5454 5454 3DS frictionless; 4917 6100 0000 0000 3DS 2 challenge), the per-flow result codes (Authorised / Refused / Pending / RedirectShopper / Error), the HMAC-SHA256 webhook validation, and notifications-vs-API duality (Adyen separates synchronous API calls from asynchronous notifications). Use when testing Adyen-integrated code.
braintree-test-cards
Wraps Braintree (PayPal-owned) sandbox testing patterns: sandbox merchant credentials, Drop-in / Hosted Fields client-side patterns, the Transaction lifecycle (submitted_for_settlement → settled), Braintree's distinctive test-card behaviours (specific PANs trigger specific errors), and the webhook verification (Braintree Webhook Parser). Use when testing Braintree-integrated code.
chargeback-flow-test-author
Workflow-driven skill that builds the chargeback / dispute test suite: canonical reason codes (Visa 10.4 fraud, 13.1 services-not-provided; Mastercard MCC 4855), per-gateway dispute APIs (Stripe Disputes, Adyen Chargeback notifications, PayPal Disputes), the evidence-submission flow + window, and disposition outcomes (won / lost / accepted). Use when designing dispute coverage; for refunds use refund-test-matrix-builder, for webhook redelivery + idempotency use payment-webhook-replay, and for the lifecycle state model use payment-flow-states-reference.
payment-flow-states-reference
Pure-reference catalog of payment lifecycle state machines across Stripe, Adyen, PayPal, and Braintree: canonical states (created / requires_action / processing / succeeded / requires_capture / canceled / failed), authorisation vs capture, asynchronous webhook states, and refund / dispute / chargeback transitions. Use when designing tests for payment flows or auditing state-handling code; this is the state model, not a builder - to author suites on it use refund-test-matrix-builder (refunds), chargeback-flow-test-author (disputes), or payment-webhook-replay (webhook replay).
payment-webhook-replay
Workflow-driven skill that builds payment webhook replay + recovery tests. Covers the idempotency contract (every webhook handler must handle redelivery without side effects), the replay simulators (Stripe CLI `stripe trigger`, Adyen Customer Area resend, PayPal Webhook Simulator, Braintree webhook test), the signature-verification gauntlet (HMAC-SHA256 per gateway, expired-timestamps rejection), and the partial-failure recovery scenarios. Use when designing webhook robustness tests.
paypal-sandbox
Wraps PayPal Sandbox testing patterns: sandbox account creation (Business + Personal accounts in developer.paypal.com), the Orders v2 API (create / capture / refund), webhook event simulator (developer.paypal.com webhook simulator), sandbox-account-specific test cards, and the OAuth2 client-credentials flow for sandbox. Use when testing PayPal-integrated code.
pci-dss-scope-reference
Pure-reference catalog of PCI DSS v4.0 scope reduction techniques + the testable scope boundaries. Covers the SAQ levels (A through D, picked by how cardholder data flows), the PAN-storage prohibitions (only first-6 + last-4 retained; nothing else cleartext), the tokenization + hosted-fields scope-reduction patterns (Stripe Elements / Adyen Drop-in / Braintree Hosted Fields keep PAN off your servers), Network-Segmentation as PCI scope-reduction, and the testable behaviours the scope boundary creates. This is the catalog of WHY the boundary matters and what it makes testable - not a checker that verifies a given integration against the standard. Use when designing or auditing the PCI scope of a payment integration.
refund-test-matrix-builder
Workflow-driven skill that builds a refund test matrix from a payment-flow inventory: refund variants (full / partial / multiple-partials / over-refund / refund-on-disputed / refund-on-already-refunded), per-gateway nuances (Stripe, Adyen, PayPal, Braintree refund APIs), and timing variants (immediate / next-day / declined-by-bank), one test case per cell. Use for refund coverage on a new integration; for chargeback / dispute coverage use chargeback-flow-test-author, for webhook redelivery + idempotency use payment-webhook-replay, and for the state model use payment-flow-states-reference.
stripe-subscription-billing-test-author
Builds test suites for Stripe recurring-billing flows: trial-to-paid conversion, proration on plan upgrade and downgrade, dunning on failed renewal, cancel and reactivation, and the full subscription webhook event matrix (invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted, invoice.paid). Uses Stripe Billing test clocks (POST /v1/test_helpers/test_clocks) to time-travel through billing cycles without calendar delay. Distinct from stripe-test-cards-and-webhooks (one-time PaymentIntents) and payment-webhook-replay (idempotency + replay robustness). Does not cover single-event CLI replay or handler idempotency testing (see payment-webhook-replay for those). Use when authoring tests for subscription or recurring-billing integrations.