Stripe Integration Setup

Choose the setup guide that matches how you're using Event Schedule.

Overview

Event Schedule supports Stripe for payment processing, but the setup varies depending on how you're using the platform. This guide is organized by user type to help you find exactly what you need.

Recommended: Use Invoice Ninja with Stripe

For the best ticket selling experience, we recommend using Invoice Ninja alongside Stripe. Invoice Ninja provides additional features like professional invoicing, payment reminders, and detailed financial reporting that complement Stripe's payment processing.

Choose Your Setup

Find your setup type and follow the corresponding guide:

You are... Money flows to... Follow this guide
Running your own Event Schedule instance for your organization Your single Stripe account Selfhosted Users
Running a white-label SaaS platform Your customers' Stripe accounts + your account for subscriptions SaaS Operators

Decision Tree

Q: Will multiple people create events and need their own payment accounts?

→ Yes: SaaS Operators

→ No: Selfhosted Users

For Selfhosted Users

If you're running your own Event Schedule instance for your organization, venue, or community, all ticket payments go to a single Stripe account that you control.

This guide is for you if...

You want all ticket revenue from all events on your instance to go to one Stripe account. Event creators don't need their own Stripe accounts.

1. Get Your Stripe API Keys

  1. Go to the Stripe Dashboard
  2. Navigate to DevelopersAPI keys
  3. Note your Publishable key and Secret key

2. Configure Environment Variables

Add these to your .env file:

.env
# Stripe Direct Payments (selfhosted)
STRIPE_PLATFORM_KEY=pk_live_your_publishable_key
STRIPE_PLATFORM_SECRET=sk_live_your_secret_key
STRIPE_PLATFORM_WEBHOOK_SECRET=whsec_your_webhook_secret
  • STRIPE_PLATFORM_KEY: Your publishable key (starts with pk_live_ or pk_test_)
  • STRIPE_PLATFORM_SECRET: Your secret key (starts with sk_live_ or sk_test_)
  • STRIPE_PLATFORM_WEBHOOK_SECRET: Webhook signing secret (next step)

3. Set Up Webhooks

  1. In Stripe Dashboard, go to DevelopersWebhooks
  2. Click Add endpoint
  3. Set URL to: https://yourdomain.com/stripe/webhook
  4. Select event: checkout.session.completed
  5. Save and copy the Signing secret to STRIPE_PLATFORM_WEBHOOK_SECRET

Important: Webhook Event

Make sure to select checkout.session.completed—this is different from SaaS setups which use payment_intent.succeeded.

4. Enable Stripe for Events

Once configured, event creators can select "Stripe" as the payment method when creating events with tickets. All payments automatically use your platform Stripe account.

How Checkout Works

  1. Customer selects tickets and fills out the checkout form
  2. Event Schedule creates a Stripe Checkout Session
  3. Customer completes payment on Stripe's hosted page
  4. Webhook confirms payment and marks the sale as paid
  5. Customer receives their tickets

For SaaS Operators

If you're running your own white-label SaaS platform (like eventschedule.com but with your own branding), you need two Stripe integrations:

Stripe Connect

So your event creator customers can connect their Stripe accounts and receive ticket payments directly.

Laravel Cashier

To charge your customers for Pro subscriptions (the money you make as the platform operator).

Part A: Stripe Connect (Ticket Sales)

Allow your event creators to receive payments for their ticket sales

1. Enable Stripe Connect

  1. Go to the Stripe Dashboard
  2. Navigate to SettingsConnectSettings
  3. Enable Connect for your platform
  4. Configure your branding and platform profile
  5. Get your API keys from DevelopersAPI keys

2. Environment Configuration

.env
# Stripe Connect (for event creators to receive ticket payments)
STRIPE_KEY=sk_live_your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=whsec_your_connect_webhook_secret
  • STRIPE_KEY: Your platform's Stripe secret key
  • STRIPE_WEBHOOK_SECRET: Webhook secret for Connect events

3. Webhook Configuration

  1. Go to DevelopersWebhooks
  2. Click Add endpoint
  3. Set URL to: https://yourdomain.com/stripe/webhook
  4. Select event: payment_intent.succeeded
  5. Save and copy the signing secret to STRIPE_WEBHOOK_SECRET

User Onboarding Flow

Your event creators will:

  1. Go to ProfilePayment Methods
  2. Click "Connect Stripe Account"
  3. Complete Stripe's onboarding
  4. Return to your platform ready to sell tickets

Connect API Endpoints

Endpoint Description
GET /stripe/link Start Stripe Connect onboarding
GET /stripe/complete Complete onboarding callback
GET /stripe/unlink Disconnect Stripe account
POST /stripe/webhook Handle payment_intent.succeeded

Part B: Laravel Cashier (Subscription Billing)

Charge your customers for Pro plan subscriptions

1. Create Subscription Products

  1. In Stripe Dashboard, go to ProductsAdd product
  2. Create a "Pro Plan" product with:
    • Monthly price (e.g., $9.99/month, recurring)
    • Yearly price (e.g., $99.99/year, recurring)
  3. Note the Price IDs (starts with price_)

2. Environment Configuration

.env
# Laravel Cashier (for subscription billing)
STRIPE_PLATFORM_KEY=pk_live_your_publishable_key
STRIPE_PLATFORM_SECRET=sk_live_your_secret_key
STRIPE_PLATFORM_WEBHOOK_SECRET=whsec_your_subscription_webhook_secret
STRIPE_PRICE_MONTHLY=price_monthly_price_id
STRIPE_PRICE_YEARLY=price_yearly_price_id
  • STRIPE_PLATFORM_KEY: Publishable key for your platform
  • STRIPE_PLATFORM_SECRET: Secret key for your platform
  • STRIPE_PLATFORM_WEBHOOK_SECRET: Webhook secret for subscription events
  • STRIPE_PRICE_MONTHLY: Price ID for monthly subscription
  • STRIPE_PRICE_YEARLY: Price ID for yearly subscription

3. Subscription Webhook

  1. Go to DevelopersWebhooks
  2. Click Add endpoint (this is a second webhook, separate from Connect)
  3. Set URL to: https://yourdomain.com/stripe/subscription-webhook
  4. Select events:
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • customer.subscription.trial_will_end
    • invoice.payment_succeeded
    • invoice.payment_failed
  5. Save and copy signing secret to STRIPE_PLATFORM_WEBHOOK_SECRET

4. Customer Portal Setup

  1. Go to SettingsBillingCustomer portal
  2. Enable subscription management features:
    • Subscription cancellation
    • Plan switching
    • Payment method updates
  3. Customize branding to match your platform

Subscription API Endpoints

Endpoint Description
GET /{subdomain}/subscribe Show subscription page
POST /{subdomain}/subscribe Create new subscription
GET /{subdomain}/subscription/portal Redirect to Stripe Customer Portal
POST /{subdomain}/subscription/cancel Cancel subscription
POST /{subdomain}/subscription/resume Resume cancelled subscription
POST /{subdomain}/subscription/swap Switch between monthly/yearly
POST /stripe/subscription-webhook Handle subscription events

Architecture Note

Laravel Cashier uses the Role model (schedule/calendar) as the billable entity, not User. This means each schedule has its own subscription, and users can have multiple schedules with different plans.

Testing

Test Mode Setup

For development and testing:

  1. Toggle to "Test mode" in the Stripe Dashboard
  2. Use test API keys (starting with pk_test_ and sk_test_)
  3. Create test webhook endpoints pointing to your development environment

Test Card Numbers

Card Number Result
4242 4242 4242 4242 Successful payment
4000 0000 0000 3220 3D Secure required
4000 0000 0000 9995 Declined (insufficient funds)
4000 0000 0000 0002 Declined (generic)

Use any future expiration date and any 3-digit CVC.

Testing Webhooks Locally

Use the Stripe CLI to forward webhooks:

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

# Login to your Stripe account
stripe login

# Forward webhooks (selfhosted or Connect)
stripe listen --forward-to localhost:8000/stripe/webhook

# For SaaS: Forward subscription webhooks (separate terminal)
stripe listen --forward-to localhost:8000/stripe/subscription-webhook

The CLI displays a webhook signing secret to use in your .env file.

Trigger Test Events

bash
# Test checkout completion (selfhosted)
stripe trigger checkout.session.completed

# Test payment success (Connect)
stripe trigger payment_intent.succeeded

# Test subscription creation
stripe trigger customer.subscription.created

Troubleshooting

Common Issues

"Stripe account not connected" error

Applies to: SaaS operators (Connect)

  • User needs to complete Stripe Connect onboarding
  • Check if stripe_account_id and stripe_completed_at are set on the user

"Invalid signature" webhook error

Applies to: All setups

  • Verify the webhook secret matches the one in Stripe Dashboard
  • Make sure you're using the correct secret for each endpoint:
    • STRIPE_WEBHOOK_SECRET for Connect webhooks
    • STRIPE_PLATFORM_WEBHOOK_SECRET for selfhosted/subscription webhooks

Payments not being recorded

Applies to: Selfhosted users, SaaS operators

  • Check webhook logs in Stripe Dashboard → Webhooks → View logs
  • Verify the webhook endpoint is accessible (not blocked by firewall)
  • Check storage/logs/laravel.log for errors

Subscription not updating after payment

Applies to: SaaS operators

  • Verify webhook events are being received
  • Check that all required subscription events are selected in Stripe
  • Review storage/logs/laravel.log for errors

"No such price" error

Applies to: SaaS operators

  • Verify STRIPE_PRICE_MONTHLY and STRIPE_PRICE_YEARLY contain valid Price IDs
  • Ensure the prices exist in the same Stripe mode (test vs live)

Debugging Logs

  • Application: storage/logs/laravel.log
  • Stripe API: Dashboard → Developers → Logs
  • Webhooks: Dashboard → Developers → Webhooks → Select endpoint → View logs

Security

  1. API Key Security: Never expose secret keys in client-side code or version control. Use environment variables.
  2. Webhook Verification: Always verify webhook signatures—Event Schedule does this automatically.
  3. HTTPS Required: Stripe requires HTTPS for webhook endpoints in production.
  4. PCI Compliance: Using Stripe Checkout and Elements keeps you out of PCI scope—card data never touches your server.