Testland
Browse all skills & agents

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-webhooks
View source

stripe-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

  • Tests for code that integrates Stripe.
  • Verifying webhook handling.
  • 3DS challenge flow tests per 3ds-test-flow-reference.

Authoring

Install

npm install stripe
pip install stripe

Test 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):

CardBehaviour
4242 4242 4242 4242Success (Visa)
5555 5555 5555 4444Success (Mastercard)
4000 0000 0000 0002Decline (generic_decline)
4000 0000 0000 9995Decline (insufficient_funds)
4000 0000 0000 9987Decline (lost_card)
4000 0000 0000 0069Expired card
4000 0027 6000 31843DS authentication required (challenge)
4000 0000 0000 30553DS supported but frictionless
4100 0000 0000 0019Fraud-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 endpoint
stripe trigger payment_intent.succeeded
# Sends a synthetic event to test your handler

The 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 test

For webhook-integration tests with stripe listen running:

stripe listen --forward-to http://localhost:3000/webhooks/stripe &
npm test

CI 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-patternWhy it failsFix
Mocking Stripe SDK directlyLoses signature verification, error mappingTest against real test-mode API
Hardcoded test cards in many testsUpdate breakage when Stripe changesPer 3ds-test-flow-reference, use named constants
Skip webhook signature verificationWebhook replay attackAlways verify
Tests without idempotency keyRetried tests duplicate-createAlways set idempotency
Mix prod + test keysReal money riskStrict per-env key separation
Test webhook handler without raw bodybody-parser strips bytes; signature mismatchUse raw body middleware
One-off webhook event testsPer-event-type integration coverage missedTest the full event matrix relevant to your flow
Long-running stripe listen in CIHangs buildsUse stripe listen --print-secret then background

Limitations

  • Test mode is real API surface. Rate limits apply; long CI runs can hit them.
  • Dispute timelines compress in test mode. Use Stripe CLI trigger to fast-forward dispute states.
  • Webhook delivery has ~30s SLA. Tests waiting for webhooks need timeouts.
  • Doesn't validate platform-side bookkeeping. Stripe reports are separate.

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.