Billing & Stripe
Setting up Stripe, and how subscriptions reach Pingi.
Pingi sells plans through Stripe. Online billing stays switched off until Stripe is configured; until then every workspace uses its trial or the default plan, and the Billing page shows plans and usage only.
What lives where
| Decided in Stripe | Decided in Pingi |
|---|---|
| Prices and currencies | Which plans exist and their limits (config/pingi.php) |
| Trials on paid plans, coupons, tax | Which Stripe price belongs to which plan and interval |
| Proration, cancellation (immediately or at period end) | What each subscription status grants |
| Payment retries and dunning emails | |
| Invoices and receipts |
Pingi never sees card data. Customers pay on Stripe Checkout and manage payment methods, invoices, plan changes and cancellation in the Stripe Customer Portal.
Setup
- In Stripe, create a product and recurring price(s) per paid plan.
- Set the keys in
.env:STRIPE_SECRET— secret API key (sk_live_…/sk_test_…)STRIPE_WEBHOOK_SECRET— signing secret of the webhook endpoint belowSTRIPE_PRICE_<PLAN>_MONTHLY/_YEARLY— price ids (price_…)
- Copy the price ids into the database:
php artisan db:seed --class=ReferenceDataSeeder - In Stripe, add a webhook endpoint
https://<your-domain>/api/stripe/webhooksending these events:customer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedcustomer.subscription.pausedcustomer.subscription.resumed
- Configure the Customer Portal in Stripe (which plans customers may switch between, cancellation behaviour).
Only plans with a price id show a Monthly / Yearly button, and only to workspace owners.
How subscriptions reach Pingi
The redirect back from Checkout changes nothing by itself. A plan changes only when Stripe's signed webhook arrives:
- The signature (
Stripe-Signature) is checked with the webhook secret, and requests older than 5 minutes are rejected. - Each event id is applied at most once, and an event older than the last one applied to a subscription is ignored (Stripe does not guarantee order).
- The workspace is found by the Stripe customer id Pingi stored at checkout — never by metadata in the event.
- A price Pingi doesn't know is logged and ignored, never guessed.
Access by status
| Stripe status | Plan limits apply? |
|---|---|
trialing, active |
Yes |
past_due |
Yes, while Stripe retries the payment |
unpaid, paused, incomplete, incomplete_expired, canceled |
No — the workspace falls back to the default plan |
When a workspace falls back to a smaller plan, existing monitors, members and status pages are kept; adding more is blocked until usage is within the limit.
Open decision: after a downgrade, existing monitors keep their check interval even if it is faster than the new plan allows (limits are enforced when something is added or edited). Whether to slow them down automatically, pause the excess, or keep them is a product decision still to be made.