AstroBaaS

Pricing & promotions

Subscriptions, gift cards, payment plans

Paid pluginsize Lplanned, not built

Indicative price, not an offer: €49–199/month

Generated from docs/plan/paid/subscriptions-gift-cards-payment-plans/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

Recurring revenue and gift cards are complex state machines requiring tight payment processor ties. This paid feature adds subscription products with billing intervals, automatic recurring charges, gift cards with balance tracking and redemption, draft orders for quote-to-pay workflows, and post-purchase order editing before shipment.

The problem

Merchants cannot offer subscriptions (recurring billing), gift cards (prepaid balance), draft orders (quote-to-pay workflows), or post-purchase order editing. Customers can only make one-time purchases. Revenue is one-off, not recurring. No way to send gift cards or allow customers to review/edit orders before payment.

What it does

  • Create subscription products with billing intervals (weekly, monthly, quarterly, annually)
  • Store subscription state: active, paused, cancelled, expired, failed_payment_pending
  • Automatic recurring charge via payment processor on next_charge_date
  • Subscription customer self-service: pause, resume, cancel, update payment method
  • Gift card entity with code, balance (integer minor units), creation, expiry (optional)
  • Redeem gift cards at checkout as partial or full payment
  • Draft orders: admin creates, sends unique token-link to customer
  • Customer can view draft order via token-link and pay it online
  • Post-purchase order editing: change item quantities, add/remove items, recalculate total (before shipment only)
  • Track subscription charges and failed attempts in SubscriptionCharge ledger

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • Does not build payment processor recurring-billing implementation—Stripe, Square, etc. APIs are merchant’s responsibility; AstroBaaS calls them
  • Does not auto-retry failed subscription charges—processor handles retry logic; AstroBaaS logs webhooks and updates state
  • Does not build subscription analytics or revenue forecasting—that’s a reporting feature, not core
  • Does not support multi-tiered/stacked subscriptions (e.g., base + add-ons)—one subscription per customer at a time keeps state simple
  • Does not provide wholesale quote-to-order workflows—that’s b2b-wholesale feature; draft orders are for standard sales
  • Does not design or print physical gift cards—gift card is digital balance only, no design/template customization
  • Does not auto-refund gift card balance on refund—manual merchant decision; system tracks balance but policy is manual

Data model

New entity: Subscription with customer_id, product_id, interval (enum: weekly/monthly/quarterly/annual), status (active/paused/cancelled/expired/failed_payment_pending), next_charge_date (timestamp), payment_method_id (string, stored from processor). New entity: SubscriptionCharge with subscription_id, order_id (nullable FK), charge_date, status (pending/paid/failed/refunded), amount (integer minor units). New entity: GiftCard with code (unique, string), balance (integer minor units), created_by (staff user_id), created_date, expiry_date (nullable), status (active/inactive). New entity: GiftCardRedeem with giftcard_id, order_id, amount_redeemed (integer minor units), date. New field on Product: is_subscription (boolean), subscription_interval_days (integer, nullable). New field on Order: type (enum: standard/draft/subscription/gift-card-funded), subscription_id (nullable FK), draft_token (string 64, nullable, unique), draft_expires_at (timestamp, nullable). Migration: add columns, default existing orders to type=‘standard’ and null foreign keys.

API

  • POST /store/api/subscriptions — create subscription (customer or admin)
  • GET /store/api/subscriptions/:id — get subscription detail (customer self-service)
  • PUT /store/api/subscriptions/:id/pause — pause subscription
  • PUT /store/api/subscriptions/:id/resume — resume subscription
  • DELETE /store/api/subscriptions/:id — cancel subscription
  • PUT /store/api/subscriptions/:id/payment-method — update payment method token
  • POST /admin/api/subscriptions — list all subscriptions with filter/search
  • GET /admin/api/subscriptions/:id/charges — view subscription charge history
  • POST /admin/api/gift-cards — create gift card batch (CSV: quantity, face_value)
  • GET /admin/api/gift-cards — list gift cards with balance, status, redemptions
  • PUT /admin/api/gift-cards/:id/status — deactivate or reactivate
  • GET /admin/api/gift-cards/:code/balance — check balance (for customer verification)
  • POST /store/api/draft-orders — create draft order (admin creates; optionally fills customer_email)
  • GET /store/api/draft-orders/:token — get draft order by token (unauthenticated)
  • PUT /store/api/draft-orders/:token — edit draft order (add/remove items, recalculate total)
  • POST /store/api/draft-orders/:token/pay — pay draft order (creates Order with type=‘draft’)
  • POST /admin/api/draft-orders — create draft order (admin only, returns token and link)
  • POST /admin/api/orders/:id/edit — edit order before shipment (add/remove items, change qty)
  • POST /admin/api/orders/:id/finalize-edits — finalize edit and recalculate shipping/tax

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Subscription management: list subscriptions (active/paused/cancelled), view detail (customer, product, interval, next charge, charge history). Pause/resume/cancel buttons. Retry failed charge button. Gift card management: generate batch (specify quantity and face value), view all cards (balance, status, redemptions, expiry). Deactivate/reactivate UI. Export codes as CSV. Draft orders: list drafts (pending payment, completed), view detail, resend link, convert to order. Order edit UI: shown before shipment, add/remove items from order, quantity adjustment, recalculate tax and shipping, ‘Apply’ to finalize.

The seam — why this is paid

Core owns: order entity, payment processing interface, webhook system. Paid pack owns: subscription state machine (pause/resume/cancel transitions), recurring charge scheduling and failure tracking, gift card entity and redemption, draft orders, post-purchase order edit workflow (before shipment gate). Why: recurring models require sophisticated state machines, payment processor ties, charge reconciliation, and support escalations (failed charges, refund auditing). Gift cards and draft orders are merchant convenience features, not legal/compliance requirements.

Recurring models require sophisticated state machines and close payment processor ties.

Dependencies

  • Payment processor API integration must exist and support recurring charges (Stripe, Square, etc.)
  • Webhook system must exist and be tested to handle processor notifications (charge.succeeded, charge.failed)
  • Order entity must be extensible with type, subscription_id, draft_token, draft_expires_at fields
  • Scheduler must exist to trigger subscription charge attempts on next_charge_date
  • Shipment tracking must exist: order must have a shipped_date or status field to gate post-purchase edit

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • Create subscription for customer at €10/month; Subscription.next_charge_date is 30 days from creation date
  • Pause subscription; next_charge_date does not advance, status=‘paused’
  • Resume subscription; status=‘active’, next_charge_date recalculates to 30 days from resume_date
  • Scheduler triggers charge on next_charge_date; creates SubscriptionCharge with status=‘pending’
  • Webhook from processor arrives (charge.succeeded); SubscriptionCharge.status=‘paid’, new Order created with type=‘subscription’
  • Charge fails (insufficient funds); SubscriptionCharge.status=‘failed’, Subscription.status=‘failed_payment_pending’, customer notified
  • Subscription with 3 failed charges: status remains ‘failed_payment_pending’ (not auto-cancelled); customer can retry via self-service
  • Create gift card code ‘GIFT50’ with €50 balance; customer enters code at checkout; order funded_by_giftcard, GiftCard.balance reduced by €50
  • GiftCard.balance = €0; customer attempts to redeem; checkout fails with ‘insufficient balance’
  • Create draft order with 2 items (total €100), send link; customer receives email with unique token and link
  • Customer clicks link, views draft order, edits qty of item 1 (qty 1→3), recalculates (total €150), pays via draft-token endpoint
  • Order.type=‘draft’ created with order.applied_promotion_ids, order.items matching final draft state
  • Order already shipped (shipped_date not null); admin attempts to edit items; request fails with ‘Cannot edit shipped order’
  • Order not shipped; admin changes qty, removes item, recalculates; changes applied and order total updated

Risks

Recurring charges tied to payment processor retries—if processor changes retry behavior mid-flow, subscription charge tracking can drift; add idempotency keys to processor calls. Subscription state machine is complex (active→paused→active vs. active→cancelled→reactivate); add comprehensive integration test covering all transition paths and edge cases (e.g., cancel during pending charge). Gift card code collision: use cryptographic random generation (e.g., sha256(uuid + timestamp)), not sequential; collisions would allow fraudulent balance transfer. Draft order token expiry: if token doesn’t expire, customer could pay it multiple times; enforce token uses_count <= 1 and expiry_at <= now() on payment. Order edit post-shipment: define ‘shipped’ explicitly (shipped_date not null, or status=‘shipped’); if shipment date is null but in-transit, edit must still be blocked. Concurrent subscription pause/resume: add optimistic lock or explicit state guard; test race condition.

Commercial context

Suggested price€49–199/month
Rival anchorMagento Open Source: free; Adobe Commerce: advanced subscriptions paid.

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and ../../AI-GUIDE.md for how to work on this repository.