Stripe Subscriptions Plugin

Accept payments and manage subscriptions with Stripe. Includes webhook processing, subscription sync, event logging, and an admin dashboard with tabbed UI.


Overview

The Stripe plugin provides a complete subscription management system for SonicJS, built for Cloudflare Workers (no Stripe SDK required β€” uses fetch API directly).

πŸ’³

Subscription Management

Track active, canceled, past-due, and trialing subscriptions in D1

πŸ”„

Sync from Stripe

Pull all existing subscriptions from the Stripe API with one click

πŸ“‹

Webhook Event Log

Every webhook event is logged with status (processed, failed, ignored)

πŸ”

Secure Webhooks

HMAC signature verification using Web Crypto API with timing-safe comparison


Installation

The Stripe plugin is a core plugin and can be installed from the admin plugins page at /admin/plugins.

  1. Navigate to Admin > Plugins
  2. Find "Stripe Subscriptions" and click Install
  3. Go to Admin > Plugins > Stripe > Settings to configure your API keys

Configuration

Navigate to /admin/plugins/stripe/settings to configure the plugin. You'll need two keys from your Stripe Dashboard:

SettingDescriptionExample
Publishable KeyYour Stripe publishable keypk_test_... or pk_live_...
Secret KeyYour Stripe secret API keysk_test_... or sk_live_...
Webhook Signing SecretStripe webhook endpoint secretwhsec_...
Default Price IDDefault Stripe Price ID for checkout (optional)price_...
Success URLRedirect after successful checkout/admin/dashboard
Cancel URLRedirect if checkout is cancelled/admin/dashboard

Setting Up Webhooks

  1. Go to Stripe Dashboard > Developers > Webhooks
  2. Click Add endpoint
  3. Set the URL to https://your-domain.com/api/stripe/webhook
  4. Select these events:
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • checkout.session.completed
    • invoice.payment_succeeded
    • invoice.payment_failed
  5. Copy the Signing secret and paste it into the plugin settings

Local Development

Use the Stripe CLI to forward webhooks locally:

stripe listen --forward-to localhost:8787/api/stripe/webhook

The CLI prints a whsec_... signing secret to use during development.


Admin Dashboard

The Stripe admin UI at /admin/plugins/stripe has three tabbed pages:

Subscriptions Tab

Displays all subscriptions stored in D1 with:

  • Status badges (active, canceled, past_due, trialing)
  • Customer email (joined from users table)
  • Stripe subscription and customer IDs
  • Period dates and cancel-at-period-end status
  • Pagination and status filtering
  • Stats cards showing totals by status

Events Tab

Displays the webhook event log at /admin/plugins/stripe/events with:

  • Stats cards (total, processed, failed, ignored)
  • Filter by event type and status
  • Event details: timestamp, type, object ID, Stripe event ID
  • Error details shown on hover for failed events
  • Pagination

Settings Tab

Configure API keys, webhook secret, and checkout URLs.


Subscription Sync

Click the Sync from Stripe button on the Subscriptions tab to pull all existing subscriptions from the Stripe API into D1.

The sync:

  • Fetches all subscriptions from GET /v1/subscriptions (auto-paginates)
  • Upserts each subscription into the subscriptions table by stripe_subscription_id
  • Links subscriptions to SonicJS users via sonicjs_user_id metadata or customer ID lookup
  • Reports total synced and any errors

This is useful when connecting Stripe to an existing account with pre-existing subscriptions.


Webhook Events

The plugin automatically logs every webhook event to the stripe_events table:

ColumnDescription
stripe_event_idUnique Stripe event ID
typeEvent type (e.g., customer.subscription.created)
object_idID of the related Stripe object
object_typeType of the related object
dataFull event payload (JSON)
statusprocessed, failed, or ignored
errorError message if processing failed
processed_atTimestamp of when the event was received

Events are logged with three statuses:

  • processed β€” Successfully handled (subscription created/updated/deleted, payment succeeded/failed)
  • failed β€” Handler threw an error (logged for debugging)
  • ignored β€” Unhandled event type (logged for visibility)

API Reference

All API routes are prefixed with /api/stripe.

Public Endpoints

MethodPathAuthDescription
POST/api/stripe/webhookStripe signatureReceive and process webhook events

Authenticated Endpoints

MethodPathAuthDescription
POST/api/stripe/create-checkout-sessionUserCreate a Stripe Checkout session
GET/api/stripe/subscriptionUserGet current user's subscription

Admin Endpoints

MethodPathAuthDescription
GET/api/stripe/subscriptionsAdminList all subscriptions with filters
GET/api/stripe/statsAdminGet subscription statistics
POST/api/stripe/sync-subscriptionsAdminSync all subscriptions from Stripe
GET/api/stripe/eventsAdminList webhook events with filters

Query Parameters

GET /api/stripe/subscriptions:

ParamTypeDescription
statusstringFilter by status (active, canceled, etc.)
pagenumberPage number (default: 1)
limitnumberItems per page (default: 50, max: 100)
sortBystringSort field (created_at, updated_at, status)
sortOrderstringSort direction (asc, desc)

GET /api/stripe/events:

ParamTypeDescription
typestringFilter by event type
statusstringFilter by status (processed, failed, ignored)
objectIdstringFilter by Stripe object ID
pagenumberPage number (default: 1)
limitnumberItems per page (default: 50, max: 100)

Checkout Integration

To create a checkout session from your frontend:

const response = await fetch('/api/stripe/create-checkout-session', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${userToken}`
  },
  body: JSON.stringify({
    priceId: 'price_...' // Optional - uses default from settings
  })
})

const { url } = await response.json()
window.location.href = url // Redirect to Stripe Checkout

The plugin automatically:

  • Finds or creates a Stripe customer for the logged-in user
  • Attaches sonicjs_user_id metadata for webhook linking
  • Redirects to Stripe Checkout with the configured success/cancel URLs

Was this page helpful?