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:
// 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:
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:
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:
# 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.