08.custom_plan
- Back
- 00.overview.md
-
01.admin
- 00.overview.md
-
01.auth
-
02.user-management
-
03.group-management
-
04.announcement-management
-
05.guide-management
-
02.general
- 00.overview.md
-
01.analysis
-
02.auth
-
03.chat
-
04.checkout
-
00.general
- 00.overview.md
-
01.retail_buyer
-
02.subscription
-
-
05.packages
-
06.wishlist
- 00.overview.md
-
01.temp-wishlist-to-group
-
02.wishlist-to-group
-
07.file-storage
-
08.unique-product-upload
-
09.dataset-workflow
-
10.landing-page
-
11.download-review-csv
-
03.widget
Custom Plan (Custom Contract)
Overview
This document describes the custom plan flow (contract-based pricing) using Stripe Checkout with price_data. Custom plans are isolated from base plan mapping to avoid impacting catalog prices and standard plan-change flows.
Core Data Model
subscriptions
pricing_type:customcustom_contract_id: contract linkpayment_provider_subscription_id: Stripesub_...
custom_contracts
- Terms:
amount,currency,billing_interval,starts_at,ends_at, limits - Stripe anchors:
provider_checkout_session_id(Stripecs_...)provider_price_id(Stripeprice_...)provider_subscription_item_id(Stripesi_...)
subscription_histories
Per-invoice snapshots:
custom_contract_idprovider_price_idprovider_subscription_item_idinvoice_id/payment_intent_id
End-to-End Flow
1) Create Custom Contract (Admin)
API:
POST /api/v1/admin/custom-contracts
Logic:
- Create contract in
draft - Link existing subscription or create a new one with
pricing_type = custom
2) Send Payment Link (Admin)
API:
POST /api/v1/admin/custom-contracts/{id}/send-payment-link
Logic:
- Create Stripe Checkout Session with
price_data - Set metadata:
custom_contract_idsubscription_slug
- Update
custom_contracts.provider_checkout_session_id - Move status
draft→offered
3) Stripe Webhooks (Checkout + Subscription)
customer.subscription.created
- Map subscription to contract:
subscriptions.pricing_type = customsubscriptions.custom_contract_id = <id>
- Update
custom_contracts:provider_price_idprovider_subscription_item_idsubscription_id
invoice.paid
- Create/update
subscription_historiesfor the billing cycle - Update
custom_contracts.provider_price_id/provider_subscription_item_id(fallback from invoice line) - On success:
custom_contracts.status = active
customer.subscription.updated
- Sync subscription status
- Create renewal/change history for the custom contract if needed
customer.subscription.deleted
- Cancel subscription and update contract:
cancelledif terminated earlyexpiredifends_athas passed
Guardrails
- Custom plans do not use
package_plan_to_providers - Price or scope changes must go through the custom contract + webhooks
- Stripe metadata is the key for correct mapping
Notes
- Webhooks can arrive out of order; the handlers are designed to be idempotent.
- If
amount = 0, still rely oninvoice.paidto activate the contract.