1. Added getblockhash() method to DogecoinClient for late payment scanning
- Fixes 'DogecoinClient' object has no attribute 'getblockhash' error
- Required for crypto watcher to scan for missed payments
2. Added sweep-all-doge Makefile target for dust collection
- Sweeps ALL spendable Dogecoin balance to cold storage
- Uses sendtoaddress with subtractfeefromamount=true for complete sweep
- Includes safety confirmations and balance checks
- Perfect for cleaning up dust from hot wallet
Usage:
COLD_WALLET_ADDRESS=DYourColdAddress make sweep-all-doge
Critical for production Dogecoin deployment and hot wallet management.
The crypto watcher's late payment scanner was failing with:
'DogecoinClient' object has no attribute 'getblockhash'
Added getblockhash() method to both DogecoinClient and MockDogecoinClient:
- Uses standard Bitcoin RPC getblockhash call with block height parameter
- Enables proper Dogecoin late payment detection in production
- Required for crypto watcher to scan for missed payments
Critical for production Dogecoin support.
The crypto watcher was using uncapped fee estimation (1.77 DOGE/KB) when
sweeping funds, causing ~1 DOGE fees instead of the capped quote fees.
Applied same optimizations as quote system:
- Use 6 block target instead of 2 blocks for cheaper fees
- Reduce transaction size estimate from 0.5KB to 0.25KB
- Cap fee rate at 0.1 DOGE/KB maximum
- Use 0.002 DOGE fallback instead of 0.1 DOGE
This ensures backend sweep fees match the frontend quote fees,
preventing excessive sweep costs that eat into payment amounts.
The estimatesmartfee RPC was returning inflated fee rates (3+ DOGE/KB)
causing fees of ~/bin/bash.20 instead of expected ~/bin/bash.024.
Added fee rate cap of 0.1 DOGE/KB which results in:
- Max fee: 0.1 * 0.25KB * 2 = 0.05 DOGE (~/bin/bash.011)
- Much closer to wallet estimates of ~0.102 DOGE (~/bin/bash.024)
- Still covers inbound + outbound transaction costs
This prevents customers from being overcharged due to temporary
network fee spikes or misconfigured fee estimation.
UUID objects need to use .uuid_str property to get string representation,
as documented in CLAUDE.md. Using str() on UUID objects doesn't work properly.
Added comprehensive logging to see if fee estimation is:
- Using dynamic network fees from estimatesmartfee RPC
- Consistently falling back to hardcoded 0.004 DOGE fee
- Encountering RPC errors
This will help determine if high fees are due to fallback usage
vs actual network conditions.
The fee estimation was calculating ~/bin/bash.20 fees when it should be much lower.
Changes:
- Use 6 block target (~1 hour) instead of 2 blocks for cheaper estimates
- Reduce transaction size estimate from 0.5KB to 0.25KB (more realistic for DOGE)
- Lower fallback fee from 0.01 DOGE to 0.002 DOGE (~/bin/bash.0006 vs /bin/bash.003)
- Update development.ini default to match new lower fallback
This should reduce typical fee estimates from ~/bin/bash.20 to ~/bin/bash.02-0.05 range,
making Dogecoin payments more cost-effective for users while still covering
both inbound and sweep transaction costs.
Removed the 'Received: X / Y piconero' display that was showing raw atomic units
(piconero for XMR, koinu for DOGE) which confused users during testing.
Changes:
- Removed HTML div showing received/expected atomic units
- Removed JavaScript variables and logic for updating atomic unit display
- Users now see only human-readable cryptocurrency amounts
This improves UX by showing only meaningful amounts users can understand.
TEMPORARY: /crypto/debug/wallet-scan endpoint to diagnose why late payment
scanner shows 'No transfers found' while wallet actually has transactions.
READ-ONLY route that:
- Shows crypto processor count and scan positions
- Tests wallet RPC with same parameters as scanner
- Compares transfer counts to identify if issue is RPC or scanner logic
- Masks sensitive data (shop IDs, amounts, addresses)
TODO: Comment out this route when debugging is complete.
The late payment scanner stopped finding payments after double payment fixes.
Added detailed logging to diagnose the issue:
- Log each processor's scan position and calculated height
- Log minimum scan position used for wallet queries
- Log exact RPC query parameters sent to wallet
- Log all transfers found with height, amount, confirmations, subaddress
- Log payment lookup results for each transfer
- Log late payment decision logic with accept/reject reasons
This will reveal if:
- Scan positions are too high and skipping late payments
- Transfers are found but not matched to payment records
- Payments are found but rejected by late payment logic
Critical for debugging production issue where real customer funds
may not be getting processed due to missed late payments.
Fixed template syntax error where {% break %} is not supported in Jinja2.
Replaced with proper Jinja2 pattern using set variable and conditional check
to find first available thumbnail for Open Graph images.
Changes:
- Use {%- set og_thumbnail = none %} and conditional assignment
- Check og_thumbnail is none to ensure only first match is used
- Maintains same functionality with valid Jinja2 syntax
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced social media sharing by updating og:image meta tags to use the first
available thumbnail (thumbnail1-4) instead of hardcoding thumbnail1. This ensures
better social media previews on Slack, Twitter, Facebook when products don't have
thumbnail1 but have other thumbnails available.
Updated templates:
- product.j2: Smart thumbnail fallback for product pages
- content.j2: Smart thumbnail fallback for content pages
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated intro description to mention that Make Post Sell accepts both
credit cards and Monero (XMR) payments for better feature visibility.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This test reproduces the exact issue where duplicate payments weren't getting
their confirmation counts updated in the main processing loop, causing stuck refunds.
The test:
- Creates a duplicate payment with STATUS_DOUBLEPAY_REFUND and 0 confirmations
- Mocks blockchain data showing the transaction has 15 confirmations
- Calls process_payment() to simulate the main processing cycle
- Asserts that current_confirmations gets updated from 0 to 15
This test would have caught the bug where duplicate payments remained stuck
at 0 confirmations despite transactions having 10+ confirmations on blockchain.
Currently FAILS (as expected) since the fix hasn't been fully verified.
The test will pass once the confirmation update logic is working correctly.
The previous fix for update_payment_confirmations_only only worked for REFUND_PENDING_STATUSES,
but duplicate payments in STATUS_DOUBLEPAY_REFUND were being processed in the main loop
without updating their confirmation counts.
Added confirmation count updates directly in the duplicate payment processing logic:
- After finding specific duplicate transactions for XMR payments
- After fetching transaction details for other coin types
- Logs the confirmation count change for debugging
This ensures duplicate payments get their confirmation counts updated during processing,
allowing stuck refunds to proceed when transactions have sufficient confirmations.
Production logs showed payment stuck at "incoming: 0/2" despite transaction having 10+ confirmations.
This fix will update the count to show actual confirmations from the blockchain.
Added 3 comprehensive unit tests to CryptoWatcherUnitTests class:
1. test_update_payment_confirmations_only_duplicate_payment
- Tests that duplicate payments use specific transaction confirmation count
- Simulates scenario with multiple transactions where min() would give wrong result
- Verifies it finds specific txid from tx_hashes and uses its confirmation count
2. test_update_payment_confirmations_only_regular_payment
- Tests that regular payments still use min() confirmation logic
- Ensures backward compatibility for non-duplicate payments
3. test_update_payment_confirmations_only_duplicate_payment_not_found
- Tests fallback behavior when specific transaction ID is not found
- Verifies warning is logged and falls back to min() logic
These tests ensure the bug fix for stuck duplicate payment refunds is working correctly
and will prevent regression of the confirmation counting logic.
The update_payment_confirmations_only function was using min() across ALL transactions
to the same address instead of checking the specific transaction for duplicate payments.
This caused duplicate payments with old, confirmed transactions to show 0 confirmations
if there were newer unconfirmed transactions to the same address.
Fixed by:
- Checking if payment is STATUS_DOUBLEPAY_REFUND
- Looking up the specific transaction ID from tx_hashes
- Using confirmation count for that specific transaction only
- Falling back to min() for regular payments
This will allow stuck duplicate payment refunds to proceed once their specific
transaction has 10+ confirmations.
- Mock send_purchase_email and send_sale_email in test_original_payment_moves_to_confirmed_despite_duplicates
- Test was failing with TypeError: unsupported format string passed to MagicMock.__format__ when finalize_invoice called email functions
- Now that payments properly reach confirmed status and call finalize_invoice, tests need to mock the email functions
- Added test_process_payment_confirmation_flow_integration to catch the AttributeError: 'Invoice' object has no attribute 'invoice' bug
- Test sets up realistic payment scenario and verifies finalize_invoice is called correctly during confirmation flow
- Uses DummyRequest instead of over-mocked objects to trigger real function calls
- Test would have caught the production bug where finalize_invoice was called with crypto_payment.invoice instead of crypto_payment
- Verified test catches the bug when code is broken and passes when code is fixed
Line 1670 was incorrectly calling:
finalize_invoice(env_request, crypto_payment.invoice)
Should be:
finalize_invoice(env_request, crypto_payment)
This was causing AttributeError: 'Invoice' object has no attribute 'invoice'
when processing confirmed payments, breaking the entire payment confirmation flow.
- Fix ShopContextRequestWrapper to handle missing attributes gracefully with getattr default
- Add send_refund_email mock to test_full_payment_flow_with_double_spend test
- Update test to check for both STATUS_DOUBLEPAY_REFUND and STATUS_DOUBLEPAY_REFUND_COMPLETE
- Duplicate payments now properly send refund emails with correct shop domain context
- Email sending failures no longer prevent duplicate payment processing completion
This resolves the test failure where duplicate payments were being created but not
committed due to email sending errors in test environments.
- Add status transition to STATUS_DOUBLEPAY_REFUND_COMPLETE after successful refund
- Keep STATUS_DOUBLEPAY_REFUND status for failed refunds to enable retries
- Fix refund email domain by using create_shop_context_request for proper shop context
- Add comprehensive test for duplicate payment refund status transitions
- Consolidate test logic to reduce complexity while maintaining coverage
This prevents infinite refund retry loops for successful refunds while allowing
retries for failed refunds due to insufficient funds. Email notifications now
use the correct shop domain instead of localhost.
The issue was that STATUS_DOUBLEPAY_REFUND was in REFUND_PENDING_STATUSES,
which meant the crypto watcher only monitored confirmations but never
initiated the actual refund processing.
Moved STATUS_DOUBLEPAY_REFUND from REFUND_PENDING_STATUSES to ACTIVE_STATUSES
so that duplicate payments are processed through the main payment flow where
the refund logic is located.
This should fix the stuck duplicate payment that wasn't being refunded
despite having 650+ confirmations and a valid refund address.
The json import was inside a conditional block but needed later in the function.
Moved import to top of function to fix UnboundLocalError.
All crypto_watcher and double_spend_protection tests now pass.
The issue was that incoming_transfers was empty because duplicate payments
need to look up their specific historical transaction IDs, not just current
scanning window transfers.
Changes:
- Query historical transfers using stored tx_hashes for duplicate payments
- Use filter_by_height=False to get ALL transfers for the subaddress
- Find specific duplicate transaction by matching stored transaction IDs
- Add detailed logging to show transaction lookup results
This should fix the stuck doublepay-refund payment that has a valid
refund address but wasn't processing because no incoming transfers
were found in the current scanning window.
This will help diagnose why the refund isn't being processed:
- Shows refund_address status
- Shows incoming_transfers count
- Shows payment_rescue availability
- Shows client availability
Deploy this to see which condition is failing.
- Add UserCryptoRefundAddress import and lookup function
- Update _create_duplicate_payment to lookup saved refund address
- Fix refund processing to check saved addresses if not set on payment
- Add test for refund address inheritance
- Add Makefile target for sweeping all wallet funds (sweep-all)
This fixes the issue where duplicate payments weren't being refunded
because they had no refund_address set. Now the system will:
1. First check if original payment has refund_address
2. If not, lookup user's saved refund address for that coin type
3. Use the found address for the duplicate payment record
Also added sweep-all target to collect dust from all accounts.
- Fix confirmed payments stuck in limbo when duplicates detected
- Add email sending for confirmed payments using boolean flags
- Add auto-sweep processing for confirmed payments
- Always finalize invoice when payment moves to confirmed status
- Add comprehensive unit tests for confirmed payment email handling
- Ensure original payment can move to confirmed despite ongoing duplicates
- Prevent double refunds while ensuring payments show in history
This fixes production issues where:
- Payments got 5+ confirmations but stayed in "received" status
- No purchase/sales emails sent for confirmed payments
- Auto-sweep skipped for confirmed payments with duplicates
- Invoice finalization conditional logic caused limbo states
Tests added:
- test_confirmed_payment_with_ongoing_duplicates_sends_emails
- test_confirmed_payment_skips_emails_if_already_sent
- test_original_payment_moves_to_confirmed_despite_duplicates
CRITICAL FIX: Removes broken conditional logic that could leave confirmed
payments in limbo without invoice finalization.
BEFORE: Only finalized invoice if products were still locked
AFTER: ALWAYS finalize invoice when payment moves to confirmed status
This fixes the spaghetti code logic where payment 37ae9bf3-9bcf-11f0-a140-02dfe05770ee
moved to confirmed status but invoice was never finalized because products
appeared to already be unlocked.
Invoice finalization handles:
- Product unlocking
- Email sending (purchase + sales)
- Proper state management
Much cleaner and more reliable than conditional finalization.
CRITICAL FIX: Auto-sweep was being skipped when payments moved to CONFIRMED
status via the duplicate detection path due to early return.
- Add auto-sweep logic to duplicate detection path before return
- Check if payment is CONFIRMED, has sweep address, and hasn't been swept
- Ensures funds are swept to cold storage even when duplicates are detected
- Maintains same sweep logic as normal payment processing path
This fixes the issue where payment 37ae9bf3-9bcf-11f0-a140-02dfe05770ee moved
to confirmed status but funds were never swept because the duplicate detection
path returned early before reaching auto-sweep logic.
CONFIRMATION EMAIL FIX:
- Send purchase and sales emails when payment moves to CONFIRMED status
- Use purchase_email_sent and sales_email_sent boolean flags to prevent duplicates
- Independent of invoice finalization status (products may already be unlocked)
- Add proper error handling and logging
REFUND EMAIL FIX:
- Check refund_email_sent boolean flag before sending refund emails
- Set flag to true after successful email send
- Prevents duplicate refund email notifications
This fixes the issue where confirmed payment 37ae9bf3-9bcf-11f0-a140-02dfe05770ee
moved to confirmed status but no emails were sent because finalize_invoice()
wasn't called.
LOGGING CHANGES:
- Add detailed debug logging to track duplicate record detection
- Log when existing duplicates are found vs when new ones are created
- Add counts of doublepay-refund records being examined
TEST IMPROVEMENTS:
- Add test_duplicate_record_singleton_per_transaction_id to verify each transaction creates only ONE duplicate record across multiple cycles
- Add test_original_payment_moves_to_confirmed_despite_duplicates to verify original payment status progression
- Tests now simulate the real 20-second cycle behavior that was missing
These tests will catch the production issue where transaction ff8d14fa1deef6034f2cd16e7cbcd1c76d5e589f37cd55d718d122148783014c was creating new duplicate records every cycle instead of being deduplicated.
Adds database check to prevent creating multiple duplicate payment records
for the same transaction ID. Now checks if duplicate record already exists
before creating a new one.
Fixes production issue where same transaction was creating new duplicate
records every 20 seconds.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updates test expectations to match new duplicate detection behavior:
- Main loop creates duplicate records for scanner processing
- Confirmation counts calculated from legitimate transactions only
- Log messages updated to reflect "Creating duplicate records for scanner to process"
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Duplicate payments now create separate CryptoPayment records with STATUS_DOUBLEPAY_REFUND
- Original payment continues through normal confirmation logic without duplicate amounts
- Fixes issue where payments stuck in 'received' status instead of moving to 'confirmed'
- Each duplicate now shows up in crypto payments history as intended
- Update all related tests to match new behavior
- Add refund email notifications for crypto payment failures
- Enhance double-spend protection with comprehensive integration tests
- Improve alembic migration formatting for SQLite compatibility
- Format code with Black for consistent style
- Add duplicate payment detection in main processing loop that prevents
new transactions to payments with received_amount > 0 and status \!= PENDING
- Rename scan_wallet_for_late_payments to scan_wallet_for_double_or_late_payments
- Create comprehensive test suite with 16 tests covering all scenarios:
* Duplicate payment detection for confirmed/received payments
* Race condition protection between main loop and scanner
* Concurrent transaction handling within processing windows
* Wallet drain prevention and edge cases
- Fix _create_duplicate_payment to use proper CryptoPayment constructor
- Update existing idempotent test to reflect correct business logic
The vulnerability allowed duplicate payments to trigger multiple full refunds
plus extra funds, catastrophically draining the hot wallet. Now any second
payment to an address that already received funds is rejected by main loop
and handled by scanner with 9% restocking fee refund.
Extended double payment detection to include the terminal overpaid refunded
state. Now detects double/triple payments to all confirmed order states:
- STATUS_CONFIRMED (payment complete)
- STATUS_CONFIRMED_OVERPAID (payment complete but overpaid, refund pending)
- STATUS_CONFIRMED_OVERPAID_REFUNDED (overpayment refund complete)
This ensures that if customers send additional payments even after their
overpayment has been refunded, the system will still detect and handle
these correctly.
The double payment detection had dead code checking for STATUS_PAID which
doesn't exist. Fixed to properly detect double/triple payments to both
confirmed states:
- STATUS_CONFIRMED (payment complete)
- STATUS_CONFIRMED_OVERPAID (payment complete but overpaid, refund pending)
This ensures that if customers accidentally send additional payments to
already-confirmed orders, the system will detect them and handle refunds
appropriately.
Removed the non-existent STATUS_PAID check and consolidated the logic
to handle both confirmed states properly.
The crypto checkout page was polling indefinitely even after reaching terminal
states like expired, cancelled, or completed refunds. This wastes resources
and can cause unnecessary server load.
Changed the polling logic to:
- Store the polling interval reference
- Check after each poll if a terminal status has been reached
- Clear the interval and stop polling for terminal statuses
- Also stop polling for 'confirmed' status since the order is complete
Terminal statuses that stop polling include:
- expired
- cancelled
- expired-refunded-complete
- underpaid-refunded-complete
- confirmed-overpaid-refunded
- out-of-stock-refunded-complete
- no-refund
- no-refund-complete
- confirmed (order complete)
Non-terminal statuses like 'underpaid-refunded' continue polling until they
transition to their complete state.