Nobody has started this yet — be first.
Business impact
captured_at and settled_at are exactly the kind of timestamps finance and support pull for "how long did settlement take" reporting and for answering a merchant's "why hasn't my money arrived" ticket. If they're swapped on every single read, every report built on them is silently wrong -- not crashing, not erroring, just quietly showing the wrong timeline for every payment in the system. This is the kind of bug that doesn't get caught until someone cross-checks the API's numbers against the raw database and the two don't match, at which point nobody trusts either.
Problem
After a payment is captured (but not yet settled), GET /v1/payments/{id} -- and any other caller of Store.Get/GetTx -- shows captured_at: null and settled_at: <the actual capture time>.
Current behavior
A payment that has only been captured (not settled) shows a non-null settled_at and a null captured_at when read back.
Expected behavior
captured_at should reflect when the payment was captured; settled_at should be null until it's actually settled.
Steps to reproduce
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket08 -v
Or manually: create a payment, let it auto-authorize, capture it, then GET /v1/payments/{id} and compare captured_at/settled_at against what you'd expect.
Why this matters
scanPayment does one row.Scan(&p.ID, ..., &p.AuthorizedAt, &p.CapturedAt, &p.SettledAt, &p.RefundedAt, ...)-shaped call, and its argument order must exactly match the column order of the SELECT that produced the row -- paymentColumns, declared right above it as a shared string constant. The two lists have drifted out of sync for the middle two columns.
Suggested approach
Put paymentColumns and the Scan(...) argument list from scanPayment side by side and check each position matches, specifically around captured_at/settled_at. This is purely an argument-ordering fix inside scanPayment -- no SQL, no migration, no other function needs to change.
Acceptance criteria
Verification
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket08 -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/captured-settled-scan-swapFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/captured-settled-scan-swapSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign indocker composepaystate/paystate/go.modreplace reliable => ../reliabledocker compose up --buildpaystate/context: ..docker-compose.ymlCREATED_TIMEOUTAUTHORIZED_TIMEOUTWork the tickets in tickets/docs/ (ticket-01 through ticket-25); each names one Go test in tickets/tests/:
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket01 -v # a single ticket
TEST_DATABASE_URL=postgresql://... ./tickets/run_tickets.sh # all 25, clean pass/fail summary
Tickets that don't touch Postgres (02, 03, 04, 05, 06, 14, 15, 24) run fine without TEST_DATABASE_URL set; the rest report FAIL with a "TEST_DATABASE_URL not set" message if it's missing. make test-race runs the full go test -race -p 1 ./... suite the same way.
Level 1
Fix a bug
Read existing behaviour, correct it.