The payments arc.
I migrated payments to Stripe and rewrote the payments architecture for subscription idempotency, off-session charging and 3D Secure handling.
Three bosses. No continues.
A portfolio in lights. Lahore, 31.52 N, 74.36 E
Senior full-stack engineer. 8+ years in software. Lahore, Pakistan (UTC+5), working remote.
TypeScript, Python (FastAPI), React.js and Node.js, with REST APIs, Firebase, Stripe and LLM integration. AI-assisted development (Claude Code), test-first.
Open to remote full-time, part-time or contract work.
Based in Lahore, Pakistan (UTC+5). Works remote.
Turn the ring to try another hour: drag it, use the buttons, or focus it and use the arrow keys.
Light a lantern to open its chapter. Reading a chapter lights its lantern too.
Head of Technology (2022-2026), joined as Full-Stack Developer (2021-2022)
A telehealth platform on iOS, Android and web; the patient app has 100K+ Google Play downloads.
I led engineering for the patient and doctor apps on iOS, Android and web, built around real-time chat, voice and video consultations, scheduling and prescriptions.
Same laptop. Bigger blast radius.
I migrated payments to Stripe and rewrote the payments architecture for subscription idempotency, off-session charging and 3D Secure handling.
Three bosses. No continues.
A retry must never charge twice. Idempotency: send it again, pay once.
Demo: a Pay button that sends the same request again and again under one idempotency key: the requests climb, the charges stay at one.
Idempotency key ------
0 Requests
0 Charges
Press Pay. Then press it again.
A demo in your browser. No money moves.
New key: charged once. Requests {r}, charges {c}.
Same key, same answer, no second charge. Requests {r}, charges {c}.
Press all you like: one key, one charge. Requests {r}, charges {c}.
New payment, new key. The next Pay charges once more.
Off-session charging: a saved card, charged later, with nobody at the screen. It has to just work, and it has to be right.
Demo: a saved card renews in the night while its owner sleeps: it is charged with nobody at the screen.
Renewal Due tonight.
Renewal
Paid at 03:00
Nobody at the screen.
Clock 21:00
A demo in your browser. No money moves.
Run the renewal
Run it again
Night falls. The customer is asleep.
Renewed at {t} with nobody at the screen. Charged once; they slept through it.
3D Secure: the bank steps in and asks the cardholder to prove it's them. The flow has to survive the detour.
Demo: a phone checkout where the bank steps in to ask "Is this you?": yes pays after the bank's check, no stops with no charge, and either way the app carries on.
Checkout
Total
Saved card
Paid
Stopped. No charge.
Your bank
Is this you?
The detour
Tap Pay on the phone.
A demo in your browser. No money moves.
Paying
Pay
The bank steps in: is this you?
Yes: the bank checked, then it paid. Back in the app, flow intact.
No: stopped, no charge. Back in the app, flow intact.
Tap Pay on the phone.
I owned the payments architecture. Stripe since 2021. I also shipped production-critical fixes in payments, communications and webhook handling.
Retry all you like.
I established the foundations of a new app, including its authentication flow. Then I ran a security audit of it.
The highest-risk changes, to sign-in, account recovery and payments, I personally owned.
If it breaks here, it breaks for real.
I ran on-call triage for production issues and routed incoming issues through the team's tracker.
Demo: a phone buzzes with "Webhook failed." Pressing On it plays it through: triage, routed through the team's tracker, fixed.
Webhook failed.
On it.
The team's tracker
A demo in your browser. The alert is made up.
Triage.
Routed through the team's tracker.
Triaged, routed through the team's tracker, fixed.
I oversaw App Store and Google Play releases.
I managed a team of about 5 web, mobile and backend engineers and reviewed every merge. I mentored developers on React, TypeScript and state management, and set frontend standards and a reusable component library for the React.js web apps.
Demo: a change waiting for review, with you as the reviewer: ask for changes or approve it, then merge it.
Use the shared button
Waiting for review
Author Dev A
Reviewer You
You are the reviewer. Approve it, or ask for changes.
You
Dev A
One test for the empty state, please.
Done. Test added.
Nice. One clearer name, please.
Renamed. Over to you.
Waiting for review
Changes requested
Approved
Merged
Changes requested: one short comment for the author.
Fixed and pushed. Round {n}: your review again.
Approved. Merge is unlocked.
Merged, after a review. Like every merge.
Back to the start. You are the reviewer again.
I built Python (FastAPI) services used in production and added Redis caching for peak-load performance. I built an LLM-based patient-triage workflow with LangChain and the OpenAI API.
I ran the team's AI-assisted engineering setup on Claude Code, with TypeScript agents and Model Context Protocol (MCP) servers.
FastAPI in production.
Agora, Twilio, Telnyx. I owned the voice and video integrations.
I shipped pricing plans and rollouts, post-chat upsells, email feature flags and A/B/C testing infrastructure. I built revenue-recovery services, including activation watchdogs and auto-chat triggers. I shipped prescription home delivery, pharmacy preference and cancellation flows.
Nobody claps. It compounds.
Software Engineer
Internal apps for a marketplace group: an inspection app and Dubizzle CRM.
I led the junior engineers on one project, and worked with distributed teams across several countries.
One app. Many tenants.
I built features for a multi-tenant inspection app in React Native, with tenant-specific theming, custom branding, feature toggles and regional settings.
Demo: one inspection app with three made-up tenants; the same code gives each its own colours, brand mark, features and regional settings.
A demo in your browser, with made-up features. Flip one to see the toggle.
km
mi
Photos
Signature
{name}: same code, its own look.
{feature} on for {name}.
{feature} off for {name}.
I delivered Dubizzle CRM features on mobile (React Native) and web (React.js), including dashboards with real-time updates, filtering and pagination.
Demo: a made-up dashboard where rows arrive live, with filters and pages. Here, new rows wait behind a pill until you bring them in, so the page you are reading does not jump.
0New
0Open
0Closed
| Row | Status | Arrived |
|---|
A demo in your browser. The rows are made up.
Record {n}
New
Open
Closed
All
1 new row
{n} new rows
Page {n} of {total}
No rows here yet.
1 new row in, on top.
{n} new rows in, on top.
New rows are waiting. Your page stays put.
Paused. No new rows for now.
Live again. Rows arrive every few seconds.
{n} rows waiting, so it paused. Bring them in.
Showing {filter}: {n} rows.
Page {n} of {total}.
Software Engineer
Client apps in e-commerce, services and healthcare.
Where it starts: React Native.
I shipped several cross-platform mobile apps in React Native for B2B and B2C clients in e-commerce and services, with complex navigation, Redux state management, form validation and reusable UI components.
I developed a telemedicine client app in React Native in a team of 3 developers, covering the iOS and Android apps through to the cloud backend.
I added offline sync with local storage, push notifications and background data fetching, and integrated third-party SDKs for payments, analytics and social authentication.
No signal? No problem.
The same app built twice: once in Flutter 3.47 and Dart 3 (flutter_bloc, go_router), once in React Native 0.86 with Expo SDK 57 and TypeScript.
I built customer, staff and admin apps from one Flutter and Dart codebase for mobile and web, with an English and Urdu interface that mirrors right to left.
Two builds. One app.
English and Urdu, mirrored.
Demo: one app screen in two languages: in Urdu the whole layout mirrors, right to left, from the back arrow to the progress bars.
In Urdu
Tap Next step in either language. The Urdu here is drawn as calligraphy-like strokes, not real words.
English: the screen reads left to right.
Urdu: the same screen, mirrored right to left.
Progress grows from the left.
In Urdu, progress grows from the right.
All done. The bookings start again.
I implemented real-time messaging with photos over a WebSocket relay written in Dart: reconnect with backoff, an offline outbox, relay acknowledgements, and sent, delivered and read marks.
Demo: two phones and a relay: with signal, a message gets one tick when the relay has it, two when it arrives and two filled ticks once it is read; without signal it waits in the outbox, then goes out when the signal comes back.
Next
You
Online
Outbox 0
Nothing sent yet.
Relay
Them
Online
Nothing here yet.
Reconnecting
A demo in your browser: nothing leaves this page.
Send
Start over
Online
No signal
Reconnecting
Reconnecting, waiting longer after each try
Back online
In the outbox
One tick: the relay has it.
Two ticks: delivered.
Two filled ticks: read.
Sent
Delivered
Read
Signal off. New messages wait in the outbox.
No signal. In the outbox: {n}.
Signal lost mid-send. The relay never got it, so it waits in the outbox.
Signal back. Reconnecting, waiting longer after each try.
Back online.
Back online. Sent from the outbox: {n}.
A fresh chat.
I encrypted every message and photo end to end (X25519, HKDF, AES-256-GCM) so the relay cannot read, alter or replay them, kept private keys in the platform's secure storage, and stripped location metadata from photos.
Demo: three views of one message: your phone sees the words, the relay sees only scrambled bytes that change with every send, and their phone sees the words again. Change one byte on the relay and their phone refuses it.
Your phone
The relay sees
NonceCiphertextTagChanged
Their phone
A demo in your browser, with Web Crypto: a fresh AES-256-GCM key made when this page opened, a new nonce for every message. Not the app's code.
Sent. A new nonce, so brand-new bytes on the relay.
Same words, new nonce: the relay sees completely different bytes.
Type something first.
Refused: the message was changed on the way.
One byte flipped on the relay. Their phone caught the change and refused it.
Another byte flipped. Still caught.
Encryption is not available here.
The relay sees {n} scrambled bytes and cannot read them.
Decrypted on theirs
Their phone refused it
Flutter: I wrote 452 tests, including whole conversations over real sockets, and fixed 31 of 32 findings from an independent code review.
React Native: I wrote every behaviour test-first with Jest and React Native Testing Library, 198 tests including full journeys through each app. I fixed 29 of 30 code-review findings, then tested the release APK on an Android emulator.
Real-time, end-to-end encrypted phone app (PWA). JavaScript, WebSockets, Cloudflare Workers, Node.js.
Several Macs. One phone.
I created an iPhone app that streams live sessions from several Macs over a WebSocket relay, with push notifications, and shipped about 30 releases.
Demo: three Macs stream their live sessions through a relay to one phone: every message is locked end to end, a push arrives when a session needs attention, and the phone can be logged out while the Macs keep working.
Press a Mac to stream its session to the phone.
A demo in your browser. The bars stand in for a session's output.
Revoke this phone
Log in again
Streaming {mac} to the phone.
{mac} is working. The phone is logged out, so nothing arrives.
{mac} is done for now. Try another Mac.
{mac} needs attention.
Phone logged out. The stream stops; the Macs keep working.
Logged in again. Caught up with {mac}.
Logged in again.
I secured every message with end-to-end AES-256-GCM encryption so the relay cannot read, replay or reorder what it carries, with keys derived by PBKDF2 and HKDF and a separately revocable login for each device.
I built the Node.js host daemon on the Claude Agent SDK, the relay on Cloudflare Workers and Durable Objects, and a screen-by-screen phone test harness for the app.
Bachelor of Science (BS) in Computer Science, National University of Computer and Emerging Sciences (FAST-NUCES), Lahore, Pakistan.
Origin story.
Foosball, chess and snooker. Anime and TV series.
Demo: a snooker table seen from above: six reds, a yellow and the white, with six pockets to pot them in.
0 of 7 potted
0 Shots
Drag back from the white and release to shoot, or tap where to aim and press Shoot. Keys: left and right aim, up and down set power, Space shoots.
Aim {a} degrees, power {p} of 10.
Drag back from the white, then release. Or tap where to aim and press Shoot.
Aimed. Press Shoot.
Fresh rack. Break when ready.
No pot. Line up another.
Missed everything. Line up another.
Pot! {left} left on the table.
{n} in one shot! {left} left on the table.
In-off. The white goes back in the D.
{n} potted, but the white went in too. It goes back in the D.
Table cleared in {shots} shots. Rack again for another go.
The table is clear. Rack again for another go.
Demo: a chess puzzle: White to move and checkmate in one, with the black king boxed into its corner.
White to move. Find the checkmate in one.
white king
white queen
white rook
white bishop
white knight
white pawn
black king
black queen
black rook
black bishop
black knight
black pawn
a legal move
{piece} on {sq}. Choose a square.
The {piece} on {sq} has no legal moves.
Put back. Pick any white piece.
That is a black piece. It is White's move.
{move}. Not mate. Try again.
{move}. Check, but not mate. Try again.
{move}. Checkmate: the king is smothered by its own pieces.
Solved. Press Reset to play it again.
Hint: the black king is boxed in by its own pieces. Which piece can jump?
Hint: the knight on g5.
Back to the start. White to move.
Tries: {n}
Demo: a foosball table: a ball rolls towards a rod of three men, and a kick at the right moment puts it in the goal.
0 Goals
0 Shots
Kick when the ball reaches a man. Space or a tap on the table kicks too.
Here it comes.
The ball is at a man's feet. Kick.
Roll the ball first.
Goal! {goals} from {shots}.
In off the post! {goals} from {shots}.
Right in the middle. Goal! {goals} from {shots}.
Off the post. A touch early.
Off the post. A touch late.
Wide. Too early.
Wide. Too late.
Missed it. The ball got past.
It got past. Kick when it reaches a man.
Demo: a small TV with a remote: each episode plays a short opening with a made-up title, and twelve episodes make a season.
Episode {e}
Episode {e}. Season finale
S{s} E{e}
Episode {e} of {n}
Now playing: season {s}, episode {e}. {title}.
Now playing the season {s} finale. {title}.
Season {s} begins. Episode 1. {title}.
If you are hiring, I am one message away.
shayanshokat@gmail.com. Lahore, Pakistan, UTC+5. CV on request by email.
Type your team's name. My seal and your name draw one pattern together.
Lights on: {n} of {total}.
All six lanterns lit. Your move.
All lit
{time} in Lahore, {et} in Toronto and New York
World clock: turn to try another hour
Your seal and mine, once you type a name.
Our seal, for {name}. The same name always draws the same seal.
Copied.
Copy failed. The address is shayanshokat@gmail.com.