Skip to main content
The portal is the consumer face of the universal identity. A customer logs in with their phone (WhatsApp OTP, no password) and sees — and manages — every subscription they hold across every Duro merchant.

Listing across merchants, scoped to one mode

A customer’s Customer rows are spread across many tenants (one per merchant they’ve paid). The portal finds them all by the identity’s phone, then their subscriptions, plans, and merchant names — in a fixed number of batched queries, never N+1: Four queries regardless of how many merchants or subscriptions — customers, subscriptions IN, plans IN, tenants IN. (An earlier version looped both modes and ran a query per subscription; this is the documented fix.)
Mode is explicit, never doubled. The dashboard has a test/live switcher, so the portal takes the current mode as a parameter and lists only that schema — real customers see live. The list endpoint defaults to live; it never silently merges both modes.

Ownership is enforced on every action

Reads are filtered by the identity’s phone. Actions (cancel, pause, resume) re-verify ownership before touching anything: A stranger with a valid identity token for a different phone cannot read or mutate someone else’s subscription — verified live, including the negative case. And the merchant’s appearance config governs which actions the portal even offers (cancelSubscription, pauseSubscription, updatePlan…), so a merchant who disables cancellation has it disabled here too.

What the customer can do

  • See all active subscriptions, with merchant, plan, next charge, status.
  • View invoices and receipts per subscription.
  • Pause / resume / cancel (where the merchant allows).
  • Fix a past-due charge — update the card on their phone wallet and trigger an instant retry, closing the recovery loop from the customer’s side.
Next: the webhook system that powers the merchant’s view of all this.