Webhook Setup

Configure your Stripe webhook endpoint to detect and handle purchases originating from Sumo Marketplace.

Prerequisites

  • Stripe account connected to Sumo via Partner Connect
  • Webhook endpoint URL accessible from the internet
  • Stripe webhook signing secret from your Stripe Dashboard

Required Webhook Events

Configure your Stripe webhook endpoint to listen for these events:

Event When It Fires Priority
checkout.session.completed Customer completes checkout successfully Required
customer.subscription.updated Subscription changes (upgrade, downgrade, cancel) Recommended
customer.subscription.deleted Subscription is cancelled or expires Recommended
invoice.payment_failed Recurring payment fails Optional

Detecting Sumo Purchases

The key to identifying Sumo-originated purchases is checking for the sumo_campaign_id in the checkout session metadata:

detection-logic.js
JavaScript
// The checkout session object from Stripe webhook
const session = event.data.object;

// Check for Sumo metadata
const isSumoPurchase = session.metadata?.sumo_campaign_id !== undefined;

if (isSumoPurchase) {
  // This purchase came from Sumo Marketplace
  // Create user and provision access
  await handleSumoPurchase(session);
} else {
  // Regular purchase from your own checkout
  await handleDirectPurchase(session);
}

Webhook Handler Structure

Here's the recommended structure for your webhook handler:

webhook-handler.js
JavaScript
async function handleWebhook(request) {
  // 1. Verify webhook signature
  const event = verifyWebhookSignature(request);

  // 2. Handle different event types
  switch (event.type) {
    case 'checkout.session.completed':
      const session = event.data.object;

      // 3. Check if this is a Sumo purchase
      if (session.metadata?.sumo_campaign_id) {
        // 4. Extract customer information
        const email = session.customer_details.email;
        const customerId = session.customer;
        const subscriptionId = session.subscription;

        // 5. Create or find user
        const user = await findOrCreateUser(email, customerId);

        // 6. Link subscription
        await linkSubscription(user.id, subscriptionId);

        // 7. Provision access based on tier
        const tierId = session.metadata.sumo_product_tier_id;
        await provisionAccess(user.id, tierId);

        // 8. Send welcome email
        await sendWelcomeEmail(user.email);
      }
      break;

    case 'customer.subscription.deleted':
      // Handle cancellation
      await revokeAccess(event.data.object.id);
      break;
  }

  return { status: 'ok' };
}

Signature Verification

Critical Security Step

Always verify the webhook signature before processing. This ensures the webhook actually came from Stripe and hasn't been tampered with. Never process webhooks without verification.

Your webhook signing secret is found in your Stripe Dashboard under Webhooks. Store it as an environment variable:

.env
Bash
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_signing_secret_here

Available Data in Checkout Session

The checkout.session.completed event contains all the data you need:

Customer Data

  • customer_details.email
  • customer_details.name
  • customer (Stripe customer ID)

Subscription Data

  • subscription (Stripe subscription ID)
  • amount_total (in cents)
  • currency

Sumo Metadata

metadata.sumo_campaign_id

Active discount campaign

metadata.sumo_product_tier_id

Purchased tier for access level

metadata.sumo_quantity

Number of licenses

Testing with Stripe CLI

Use the Stripe CLI to forward webhooks to your local development server:

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

# Login to your Stripe account
stripe login

# Forward webhooks to your local server
stripe listen --forward-to localhost:8000/webhooks/stripe

The CLI will provide a webhook signing secret (starts with whsec_) for local testing.

Ready to Code?