Endpoints for receiving webhooks from external services.
Stripe Webhooks
The /webhooks/stripe endpoint receives Stripe events to:
- Automatically credit accounts after successful purchase
- Log individual payments for traceability
⚠️ Security: These endpoints use signature verification instead of standard JWT authentication.
Stripe Webhook - Event reception
Endpoint to receive Stripe webhooks and process payment events.
⚠️ IMPORTANT: Security
This endpoint DOES NOT USE standard JWT authentication. Instead, it uses Stripe signature verification to ensure that requests genuinely come from Stripe.
Signature verification
Each webhook is verified with HMAC SHA256 according to Stripe documentation:
- Extract the timestamp and v1 signature from the
Stripe-Signatureheader - Build the signed payload:
timestamp + "." + payload_body - Compute HMAC SHA256 with the webhook secret (
STRIPE_WEBHOOK_SECRET) - Constant-time comparison with the received signature
- Validate the timestamp (reject webhooks > 5 minutes old)
Stripe-Signature header format:
Code
Replay attack protection
Webhooks with a timestamp too old (> 5 minutes) are rejected to prevent replay attacks.
Handled events
checkout.session.completed
Triggered: When a user completes a payment via Stripe Checkout Session.
Actions:
- Extract metadata (
tenant_id,user_id,package_id) - Idempotency check (avoid double processing)
- Retrieve the package from DB via
stripe_price_id - Credit the user's account
- Create a
purchasetype transaction
Idempotency:
- Check by
payment_intent_idin existing transactions - Check by
session_idin transaction reasons - If already processed, returns 200 OK without action
Payload example:
Code
payment_intent.succeeded
Triggered: When a Stripe PaymentIntent is confirmed successfully.
Actions:
- Logging for individual payment traceability
- No action on credits (handled by
checkout.session.completed)
Usage: Primarily for individual payments (non-credits).
payment_intent.payment_failed
Triggered: When a payment fails.
Actions:
- Logging for traceability
- No action on credits (no deduction)
Stripe Dashboard Configuration
For this endpoint to work, you must configure a webhook in Stripe Dashboard:
- Go to Developers → Webhooks
- Click Add endpoint
- URL:
https://api.faireplace.com/api/webhooks/stripe(or dev URL for testing) - Select the events:
- ✅
checkout.session.completed - ✅
payment_intent.succeeded - ✅
payment_intent.payment_failed
- ✅
- Copy the Signing secret (
whsec_...) and configure it inSTRIPE_WEBHOOK_SECRET
Responses
The endpoint always returns 200 OK for handled or ignored events, per Stripe recommendations. This prevents Stripe from retrying webhooks indefinitely.
Exceptions:
- 401 Unauthorized: Invalid or missing signature
- 400 Bad Request: Invalid payload or missing metadata
- 500 Internal Server Error: Processing error
Request example
Code
Response:
Code
Logs and traceability
All webhooks are logged with:
- Event type
- Stripe Event ID
- Session ID or Payment Intent ID
- Tenant ID and User ID (from metadata)
- Processing result (success, idempotency, error)
Error handling
In case of error during webhook processing:
- The error is logged with all details
- A 500 response is returned to Stripe
- Stripe will automatically retry the webhook (up to 3 attempts)
Important: Validation errors (missing metadata, etc.) return 400 and will not be retried by Stripe.
Headers
Stripe-SignatureStripe signature to verify the authenticity of the webhook.
Format: t=<timestamp>,v1=<signature>
This signature is computed by Stripe using HMAC SHA256 and the webhook secret. It ensures that the webhook genuinely comes from Stripe.
Stripe Webhook - Event reception › Request Body
idUnique identifier of the Stripe event (starts with evt_).
Used for idempotency and traceability.
typeStripe event type.
Supported types:
checkout.session.completed: Payment via Checkout Session completedpayment_intent.succeeded: PaymentIntent confirmed successfullypayment_intent.payment_failed: Payment failed
Event data.
Contains the relevant Stripe object (Checkout Session, PaymentIntent, etc.)
in the object property.
Stripe Webhook - Event reception › Responses
Webhook received and processed successfully.
Always returns 200 OK even for unhandled events, per Stripe recommendations.
receivedIndicates that the webhook has been received.
Always true on success.