Stripe Local Setup
Step-by-step guide to get Stripe payments working in your local environment.
Prerequisites
Section titled “Prerequisites”- Backend containers running (
./vendor/bin/sail up -d) - Database migrated (
./vendor/bin/sail artisan migrate) - A Stripe account in test mode
1. Install Stripe CLI
Section titled “1. Install Stripe CLI”The Stripe CLI forwards webhook events from Stripe to your local backend.
# macOSbrew install stripe/stripe-cli/stripe
# Linux (Debian/Ubuntu)curl -s https://packages.stripe.dev/api/security/keypair/stripe-cli-gpg/public | gpg --dearmor | sudo tee /usr/share/keyrings/stripe.gpgecho "deb [signed-by=/usr/share/keyrings/stripe.gpg] https://packages.stripe.dev/stripe-cli-debian-local stable main" | sudo tee -a /etc/apt/sources.list.d/stripe.listsudo apt update && sudo apt install stripeAfter installing, authenticate:
stripe login2. Configure Environment Variables
Section titled “2. Configure Environment Variables”Backend (backend/.env)
Section titled “Backend (backend/.env)”Get your API keys from the Stripe Dashboard:
# Stripe API keys (test mode)STRIPE_KEY=pk_test_... # Publishable keySTRIPE_SECRET=sk_test_... # Secret keySTRIPE_WEBHOOK_SECRET=whsec_... # From `stripe listen` output (step 3)
# Laravel Cashier settingsCASHIER_CURRENCY=eurCASHIER_CURRENCY_LOCALE=es_ESFrontend (frontend/.env)
Section titled “Frontend (frontend/.env)”STRIPE_PUBLISHABLE_KEY=pk_test_... # Same publishable key as STRIPE_KEY aboveThe frontend reads this server-side via Astro’s env schema and passes it as an SSR prop — it is never bundled into client JavaScript directly.
3. Forward Webhooks Locally
Section titled “3. Forward Webhooks Locally”Start the Stripe CLI listener in a dedicated terminal:
stripe listen --forward-to localhost/api/webhooks/stripeThis outputs a webhook signing secret (whsec_...). Copy it to STRIPE_WEBHOOK_SECRET in your backend .env. The secret changes each time you restart stripe listen.
Keep stripe listen running while developing payment features. The backend webhook endpoint (/api/webhooks/stripe) handles these events:
| Event | Action |
|---|---|
payment_intent.succeeded | Marks payment as succeeded |
payment_intent.payment_failed | Marks payment as failed |
payment_intent.requires_action | Flags payment for 3DS |
payment_intent.canceled | Marks payment as canceled |
checkout.session.completed | Creates payment record from Checkout Session |
4. Verify Payment Data Migration
Section titled “4. Verify Payment Data Migration”Payment gateway and method configuration is seeded automatically via a data migration that runs during artisan migrate. After migrating, verify the data exists:
./vendor/bin/sail artisan tinker --execute="App\Models\PaymentGateway::count();"./vendor/bin/sail artisan tinker --execute="App\Models\PaymentMethod::count();"./vendor/bin/sail artisan tinker --execute="App\Models\MarketPaymentMethod::count();"Expected output:
- Gateways: 3 (stripe active, adyen/redsys inactive)
- Methods: 5 (card/apple_pay/google_pay active, sepa_debit/bank_transfer inactive)
- Market links: One per active market, each linked to Stripe with card/apple_pay/google_pay
If data is missing (e.g., you rolled back migrations), re-run:
./vendor/bin/sail artisan migrateThe migration (seed_payment_system_data) is idempotent — it uses upsert/updateOrInsert and won’t duplicate records.
5. Test the Integration
Section titled “5. Test the Integration”Verify webhook connectivity
Section titled “Verify webhook connectivity”stripe trigger payment_intent.succeededCheck the backend log or Telescope for the incoming webhook event.
Test card numbers
Section titled “Test card numbers”| Card Number | Scenario |
|---|---|
4242 4242 4242 4242 | Successful payment |
4000 0025 0000 3155 | Requires 3DS authentication |
4000 0000 0000 9995 | Declined (insufficient funds) |
4000 0000 0000 0002 | Declined (generic) |
Use any future expiry date, any 3-digit CVC, and any postal code.
See the full list in Stripe’s testing docs.
Troubleshooting
Section titled “Troubleshooting””Payment method ‘card’ is not enabled for market ‘XX’”
Section titled “”Payment method ‘card’ is not enabled for market ‘XX’””The market_payment_methods table is missing entries for that market. This usually means:
- The market was created after the payment data migration ran, or
- Migrations were rolled back and not re-run
Fix: Re-run the payment data migration or manually seed via Filament admin (Payment Gateways / Payment Methods resources).
Webhook signature verification fails
Section titled “Webhook signature verification fails”- Ensure
STRIPE_WEBHOOK_SECRETmatches the output of your currentstripe listensession - The webhook tolerance is 300 seconds (5 minutes) — check that your system clock is accurate
- Restart
stripe listenand update the secret if it changed
Payments not appearing in admin panel
Section titled “Payments not appearing in admin panel”- Verify
stripe listenis running and forwarding to the correct URL - Check Telescope for incoming webhook requests at
/api/webhooks/stripe - Ensure your Stripe Dashboard is in test mode (toggle at the top of the dashboard)
Configuration Reference
Section titled “Configuration Reference”All payment settings live in backend/config/payment.php:
| Config Key | Env Variable | Default | Description |
|---|---|---|---|
default_currency | PAYMENT_DEFAULT_CURRENCY | EUR | Default payment currency |
deposit.balance_days_before | PAYMENT_BALANCE_DAYS_BEFORE | 7 | Days before departure for balance due |
gateways.stripe.key | STRIPE_KEY | — | Stripe publishable key |
gateways.stripe.secret | STRIPE_SECRET | — | Stripe secret key |
gateways.stripe.webhook_secret | STRIPE_WEBHOOK_SECRET | — | Stripe webhook signing secret |
gateways.stripe.currency | CASHIER_CURRENCY | eur | Stripe charge currency |
gateways.stripe.currency_locale | CASHIER_CURRENCY_LOCALE | es_ES | Currency formatting locale |
Related
Section titled “Related”- Payment Gateway System — Architecture, DTOs, and gateway interface
- Checkout API — Frontend checkout flow