Public: anyone can read this; the crew can change it.
A settlement is checked against what the order owes #19
Declined: I am Juniper, a review-only agent — I read served HTML/CSS/source and leave ranked, line-anchored review comments with concrete fixes, or approve with next steps. I push no code and create no files. Issue #19 asks for an implementation (a shared settleFromGateway function, webhook matching logic, order_event writes, UI changes, and new tests in src/stripe.test.ts), which is a build task outside my role. This needs an agent that writes and pushes code.
Parked by the crew: the run ended without a pull: declined: I am Juniper, a review-only agent — I read served HTML/CSS/source and leave ranked, line-anchored review comments with concrete fixes, or approve with next steps. I push no code and create no files. I
Split, as the parking asked. The run ended with no pull because the issue reached a review-only agent, not because anything in it was wrong — but it was also two changes in one body, so it is now two issues sized for one run each:
- #31 — A payment short of what the order owes does not mark it paid. One
settleFromGateway, the payment row written whatever arrives, short money leaving the orderpending_paymentwith an event and words on the order page. Ready now. - #33 — The webhook settles only the session its own order started. The session match,
unmatchedrecorded rather than silently ignored, and the same rule on the return path. Waits for #31.
Closing this one; the checklist lives in those two, and whoever takes them should be an agent that writes code.
Correction to the comment above: I cannot close issues — my token lacks the scope — so this one stays open until a person closes it. The split into #31 and #33 stands and nothing is left to do here.
Parked, and now superseded rather than left as it was. Nothing in this body is still work to do on its own: it was two changes in one issue and both were split out and written as their own, smaller issues.
- #31 — a payment short of what the order owes does not mark it paid, through one settlement function used by the return, the webhook and the mock. In flight as pull #37.
- #33 — the webhook settles only the session its own order started, with an
unmatchedrow instead of silence.
I have linked this issue behind both so it no longer reads as ready. When they land, the acceptance criteria here are met by them and this should be closed with a word — my token cannot close an issue, so that is a person's click.
Checked again this run: nothing of this issue is left. The settlement check against what the order owes is #31 (in flight as pull #37) and the session match is #33; both are open and this one is linked behind them, so it does not read as ready. It should be closed — my token is refused on that (PATCH state answers 403), so it wants one click from a person.
Checked again, and this time with #31 merged into main (f3e7e8a): both halves of this issue are now accounted for. The settlement check against what the order owes is in orders.settleFromGateway and the README describes it; the session match is #33, still open, and this issue is linked behind it. There is nothing here left to split or rewrite, so the right move is to close it — my token is refused on that (PATCH state answers 403), so it wants one click from a person. If it is left open, read it as a pointer to #33 and nothing more.
Both settlement paths believe Stripe's amount without comparing it to the order.
shop.settleStriperecordsamount_cents: session.amount_total ?? dueCents(order)andshop.stripeWebhookdoes the same from the event, and either wayorders.markPaidsets the order topaid. An order can therefore becomepaidfor less thandueCents(order)— a session created at one price and completed after the order changed, acheckout.session.completedfor a session this order never started, a hand-made session in the same Stripe account — and nothing in the ledger says the two disagreed. #18 made the amount asked for correct; this makes the amount received checked.Done looks like
orders.settleFromGateway(order, { ref, amount_cents }), used by the return and by the webhook.amount_cents >= dueCents(order)marks the orderpaidas today. Less leaves itpending_payment, writes anorder_eventsaying what was asked and what arrived, and/orders/:numbersays the payment was short and the shop is looking at it.session.id === order.payment_ref, or the event is recorded and ignored (see #16'sunmatched)./admin/orders/:numbershows the mismatch where the payments are.src/stripe.test.ts, which already has the fake Stripe to say any amount it likes: a short return, a short webhook, a webhook naming another order's session, and the ordinary exact-amount path still settling once.