● Open for small jobs · remote / async · Thailand time (UTC+7)
Find where the data goes wrong, then fix it
Junior backend / API developer. I investigate backend bugs, connect APIs, and review an existing system before it is redesigned. I always write down what I understood, so you can check it first.
Try it:webhook signatures
A payment system must be sure a request really came from the provider. Edit the message a little and see why I check the raw bytes before reading any content.
1 · Sender: signature of the original message
…
2 · Receiver: the message that arrived (editable)
3 · Receiver: signature computed from what arrived
…
4 · Compare (constant time)
The bytes differ from what was signed, so the content is never read.
Try "Add one space" or "Re-format the JSON": the meaning of the JSON does not change, but the signature no longer matches, because a signature is computed over bytes. The receiver must check the raw body before parsing, then read the values. My system works in that order.
This example computes HMAC-SHA256 in your browser with a made-up key, demo-secret-key. It has nothing to do with any real system's key.
Try it:the many ways a webhook arrives
Real situations a payment system must survive: retries, paying twice through two channels, wrong amounts, a QR that expires before the money arrives. Pick one and watch each decision step.
Incoming request · POST /beam/webhook
x-beam-signature: valid
body: SUCCEEDED · THB · 15000 satang
Before the webhook
- Bill total
- ฿150.00
- payment
- pending
- order
- served
- Note
- —
After the webhook
Press "Send webhook" to see the result
A simulation of the real code in rice-shop-pos (promptpay.rs · webhook / settle). It is not connected to Beam or any live system.
rice-shop-pos
A personal project: a multi-branch POS PWA for rice shops. The code is public. It is not work from my employer, and I make no claims about users or revenue.
- Money
- Stored in satang as whole numbers. No decimals.
- Financial history
- Orders and payments are append-only. A cancellation or refund is a status with a reason. Nothing is deleted.
- PromptPay payments
- A QR code is created through Beam, then the system waits for a webhook whose signature is checked on the raw bytes. A bill closes only when Beam reports success for exactly the right amount. Staff have no button to close it by hand.
- Many shops
- Every table has a shop_id. The owner signs in through Cloudflare Access. Staff use a PIN, with a lockout after repeated wrong tries.
- Reading slips
- Slips go to a reader on the owner's PC first. If that is offline, Workers AI is the fallback. A job sweeps the queue every minute.
- Shipping
- CI runs on every push and PR. A deploy only happens when you push a tag that is on main.
- Rust (axum)
- Cloudflare Workers
- D1
- R2
- React PWA
- GitHub Actions
How I work
- Write down what I understood, for you to check firstA summary of the problem, my assumptions and open questions, confirmed by you before I start.
- Walk through edge cases that touch money and stateDouble presses, cancelling halfway, data arriving twice or out of order.
- AI is my assistant, not the owner of the workI use it to analyse, compare options and review. The logic and the checking are my responsibility.
- Deliver in steps you can acceptPayments are split into milestones. If the first step is not right, you do not have to continue.
What I don't take on
- Senior-level work, or owning a production architecture
- DevOps / running live systems around the clock
- Design-heavy frontend work or native mobile apps
- Real work without pay
Tell me aboutthe problem
I have a full-time job, so I work asynchronously in the evenings and on weekends. I reply within 1 working day.