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.

FieldTypeDescription
namestringUnique plan name (e.g. Silver, Gold).
categoryenumsilver | gold | platinum | diamond
pricedecimalPrice in account currency (EUR for EU accounts).
default_cycleenummonthly | yearly
badgestringOptional label shown in the UI, e.g. Popular.
featuresJSONKey/value feature limits (properties, photos, api_keys, invoices_per_month, etc.).
benefitsJSON arrayHuman-readable benefit strings shown on the pricing page.
is_popularbooleanHighlights the plan card in the pricing UI.

Known Tier Limits

LimitFreeSilver (€44/mo)Gold+
Properties13Unlimited
Photos per property1030Unlimited
API Keys13Unlimited
Partner connections310Unlimited
Invoices per month30150Unlimited
Data refresh interval24 h1 hReal-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.

FieldDescription
planFK to SubscriptionPlan. null means free tier.
is_activeWhether the subscription is currently valid.
start_date / end_dateSubscription validity window.
billing_cyclemonthly or yearly.
auto_renewIf true, Stripe will charge at period end.
cancel_at_period_endStripe cancellation flag — plan remains active until end_date.
provider_subscription_idStripe sub_xxx ID.
stripe_customer_idStripe cus_xxx ID.
renewal_attemptsCount of failed renewal attempts before cancellation.
next_billing_dateDate 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.

ModelPurpose
PaymentCardSaved payment methods. One card can be marked is_primary for automatic renewals.
BillingAddressOneToOne to owner user. Stores company name, VAT number, country, and billing email for invoice generation.
BillingHistoryImmutable 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.

1
Setup Intent — frontend calls POST /api/payments/stripe/setup-intent/ to get a client secret for Stripe.js card collection without storing raw numbers.
2
Attach Method — after Stripe.js confirms, POST /api/payments/stripe/attach-method/ links the payment method to the customer and saves a PaymentCard record.
3
Payment IntentPOST /api/payments/stripe/payment-intent/ initiates the charge for plan upgrade.
4
VerifyPOST /api/payments/verify-payment/ confirms the payment intent succeeded and activates the new plan.
5
Webhook — Stripe posts events to /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

MethodPathDescription
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.