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

@xenterprises/fastify-xstripe

v1.2.1

Published

Fastify plugin for Stripe webhooks with simplified, testable handlers for subscription events.

Downloads

42

Readme

@xenterprises/fastify-xstripe

Fastify v5 plugin for Stripe webhook handling with built-in signature verification, 23 default event handlers, and the Stripe client decorated on the Fastify instance.

Install

npm install @xenterprises/fastify-xstripe stripe

Quick Start

import Fastify from 'fastify';
import xStripe from '@xenterprises/fastify-xstripe';

const fastify = Fastify({ logger: true });

await fastify.register(xStripe, {
  apiKey: process.env.STRIPE_API_KEY,
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
});

// Use the Stripe client directly
const customer = await fastify.stripe.customers.create({ email: '[email protected]' });

await fastify.listen({ port: 3000 });

Options

| Name | Type | Default | Required | Description | |------|------|---------|----------|-------------| | apiKey | string | — | Yes | Stripe secret API key (sk_test_... or sk_live_...) | | webhookSecret | string | — | Yes | Stripe webhook signing secret (whsec_...) | | webhookPath | string | "/stripe/webhook" | No | Path where the webhook POST route is registered | | handlers | object | {} | No | Custom event handlers that override the defaults | | apiVersion | string | "2024-11-20.acacia" | No | Stripe API version |

All options are validated at startup. Invalid or missing required options throw with an [xStripe] prefix.

Decorated Properties

| Property | Type | Description | |----------|------|-------------| | fastify.stripe | Stripe | The initialized Stripe SDK client — use it to call any Stripe API |

Custom Handlers

Override any default handler with your business logic. Handlers receive (event, fastify, stripe):

await fastify.register(xStripe, {
  apiKey: process.env.STRIPE_API_KEY,
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
  handlers: {
    'customer.subscription.created': async (event, fastify, stripe) => {
      const subscription = event.data.object;
      await db.users.update({
        where: { stripeCustomerId: subscription.customer },
        data: { subscriptionId: subscription.id, status: subscription.status },
      });
    },
    'invoice.payment_failed': async (event, fastify, stripe) => {
      const invoice = event.data.object;
      const customer = await stripe.customers.retrieve(invoice.customer);
      await sendEmail(customer.email, 'Payment Failed', 'Please update your card.');
    },
  },
});

Default Event Handlers

All 23 built-in handlers log structured data via fastify.log. Override any of them via the handlers option.

Subscription Events

  • customer.subscription.created — logs subscriptionId, customerId, status, planId
  • customer.subscription.updated — logs subscriptionId, customerId, status, previous changes
  • customer.subscription.deleted — logs subscriptionId, customerId, canceledAt
  • customer.subscription.trial_will_end — logs subscriptionId, customerId, trialEnd
  • customer.subscription.paused — logs subscriptionId, customerId
  • customer.subscription.resumed — logs subscriptionId, customerId

Invoice Events

  • invoice.created — logs invoiceId, customerId, amount, status
  • invoice.finalized — logs invoiceId, customerId, amount
  • invoice.paid — logs invoiceId, customerId, subscriptionId, amount
  • invoice.payment_failed — logs (warn) invoiceId, customerId, amount, attemptCount
  • invoice.upcoming — logs customerId, subscriptionId, amount, periodEnd

Payment Events

  • payment_intent.succeeded — logs paymentIntentId, customerId, amount, currency
  • payment_intent.payment_failed — logs (warn) paymentIntentId, customerId, amount, lastPaymentError

Customer Events

  • customer.created — logs customerId, email
  • customer.updated — logs customerId, previous changes
  • customer.deleted — logs customerId

Payment Method Events

  • payment_method.attached — logs paymentMethodId, customerId, type
  • payment_method.detached — logs paymentMethodId, type

Checkout Events

  • checkout.session.completed — logs sessionId, customerId, subscriptionId, mode, paymentStatus
  • checkout.session.expired — logs sessionId

Charge Events

  • charge.succeeded — logs chargeId, customerId, amount, currency, paymentMethod
  • charge.failed — logs (error) chargeId, customerId, amount, failureCode, failureMessage
  • charge.refunded — logs chargeId, customerId, amountRefunded, refundCount

Helper Utilities

Import from @xenterprises/fastify-xstripe/helpers:

import { helpers } from '@xenterprises/fastify-xstripe';

helpers.formatAmount(2000, 'USD');           // "$20.00"
helpers.getPlanName(subscription);            // "Pro Plan"
helpers.isActiveSubscription(subscription);   // true
helpers.isInTrial(subscription);              // true/false
helpers.getDaysUntilTrialEnd(subscription);   // 3
helpers.isRenewal(event);                     // true/false
helpers.calculateMRR(subscription);           // 2000 (cents)
helpers.getSubscriptionStatusText('active');   // "Active"
helpers.getEventDescription(event);           // "Payment received"
helpers.getCustomerEmail(event, stripe);      // "[email protected]"
helpers.isTestEvent(event);                   // true/false
helpers.getMetadata(event);                   // { key: "value" }
helpers.getPaymentMethodType(pm);             // "Card"
helpers.getInvoiceLineItems(invoice);         // [{ description, amount, ... }]
helpers.isSubscriptionInvoice(invoice);       // true/false
helpers.getNextBillingDate(subscription);     // Date
helpers.formatDate(1700000000);               // "November 14, 2023"

Environment Variables

| Name | Required | Description | |------|----------|-------------| | STRIPE_API_KEY | Yes | Stripe secret key (sk_test_... or sk_live_...) | | STRIPE_WEBHOOK_SECRET | Yes | Webhook signing secret from Stripe Dashboard or CLI (whsec_...) |

Error Reference

All errors use the [xStripe] prefix for easy identification in logs.

| Error | When | |-------|------| | [xStripe] apiKey is required and must be a string | Missing or non-string apiKey option | | [xStripe] webhookSecret is required and must be a string | Missing or non-string webhookSecret option | | [xStripe] webhookPath must be a string | Non-string webhookPath option | | [xStripe] handlers must be a plain object | handlers is not an object or is an array | | [xStripe] apiVersion must be a string | Non-string apiVersion option | | [xStripe] Missing stripe-signature header | Webhook request without signature header (HTTP 400) | | [xStripe] Webhook signature verification failed: ... | Invalid webhook signature (HTTP 400) |

How It Works

  1. Registration — Validates all options, initializes the Stripe SDK client, and decorates it as fastify.stripe.
  2. Webhook Route — Registers a POST route at webhookPath that reads the raw body, verifies the Stripe signature using stripe.webhooks.constructEvent(), and dispatches to the matching handler.
  3. Handler Dispatch — User-provided handlers override defaults via object spread ({ ...defaultHandlers, ...userHandlers }). If a handler throws, the error is logged but the webhook still returns HTTP 200 to prevent Stripe retries.
  4. Stripe Client — The fastify.stripe decorator gives full access to the Stripe SDK for any API call (customers, subscriptions, invoices, etc.).

Testing Webhooks Locally

# Install Stripe CLI
brew install stripe/stripe-cli/stripe

# Login and forward webhooks
stripe login
stripe listen --forward-to localhost:3000/stripe/webhook

# Trigger test events
stripe trigger customer.subscription.created
stripe trigger invoice.payment_failed
stripe trigger checkout.session.completed

License

UNLICENSED