Skip to main content

Simplified Offering System Guide

Overview

The offering system supports external payment links from any payment provider (Stripe, PayPal, etc.) with generic webhook verification.

How It Works

1. Admin Creates an Offering

  1. Go to Admin Panel → Offerings → "Create New Offering"
  2. Fill in basic information:
    • Name, description, image
    • Select communities to grant access
    • Set a single price (in cents)
    • Choose pricing type (one-time, monthly, yearly)
  3. Important: Add your external payment link
    • This can be from Stripe, PayPal, or any payment service
    • Example: https://buy.stripe.com/test_... or https://paypal.me/...
  4. Save the offering

2. System Generates Webhook URL

When you create an offering, the system automatically generates:

  • Webhook URL: /webhooks/offerings/{offering-id}
  • Webhook Secret: A secure random string for verification

You can find these in the database or by querying the offering.

3. Configure Your Payment Provider

After creating the offering, configure your payment provider's webhook:

For Stripe

  1. Go to Stripe Dashboard → Developers → Webhooks
  2. Add endpoint: https://your-domain.com/webhooks/offerings/{offering-id}
  3. Select events: checkout.session.completed
  4. Add custom header: x-offering-secret: {your-webhook-secret}

For PayPal

  1. Go to PayPal Developer Dashboard → Webhooks
  2. Add webhook URL: https://your-domain.com/webhooks/offerings/{offering-id}
  3. Select events: PAYMENT.SALE.COMPLETED
  4. Add custom header: x-offering-secret: {your-webhook-secret}

For Other Providers

Configure the webhook to POST to the offering webhook URL with the payment details.

4. User Purchase Flow

  1. User browses offerings at /offerings
  2. User clicks "Get Started" on an offering
  3. User clicks "Proceed to Payment"
  4. System creates a pending purchase record
  5. User is redirected to your external payment link
  6. User completes payment on external site
  7. Payment provider sends webhook to our system
  8. System verifies webhook and grants access automatically

Webhook Payload Format

The webhook handler accepts flexible payload formats. Send a POST request with:

Required Fields

{
"status": "succeeded",
"user_id": "uuid",
"user_email": "email",
"purchase_id": "uuid"
}

Optional Fields

{
"payment_id": "external_payment_id",
"external_payment_id": "stripe_payment_intent_id",
"amount": 2500,
"currency": "USD"
}

Webhook Security

The webhook handler verifies requests using the webhook secret. Include it in one of these ways:

  1. Custom Header (Recommended):

    x-offering-secret: your-webhook-secret-here
  2. Authorization Header:

    Authorization: Bearer your-webhook-secret-here

Access Provisioning

When a webhook is verified:

  1. Purchase status is updated to "succeeded"
  2. User is automatically added to all communities in the offering
  3. Access is marked as granted with timestamp
  4. User receives immediate access to all content

Testing

Manual Webhook Testing

curl -X POST https://your-domain.com/webhooks/offerings/{offering-id} \
-H "Content-Type: application/json" \
-H "x-offering-secret: your-webhook-secret" \
-d '{
"status": "succeeded",
"user_id": "user-uuid",
"external_payment_id": "test_payment_123"
}'

Testing Purchase Flow

  1. Create a test offering with a test payment link
  2. As a non-admin user, go to /offerings
  3. Click "Get Started" on the offering
  4. Click "Proceed to Payment"
  5. System creates pending purchase
  6. Manually trigger webhook (as shown above)
  7. Verify access is granted

Troubleshooting

Payment Not Processing

  1. Check webhook logs in your dashboard
  2. Verify webhook secret is correct
  3. Ensure status: "succeeded" is in payload
  4. Check that user_id, user_email, or purchase_id is provided

Access Not Granted

  1. Verify purchase status changed to "succeeded"
  2. Check access_granted_at timestamp
  3. Verify user was added to community
  4. Check function logs for errors

Webhook 404 Error

  • Verify offering ID in webhook URL matches actual offering ID
  • Check that offering exists in database

Webhook 401 Error

  • Verify webhook secret matches
  • Check header format (x-offering-secret or Authorization)

Best Practices

  1. Always test webhooks before going live
  2. Store webhook secrets securely - don't expose them in frontend code
  3. Monitor webhook logs for failed payments
  4. Set up payment provider alerts for webhook failures
  5. Use HTTPS for all webhook endpoints
  6. Keep webhook URLs consistent - don't change offering IDs after setup
  7. Provide clear payment success page on your payment provider
  8. Handle edge cases - duplicate webhooks, delayed webhooks, etc.