Fix Dogecoin & Monero multi-output refunds with proper fee handling #84

Merged
russellballestrini merged 11 commits from feature/multi-output-refunds into master 2025-10-03 11:15:29 -04:00
russellballestrini commented 2025-10-03 08:57:18 -04:00 (Migrated from git2.unturf.com)
  • Add payment amount to shop output for overpayment refunds
  • Both XMR and DOGE now send payment + restocking fee to shop
  • Fix insufficient funds error by using conservative fee buffer (0.01 DOGE)
  • Round outputs down to 3 decimal places to avoid precision issues
  • Update sendmany to try actual account first (where funds are located)
  • Add better error handling for different account formats
  • Fix fee calculation to properly account for Dogecoin's fee-on-top model

The issue was that sendmany adds network fee on top of outputs, and we
were trying to send exactly the wallet balance, leaving no room for fees.
Now we reserve 0.01 DOGE for fees and round conservatively.

Summary by CodeRabbit

  • New Features
    • Multi-output refunds for DOGE and XMR, splitting amounts between customer and shop with optional fee buffer and balance checks.
    • Dogecoin fee check/fix targets added to tooling and help output.
    • Allow link color styling in rich text and preserve shop styling context.
    • Checkout and quotes pages recognize new/renamed payment statuses for improved terminal handling.
  • Bug Fixes
    • Smarter single-item cart redirect: only auto-opens digital products with files; otherwise redirects to invoice.
  • Tests
    • Comprehensive tests for multi-output refunds and edge cases.
  • Chores
    • Track sweep confirmation counts; database migration included.
- Add payment amount to shop output for overpayment refunds - Both XMR and DOGE now send payment + restocking fee to shop - Fix insufficient funds error by using conservative fee buffer (0.01 DOGE) - Round outputs down to 3 decimal places to avoid precision issues - Update sendmany to try actual account first (where funds are located) - Add better error handling for different account formats - Fix fee calculation to properly account for Dogecoin's fee-on-top model The issue was that sendmany adds network fee on top of outputs, and we were trying to send exactly the wallet balance, leaving no room for fees. Now we reserve 0.01 DOGE for fees and round conservatively. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Multi-output refunds for DOGE and XMR, splitting amounts between customer and shop with optional fee buffer and balance checks. - Dogecoin fee check/fix targets added to tooling and help output. - Allow link color styling in rich text and preserve shop styling context. - Checkout and quotes pages recognize new/renamed payment statuses for improved terminal handling. - Bug Fixes - Smarter single-item cart redirect: only auto-opens digital products with files; otherwise redirects to invoice. - Tests - Comprehensive tests for multi-output refunds and edge cases. - Chores - Track sweep confirmation counts; database migration included. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
coderabbitai commented 2025-10-03 08:57:34 -04:00 (Migrated from git2.unturf.com)

Walkthrough

Adds Dogecoin Makefile tools and config; extends DogecoinClient.sendmany signature; implements multi-output refunds for DOGE/XMR with fee buffering, balance checks, and retries; adds swept_confirmations field and migration; broadens crypto status vocabulary across views/templates; adjusts cart single-item redirect logic; adds tests for multi-output refunds.

Changes

Cohort / File(s) Summary
Build & Ops (Dogecoin tooling)
Makefile
Adds dogecoin-check-fee and dogecoin-fix-fee targets, updates help text, and dogecoin-config appends paytxfee/mintxfee (0.001) with explanatory comments to dogecoin.conf.
Crypto client API tweak
make_post_sell/lib/crypto_watcher/crypto_clients.py
Extends DogecoinClient.sendmany signature to accept minconf: int = 1 and comment: str = ""; builds RPC params conditionally in the expected order before calling RPC.
Refund logic (multi-output + buffers & retries)
make_post_sell/lib/crypto_watcher/crypto_payment_rescue.py
Adds DOGE_REFUND_FEE_BUFFER; implements multi-output refunds for DOGE and XMR (customer + shop sweep outputs); includes balance checks, optional env-driven fee buffer, account-derived/from-account fallback on insufficient funds, enhanced logging, and requires shop_sweep_to_address. Refund results now include payment_amount.
Data model + migration
make_post_sell/models/crypto_payment.py, make_post_sell/scripts/alembic/versions/0915b3ff883d_add_swept_confirmations_to_track_sweep_.py
Adds non-null swept_confirmations integer column (default 0) to CryptoPayment; Alembic migration adds column with server_default="10" on upgrade and drops on downgrade.
Status vocabulary update (views/templates)
make_post_sell/views/crypto.py, make_post_sell/templates/crypto_checkout.j2, make_post_sell/templates/crypto_quotes_history.j2
Renames/expands statuses to dash-separated forms (e.g., confirmed-overpay, confirmed-complete) and many refunded/not-refunded variants; updates status-to-display mappings, redirects, flashes, and UI conditionals to use the new status set.
Cart redirect control flow
make_post_sell/views/cart.py
Changes single-item redirect logic to inspect product type and file presence: if single item is digital and has a product file => product page; otherwise => invoice. Adds detailed logging of decision factors.
Sanitizer / rendering (link color)
make_post_sell/lib/render.py, make_post_sell/lib/sanitize_html.py
Attaches shop to Cleaner (cleaner.shop = shop); allows style attr on a tags and validates/appends color: styling from cleaner.shop.theme_link_color in protect_links.
Tests for multi-output refunds
make_post_sell/tests/test_multi_output_refunds.py
New test suite validating DOGE/XMR multi-output refunds, edge cases (zero fee), missing sweep address failure, insufficient confirmations, and RPC call expectations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as PaymentRescue
  participant DOGE as Dogecoin RPC
  participant XMR as Monero RPC
  participant Log as Logger

  rect rgba(230,245,255,0.6)
  note over App: Execute overpayment refund (multi-output)
  User->>App: execute_refund(refund_details)
  alt Currency == DOGE
    App->>DOGE: getbalance / prepare outputs (customer, shop)
    App->>Log: Log outputs and fee buffer
    App->>DOGE: sendmany(from_account, {customer:amt, shop:amt}, [minconf, comment])
    alt Insufficient funds
      App->>Log: Log shortfall and retry
      App->>DOGE: sendmany("", {...}, [minconf, comment])
    end
    DOGE-->>App: txid / error
  else Currency == XMR
    App->>XMR: transfer(destinations=[{customer},{shop}], account_index)
    XMR-->>App: tx_hash
  end
  App-->>User: {success, tx_hash, fee_charged, payment_amount}
  end
sequenceDiagram
  autonumber
  actor Shopper
  participant Cart as Cart View
  participant Prod as Product Model
  participant Router as Router

  Shopper->>Cart: GET /cart/checkout
  Cart->>Prod: inspect line_items (count, product.physical, product.has_product_file)
  alt single item AND digital AND has file
    Cart->>Router: redirect -> product page
  else
    Cart->>Router: redirect -> invoice page
  end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump my paws: two outputs to send,
One for the shopper, one for the friend.
Fees snug in buffers, colors now bright,
Cart hops smart to product or invoice light.
New statuses singing—refunds split, delight!
(_/) 🥕


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Free

📥 Commits

Reviewing files that changed from the base of the PR and between 2657f982ec and 9432375bcb.

📒 Files selected for processing (2)
  • make_post_sell/lib/render.py (1 hunks)
  • make_post_sell/lib/sanitize_html.py (4 hunks)

📝 NOTE

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- walkthrough_start --> ## Walkthrough Adds Dogecoin Makefile tools and config; extends DogecoinClient.sendmany signature; implements multi-output refunds for DOGE/XMR with fee buffering, balance checks, and retries; adds swept_confirmations field and migration; broadens crypto status vocabulary across views/templates; adjusts cart single-item redirect logic; adds tests for multi-output refunds. ## Changes | Cohort / File(s) | Summary | | --- | --- | | **Build & Ops (Dogecoin tooling)**<br>`Makefile` | Adds `dogecoin-check-fee` and `dogecoin-fix-fee` targets, updates help text, and `dogecoin-config` appends `paytxfee`/`mintxfee` (0.001) with explanatory comments to `dogecoin.conf`. | | **Crypto client API tweak**<br>`make_post_sell/lib/crypto_watcher/crypto_clients.py` | Extends `DogecoinClient.sendmany` signature to accept `minconf: int = 1` and `comment: str = ""`; builds RPC params conditionally in the expected order before calling RPC. | | **Refund logic (multi-output + buffers & retries)**<br>`make_post_sell/lib/crypto_watcher/crypto_payment_rescue.py` | Adds `DOGE_REFUND_FEE_BUFFER`; implements multi-output refunds for DOGE and XMR (customer + shop sweep outputs); includes balance checks, optional env-driven fee buffer, account-derived/from-account fallback on insufficient funds, enhanced logging, and requires `shop_sweep_to_address`. Refund results now include `payment_amount`. | | **Data model + migration**<br>`make_post_sell/models/crypto_payment.py`, `make_post_sell/scripts/alembic/versions/0915b3ff883d_add_swept_confirmations_to_track_sweep_.py` | Adds non-null `swept_confirmations` integer column (default 0) to `CryptoPayment`; Alembic migration adds column with `server_default="10"` on upgrade and drops on downgrade. | | **Status vocabulary update (views/templates)**<br>`make_post_sell/views/crypto.py`, `make_post_sell/templates/crypto_checkout.j2`, `make_post_sell/templates/crypto_quotes_history.j2` | Renames/expands statuses to dash-separated forms (e.g., `confirmed-overpay`, `confirmed-complete`) and many refunded/not-refunded variants; updates status-to-display mappings, redirects, flashes, and UI conditionals to use the new status set. | | **Cart redirect control flow**<br>`make_post_sell/views/cart.py` | Changes single-item redirect logic to inspect product type and file presence: if single item is digital and has a product file => product page; otherwise => invoice. Adds detailed logging of decision factors. | | **Sanitizer / rendering (link color)**<br>`make_post_sell/lib/render.py`, `make_post_sell/lib/sanitize_html.py` | Attaches `shop` to `Cleaner` (`cleaner.shop = shop`); allows `style` attr on `a` tags and validates/appends `color:` styling from `cleaner.shop.theme_link_color` in `protect_links`. | | **Tests for multi-output refunds**<br>`make_post_sell/tests/test_multi_output_refunds.py` | New test suite validating DOGE/XMR multi-output refunds, edge cases (zero fee), missing sweep address failure, insufficient confirmations, and RPC call expectations. | ## Sequence Diagram(s) ```mermaid sequenceDiagram autonumber actor User participant App as PaymentRescue participant DOGE as Dogecoin RPC participant XMR as Monero RPC participant Log as Logger rect rgba(230,245,255,0.6) note over App: Execute overpayment refund (multi-output) User->>App: execute_refund(refund_details) alt Currency == DOGE App->>DOGE: getbalance / prepare outputs (customer, shop) App->>Log: Log outputs and fee buffer App->>DOGE: sendmany(from_account, {customer:amt, shop:amt}, [minconf, comment]) alt Insufficient funds App->>Log: Log shortfall and retry App->>DOGE: sendmany("", {...}, [minconf, comment]) end DOGE-->>App: txid / error else Currency == XMR App->>XMR: transfer(destinations=[{customer},{shop}], account_index) XMR-->>App: tx_hash end App-->>User: {success, tx_hash, fee_charged, payment_amount} end ``` ```mermaid sequenceDiagram autonumber actor Shopper participant Cart as Cart View participant Prod as Product Model participant Router as Router Shopper->>Cart: GET /cart/checkout Cart->>Prod: inspect line_items (count, product.physical, product.has_product_file) alt single item AND digital AND has file Cart->>Router: redirect -> product page else Cart->>Router: redirect -> invoice page end ``` ## Estimated code review effort 🎯 4 (Complex) | ⏱️ ~60 minutes ## Poem > I thump my paws: two outputs to send, > One for the shopper, one for the friend. > Fees snug in buffers, colors now bright, > Cart hops smart to product or invoice light. > New statuses singing—refunds split, delight! > (\_/) 🥕 <!-- walkthrough_end --> --- <details> <summary>📜 Recent review details</summary> **Configuration used**: CodeRabbit UI **Review profile**: CHILL **Plan**: Free <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 2657f982ecaf9b2a6877415e6d428924b7f2cd6e and 9432375bcbc8e32866ffa6468d0ed8d56b1b13ac. </details> <details> <summary>📒 Files selected for processing (2)</summary> * `make_post_sell/lib/render.py` (1 hunks) * `make_post_sell/lib/sanitize_html.py` (4 hunks) </details> </details> <!-- tips_start --> --- > 📝 **NOTE** > <details> > <summary>🎁 Summarized by CodeRabbit Free</summary> > > Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting <https://app.coderabbit.ai/login>. > > </details> <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end --> <!-- internal state start --> <!-- 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 --> <!-- internal state end -->
coderabbitai commented 2025-10-03 08:58:45 -04:00 (Migrated from git2.unturf.com)

changed the description

changed the description
russellballestrini commented 2025-10-03 09:46:29 -04:00 (Migrated from git2.unturf.com)

added 1 commit

  • 9432375b - Add shop ribbon color styling to links in product and content descriptions

Compare with previous version

added 1 commit <ul><li>9432375b - Add shop ribbon color styling to links in product and content descriptions</li></ul> [Compare with previous version](/engineering/make-post-sell/make_post_sell/-/merge_requests/48/diffs?diff_id=731&start_sha=2657f982ecaf9b2a6877415e6d428924b7f2cd6e)
coderabbitai commented 2025-10-03 09:47:14 -04:00 (Migrated from git2.unturf.com)

changed the description

changed the description
russellballestrini (Migrated from git2.unturf.com) merged commit fd267aec0f into master 2025-10-03 11:15:29 -04:00
russellballestrini commented 2025-10-03 11:15:30 -04:00 (Migrated from git2.unturf.com)

mentioned in commit fd267aec0f

mentioned in commit fd267aec0f78fce65ac0012bba83177baa0d055b
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: engineering/make_post_sell#84
No description provided.