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

davepi-plugin-stripe

v0.1.0

Published

Stripe payments + subscriptions + webhooks for dAvePi. Mounts opt-in /api/checkout, /api/portal, and /api/webhooks/stripe routes; verifies webhook signatures via the official Stripe SDK; dedupes events; rebroadcasts every verified event onto the framework

Readme

davepi-plugin-stripe

Stripe payments + subscriptions + webhooks for dAvePi. First-party plugin distributed as its own npm package.

npm install davepi-plugin-stripe stripe

Register it in your project's package.json:

{
  "davepi": {
    "plugins": ["davepi-plugin-stripe"]
  }
}

Configure via env:

| Variable | Required | Default | Description | | ------------------------- | ------------------ | ------------------------ | -------------------------------------------------------- | | STRIPE_SECRET_KEY | yes (else dormant) | — | sk_test_... / sk_live_... | | STRIPE_WEBHOOK_SECRET | yes for webhook | — | whsec_... — needed for signature verification | | STRIPE_WEBHOOK_PATH | no | /api/webhooks/stripe | Empty disables the route | | STRIPE_CHECKOUT_PATH | no | /api/checkout | Empty disables | | STRIPE_PORTAL_PATH | no | /api/portal | Empty disables | | STRIPE_API_VERSION | no | latest | Pin to avoid surprise API drift | | STRIPE_AUTOMATIC_TAX | no | false | Toggles Stripe Tax on Checkout |

If STRIPE_SECRET_KEY is unset, the plugin logs a warning and stays dormant. The same posture applies to the webhook: if STRIPE_WEBHOOK_PATH is set but STRIPE_WEBHOOK_SECRET is missing, the route is not mounted — an unverified webhook endpoint is worse than no endpoint.

What you get

POST /api/checkout

Authenticated (real JWT — client-id callers are refused with 403). Request body:

{
  "priceId": "price_xyz",
  "mode": "subscription",
  "successUrl": "https://app.example.com/billing/success",
  "cancelUrl":  "https://app.example.com/billing/cancel",
  "quantity": 1,
  "allowPromotionCodes": true
}

Response:

{ "url": "https://checkout.stripe.com/c/pay/cs_test_...", "id": "cs_test_..." }

The plugin auto-creates a Stripe customer for the user on first hit and records stripeCustomerId on the User document.

POST /api/portal

Authenticated. Request body:

{ "returnUrl": "https://app.example.com/account" }

Response: { "url": "https://billing.stripe.com/p/session/..." }.

POST /api/webhooks/stripe

Public endpoint. Stripe POSTs verified events here; the plugin:

  1. Verifies the Stripe-Signature header via stripe.webhooks.constructEvent (timing-safe HMAC under the hood).
  2. Inserts event.id into stripe_event_seen (TTL 7 days). Duplicate → 200 + short-circuit, no double-processing on Stripe retries.
  3. ACKs Stripe with 200 { received: true } immediately.
  4. Syncs the stripe_subscription mirror for customer.subscription.* events.
  5. Rebroadcasts onto the framework's record bus as record events typed stripe.<event.type> so audit / slack / postmark plugins compose without extra wiring.

stripe_subscription (auto-registered schema)

REST: GET /api/stripe_subscription, etc. GraphQL: stripeSubscriptionFindMany and friends. Tenant-scoped by userId. Fields include status, priceId, currentPeriodEnd, cancelAtPeriodEnd, and a raw payload of the last subscription object from Stripe so consumers can read any field without a schema migration.

stripe_event_seen (auto-registered schema)

Idempotency dedupe. The framework's auto-generated REST/GraphQL is incidental — operators can see what's been processed.

Programmatic API

const stripe = require('davepi-plugin-stripe');

// From a schema lifecycle hook
const session = await stripe.createCheckoutSession({
  user: req.user,
  priceId: 'price_xyz',
  mode: 'subscription',
  successUrl: 'https://app/.../success',
  cancelUrl:  'https://app/.../cancel',
});
// session.url

const portal = await stripe.createPortalSession({
  user: req.user,
  returnUrl: 'https://app/.../account',
});

// Subscribe to verified webhook events directly
stripe.onWebhookEvent('customer.subscription.updated', async (event) => {
  // event is the raw Stripe Event object
});

// Direct SDK access (escape hatch)
const client = stripe.client; // new Stripe(secret)
await client.invoices.list({ customer: 'cus_...' });

All helpers throw with a clear message if called while the plugin is dormant.

Customer-creation race

Two concurrent first-hits from the same user could race to create two Stripe customers. The plugin uses an Idempotency-Key of davepi-customer-<user_id> on customers.create so Stripe coalesces the duplicate server-side. The local stripeCustomerId pointer is findByIdAndUpdate (last-write-wins); either id remains valid for the same Stripe customer.

Notes on raw-body

The webhook handler verifies the request's raw bytes against the Stripe-Signature header. The framework's express.json() mount uses a verify callback that stashes the raw buffer on req.rawBody, so signature verification works without per-path middleware reordering. If you're on a framework version older than the one that ships this hook, the webhook handler returns 400 with a diagnostic.

License

ISC