Problem
Invoice payments cross an external provider boundary. The application must keep its own record of intent and status while handling provider initiation failures and asynchronous callback results.
Fintech · Payment lifecycle · Go
A payment workflow that creates invoices, initiates hosted payment links, records callbacks, and keeps invoice status in sync.
Overview
A Go and MongoDB backend organized around domain, repository, use-case, delivery, and gateway layers, with a Next.js client for invoice and payment flows.
Invoice payments cross an external provider boundary. The application must keep its own record of intent and status while handling provider initiation failures and asynchronous callback results.
Implemented the invoice and payment lifecycle, the SantimPay gateway boundary, callback-driven status updates, and persistence-backed transaction history.
Architecture
Engineering
Persist an invoice as PENDING before initiating payment so the application has a durable record of the attempt.
Mark initiation failures explicitly and store the returned payment URL and provider reference on success.
Isolate provider-specific payloads and signed-token handling behind a PaymentGateway interface.
Give outbound payment requests an explicit 10-second HTTP client timeout.
invoice.Status = "PENDING"
if err := invoices.Create(invoice); err != nil { return err }
url, reference, err := gateway.CreatePayment(invoice)
if err != nil {
_ = invoices.UpdateStatus(invoice.ID, "FAILED")
return err
}
return invoices.UpdatePaymentInfo(invoice.ID, url, reference)Stack & evidence