Skip to main content

Overview

The HTTP Endpoints provide essential web services including Stripe webhook processing for automatic order creation, health monitoring, and configuration retrieval for client applications. Location: convex/http.ts

Stripe Webhook

Handles Stripe webhook events for automatic payment processing and order creation.
endpoint
string
POST /stripe/webhook
stripe-signature
string
required
Stripe webhook signature header for verification

Supported Events

Payment Succeeded

Event: payment_intent.succeeded
Action: Creates order from cart items

Payment Failed

Event: payment_intent.payment_failed
Action: Logs failure and notifies customer

Payment Canceled

Event: payment_intent.canceled
Action: Cleans up resources and preserves cart

Webhook Processing Flow

1

Signature Verification

Validates Stripe webhook signature for security
2

Event Parsing

Extracts payment data and metadata from webhook payload
3

Order Processing

Creates orders for successful payments or handles failures
4

Cart Cleanup

Removes items from customer cart after successful order creation
5

Notifications

Sends confirmation notifications to customers and stores
// This runs automatically when Stripe sends webhook events
// Location: convex/http.ts

export default httpRouter;

httpRouter.route({
  path: "/stripe/webhook",
  method: "POST",
  handler: async (request: Request) => {
    const signature = request.headers.get("stripe-signature");
    const body = await request.text();

    try {
      // Verify webhook signature
      const event = stripe.webhooks.constructEvent(
        body,
        signature!,
        process.env.STRIPE_WEBHOOK_SECRET!
      );

      switch (event.type) {
        case 'payment_intent.succeeded':
          // Automatic order creation
          const paymentIntent = event.data.object;
          const metadata = paymentIntent.metadata;
          
          // Create order from cart
          await convex.mutation("customers.cartToOrder.createOrderFromCart", {
            customerId: metadata.customerId,
            storeId: metadata.storeId,
            deliveryAddress: JSON.parse(metadata.deliveryAddress),
            deliveryType: metadata.deliveryType,
            deliveryFee: parseFloat(metadata.deliveryFee),
            paymentMethod: "card",
            paymentIntentId: paymentIntent.id,
            paymentStatus: "paid"
          });
          
          break;

        case 'payment_intent.payment_failed':
          // Handle payment failure
          console.error('Payment failed:', event.data.object);
          break;

        case 'payment_intent.canceled':
          // Handle payment cancellation
          console.log('Payment canceled:', event.data.object);
          break;
      }

      return new Response("Webhook processed", { status: 200 });
    } catch (error) {
      console.error('Webhook error:', error);
      return new Response("Webhook failed", { status: 400 });
    }
  }
});

Health Check

Simple health check endpoint for monitoring and uptime verification.
endpoint
string
GET /health
curl https://your-deployment.convex.cloud/health
Twigz Backend is healthy!

Stripe Configuration

Retrieve Stripe publishable key for client-side integration.
endpoint
string
GET /stripe/config
const config = await fetch('https://your-deployment.convex.cloud/stripe/config');
const { publishableKey } = await config.json();

// Use with Stripe.js
import { loadStripe } from '@stripe/stripe-js';
const stripe = await loadStripe(publishableKey);
{
  "publishableKey": "pk_test_51234567890abcdef..."
}

Webhook Security

All webhooks are verified using Stripe’s signature verification:
const signature = request.headers.get("stripe-signature");
const event = stripe.webhooks.constructEvent(
  body,
  signature!,
  process.env.STRIPE_WEBHOOK_SECRET!
);
Webhook events are processed idempotently to handle retries:
// Check if order already exists for this payment
const existingOrder = await convex.query("customers.orders.getOrderByPaymentIntent", {
  paymentIntentId: paymentIntent.id
});

if (existingOrder) {
  return new Response("Order already processed", { status: 200 });
}
Robust error handling for webhook processing:
try {
  await processWebhookEvent(event);
  return new Response("Success", { status: 200 });
} catch (error) {
  console.error('Webhook processing failed:', error);
  // Return 500 to trigger Stripe retry
  return new Response("Processing failed", { status: 500 });
}

Webhook Testing

# Install Stripe CLI
# macOS
brew install stripe/stripe-cli/stripe

# Login to Stripe
stripe login

# Forward webhooks to local development
stripe listen --forward-to localhost:3000/stripe/webhook

# In another terminal, trigger test events
stripe trigger payment_intent.succeeded
stripe trigger payment_intent.payment_failed

Monitoring & Observability

Health Monitoring

Use the /health endpoint for uptime monitoring and load balancer health checks

Webhook Logs

Monitor webhook processing in Convex logs for debugging payment issues

Error Tracking

Failed webhooks are logged with full context for troubleshooting

Performance Metrics

Track webhook processing times and success rates

CORS Configuration

The endpoints include proper CORS headers for web applications:
// CORS headers included in responses
const headers = {
  'Content-Type': 'application/json',
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization, stripe-signature'
};

Error Responses

Status Code: 400
{
  "error": "Invalid webhook signature"
}
Status Code: 200 (Success, but ignored)
{
  "message": "Event type not supported",
  "eventType": "customer.created"
}
Status Code: 500
{
  "error": "Webhook processing failed",
  "eventId": "evt_123456789"
}
Status Code: 503
{
  "error": "Service temporarily unavailable",
  "retryAfter": 30
}
Webhook endpoints are automatically secured and include signature verification. Failed webhook processing triggers Stripe’s automatic retry mechanism.
Use the Stripe CLI during development to test webhook integration locally. Always verify webhook signatures in production for security.