Paywall / Customer portal
Customer portal
The portal is a hosted self-service page for your buyers — receipts, refunds, cancelling and resuming, switching plans, even deleting their account. You don't build any of it. This page covers all three sides: what the buyer sees, the dashboard switches that shape it, and how to open it from your app.
Availability follows your Onelo plan: the portal itself is a plan capability, and two of its actions — Change plan and Request refund — are off on the Free plan (paid Onelo plans include them). Cancelling and receipts are never plan-gated.
What the buyer sees inside
The portal greets them with “Manage your plan” (or “Welcome back” if their plan lapsed) and shows, per purchase: a status pill (Active / Canceled / Payment failed), the plan name and price, the key date (“renews 12 Jul 2026”, “free trial — first charge…”, “ends…” or “access until…”), when they started, and their last payment. Below sit the action buttons — each only appears when it actually applies:
| Action | What happens |
|---|---|
| Change plan / Upgrade plan | Shown when you have more than one plan (and your Onelo plan includes portal plan-changes). Opens your store with the buyer recognised — the unused part of what they already paid is shown as a credit and deducted at checkout, so they only pay the difference (“Pay €X today after applying credits”). |
| Switch to annual · save X% / Switch to monthly | Shown when the product has both prices. Going up (monthly → annual, or a higher tier): a preview shows the exact prorated amount due today, line by line, before they confirm. Going down: no charge today — the cheaper plan simply takes over at the next renewal, so the time already paid for is never cut short. |
| Cancel subscription | Two gentle steps: a confirm (“You'll keep access until {date}, then won't be charged again”) and a why-are-you-leaving picker (8 reasons + optional note) — those reasons land in your dashboard as churn insight. Access runs to the period end; nothing stops immediately. |
| Resume subscription | A cancelled-but-not-yet-ended buyer can undo with one click — “No charge today.” |
| Request refund | Only inside the refund window you set, with the product's self-serve refund opt-in enabled (off by default on every new product — buyers then cancel instead), and when your Onelo plan includes portal refunds. The modal states fees aren't refundable, warns access ends now, asks a reason (7 options) — then: “Your refund is on its way… 5–10 business days.” Outside the window the button is greyed with a plain explanation (“Refund window closed · purchase was N days ago”). |
| Receipts | A list of every payment with date, amount (struck through if refunded), billing address, and a PDF / Receipt link straight from Stripe — if you allow it (see below). |
| Delete account | A quiet link, deliberately heavy to use: password re-entry + an “I understand” checkbox. It schedules deletion in 30 days, cancels subscriptions at period end (“no surprise charges”), signs them out everywhere, and emails them. Signing in before the date cancels it. (Buyers who signed up with Google/Apple — no password — can't use this path.) |
Your switches — what you control
In Paywall → Customer portal, five app-wide decisions shape every buyer's experience:
| Setting | What it does | Default |
|---|---|---|
| Refund outcome | What a buyer's plan becomes when a full refund leaves them with no active plan. Mark as lapsed — the account stays but paid features lock. Enroll in free tier — they're auto-moved onto your free product (price 0) instead. “Enroll” is disabled until a free product exists in Products. | Mark as lapsed |
| Default refund mode | Full refunds 100% of what was paid within the window. Proportional refunds only the unused part — paid amount × time remaining ÷ period length. This is the default for products that don't set their own (each product can override it under Refunds & Legal). | Full |
| Refund platform fee | On a refund, whether Onelo also returns its platform fee to your connected account. On — you're made whole on a cancelled sale. Off — Onelo keeps its processing fee (you absorb it). | On |
| Session after refund | Only applies when the outcome is Mark as lapsed: after a refund the buyer is signed out everywhere and the next launch goes sign-in → store. Off = they stay signed in with paid features locked. Hidden when the outcome is free tier — there they keep an active plan, so signing them out would make no sense. | On |
| Payment receipts | App-wide default for the Receipt / PDF links. Per-product override under Products → Refunds & Legal → Receipt visibility. | On |
Three more levers live per product (Products → Refunds & Legal): the refund window in days (it drives the Request-refund button's availability — leave it empty to inherit the app-wide window; set 0 to disable self-service refunds for that product), allow self-serve refunds (off by default on every new product, subscriptions and one-time alike — buyers then cancel instead of refunding), and a per-product refund mode and receipt visibility that override the app-wide defaults above.
Open it from your app
The portal opens with one SDK call — Onelo signs the buyer in behind the scenes (a short-lived secure link tied to the signed-in user; no extra password). It ships in every SDK: Swift (macOS, iOS), JS / Web, Electron, React Native, Flutter and Android. The snippet below assumes the Onelo SDK is already initialised and the buyer is signed in — see the Auth guide if you haven't set that up yet.
onelo_pk_live_YOUR_KEY with your app's publishable key from your app's SDK page in the dashboard. The API URL is already the correct Onelo endpoint — use it as-is.Install
npm install github:onelo-tools/onelo-jsInitialize
import { Onelo } from '@onelo/js'
const onelo = new Onelo({
apiUrl: 'https://api.onelo.tools',
publishableKey: 'onelo_pk_live_YOUR_KEY',
})Usage
// Open the hosted customer portal (manage subscription, cancel, receipts…). // The user must be signed in. Resolves when they close the portal. await onelo.customerPortal.open()
That embedded-window call covers most apps. Two more entry points exist for other UI patterns:
| Call | Use it when |
|---|---|
openCustomerPortal(from:) | The default — opens the portal in an embedded ASWebAuthenticationSession inside your app and returns when the buyer is done. |
openCustomerPortalInBrowser() | Hands off to the system browser instead — no presentation anchor needed. |
initiateCustomerPortal() | Returns the signed URL so you can present it yourself in custom UI. |
| Platform | Status |
|---|---|
| Swift (macOS / iOS) | Available — the calls above |
| JS / Web | Available — onelo.customerPortal.open() |
| Electron | Available — await onelo.customerPortal.open() |
| React Native | Available — mount <CustomerPortalModal>, then call await onelo.customerPortal.open() |
| Flutter | Available — push OneloCustomerPortalView |
| Android | Available — registerLauncher + openCustomerPortal(...) |
Typical placement: a “Manage subscription” item in your app's account or settings menu. The portal inherits your Branding (logo, colors), so it feels like part of your product.