How to Implement Subscriptions with Tirdad
Follow one customer from plan selection to activation, a mid-cycle upgrade, renewal, payment recovery, and cancellation—and see which system owns every decision.
Updated 2026-08-15
Noura chooses Growth at SAR 299 a month. Twelve days later, her team needs Scale. At renewal, the card fails.
If your implementation reduces that journey to is_paid = true, it will break at the first change. A subscription is the agreement connecting time, access, price, invoices, and payment. This guide shows how to model that agreement without rebuilding billing inside your product.
A subscription is a state machine—not a paid checkbox.
Every state answers two questions: what may the customer use now, and what should happen next?
TRIAL
Evaluate
Expires or converts
ACTIVE
Use the plan
Renews or changes
PAST DUE
Recovery window
Payment retries
CANCELED
Access ends
History remains
Start with the decisions code cannot make
Before opening an API reference, write down the commercial rules. These rules become your implementation contract.
| Decision | Example for Noura | Why it matters |
|---|---|---|
| When access starts | After the first payment succeeds | Prevents unpaid access |
| Upgrade timing | Immediately | Controls when Scale entitlements open |
| Upgrade charge | Prorate the remaining 18 days | Determines the next invoice |
| Downgrade timing | At period end | Avoids removing paid access early |
| Failed-payment window | 7 days | Defines recovery access |
| Cancellation | End of current period | Keeps the promise already paid for |
An API cannot decide whether a downgrade is immediate or scheduled. If the business leaves that ambiguous, the ambiguity moves into production.
Give every fact one owner
Your application, Tirdad, and the payment provider participate in the same journey. They should not all decide the same thing.
One journey. Three systems. No duplicated truth.
Owns the experience
Owns the agreement
Moves the money
When Noura subscribes, Tirdad needs to know which account belongs to her in your product, and your product needs to know which Tirdad subscription belongs to that account. That relationship keeps every plan change and renewal attached to the right customer.
| In your application | In Tirdad | What connects them |
|---|---|---|
| Noura’s account | Customer record | The same customer even if the email changes |
| Her workspace | Subscription | Current plan and renewal date |
| The plan she selected | Agreed price | The price used on her invoices |
Do not rely on email alone. Noura can change it; her account and subscription still represent the same relationship.
Create the commercial model before the subscription
Build the plan in this order:
- Define the features customers recognize.
- Add the entitlements and limits included in Growth.
- Attach the monthly or annual recurring price.
- Publish a version that new subscriptions can reference.
Do not overwrite Growth when its price changes. Publish a new price version. Noura’s historical invoices should still explain the agreement she bought, while new customers receive the new terms.
Activate from confirmed state—not browser navigation
Noura clicks Choose Growth. Your frontend begins the journey, but it must not be the authority that opens access.
Noura chooses Growth. When should access open?
The reliable flow is:
- Your backend creates or matches the Tirdad customer.
- It creates the subscription with an idempotency key.
- The payment provider returns the payment result to Tirdad.
- Tirdad moves the subscription to its confirmed state.
- Your application receives the result or webhook, then asks for entitlements.
Why not trust the success page? The customer may close the tab before it loads. A malicious request may call it directly. The payment may also finish after the browser leaves. Subscription state must survive all three.
Make webhook handlers boring
Webhooks arrive more than once, sometimes out of order. Treat that as normal.
For each event:
- verify the signature;
- store the event ID before applying its effect;
- ignore an event already processed;
- compare versions or timestamps before replacing newer state;
- fetch the current subscription when the local view is uncertain.
The event says something changed. The subscription record tells you what is true now.
Change the plan, not the identity
On day 12, Noura upgrades from Growth to Scale. Keep the same customer and subscription. Change the commercial terms attached to it.
Growth becomes Scale on day 12. What changes immediately?
Growth
Day 1 → 12
Already earned
Scale
Day 12 → 30
New access
After Tirdad confirms the change, your product refreshes the entitlements. That lets Scale access open immediately without teaching your application how to calculate unused Growth value, remaining Scale value, tax, or invoice balance.
For a downgrade, the same request can schedule the new plan for the next renewal. Noura keeps what she already paid for, and the future change remains visible and reversible.
Decide what a failed payment means for access
At renewal, Noura’s card fails. Three facts now coexist:
- the renewal invoice is unpaid;
- the subscription is in recovery;
- your policy may allow seven more days of access.
Do not compress those facts into active = false. A payment failure is not necessarily a cancellation.
| State | Product behavior | Customer message |
|---|---|---|
| Active | Normal access | Next renewal date |
| Past due, in recovery | Access based on your policy | Payment failed; update the method |
| Recovered | Normal access | Payment received |
| Canceled | Remove access at the agreed time | Access end date |
Tirdad owns the invoice and subscription state. Your application turns that state into the right product behavior and a clear next step.
Test time, retries, and failure
The happy path proves almost nothing. Before launch, verify these moments:
- the create request times out, then retries with the same idempotency key;
- the activation webhook arrives twice;
- an older event arrives after a newer one;
- an upgrade happens exactly at the billing boundary;
- the upgrade payment fails;
- renewal fails, then succeeds on retry;
- cancellation is scheduled, then reversed;
- historical invoices keep their original price version.
The implementation is ready when you can answer one question for every transition: which system decides the truth, and how does every other system learn it?
Model one subscription lifecycle. Do not spread it across a payment flag, a plan string, and a cancellation date that can disagree.