Skip to main content
This page covers three related endpoints that manage a user’s subscription lifecycle:

Get subscription status

GET /api/payments/status Returns the authenticated user’s current subscription plan, status, billing period dates, and usage counters.
Authorization
string
required
Bearer token. Format: Bearer <token>.

Response

message
string
Status message.
data
object

Example

curl --request GET \
  --url https://api.hayon.app/api/payments/status \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
Response
{
  "message": "Subscription status",
  "data": {
    "subscription": {
      "plan": "pro",
      "status": "active",
      "stripeCustomerId": "cus_QfT3x9kLmNpR7s",
      "stripeSubscriptionId": "sub_1Oq4AbCdEfGhIjKl",
      "currentPeriodStart": "2025-03-01T00:00:00.000Z",
      "currentPeriodEnd": "2025-04-01T00:00:00.000Z",
      "cancelAtPeriodEnd": false
    },
    "usage": {
      "captionGenerations": 12,
      "postsCreated": 8
    },
    "limits": {
      "maxCaptionGenerations": 100,
      "maxPosts": 500
    },
    "plan": "pro"
  }
}

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.
This action cannot be undone from the API. To reactivate before the period ends, the user must visit the billing portal and remove the scheduled cancellation.
Authorization
string
required
Bearer token. Format: Bearer <token>.

Validation

The endpoint returns 400 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

message
string
Confirmation message on success.

Example

curl --request POST \
  --url https://api.hayon.app/api/payments/cancel \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
Response
{
  "message": "Subscription will be cancelled at the end of the billing period"
}

Stripe webhook

POST /api/payments/webhook
This endpoint is for Stripe only. Do not call it directly. It must be registered in the Stripe Dashboard under Developers → Webhooks.
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 the stripe-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

stripe-signature
string
required
HMAC signature computed by Stripe. The server uses this to verify the payload has not been tampered with.

Handled events

EventEffect
checkout.session.completedUpgrades the user to Pro, stores stripeCustomerId, stripeSubscriptionId, and billing period dates.
invoice.payment_succeededRenews 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_failedMarks the subscription status as pastDue.
customer.subscription.deletedDowngrades the user to the free plan and clears subscription fields.
customer.subscription.updatedSyncs the cancelAtPeriodEnd flag (e.g., when the user toggles cancellation in the billing portal).

Response

Stripe requires a 200 response as quickly as possible to acknowledge receipt. The server returns:
{ "received": true }
Any processing errors result in a 400 response, which causes Stripe to retry the event.

Setup instructions

1

Open the Stripe Dashboard

Go to Developers → Webhooks in your Stripe account.
2

Add endpoint

Set the endpoint URL to https://api.hayon.app/api/payments/webhook (replace with your domain).
3

Select events

Subscribe to: checkout.session.completed, invoice.payment_succeeded, invoice.payment_failed, customer.subscription.deleted, customer.subscription.updated.
4

Copy the signing secret

Copy the Signing secret and set it as the STRIPE_WEBHOOK_SECRET environment variable on your server.

Example (for testing with Stripe CLI)

stripe listen --forward-to https://api.hayon.app/api/payments/webhook

Build docs developers (and LLMs) love