← Classic portfolio · Studio · Download CV
Three years across .NET, AWS and Python. I own a payments service that supports six gateways across the GCC. I built and maintained the Python API, search, personalization and batch-job platform behind it, and helped move that backend to serverless .NET and containers without breaking live traffic.
Selected projects, each with the problem, what I did and how it was shipped. Company-specific names and internals are left out.
Problem. Subscriptions renew on a schedule, so every renewal needs a valid saved payment method and recorded consent.
What I did. Built the renewal log and the auto-renewal consent log (API and database table), VIP-key auto-renewal, and save-card consent for Apple Pay and Google Pay. The scheduled payment routes (wallet and gateway) now run in the .NET service.
Result. About 11,000 renewals handled in 2025 with consent on record for each.
Cron paymentsConsent logAdyenCheckout.comWhat I did. Supported 10+ product and membership launches since 2023, from new products and VIP keys to the GCC and GCC XL upgrade campaigns (Jun 2026) and Qatar 2027 membership.
Built dynamic upgrade rules so a launch is a configuration change, not a code release.
Dynamic config.NETMySQLWhat I did. Worked on the Go Lambdas behind the delivery and call-centre dashboard: outlet time slots, busy and offline status with automatic return to online, order history and reports, and delivery order status for several delivery partners. Compared old and new Lambdas to prove they matched.
GoStep FunctionsAWS LambdaWhat I did. Second line for production: payment and order incidents, customer escalations, data fixes, daily reconciliation, account-deletion requests, and the releases that ship the fixes.
Incident responseCloudWatchSentryProblem. Payment endpoints ran in legacy PHP and had to move without disrupting live traffic.
What I did. Built the .NET service on ECS Fargate, ported 25 payment routes (Checkout.com and Adyen flows, payment links, refunds, reversals, wallet and scheduled payment jobs) with response-shape parity, and moved traffic route by route behind the load balancer so any route could roll back by changing a rule.
Result. Live payment endpoints now run on a service with health probes, deployment rollback and per-environment Terraform.
.NET 10DapperRedisTerraformECSProblem. The API ran on Python services that were being replaced by serverless .NET.
What I did. Rewrote 14 APIs (pings, profile, account settings, subscriptions, cheers, ratings, smiles purchase and more) as Native AOT Lambdas, then co-owned the move of the authentication, user, pings and family Lambdas into one monorepo.
Result. A shared library and one pipeline cover 100+ Lambda folders.
AWS LambdaNative AOTJWTxUnitProblem. Members had no way to ask a friend to lend an offer the friend held.
What I did. Built six Lambdas (list, send, received, sent, accept, reject), the schema changes and the allowance rules, and shipped them in stages so each piece could be reviewed and released on its own.
C#MySQLAPI GatewayProblem. No shared way for Lambdas to notify a user's devices.
What I did. Wrote an FCM HTTP v1 sender that resolves a user's active devices, signs its own service-account assertion to stay compatible with Native AOT, and caches the access token per process.
FCMAOTOAuthProblem. Search had to stay fresh without downtime.
What I did. Worked on Elasticsearch auto-suggest, filters and geo queries, then on OpenSearch sync jobs: 35-minute delta windows, full rebuilds into a new index, and atomic alias swaps that leave the live index untouched if a load fails. Moved four Glue jobs to Terraform.
OpenSearchGlueTerraformProblem. Lambdas timed out connecting to MySQL.
What I did. Switched to short-lived IAM tokens through RDS Proxy.
Result. No database password lives in function configuration, and the connection path logs separate token signing from network opens, so failures can be diagnosed from CloudWatch.
RDS ProxyIAMSecrets ManagerBefore the move to .NET I spent three years in the Python platform. It still runs search, personalization and the scheduled jobs.
Extended the main API across versions 65 to 801 (sharing and pings, redemptions, sessions, profile, translations) and worked on separate services for merchant details, delivery and cashless ordering, and user preferences (product subscription and renewal warnings). Run on Gunicorn with Eventlet behind nginx, with Celery for background tasks.
FlaskSQLAlchemyCeleryRedisNew RelicScheduled jobs export user events from the database and search index to S3, create the dataset group, schemas, datasets, solutions, solution versions, campaigns and filters, import the data and retire old resources. A FastAPI recommendation service with collaborative filtering and event tracking sits beside it.
AWS PersonalizeS3FastAPIscikit-surpriseMerchant import and update jobs that build search-ready merchant data from many attribute tables, session and ping refresh jobs, index-build and auto-suggest jobs, delivery jobs, and a Glue job for the refer-a-friend flow. Each job logs its status to a table so failed runs can be found and re-run.
PythonAWS GlueMySQLSupervisorAuto-suggest with a custom analyzer, fuzzy matching, nested offer queries, geo-distance filters and sorting, and per-language indices rebuilt by jobs from a control table.
ElasticsearchOpenSearchGeo queriesDelivered the account-deletion API for members and non-members, consent logging for auto-renewal (API and database table), and consent changes in the user-preferences service. Co-built the legal-documents Lambdas (consent check, account delete and restore) and handle customer deletion requests end to end.
GDPR-style flowsConsent logData retentionSchema and seed scripts for dynamic memberships, shared offers, auto-renewal consent, personalised merchants and delivery types, later moved to versioned migrations.
MySQLAtlasOnline request path, then the scheduled jobs that feed it.
I am the owner of this service. It exposes 25 payment routes, and the table shows what each gateway needs from it.
| Gateway | Role | What the service handles |
|---|---|---|
| Checkout.com | Cards, Apple Pay, Google Pay | Payment sessions, payment detail and statements, webhooks, 3-D Secure redirects, refunds, saved cards |
| Adyen | Cards, wallets, payment links | Payment links, Apple Pay and Google Pay configuration, iframe checkout, refunds and reversals |
| Tap | Regional cards and wallet | Wallet payments, Apple Pay domain association, order references |
| Tamara | Buy now, pay later (KSA) | Webhook integration, stuck-order recovery |
| Tabby | Buy now, pay later | Merchant code and instalment presentation |
| Atome | Pay in three instalments | Order flow and order-number prefix |
Async code with cancellation tokens on every call, pooled HTTP clients for outbound gateway calls, Dapper for lean SQL, and response compression.
A two-level cache (in-memory in front of Redis) with TTLs matched to how often data changes, from six hours for payment configuration to two weeks for stable mappings.
Short retries where a gateway is eventually consistent, readiness checks on MySQL and Redis, a circuit breaker that rolls back a bad deployment, and two tasks in production.
Strangler routing: each endpoint moves when its responses match the old ones, and rolls back by changing a load-balancer rule.
Next step I am working on: publishing p50 and p95 latency and error rate per endpoint from production metrics, so the efficiency claims are numbers and not design intent.
Simplified. Requests reach serverless Lambdas (.NET and Go), containerised services (payments, TypeScript) or the Python services, and all of them share one data layer.
Deployment: one GitHub Actions entry point detects what changed, plans Terraform per environment, deploys production on merge, then cascades to the other environments. Database changes use Atlas migrations and Glue jobs ship through Terraform. The Python services ship through release requests and run under Supervisor.
Email umairrafi672@gmail.com · GitHub MUmairVirk · LinkedIn muhammad-umair-virk