npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stripe-test-utils

v0.1.0

Published

Test utilities for Node.js applications integrating Stripe — type-safe event factories, signed webhooks, scenario builders, and custom matchers

Readme

stripe-test-utils

Test utilities for Node.js applications integrating Stripe.

Generates realistic, type-safe Stripe event payloads — signed and ready to use — without hitting the network or copying JSON by hand.

Installation

# npm
npm install --save-dev stripe-test-utils stripe

# pnpm
pnpm add -D stripe-test-utils stripe

supertest is optional and only needed for Express support:

npm install --save-dev supertest
# or
pnpm add -D supertest

Compatibility

npm SDK version — the peer dependency supports stripe ^14 through ^22. Install whichever version your application uses.

Stripe REST API version — distinct from the SDK version. The generated events include a default api_version field matching the latest Stripe API version. If your webhook handler checks that field, override it:

stripeEvent('payment_intent.succeeded', {
  api_version: '2024-06-20',
})

stripeEvent(type, overrides?)

Generates a complete Stripe.Event with sensible defaults. Each call produces a fresh, independent set of IDs.

import { stripeEvent } from 'stripe-test-utils'

// complete event with all required fields
const event = stripeEvent('payment_intent.succeeded')

// partial override — deep merged, non-overridden fields preserved
const event = stripeEvent('payment_intent.succeeded', {
  data: { object: { amount: 9900, currency: 'eur' } },
})

// TypeScript infers the exact event type
// event: Stripe.PaymentIntentSucceededEvent
event.data.object.status // 'succeeded'

Supported event types

| Group | Events | |---|---| | PaymentIntent | payment_intent.created payment_intent.succeeded payment_intent.payment_failed payment_intent.canceled | | Subscription | customer.subscription.created customer.subscription.updated customer.subscription.deleted customer.subscription.trial_will_end | | Invoice | invoice.created invoice.finalized invoice.payment_succeeded invoice.payment_failed | | Customer | customer.created customer.updated customer.deleted payment_method.attached | | Checkout | checkout.session.completed checkout.session.expired | | Charge | charge.succeeded charge.failed charge.refunded charge.dispute.created |


stripeEvent.signed(type, options)

Generates a signed payload + stripe-signature header, ready for end-to-end webhook handler testing including signature verification.

import { stripeEvent } from 'stripe-test-utils'

const { payload, header } = stripeEvent.signed('payment_intent.succeeded', {
  secret: process.env.STRIPE_WEBHOOK_SECRET!,
  overrides: { data: { object: { amount: 9900 } } },
})

With supertest (Express)

const res = await request(app)
  .post('/webhooks/stripe')
  .set('stripe-signature', header)
  .set('content-type', 'application/json')
  .send(payload)

expect(res.status).toBe(200)

With Fastify inject

const res = await app.inject({
  method: 'POST',
  url: '/webhooks/stripe',
  headers: { 'stripe-signature': header, 'content-type': 'application/json' },
  payload,
})

expect(res.statusCode).toBe(200)

Options

| Option | Type | Description | |---|---|---| | secret | string | Webhook signing secret (whsec_…) | | overrides | DeepPartial<Event> | Optional event overrides | | timestamp | number | Unix timestamp for the signature (default: Date.now()) |


stripeScenario(name)

Returns an ordered array of Stripe.Event objects representing a real Stripe lifecycle. All IDs are consistent across events within the same scenario — same customer.id, subscription.id, etc.

import { stripeScenario } from 'stripe-test-utils'

const events = stripeScenario('subscription.created_and_active')

for (const event of events) {
  await handleWebhook(event)
}

Available scenarios

subscription.created_and_active — 6 events

customer.created
payment_method.attached
customer.subscription.created
invoice.created
invoice.payment_succeeded
customer.subscription.updated       ← status: active

payment.failed_then_succeeded — 4 events

payment_intent.created              ← first attempt (pi_A)
payment_intent.payment_failed       ← fails
payment_intent.created              ← retry (pi_B, same customer)
payment_intent.succeeded

checkout.completed_with_subscription — 3 events

customer.created
customer.subscription.created
checkout.session.completed          ← references same customer & subscription

setupStripeMatchers()

Custom Jest/Vitest matchers for readable assertions. Call once in your setup file.

// jest.setup.ts  (or vitest.setup.ts)
import { setupStripeMatchers } from 'stripe-test-utils/matchers'
setupStripeMatchers()
// in your tests
import { stripeEvent } from 'stripe-test-utils'

const event = stripeEvent('payment_intent.succeeded', {
  data: { object: { amount: 9900, currency: 'eur', metadata: { orderId: '42' } } },
})

expect(event).toBeStripeEvent('payment_intent.succeeded')
expect(event).toHaveStripeAmount(9900)
expect(event).toHaveStripeCurrency('eur')
expect(event).toHaveStripeCustomer('cus_abc123')
expect(event).toHaveStripeMetadata({ orderId: '42' })

Matcher reference

| Matcher | Description | |---|---| | toBeStripeEvent(type) | Checks event.type and event.object === 'event' | | toHaveStripeAmount(n) | Checks event.data.object.amount | | toHaveStripeCurrency(c) | Checks event.data.object.currency (case-insensitive) | | toHaveStripeCustomer(id) | Checks event.data.object.customer — works with string ID or expanded object | | toHaveStripeMetadata(obj) | Checks that event.data.object.metadata contains the expected key/value subset |


testWebhookHandler(app, options)

Tests a webhook handler end-to-end without running a server. Automatically detects Express vs Fastify via duck typing.

import { testWebhookHandler, stripeEvent } from 'stripe-test-utils'

// with a pre-built event
const result = await testWebhookHandler(app, {
  route: '/webhooks/stripe',
  event: stripeEvent('payment_intent.succeeded'),
  secret: process.env.STRIPE_WEBHOOK_SECRET,
})
expect(result.status).toBe(200)

// with event type shorthand (builds the event internally)
const result = await testWebhookHandler(app, {
  route: '/webhooks/stripe',
  event: 'invoice.payment_succeeded',
  secret: 'whsec_test',
})

When secret is provided, the request is signed and your handler's stripe.webhooks.constructEvent call will succeed. When omitted, the raw JSON is sent without a signature.

Options

| Option | Type | Description | |---|---|---| | route | string | The webhook route path | | event | Stripe.Event \| SupportedEventType | Event object or type string | | secret | string | Optional signing secret |

Result

| Field | Type | Description | |---|---|---| | status | number | HTTP status code | | body | unknown | Parsed JSON response body | | headers | Record<string, string> | Response headers |


ID consistency

Within a single stripeEvent() call or stripeScenario(), all cross-references use the same ID. For example:

  • payment_intent.data.object.customer and invoice.data.object.customer share the same cus_… ID in a scenario
  • customer.subscription.data.object.id matches invoice.data.object.subscription in the same scenario

IDs follow Stripe's prefix conventions: cus_, pi_, sub_, in_, ch_, pm_, cs_, evt_.


License

MIT