Skip to main content

Stripe - Webhooks

๐Ÿ“– Overviewโ€‹

The Stripe webhooks module manages dynamic webhook registration and configuration for different connected apps (billing, review). Webhooks are automatically registered during app connection and cleaned up on disconnection, enabling real-time event processing for charges, subscriptions, and other Stripe events.

Source Files:

  • Controller: external/Integrations/Stripe/Controllers/auth.js (webhook functions)
  • Helper: reviewStripeWebhook() function for review app webhook management

Webhook Endpoints:

  • Billing Webhook: {API_BASE_URL}/v1/billing/webhooks
  • Review Webhook: {API_BASE_URL}/v1/reputation/webhook/stripe

๐Ÿ—„๏ธ Collections Usedโ€‹

Billing Collectionsโ€‹

  • Collections: billing-charge, billing-dispute, billing-refund, billing-customer, billing-subscription, billing-notes, billing-product
  • Operations: Create, Update based on webhook events
  • Usage Context: Store and update Stripe billing data in real-time

stripe-keyโ€‹

  • Operations: Read
  • Model: external/models/stripe-key.js
  • Usage Context: Track which apps are connected for webhook cleanup logic

๐Ÿ”„ Data Flowโ€‹

Webhook Registration Flow (Review App)โ€‹

sequenceDiagram
participant Auth as Auth Controller
participant Stripe as Stripe API
participant DB as Webhook Registry

Auth->>Stripe: List existing webhooks
Stripe-->>Auth: Webhook list
alt Webhook Not Found
Auth->>Stripe: Create webhook endpoint
Stripe->>Stripe: Register URL & events
Stripe-->>Auth: Webhook created (id, secret)
else Webhook Exists
Auth->>Stripe: Update webhook (re-enable)
Stripe-->>Auth: Webhook updated
end
Auth->>DB: Store webhook configuration

Webhook Event Processing Flowโ€‹

sequenceDiagram
participant Stripe as Stripe
participant Webhook as Webhook Endpoint
participant Handler as Event Handler
participant DB as Database

Stripe->>Webhook: POST /v1/billing/webhooks
Note over Webhook: Verify signature
Webhook->>Handler: Process event
alt charge.succeeded
Handler->>DB: Create/Update BillingCharge
else customer.created
Handler->>DB: Create BillingCustomer
else subscription.updated
Handler->>DB: Update BillingSubscription
end
Handler-->>Webhook: 200 OK
Webhook-->>Stripe: Acknowledge receipt

๐Ÿ”ง Business Logic & Functionsโ€‹

reviewStripeWebhook({ stripe, apiBaseUrl })โ€‹

Purpose: Register or update webhook endpoint for automatic review request functionality

Source: Controllers/auth.js

External API Endpoint: POST https://api.stripe.com/v1/webhook_endpoints

Parameters:

  • stripe (Object) - Initialized Stripe SDK instance
  • apiBaseUrl (String) - Base URL for webhook endpoint (from API_BASE_URL env var)

Returns: Promise<WebhookEndpoint>

{
id: "we_...",
object: "webhook_endpoint",
api_version: "2023-10-16",
application: null,
created: 1234567890,
description: "Created by dashclicks...",
enabled_events: ["charge.succeeded"],
livemode: false,
metadata: {},
status: "enabled",
url: "https://api.dashclicks.com/v1/reputation/webhook/stripe"
}

Business Logic Flow:

  1. Check Existing Webhooks

    for await (const webhook of stripe.webhookEndpoints.list({ limit: 100 })) {
    if (webhook?.url == `${apiBaseUrl}/v1/reputation/webhook/stripe`) {
    alreadyExist = true;
    webhookEndpoint = { ...webhook };
    break;
    }
    }
    • List all registered webhook endpoints
    • Check if review webhook URL already exists
    • Store existing webhook if found
  2. Create New Webhook (if not exists)

    webhookEndpoint = await stripe.webhookEndpoints.create({
    url: `${apiBaseUrl}/v1/reputation/webhook/stripe`,
    enabled_events: ['charge.succeeded'],
    description:
    'Created by dashclicks. Deleting might cause failure to send auto review request.',
    connect: true,
    });
    • URL: Points to reputation module webhook handler
    • Event: charge.succeeded - Triggered when payment succeeds
    • Description: Warning about deletion consequences
    • Connect: true - Works with Stripe Connect accounts
  3. Update Existing Webhook (if exists)

    webhookEndpoint = await stripe.webhookEndpoints.update(webhookEndpoint.id, {
    enabled_events: ['charge.succeeded'],
    description:
    'Created by dashclicks. Deleting might cause failure to send auto review request.',
    disabled: false,
    });
    • Re-enables webhook if previously disabled
    • Updates event list and description
    • Ensures webhook is active
  4. Return Webhook Configuration

    • Returns webhook endpoint object
    • Contains id for future reference
    • Includes secret for signature verification

API Request Example:

// Create webhook
POST https://api.stripe.com/v1/webhook_endpoints
Authorization: Bearer {STRIPE_SECRET_KEY}
Content-Type: application/x-www-form-urlencoded

url=https://api.dashclicks.com/v1/reputation/webhook/stripe
enabled_events[]=charge.succeeded
description=Created by dashclicks...
connect=true

API Response Example:

{
id: "we_1234567890abcdef",
object: "webhook_endpoint",
api_version: "2023-10-16",
created: 1697654400,
enabled_events: ["charge.succeeded"],
livemode: false,
status: "enabled",
url: "https://api.dashclicks.com/v1/reputation/webhook/stripe",
secret: "whsec_..." // Used for signature verification
}

Error Handling:

  • Network Errors: Logged and re-thrown
  • Invalid URL: Stripe returns 400 error
  • Duplicate Webhook: Handled by update logic
  • Permission Errors: Requires valid Stripe API key

When Called:

  • During review app connection in loginAuth()
  • During OAuth callback in postAuth() for review app
  • On existing connection when adding review app

Side Effects:

  • โš ๏ธ Creates webhook endpoint in Stripe account
  • โš ๏ธ Updates existing webhook if found
  • โš ๏ธ External API call to Stripe (quota counted)
  • โš ๏ธ Webhook secret stored in Stripe (not in DashClicks DB)

Billing Webhook Registrationโ€‹

Purpose: Register webhook for billing events (implicit in billing app connection)

Webhook URL: {API_BASE_URL}/v1/billing/webhooks

Enabled Events: All billing-related events

  • charge.succeeded
  • charge.failed
  • charge.refunded
  • customer.created
  • customer.updated
  • customer.deleted
  • payment_intent.succeeded
  • payment_intent.failed
  • invoice.created
  • invoice.updated
  • invoice.paid
  • invoice.payment_failed
  • subscription.created
  • subscription.updated
  • subscription.deleted
  • dispute.created
  • dispute.updated
  • And more...

Business Logic:

The billing webhook is registered automatically when the billing app is connected. The registration logic follows a similar pattern to the review webhook but is handled by the billing module itself.

Handler Location: internal/api/v1/billing/webhooks (not in Stripe integration)


Webhook Deletion Logicโ€‹

When Disconnecting Billing App:

// Delete billing webhook
for await (const webhook of stripe.webhookEndpoints.list({ limit: 100 })) {
if (webhook?.url == `${process.env.API_BASE_URL}/v1/billing/webhooks`) {
await stripe.webhookEndpoints.del(webhook.id);
break;
}
}

When Disconnecting Review App:

// Check if other accounts still use review
const isReviewConnected = await StripeModel.findOne({ connected_apps: 'review' });
if (!isReviewConnected) {
// Safe to delete - no other accounts using review webhook
for await (const webhook of stripe.webhookEndpoints.list({ limit: 100 })) {
if (webhook?.url == `${process.env.API_BASE_URL}/v1/reputation/webhook/stripe`) {
await stripe.webhookEndpoints.del(webhook.id);
break;
}
}
}

Deletion Rules:

  1. Billing Webhook: Deleted immediately when billing app disconnected
  2. Review Webhook: Only deleted if NO other accounts use review app
  3. Reason: Review webhook is shared across all accounts using review feature

๐Ÿ”€ Integration Pointsโ€‹

Webhook Handlers (External to Stripe Integration)โ€‹

Billing Webhook Handlerโ€‹

  • Location: internal/api/v1/billing/webhooks/stripe.js
  • Purpose: Process all billing-related Stripe events
  • Signature Verification: Uses stripe.webhooks.constructEvent()
  • Event Processing:
    • Creates/updates records in billing collections
    • Syncs Stripe data to local database
    • Triggers notifications for payment failures

Review Webhook Handlerโ€‹

  • Location: internal/api/v1/reputation/webhooks/stripe.js
  • Purpose: Trigger automatic review requests after successful payments
  • Signature Verification: Validates webhook authenticity
  • Event Processing:
    • Listens for charge.succeeded events
    • Extracts customer email from charge
    • Triggers automated review request email

Internal Servicesโ€‹

  • Billing Module: Receives webhook events, updates billing data
  • Reputation Module: Receives charge events, sends review requests
  • Queue Manager: May process webhook events asynchronously

๐Ÿงช Webhook Signature Verificationโ€‹

Security Pattern:

Stripe sends a signature in the Stripe-Signature header to verify webhook authenticity.

Verification Code Example:

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

// In webhook handler
const sig = req.headers['stripe-signature'];
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}

// Process verified event
switch (event.type) {
case 'charge.succeeded':
// Handle charge succeeded
break;
// ... other event types
}

Webhook Secret:

  • Retrieved from Stripe webhook endpoint details
  • Stored as environment variable: STRIPE_WEBHOOK_SECRET
  • Used to verify webhook signatures

๐Ÿงช Edge Cases & Special Handlingโ€‹

Shared Review Webhookโ€‹

Issue: Multiple DashClicks accounts may use the review app simultaneously
Handling:

  • Review webhook is registered once for the entire platform
  • Webhook is NOT deleted when one account disconnects review app
  • Only deleted when NO accounts have review app connected
  • Prevents breaking review functionality for other accounts

Check Logic:

const isReviewConnected = await StripeModel.findOne({
connected_apps: 'review',
});
if (!isReviewConnected) {
// Safe to delete
}

Webhook Already Existsโ€‹

Issue: Webhook may already be registered (re-connection scenario)
Handling:

  • Check existing webhooks before creating
  • Update existing webhook instead of creating duplicate
  • Ensure webhook is enabled (not disabled)

Billing Webhook Cleanupโ€‹

Issue: Billing data must be deleted when disconnecting
Handling:

  1. Delete all billing collections data
  2. Remove webhook endpoint from Stripe
  3. Ensures no orphaned webhooks or data

Webhook Creation Failureโ€‹

Issue: Webhook registration may fail (network, permissions)
Handling:

  • Error logged but does NOT block authentication
  • Connection succeeds even if webhook fails
  • Manual webhook setup may be required
  • User notified of webhook setup failure

โš ๏ธ Important Notesโ€‹

  • ๐Ÿ” Signature Verification: Always verify webhook signatures to prevent spoofing
  • ๐Ÿ’ฐ Billing Impact: Webhooks are free but process billable events
  • โฑ๏ธ Event Delivery: Stripe retries failed webhook deliveries automatically
  • ๐Ÿ”„ Idempotency: Webhook handlers should be idempotent (handle duplicate events)
  • ๐Ÿ“ Logging: All webhook creation/deletion logged with initiator
  • ๐Ÿšจ Error Handling: Webhook creation failures are logged but non-blocking
  • ๐Ÿ”— Shared Webhooks: Review webhook shared across all accounts
  • ๐ŸŽฏ Event Filtering: Handlers must filter events by stripe_user_id

๐Ÿ“Š Webhook Event Typesโ€‹

Billing Webhook Eventsโ€‹

Payment Events:

  • charge.succeeded - Payment completed successfully
  • charge.failed - Payment failed
  • charge.refunded - Charge refunded
  • charge.dispute.created - Dispute opened
  • charge.dispute.closed - Dispute resolved

Customer Events:

  • customer.created - New customer created
  • customer.updated - Customer details updated
  • customer.deleted - Customer removed

Subscription Events:

  • customer.subscription.created - New subscription
  • customer.subscription.updated - Subscription modified
  • customer.subscription.deleted - Subscription canceled
  • customer.subscription.trial_will_end - Trial ending soon

Invoice Events:

  • invoice.created - New invoice generated
  • invoice.updated - Invoice modified
  • invoice.paid - Invoice paid
  • invoice.payment_failed - Invoice payment failed
  • invoice.finalized - Invoice finalized

Review Webhook Eventsโ€‹

Enabled Events:

  • charge.succeeded - Payment completed (triggers review request)

๐Ÿ”ง Testing Webhooksโ€‹

Stripe CLI Testing:

# Install Stripe CLI
stripe login

# Forward webhooks to local development
stripe listen --forward-to localhost:5003/v1/billing/webhooks

# Trigger test events
stripe trigger charge.succeeded
stripe trigger customer.subscription.created
stripe trigger invoice.payment_failed

Manual Testing:

  1. Create test account in Stripe Dashboard
  2. Register webhook endpoints in test mode
  3. Trigger events via Stripe Dashboard
  4. Monitor webhook delivery in Stripe Dashboard logs


๐ŸŽฏ Webhook Setup Checklistโ€‹

Before Using:

  • Set API_BASE_URL environment variable
  • Ensure webhook endpoints are publicly accessible
  • Set up STRIPE_WEBHOOK_SECRET for signature verification
  • Configure firewall to allow Stripe IPs

When Connecting:

  • Verify webhook creation in Stripe Dashboard
  • Check webhook status is "enabled"
  • Test webhook delivery with test events
  • Monitor webhook logs for errors

After Connection:

  • Verify events are processed correctly
  • Check billing data syncs in real-time
  • Monitor review request triggers
  • Set up alerting for webhook failures

๐Ÿ“ˆ Webhook Monitoringโ€‹

Key Metrics:

  • Webhook delivery success rate
  • Event processing latency
  • Failed webhook deliveries
  • Duplicate event handling

Monitoring Tools:

  • Stripe Dashboard webhook logs
  • DashClicks application logs
  • Database query monitoring
  • Error tracking (Sentry, etc.)

Alerting Scenarios:

  • Webhook endpoint unreachable
  • High failure rate (> 5%)
  • Signature verification failures
  • Event processing errors
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM