Subscription Purchase
Key Points
- Android: Single product (
hypnoelp_subscriptions) with multiple base plans - iOS: Single monthly subscription (
me.hypnoelp.app.subscription_1month) - Supports upgrade/downgrade with proration on Android
- Free trial detection from pricing phases
- Deferred acknowledgment (same as course purchases)
Product Configuration
Android (Google Play)
| Setting | Value |
|---|---|
| Product ID | hypnoelp_subscriptions |
| Base Plans | monthly-plan, quarterly-plan, yearly-plan, etc. |
| Offer Tags | Configured per base plan |
iOS (App Store)
| Setting | Value |
|---|---|
| Product ID | me.hypnoelp.app.subscription_1month |
| Duration | 1 month |
| Billing Period | P1M |
Subscription Flow
- Query plans —
querySubscriptionPlans()returns plans with pricing and free trial info - User selects plan
- Initiate purchase — Android uses
GooglePlayPurchaseParam(withChangeSubscriptionParamfor upgrades), iOS uses standardPurchaseParam - Buy —
buyNonConsumable(), wait up to 20 minutes - Extract data — convert date to ISO 8601, get receipt/token
- Validate — send to
/functions/v1/validate_purchase - Acknowledge —
completePurchase()only after server confirms - Update profile — set subscription object,
user_group = "premium"
Validation Payload
{
"product_id": "hypnoelp_subscriptions",
"purchase_id": "GPA.sub-1234-5678",
"transaction_date": "2025-12-23T12:00:00.000Z",
"status": "PurchaseStatus.purchased",
"plan_title": "Monthly Plan",
"plan_description": "Premium access for 1 month",
"base_plan_id": "monthly-plan",
"offer_id": "",
"platform": "android",
"purchase_token": "token-from-google-play"
}
Note: transaction_date uses ISO 8601 format for subscriptions (not milliseconds).
Free Trial Detection (Android)
Free trials are detected from pricing phases:
// First pricing phase with priceAmountMicros == 0 indicates free trial
// Billing period format (ISO 8601 duration):
// P1D = 1 day, P7D = 7 days, P1M = 1 month, P3M = 3 months, P1Y = 1 year
if (firstPhase.priceAmountMicros == 0) {
freeTrialText = _formatBillingPeriod(billingPeriod);
// e.g., "First 7 days free"
}
Upgrade/Downgrade (Android Only)
if (_activeSubscription != null) {
changeParam = ChangeSubscriptionParam(
oldPurchaseDetails: _activeSubscription!,
replacementMode: ReplacementMode.withTimeProration,
);
purchaseParam = GooglePlayPurchaseParam(
productDetails: googlePlayDetails,
changeSubscriptionParam: changeParam,
);
}
iOS does not support upgrade/downgrade since there is only a single plan.