Fix invoice tracking for retried payments (issue #864)#876
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughChangesPaid invoice reconciliation
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
When a payout to a buyer fails and they run /setinvoice with a new invoice, the retry pays that new invoice (stored only on pendingpayments.payment_request) while order.buyer_invoice keeps pointing at the original, failed invoice. Any order that reaches SUCCESS via a retry therefore records an invoice that was never paid, which breaks reconciliation/auditing against the node and misleads support when inspecting the order. The issue proposed overwriting order.buyer_invoice with the paid invoice, but the PR reviewer objected to losing the original (it makes debugging harder) and asked to persist the paid invoice alongside it. This does exactly that: - Add a new Order field buyer_invoice_paid; the original buyer_invoice is never overwritten. - Record the actually-paid invoice in completeOrderAsSuccess via a new explicit paidInvoice parameter, written in the same atomic compare-and-set as the SUCCESS flip so it is consistent under concurrent callers. - Thread the paid invoice through every success path so the field is reliable, not only the direct retry: the fresh payRequest success, and all healConfirmedOrder call sites (original invoice confirmed on poll, a previous pending payment confirmed, the current pending payment confirmed, and the /setinvoice heal in scenes.ts). In each case the caller passes the invoice that was really paid (pending.payment_request or the original buyer_invoice). - Log the real payment hash (payment.id) instead of pending.hash, which stores the hold invoice hash and made the success log line misleading. Consumers reconciling against the node should read buyer_invoice_paid when present and fall back to buyer_invoice otherwise. Tests: - tests/jobs/pending_payments.spec.ts: a successful retry forwards pending.payment_request as the paid invoice and preserves buyer_invoice; a non-confirming payment leaves both fields untouched. - tests/util/completeOrder.spec.ts: buyer_invoice_paid is written in the atomic $set and in memory when paidInvoice is given, omitted when it is not, and left untouched (with the order unchanged) when the compare-and-set loses the race. Closes #864. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pid19awYpjQUa2cRZKnRaD
32c2b35 to
ce3fddb
Compare
Luquitasjeffrey
left a comment
There was a problem hiding this comment.
tACK
Now it stores the invoice actually paid on the first payment attempt and l+all the subsequential payment attempts if the first one fails

Summary
This PR fixes a critical issue where retried payments were overwriting the original buyer invoice, losing audit trail information. The solution introduces a new
buyer_invoice_paidfield to track the invoice that was actually paid while preserving the originalbuyer_invoicefor reconciliation and debugging.Key Changes
buyer_invoice_paid: Added to the Order schema to store the invoice that was actually paid, separate from the originalbuyer_invoicecompleteOrderAsSuccess: Now accepts an optionalpaidInvoiceparameter that is recorded atomically in the database update, ensuring the paid invoice is captured without losing the originalhealConfirmedOrder: Now forwards the paid invoice information through the call chain so confirmed orders also record the real paid invoiceattemptPendingPaymentsjob now passes the actual paid invoice (pending.payment_request) to completion routines in three scenarios:payment.id) instead of the hold invoice hash for clarity/setinvoicescene now records the submitted invoice as the paid invoice when healing a confirmed orderImplementation Details
paidInvoiceparameter is optional to maintain backward compatibility$setoperations to ensure consistencyTesting
Added regression tests covering:
buyer_invoice_paidwhile preserving the originalhttps://claude.ai/code/session_01Pid19awYpjQUa2cRZKnRaD
Summary by CodeRabbit
buyer_invoice_paidvalue to record the invoice that was actually paid, while preserving the originalbuyer_invoice.