AzureMarket: Game Top-Up Marketplace and GCash Payment Platform · Matt Alexius

AzureMarket is a game top-up store with a fulfillment queue in Postgres and its own self-hosted GCash payment gateway. Built with Next.js 16, Supabase, and Cloudflare Workers.

AzureMarket is a store where players buy in-game currency. Up front there's checkout, order tracking, vouchers, and reviews that only verified buyers can leave. Behind it, a job queue in Postgres handles delivery and a self-hosted GCash gateway handles payments.

The problem

Top-up shops run on trust and speed. A player pays and waits, and if the currency is slow to arrive or the payment goes missing somewhere between the e-wallet and the store, they don't come back. Most small sellers still confirm orders by hand with GCash screenshots and chat messages. That works until volume picks up, and when something goes wrong there's no reliable record to check.

The approach

I treated every order as a pipeline instead of a chat thread. The storefront is Next.js 16 with Supabase as the backend. Fulfillment runs off a job queue inside Postgres, so each order has a state, failed jobs retry on their own, and receipts go out by email. Payments go through a self-hosted GCash gateway that talks to the store over signed webhooks. With a reconciliation step and a refunds ledger on top, any peso can be traced from checkout to delivery.

What I did

  • Built the storefront: checkout, order tracking, vouchers, and verified-purchase reviews.
  • Set up the fulfillment pipeline on a Postgres job queue, with retries and email receipts.
  • Wrote the self-hosted GCash gateway, including signed webhooks, payment reconciliation, and a refunds ledger.
  • Fixed checkout race conditions by moving pricing to the server and making payment matching idempotent.
  • Made the admin panel for orders, payments, the catalog, customers, affiliates, and reviews.

What I learned

You can't write payment code on "probably fine." The concurrency bugs only showed up when two checkouts raced each other. The fix was to move pricing to the server and make payment matching idempotent, so the same webhook can arrive twice without charging or fulfilling twice. I also learned that the admin panel is half the product. If the team can't find and fix an order in one place, every edge case turns into a support ticket.