Payments & Subscriptions
Manage subscription plans, billing, payment cards, and Stripe integration for Ananas GDS accounts.
Overview
Ananas GDS uses a tiered SaaS subscription model processed through Stripe. Each company account has one active subscription that gates feature access — the number of partner connections, API keys, photos, properties, and invoices permitted per month all depend on the active plan tier. The free tier is always available with reduced limits.
Subscription scope
Subscriptions are per owner account. Sub-users share the owner's plan and do not hold their own subscriptions.
Subscription Plans
Plans are defined in the SubscriptionPlan model and served from the database, making them configurable without a code deploy. Each plan carries a structured features JSON payload that gates behaviour at the application level.
| Field | Type | Description |
|---|---|---|
name | string | Unique plan name (e.g. Silver, Gold). |
category | enum | silver | gold | platinum | diamond |
price | decimal | Price in account currency (EUR for EU accounts). |
default_cycle | enum | monthly | yearly |
badge | string | Optional label shown in the UI, e.g. Popular. |
features | JSON | Key/value feature limits (properties, photos, api_keys, invoices_per_month, etc.). |
benefits | JSON array | Human-readable benefit strings shown on the pricing page. |
is_popular | boolean | Highlights the plan card in the pricing UI. |
Known Tier Limits
| Limit | Free | Silver (€44/mo) | Gold+ |
|---|---|---|---|
| Properties | 1 | 3 | Unlimited |
| Photos per property | 10 | 30 | Unlimited |
| API Keys | 1 | 3 | Unlimited |
| Partner connections | 3 | 10 | Unlimited |
| Invoices per month | 30 | 150 | Unlimited |
| Data refresh interval | 24 h | 1 h | Real-time |
User Subscription
UserSubscription is a OneToOne relation from the owner user to an active plan. It tracks billing cycle, renewal state, and the Stripe subscription reference.
| Field | Description |
|---|---|
plan | FK to SubscriptionPlan. null means free tier. |
is_active | Whether the subscription is currently valid. |
start_date / end_date | Subscription validity window. |
billing_cycle | monthly or yearly. |
auto_renew | If true, Stripe will charge at period end. |
cancel_at_period_end | Stripe cancellation flag — plan remains active until end_date. |
provider_subscription_id | Stripe sub_xxx ID. |
stripe_customer_id | Stripe cus_xxx ID. |
renewal_attempts | Count of failed renewal attempts before cancellation. |
next_billing_date | Date of the next scheduled charge. |
Billing & Cards
Payment cards are stored as Stripe payment method references — raw card numbers never touch Ananas GDS servers. The PaymentCard model holds the last 4 digits and expiry for display, plus the Stripe token for charges.
| Model | Purpose |
|---|---|
PaymentCard | Saved payment methods. One card can be marked is_primary for automatic renewals. |
BillingAddress | OneToOne to owner user. Stores company name, VAT number, country, and billing email for invoice generation. |
BillingHistory | Immutable record of every charge: amount, net, VAT, provider reference, status, and optional PDF file. |
Stripe Integration
Ananas GDS uses Stripe for card tokenisation, payment intent creation, and subscription management. The flow follows Stripe's recommended Payment Intents + Setup Intents pattern to support 3D Secure.
POST /api/payments/stripe/setup-intent/ to get a client secret for Stripe.js card collection without storing raw numbers.POST /api/payments/stripe/attach-method/ links the payment method to the customer and saves a PaymentCard record.POST /api/payments/stripe/payment-intent/ initiates the charge for plan upgrade.POST /api/payments/verify-payment/ confirms the payment intent succeeded and activates the new plan./api/payments/stripe/webhook/. Events handled: invoice.payment_succeeded, invoice.payment_failed, customer.subscription.deleted, and customer.subscription.updated.Webhook signature verification
The Stripe webhook endpoint is CSRF-exempt and verifies every incoming request using the STRIPE_WEBHOOK_SECRET environment variable. Never expose this secret.
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/payments/plans/ | List all active subscription plans with features and pricing. |
GET | /api/payments/overview/ | Current subscription, card list, billing address, and billing history for the authenticated user. |
GET/PUT | /api/payments/billing-address/ | Retrieve or update billing address (company name, VAT, country, etc.). |
POST | /api/payments/subscription/upgrade/ | Upgrade or change the current plan. Body: {plan_id, billing_cycle}. |
POST | /api/payments/subscription/cancel/ | Schedule cancellation at period end. Does not revoke access immediately. |
POST | /api/payments/subscription/resume/ | Resume a subscription that was set to cancel at period end. |
POST | /api/payments/subscription/auto-renew/ | Toggle auto-renew on or off. |
POST | /api/payments/subscription/renew-now/ | Force an immediate renewal attempt. |
GET | /api/payments/cards/ | List saved payment cards. |
DELETE | /api/payments/cards/{id}/ | Remove a saved card. |
POST | /api/payments/cards/set-primary/{id}/ | Set a card as the default for auto-renewal. |
GET | /api/payments/invoice/{invoice_number}/download/ | Download the PDF billing invoice for a past charge. |
POST | /api/payments/stripe/setup-intent/ | Create a Stripe SetupIntent for card collection. |
POST | /api/payments/stripe/attach-method/ | Attach a confirmed Stripe payment method to the customer. |
POST | /api/payments/stripe/payment-intent/ | Create a PaymentIntent for a plan charge. |
POST | /api/payments/verify-payment/ | Confirm payment succeeded and activate the plan. |
POST | /api/payments/stripe/webhook/ | Stripe webhook receiver (CSRF-exempt, signature-verified). |
Feature Gating
The FeatureAccess service reads the features JSON from the active plan and compares against current usage. Views that enforce tier limits call this service before permitting the action — for example, before creating a new API key or partner connection. When a limit is reached the API returns HTTP 403 with a message indicating which feature is restricted.
Checking your plan from the API
Call GET /api/payments/overview/ to inspect your current plan, usage counters, and next billing date in one request.