- Get subscription status — read the current plan and usage
- Cancel subscription — schedule cancellation at period end
- Stripe webhook — receive and process Stripe events
Get subscription status
GET /api/payments/status
Returns the authenticated user’s current subscription plan, status, billing period dates, and usage counters.
Bearer token. Format:
Bearer <token>.Response
Status message.
Example
Response
Cancel subscription
POST /api/payments/cancel
Schedules the user’s Pro subscription to cancel at the end of the current billing period. The user keeps Pro access until currentPeriodEnd. After that date, Stripe fires a customer.subscription.deleted event and the account is automatically downgraded to free.
Bearer token. Format:
Bearer <token>.Validation
The endpoint returns400 in the following cases:
- The user is not on the Pro plan.
- No Stripe Subscription ID is on file.
- The subscription is already scheduled for cancellation.
Response
Confirmation message on success.
Example
Response
Stripe webhook
POST /api/payments/webhook
Receives and processes Stripe event notifications. Stripe uses this endpoint to keep the database in sync with billing state changes.
Signature verification
The endpoint reads the raw request body (not parsed JSON) to verify thestripe-signature header using your STRIPE_WEBHOOK_SECRET. Requests that fail signature verification are rejected with 400 Bad Request.
The route uses
express.raw({ type: 'application/json' }) instead of the global express.json() middleware. Never proxy or transform this request body before it reaches the handler — doing so will break signature verification.Required headers
HMAC signature computed by Stripe. The server uses this to verify the payload has not been tampered with.
Handled events
| Event | Effect |
|---|---|
checkout.session.completed | Upgrades the user to Pro, stores stripeCustomerId, stripeSubscriptionId, and billing period dates. |
invoice.payment_succeeded | Renews the billing period dates and resets usage counters on each monthly auto-renewal. Skipped for the first invoice (handled by checkout.session.completed). |
invoice.payment_failed | Marks the subscription status as pastDue. |
customer.subscription.deleted | Downgrades the user to the free plan and clears subscription fields. |
customer.subscription.updated | Syncs the cancelAtPeriodEnd flag (e.g., when the user toggles cancellation in the billing portal). |
Response
Stripe requires a200 response as quickly as possible to acknowledge receipt. The server returns:
400 response, which causes Stripe to retry the event.
Setup instructions
Add endpoint
Set the endpoint URL to
https://api.hayon.app/api/payments/webhook (replace with your domain).Select events
Subscribe to:
checkout.session.completed, invoice.payment_succeeded, invoice.payment_failed, customer.subscription.deleted, customer.subscription.updated.