Stripe Payments & Subscriptions
Subscription Overview
The Gova backend integrates with Stripe to provide a tiered subscription model. Currently, the system supports two tiers:
- FREE: The default tier for all new users.
- PRO: A paid tier that unlocks higher limits, such as an increased number of moderators.
Payment processing is handled via Stripe Checkout, and user tiers are automatically updated and maintained through Stripe Webhooks.
API Reference
Get Payment Link
Generates a Stripe Checkout session URL for a user to upgrade to the PRO tier.
- Endpoint:
GET /payments/payment-link - Authentication: Required (JWT)
- Constraints:
- Users already on the PRO tier will receive a
400 Bad Requesterror. - If a user does not have a
stripe_customer_idin the database, a new Stripe Customer is created automatically using their email and user ID.
- Users already on the PRO tier will receive a
Response Example:
{
"url": "https://checkout.stripe.com/c/pay/cs_test_..."
}
Stripe Webhook
A public endpoint used by Stripe to communicate event changes. This endpoint should be configured in your Stripe Dashboard.
- Endpoint:
POST /payments/stripe/webhook - Security: Requires a valid
stripe-signatureheader for verification.
Supported Events:
| Event Type | Action Taken |
| :--- | :--- |
| checkout.session.completed | Provisioning of the PRO tier for the user. |
| invoice.payment_succeeded | Verification/Renewal of active subscription status. |
| invoice.payment_failed | Logging of failed payment for notification/retry logic. |
| customer.subscription.deleted | Downgrading the user back to the FREE tier. |
Pricing Tier Enforcement
The application enforces limits based on the user's pricing_tier stored in the JWT and database. When a user attempts to create resources, the backend validates their current count against the PricingTierLimits configuration.
Example: Moderator Limits
The POST /moderators/ endpoint checks the user's tier before allowing a new moderator to be created:
# Internal Logic Example
count = await db_sess.scalar(
select(func.count(Moderators.moderator_id)).where(Moderators.user_id == jwt.sub)
)
if count >= PricingTierLimits.get(jwt.pricing_tier).max_moderators:
raise HTTPException(status_code=400, detail="Max moderators reached.")
User Model & Metadata
The system tracks subscription status via the following fields in the Users model:
pricing_tier: A string representation of the current tier (e.g.,FREE,PRO).stripe_customer_id: The unique identifier for the customer in Stripe's ecosystem.verified_at: Subscriptions often require email verification before they can be initiated.
Identity Integration
When fetching the current user profile via GET /auth/me, the pricing_tier is returned in the response:
{
"username": "jdoe",
"pricing_tier": "PRO",
"connections": {
"discord": {
"username": "jdoe#1234",
"avatar": "https://cdn.discordapp.com/..."
}
}
}
Configuration Requirements
To use the payments module, ensure the following environment variables are configured:
| Variable | Description |
| :--- | :--- |
| STRIPE_API_KEY | Your Stripe secret key. |
| STRIPE_WEBHOOK_SECRET | The secret used to verify webhook signatures. |
| STRIPE_PRICING_PRO_PRICE_ID | The specific Price ID created in your Stripe Dashboard for the PRO tier. |
| DOMAIN / SCHEME | Used to construct success_url and cancel_url for Checkout sessions. |