Problem
Payment acceptance tends to accumulate: a checkout page built once, a payment link pasted into an email, a card reader at the counter, and receipts reconciled by hand afterwards. Each channel works, none of them knows about the others, and the only place the payments meet is a bank statement at the end of the month.
Our role
Integration and interface build: checkout, in-person flow, and the record that sits behind both.
Approach
Online payments go through Stripe Elements so card details reach Stripe and never touch the application. In-person payments use Stripe Terminal where the hardware and account support it. Payment links are generated for the cases where a customer would rather pay from an invoice than through a form.
All three write to the same payment record in Supabase, which is what makes a single status view possible: pending, succeeded, or refunded, regardless of which channel produced it. The application is a static build on Cloudflare Pages, so the server side is limited to the functions that handle secret operations.
Outcome
The checkout, terminal, and payment-link paths are implemented as separate entry points over one record. Implemented, tested, and live are three different states, and this write-up claims only the first until each flow has been exercised on a real account.
Notes
No secret keys, webhook endpoints, or customer payment details are included. Test mode is assumed everywhere; production readiness is a separate decision.