All docs
4 min read

Stripe billing

Formspring charges teams for paid plans through a Stripe subscription layer. This is a platform-level integration: one Stripe account per Formspring environment, not per team.

Distinct from the Stripe customer driver which mirrors form leads as Stripe customers per form.

What you need

  • A Stripe account.
  • Permission to view API keys + create webhook endpoints.
  • Products + Prices created in Stripe matching the plans configured in the billing config.

Step 1 - Get API keys

  1. Go to https://dashboard.stripe.com/apikeys.
  2. Copy:
    • Publishable key - pk_test_ or pk_live_.
    • Secret key - sk_test_ or sk_live_.

For initial setup, use test mode keys. Swap to live only when ready to charge real cards.

Step 2 - Create the webhook endpoint

The platform expects Stripe to forward subscription events to Formspring.

  1. https://dashboard.stripe.com/webhooks+ Add endpoint.
  2. Endpoint URL: https://formspring.io/spark/webhook (the default mount path; check the billing config and routes for the active value).
  3. Events to send: select the canonical subscription set:
text
customer.created
customer.updated
customer.deleted
customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
customer.subscription.trial_will_end
invoice.payment_succeeded
invoice.payment_action_required
invoice.payment_failed
customer.tax_id.created
customer.tax_id.deleted
checkout.session.completed
  1. Add endpoint.
  2. On the endpoint page, reveal Signing secret - whsec_.... Copy it.

Step 3 - Configure Formspring env

env
# config/services.php → stripe block
STRIPE_KEY=pk_test_...
STRIPE_SECRET=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# Currency / locale for invoice formatting
CASHIER_CURRENCY=eur                 # or usd, etc.
CASHIER_CURRENCY_LOCALE=de_DE

# Billing model wiring
SPARK_BILLABLE_TYPE=team

Restart the app after changing.

Step 4 - Create products and prices in Stripe

  1. https://dashboard.stripe.com/products+ Add product.
  2. Create one product per plan (Free, Pro, Team, etc. - match the id keys in config/plans.php).
  3. For each product, add monthly and yearly prices.
  4. Copy the price IDs (price_...).

Step 5 - Wire prices into config/plans.php

Open config/plans.php. Each plan entry has a stripe_monthly and stripe_yearly slot:

php
'team' => [
    'name' => 'Team',
    'stripe_monthly' => 'price_1Abc...',
    'stripe_yearly'  => 'price_2Def...',
    // ... features, limits ...
],

After editing, clear the cached config and restart.

Step 6 - Smoke test

  1. Visit /billing (or wherever the billing portal mounts).
  2. Subscribe to a paid plan with Stripe's test card 4242 4242 4242 4242, any future expiry, any CVC.
  3. Confirm:
    • The team's subscription row appears in the database.
    • Stripe shows the customer + subscription.
    • Webhook logs (Stripe Dashboard → your webhook → Webhook attempts) show 200 on each event.

Where the credential lives

  • Server: .env → loaded by the Stripe service config plus the billing config files.
  • Wiring: app/Providers/SparkServiceProvider.php.

Going live

  1. Switch STRIPE_* env vars from _test_ to _live_.
  2. Re-create the webhook endpoint in Live mode (Stripe separates test/live webhooks).
  3. Update STRIPE_WEBHOOK_SECRET to the live whsec_....
  4. Re-create products + prices in Live mode (test products don't carry over).
  5. Update config/plans.php to live price IDs.
  6. Cache the production config.
  7. Run a real test charge (refund yourself afterward).

Security

  • Restrict access to the Stripe Dashboard with two-factor required + workflow approvals on changes to webhook endpoints.
  • The webhook secret prevents replay/forgery - never log Stripe webhook bodies in plaintext (the billing layer handles signature verification correctly).
  • For multi-region: the billing layer works fine across Stripe accounts per region; Formspring isn't multi-Stripe-account out of the box.

Troubleshooting

Symptom Cause
Webhook attempts show 400 in Stripe Signing secret mismatch. Re-check STRIPE_WEBHOOK_SECRET matches the endpoint's secret.
Webhook attempts show 419 CSRF middleware is hitting /spark/webhook. Confirm the billing webhook route is excluded from CSRF (default config does this; verify if customised).
Subscription created in Stripe but not on the team Check the application log - billing handler exceptions surface there.
Test card declines Use 4242 4242 4242 4242 for clean success; other Stripe test cards trigger specific failures (https://stripe.com/docs/testing).

Provider docs