Case study 03 · Fintech / Web3 real-world-asset tokenisation
TokenGate
developer portal and usage billing for a tokenisation API
I built a self-serve developer portal and a billing proxy that let API customers sign up, subscribe, create keys and track usage with no manual setup. Headline result: sign-up to first authenticated API call in under 10 minutes*.
▶ Try the live demo- Client
- A Web3 real-world-asset tokenisation infrastructure provider
Live demo
Try TokenGate yourself
An interactive recreation that runs in your browser.
Problem
The client wanted to sell access to its tokenisation APIs. Every new customer needed a manual key, a manual plan and a manual invoice.
The APIs already sat behind a Tyk gateway. Moesif could measure usage, Stripe could bill, and Auth0 handled identity. Nothing joined these services into one place a developer could use alone.
Manual onboarding slowed sales and made billing error-prone. Each new customer cost engineering time.
Constraints:
- Four third-party services to connect.
- Stripe and Moesif secret keys must never reach the browser.
- No paid access without an active subscription.
- Three environments: develop, staging and main.
Success meant a developer could sign up, pick a plan, get a key and watch their usage with no help from the team.
Solution
The portal walks a developer from sign-up to a working key, then shows what they used and what it costs.
The end-user flow, as the code implements it:
- 01
Sign up: The developer enters first name, last name, email and password. The form checks each field ("Email is required", "Invalid email address", "Password should be at least 6 characters long") before creating the account. Login runs through Auth0.
- 02
Choose a plan: A payment guard checks the developer's Stripe subscription on every protected page. With no active subscription they land on the pricing page: "Select a payment plan to finish setting up your account." They can take the free plan or pay through Stripe Checkout. When Checkout returns, the portal registers the Stripe customer and loads the subscription.
- 03
Create API keys: The keys page explains that a token must go with every request. The developer names a key (for example "Private Credit") and gets it back with a Live status. They can rename or delete keys. Each account can hold 3 keys; a fourth attempt shows a warning.
- 04
Call the API: The key goes in each request to the Tyk gateway. Tyk policies enforce rate limits and monthly quotas per plan, and each call is recorded in Moesif.
- 05
Watch usage: The dashboard shows usage charts from Moesif. Below it, an activity table lists calls for a chosen date range, with paging and a download to Excel.
- 06
Manage billing: The billing page shows the current plan and a plan picker with each plan's features and monthly price. The button on the current plan reads "Current Plan" and is disabled, so a developer cannot subscribe to the plan they already have. Changing plan updates the Stripe subscription in place. Payment methods are listed from Stripe, and adding a card opens the Stripe-hosted page, so card numbers never touch the portal.
Key design decisions:
Small server-side proxy for Stripe and Moesif
- Reason
- Keeps secret keys on the server and gives one place to validate input
- Rejected option
- Calling Stripe and Moesif from the browser
Payment guard on every protected route
- Reason
- No key or dashboard access without an active subscription
- Rejected option
- Checking payment only at sign-up
Stripe Checkout and the Stripe-hosted card page
- Reason
- Card data stays with Stripe, which keeps the portal out of PCI scope
- Rejected option
- A custom card form
Aggregate usage in the proxy
- Reason
- The portal gets chart-ready data and stays simple
- Rejected option
- Shaping raw events in the browser
Branch-based env files and Docker builds
- Reason
- One image flow for every environment
- Rejected option
- Hand-built releases per server
I also looked at a hosted developer portal product. It did not fit the custom plan and billing rules, so I built a light portal instead.
How it works
- 01
The developer signs in through Auth0. An Axios interceptor adds the access token to every portal request.
- 02
The payment guard asks the proxy for the Stripe customer and subscription. If the status is not active, it redirects to pricing.
- 03
The proxy lists plans from Stripe, starts the free plan or confirms the Checkout session, and updates the subscription on a plan change.
- 04
The portal API service creates, renames and deletes keys in the Tyk gateway, with policies for rate and quota.
- 05
The gateway forwards calls to the tokenisation API and sends each event to Moesif.
- 06
The proxy queries Moesif by time interval (minutes up to quarters), shapes the result into chart series and table rows, and streams an Excel file on request.
Infra and deployment
Both services ship as Docker images. The proxy pipeline runs on every push to main: check out, set up Buildx, log in to the registry, tag with metadata and push. The portal pipeline picks env files by branch, so develop, staging and main get the right API URLs and keys at build time. Nginx serves the built portal inside its container. A Compose stack runs the Tyk gateway, Redis, Postgres and the portal API service locally, with Swagger for trying endpoints. Husky hooks run lint before commits.
Security
Secret keys live only in the proxy's environment and in CI secrets. An Auth0 middleware checks every proxy request. express-validator checks query and body fields before any call to Stripe or Moesif. Pino logs requests without card data.
Challenges
Secret handling
- Problem
- Stripe and Moesif calls need private keys
- Approach
- Built the proxy with Auth0 checks and input validation
- Outcome
- No secret keys in the browser
Paid access only
- Problem
- Developers could reach keys before paying
- Approach
- Added a payment guard that checks the live Stripe subscription
- Outcome
- Every protected page needs an active plan
Duplicate plan changes
- Problem
- Users could resubmit their current plan
- Approach
- Disabled the button on the current plan and during updates
- Outcome
- No duplicate subscription changes
Readable usage data
- Problem
- Raw Moesif events were hard to read
- Approach
- Aggregated in the proxy into chart and table shapes, plus Excel export
- Outcome
- Clear usage and billing views
Many services locally
- Problem
- Auth0, gateway, Moesif and Stripe to wire up
- Approach
- Docker Compose with override files
- Outcome
- Local stack starts with one script
Results
Customer onboarding
Self-serve, minutes
from Manual, days
Billing
Stripe subscriptions tied to measured usage
from Manual invoices
Release process
About 10 minute automated image build
from Manual builds
Secret keys in the browser
None
from Risk present
Business impact
the client could sell API plans without engineering help for each customer.
Technical impact
every merge to main produced a tagged Docker image, and billing logic sat in one small, testable service.
Skills and tech stack
Frontend
Backend
Cloud / DevOps
Integrations
Testing
Reflection
What worked
putting billing and analytics behind one small proxy. The portal stayed simple and the keys stayed safe.
What I would do differently
add end-to-end tests for the checkout and payment guard early. Most late bugs were in the hand-off between Stripe and the portal.
What's next
wire the yearly billing switch to yearly prices, add usage alerts before a customer hits plan limits, and add deploy steps after the image push.
Key learning
when a product is mostly third-party services, the real work is the glue. A thin, well-validated service in the middle saves a lot of pain.
Next step
Need something similar?
Code walkthrough available on request.