Checkout gateways for three payment apps, and a matcher that confirms the order when the payment alert arrives.
Role
Sole engineer: gateways, matching engine, and mail verification
Stack
PHPWordPressWooCommerceGoogle Apps ScriptHMACDKIM / DMARCREST API
Trigger: 01Payment alertNotification lands in the inbox
Process: 02Signed forwardInbox script HMACs the request
Process: 03Authenticity checkDKIM or DMARC, never SPF alone
Process: 04MatchedOrder number, then amount, then payer
Output: 05Order confirmedAnything unclear waits in review
An alert clears two separate checks before it can touch an order.
The problem
Orders paid by peer-to-peer transfer sat On hold until somebody read the inbox and matched the payment to an order by hand. It was slow for the customer and got worse with volume. A payment email is also the kind of thing worth forging.
What I built
Nine checkout gateways: three payment apps across three receiving accounts, generated from one canonical source by a clone script, so a fix gets written once instead of nine times. Each stamps its own account reference into the order data and reports on itself from its own admin screen.
Payment details stay hidden until the order exists. Show a QR code at checkout and someone sends a round number matching no order. Show it on the confirmation page and both the amount and the order number are already real.
An inbox script forwards each payment alert to a signed endpoint. Two separate layers of trust: the signature proves the script sent the request, and a second check proves the payment service sent the email, requiring DKIM or DMARC alignment. I never accept SPF on its own, since it vouches only for the envelope sender's domain.
Matching runs in tiers: order number from the payment note, then a unique exact amount, then a payer-name tiebreak. Split payments accumulate against the order, underpayments stay on hold, and an overpayment confirms the order and flags the excess for a manual refund.
Anything ambiguous goes to a review queue instead of a guess. Every email, decision and override lands in an append-only audit log, and dry-run mode ships on, so you can watch a week of decisions before it touches an order.
The parser handles two generations of one bank's template. The newer one forwards the sender's memo and the older one leaves it in the sender's app, so the order-number tier only runs when the memo is there.