Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/KingPsychopath/oooc-fete-finder/llms.txt

Use this file to discover all available pages before exploring further.

OOOC Fête Finder receives partner activation payments through Stripe Payment Links. The webhook integration processes checkout.session.completed events, verifies signatures, extracts custom fields, and enqueues activations in the admin panel.

Configuration

Environment variables

STRIPE_WEBHOOK_SECRET=whsec_...

# Optional: Payment link ID mapping for admin queue labels
STRIPE_PAYMENT_LINK_ID_SPOTLIGHT_STANDARD=plink_...
STRIPE_PAYMENT_LINK_ID_SPOTLIGHT_TAKEOVER=plink_...
STRIPE_PAYMENT_LINK_ID_PROMOTED=plink_...
STRIPE_PAYMENT_LINK_ID_ADDON_WHATSAPP=plink_...
STRIPE_PAYMENT_LINK_ID_ADDON_NEWSLETTER=plink_...
If STRIPE_WEBHOOK_SECRET is not set, the webhook endpoint returns HTTP 503 and logs a warning.

Get webhook secret

1

Create webhook endpoint

  1. Go to Stripe Dashboard
  2. Click Add endpoint
  3. Enter your webhook URL: https://your-domain.com/api/webhooks/stripe
  4. Select event: checkout.session.completed
2

Copy signing secret

After creating the endpoint, Stripe displays the signing secret starting with whsec_.Copy this value.
3

Configure environment

Add the webhook secret to your environment:
STRIPE_WEBHOOK_SECRET=whsec_...

Webhook endpoint

The webhook is handled by POST /api/webhooks/stripe (app/api/webhooks/stripe/route.ts:10-50):
1

Verify signature

Extract stripe-signature header and verify webhook authenticity using HMAC SHA256.
2

Parse payload

Parse JSON payload and extract event type and data.
3

Process event

If event type is checkout.session.completed, extract session details and enqueue activation.
4

Return response

Return HTTP 200 with {ok: true, handled: boolean, inserted: boolean}.

Response codes

  • 200 OK: Webhook processed successfully
  • 400 Bad Request: Invalid signature
  • 500 Internal Server Error: Processing failed
  • 503 Service Unavailable: Webhook not configured

Signature verification

Stripe signs webhooks using HMAC SHA256 with the webhook secret (features/partners/stripe-webhook.ts:51-75).

Signature format

The stripe-signature header contains:
t=1614556800,v1=signature1,v1=signature2
  • t: Unix timestamp of the event
  • v1: HMAC SHA256 signature(s)

Verification algorithm

1

Parse signature header

Extract timestamp and v1 signatures.
2

Check timestamp

Verify event is within 5 minutes (300 seconds) of current time to prevent replay attacks.
3

Compute expected signature

const signedPayload = `${timestamp}.${payload}`;
const expected = createHmac('sha256', secret)
  .update(signedPayload, 'utf8')
  .digest('hex');
4

Compare signatures

Use timing-safe comparison to check if any provided signature matches the expected signature.
Invalid signature: If signature verification fails, the webhook returns HTTP 400 and the event is not processed. Check that STRIPE_WEBHOOK_SECRET matches the value in Stripe Dashboard.

Session data extraction

The integration extracts data from the checkout.session.completed event (features/partners/stripe-webhook.ts:150-201):

Custom fields

Stripe custom fields are extracted with fallback key matching:
// Event name: tries "event_name", "eventname"
const eventName = extractCustomFieldValue(
  customFields,
  ['event_name', 'eventname']
);

// Event URL: tries "event_url", "event_link", "ticket_link", "eventurl"
const eventUrl = extractCustomFieldValue(
  customFields,
  ['event_url', 'event_link', 'ticket_link', 'eventurl']
);
Custom field extraction supports text, dropdown, and numeric field types. Field keys are case-insensitive.
The system maps payment link IDs to package keys for admin queue organization:
Payment Link Env VarPackage Key
STRIPE_PAYMENT_LINK_ID_SPOTLIGHT_STANDARDspotlight-standard
STRIPE_PAYMENT_LINK_ID_SPOTLIGHT_TAKEOVERspotlight-takeover
STRIPE_PAYMENT_LINK_ID_PROMOTEDpromoted-listing
STRIPE_PAYMENT_LINK_ID_ADDON_WHATSAPPaddon-whatsapp
STRIPE_PAYMENT_LINK_ID_ADDON_NEWSLETTERaddon-newsletter
If payment link ID doesn’t match any configured mapping, packageKey is null and the admin must manually categorize the activation.

Extracted fields

The following fields are extracted from the session:
{
  packageKey: string | null,           // Mapped from payment link ID
  paymentLinkId: string | null,        // Payment link ID
  stripeSessionId: string | null,      // Checkout session ID
  customerEmail: string | null,        // Customer email
  customerName: string | null,         // Customer name
  eventName: string | null,            // From custom field or metadata
  eventUrl: string | null,             // From custom field or metadata
  amountTotalCents: number | null,     // Total amount in cents
  currency: string | null,             // Currency code (e.g., "eur")
  metadata: Record<string, unknown>,   // Session metadata
}

Database storage

The webhook handler enqueues activations in the partner_activations table via enqueueFromStripe (features/partners/stripe-webhook.ts:203-233):
await repository.enqueueFromStripe({
  sourceEventId: event.id,           // Stripe event ID (for idempotency)
  packageKey: details.packageKey,
  paymentLinkId: details.paymentLinkId,
  stripeSessionId: details.stripeSessionId,
  customerEmail: details.customerEmail,
  customerName: details.customerName,
  eventName: details.eventName,
  eventUrl: details.eventUrl,
  amountTotalCents: details.amountTotalCents,
  currency: details.currency,
  metadata: details.metadata,
  rawPayload: {
    type: 'checkout.session.completed',
    session,
  },
});

Idempotency

The sourceEventId (Stripe event ID) ensures idempotency. If the same event is received multiple times, only the first insert succeeds.
The database schema includes a unique constraint on source_event_id to prevent duplicate processing.

Admin panel workflow

After webhook processing:
  1. Activation appears in Admin > Partners > Queue
  2. Admin reviews customer details, event info, and package
  3. Admin manually activates the event or requests more information
  4. Activation is marked as complete or rejected

Error handling

The integration handles errors at multiple levels:

Webhook endpoint errors

Webhook not configured (503): STRIPE_WEBHOOK_SECRET is not set in environment.
Invalid signature (400): Signature verification failed. Check that webhook secret matches Stripe Dashboard.
Webhook processing failed (500): Database error or unexpected payload structure. Check server logs for details.

Payload validation

If the payload is missing required fields, the webhook returns {handled: false}:
  • Missing event type
  • Missing event ID
  • Missing or invalid session object

Database errors

If the database is not configured or insert fails, the webhook throws an error and returns HTTP 500. Stripe will retry the webhook.

Testing webhooks

Local testing with Stripe CLI

1

Install Stripe CLI

2

Login to Stripe

stripe login
3

Forward webhooks to localhost

stripe listen --forward-to localhost:3000/api/webhooks/stripe
The CLI displays a webhook signing secret starting with whsec_. Use this for STRIPE_WEBHOOK_SECRET during local development.
4

Trigger test events

stripe trigger checkout.session.completed

Manual testing

Use Stripe Dashboard to send test webhooks:
  1. Go to Stripe Dashboard > Webhooks
  2. Select your webhook endpoint
  3. Click Send test webhook
  4. Choose checkout.session.completed
  5. Optionally edit JSON payload
  6. Click Send test webhook

Security considerations

  • Signature verification: Always verify webhook signature before processing
  • Timing-safe comparison: Use timingSafeEqual to prevent timing attacks
  • Timestamp validation: Reject events older than 5 minutes
  • HTTPS only: Never expose webhook endpoints over HTTP
  • Secret rotation: Rotate webhook secret if compromised

Event types

Currently, the integration only processes:
  • checkout.session.completed - Customer completed checkout
Other event types are acknowledged but not processed:
if (type !== 'checkout.session.completed') {
  return { handled: true, inserted: false };
}
To process additional event types (e.g., payment_intent.succeeded), extend the handleStripeWebhookPayload function in features/partners/stripe-webhook.ts:235-261.

Monitoring

Monitor webhook health in Stripe Dashboard:
  1. Go to Developers > Webhooks
  2. Click on your endpoint
  3. View Recent events and Response codes
  4. Check for failed deliveries
Stripe retries failed webhooks with exponential backoff for up to 3 days.

Build docs developers (and LLMs) love