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

  1. In Stripe, create a product and recurring price(s) per paid plan.
  2. Set the keys in .env:
    • STRIPE_SECRET — secret API key (sk_live_… / sk_test_…)
    • STRIPE_WEBHOOK_SECRET — signing secret of the webhook endpoint below
    • STRIPE_PRICE_<PLAN>_MONTHLY / _YEARLY — price ids (price_…)
  3. Copy the price ids into the database: php artisan db:seed --class=ReferenceDataSeeder
  4. In Stripe, add a webhook endpoint https://<your-domain>/api/stripe/webhook sending these events:
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • customer.subscription.paused
    • customer.subscription.resumed
  5. 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.