↑Skip to content
AshokBhatiHire me
← All work

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
TokenGate cover
Client
A Web3 real-world-asset tokenisation infrastructure provider
ReactTypeScriptNode.jsExpressStripeMoesifAuth0TykDockerGitHub Actions

Live demo

Try TokenGate yourself

An interactive recreation that runs in your browser.

Open the demo
01

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.

02

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

03

How it works

  1. 01

    The developer signs in through Auth0. An Axios interceptor adds the access token to every portal request.

  2. 02

    The payment guard asks the proxy for the Stripe customer and subscription. If the status is not active, it redirects to pricing.

  3. 03

    The proxy lists plans from Stripe, starts the free plan or confirms the Checkout session, and updates the subscription on a plan change.

  4. 04

    The portal API service creates, renames and deletes keys in the Tyk gateway, with policies for rate and quota.

  5. 05

    The gateway forwards calls to the tokenisation API and sends each event to Moesif.

  6. 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.

04

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
05

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.

06

Skills and tech stack

Frontend

ReactMUIMUI X ChartsHighchartsAxios

Backend

Node.jsExpressTypeScriptexpress-validatorPinoSwagger

Cloud / DevOps

DockerDocker ComposeNginxGitHub ActionsAzure PipelinesHusky

Integrations

Auth0Stripe Checkout and BillingMoesifTyk API GatewayRedisPostgres

Testing

JestReact Testing Library
07

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.