- Add fallback to check all account transfers when subaddress filter returns no results
- This helps detect mempool transactions that may not immediately appear in subaddress-filtered queries
- Include both 'in' and 'pool' transfers from get_transfers RPC call
- Filter results to match the specific subaddress we're monitoring
- Addresses issue where mempool transactions are visible in wallet but not detected by watcher
- Add estimate_monero_fee_for_quote() using test transfer with do_not_relay
- Add estimate_dogecoin_fee_for_quote() using estimatesmartfee RPC
- Update Monero quote creation to use dynamic fees instead of hardcoded 0.0001 XMR
- Update Dogecoin quote creation to use dynamic fees instead of hardcoded 0.01 DOGE
- Maintains fallback to hardcoded fees if RPC calls fail
- Should provide more accurate quotes and reduce dust further
- Monero: Use test transfer with do_not_relay=true for exact fee calculation
- Dogecoin: Use estimatesmartfee RPC for current network fee rates
- Replaces hardcoded fee estimates (0.0001 XMR, min_sweep_balance DOGE)
- Should reduce dust amounts by using more accurate fees
- Maintains fallback to hardcoded fees if RPC calls fail
- Remove automatic redirect when payment is confirmed - users must click link
- Add 'Crypto Payment History' link to user settings page
- Users can now view all their crypto payment attempts at /u/crypto-quotes
- Change from sweep_all to sweep_single to only sweep funds from specific subaddress
- Prevents sweeping funds from other payments in the same account
- Each payment already uses its own subaddress for proper isolation
- Fix critical bug where payments were incorrectly marked as 'swept' when funds were still locked
- Auto-sweep now properly retries until funds unlock (after Monero's 10-block lock time)
- Fix test expectation for expired-refunded status (hyphen not underscore)
When a crypto payment is cancelled, delete the associated invoice and
line items since no actual purchase occurred. Keep the crypto_payment
record for transaction history and audit trails.
This prevents cancelled quotes from cluttering user purchase history.
When a crypto quote is cancelled or becomes non-pending, completely
remove the QR code and payment instructions (amount + address) instead
of just disabling them. This provides cleaner UI for cancelled quotes.
- Add specific messaging for out-of-stock-refunded status (full refund, no fee)
- Add specific messaging for out-of-stock-no-refund status
- Add out-of-stock item to refund policy lists
- Left-align QR code instead of centering it
- Replace old insecure qrcodejs (v1.0.0 from 2013) with QRious v4.0.2 (2017)
- Update crypto checkout template to use QRious API instead of old QRCode.js
- QRious uses canvas element and modern security practices
- Add missing 'refund_amount' and 'reason' fields to test mocks
- Update status expectation from 'confirmed_overpaid' to 'confirmed-overpaid'
to match dash-based enum format
The local 'import time' inside a function was shadowing the module-level
time import, causing UnboundLocalError in crypto watcher tests. Use the
existing module-level time import instead.
Adds refund_reason, refund_tx_hash, and refund_amount columns to the
mps_crypto_payment table to enable comprehensive tracking of refund
operations for out-of-stock scenarios and payment error handling.
- Add proper inventory checking before crypto payment completion
- Implement sequential (FIFO) crypto payment processing to prevent race conditions
- Add comprehensive out-of-stock refund handling with two statuses:
- STATUS_OUT_OF_STOCK_REFUNDED: Full refund to customer (no restocking fee)
- STATUS_OUT_OF_STOCK_NO_REFUND: Sweep to shop when no refund address
- Use sweep_all with subaddr_indices for Monero to ensure complete fund recovery
- Use sendtoaddress with subtractfeefromamount for Dogecoin fee handling
- Add refund tracking fields (refund_reason, refund_tx_hash, refund_amount)
- Update MONERO.rst with subaddress isolation and refund processing documentation
- Ensure action buttons are removed for non-pending crypto quotes to prevent double payments
- Guard all refund processing with refund_address checks to avoid unnecessary loops
- Replace dangerous sweep_all with exact amount transfers
- Use transfer() with specific amounts instead of sweeping everything
- Prevents stealing funds from other concurrent payments in same account
- Only process refunds when customer has configured a refund address
Performance: Skip expensive refund calculations when no refund address exists
Security: Each payment only touches its own calculated amounts (91% + 9%)
- Customer refunds tracked in refund_* fields (amount, tx_hash, reason)
- Restocking fees tracked in swept_* fields (amount, tx_hash, timestamp)
- Provides complete audit trail: received = refund_amount + swept_amount
- Shop owners can now see exactly how much was collected in restocking fees
For expired payments with restocking fee:
1. First transaction: 91% refunded to customer
2. Second transaction: 9% swept to shop's cold wallet
This gives full transparency into both sides of the refund process.
Change status values from underscore_separated to dash-separated format:
- confirmed_overpaid → confirmed-overpaid
- expired_refunded → expired-refunded
- underpaid_refunded → underpaid-refunded
- out_of_stock_refunded → out-of-stock-refunded
This provides better consistency with web standards and URL-friendly values.
- Remove unnecessary intermediate statuses, keep only STATUS_OUT_OF_STOCK_REFUNDED
- Check inventory when processing expired payments
- Give full refund (no restocking fee) if payment expired AND items are out of stock
- Use refund_reason field to track why refunds were issued
This ensures customers aren't penalized with restocking fees when both
the quote expired AND the items went out of stock.
- Add inventory availability check before finalizing crypto payments
- Implement full refund without restocking fee for out-of-stock items
- Add new payment statuses: out_of_stock_refund_pending, out_of_stock_refunded
- Process crypto payments in FIFO order (first come, first serve) by creation timestamp
- Add transaction-level savepoints for atomic payment processing
- Refresh payment status before processing to avoid race conditions
- Add refund tracking fields: refund_reason, refund_tx_hash, refund_amount
- Hide credit card button when no active card is configured
- Move checkout confirmation message outside Stripe-specific section
- Clean up pending quotes display (remove redundant labels)
This prevents the Black Friday scenario where multiple customers pay with
different cryptocurrencies for the same limited inventory item. The first
payment to be created will be processed first, and subsequent payments
for out-of-stock items will receive full refunds automatically.
- Remove all action buttons (copy amount/address, open wallet) when payment is no longer pending
- Replace with 'View Invoice' link for confirmed/processing/expired/cancelled payments
- Handle both on page load and dynamically via status polling
- Add invoice_url to crypto status API endpoints
- Prevents double payments by removing all payment options once quote expires or payment is received
Use simple /cart route which automatically goes to active cart instead of
trying to construct specific cart checkout URL with non-existent cart_id field.
- Add CSRF token requirement to cancel endpoint for security
- Include CSRF token in crypto checkout template
- Add better error handling and debugging for cancel requests
- Remove all flex layouts and replace with CSS Grid for consistent styling
- Move pending crypto quotes section to bottom of confirmation page
- Update button text to use "confirm checkout" terminology
- Add crypto payment cancellation with redirect to active cart
- Remove cancel buttons from confirmation page to reduce clutter
- Implement smart redirect URLs for completed crypto payments
The wrapper class wasn't properly exposing the app attribute needed for email
sending during payment finalization. This caused AttributeError crashes when
processing real payments. Added explicit app property to ensure proper access.
The sync status request methods were failing due to relative import errors
when trying to import crypto clients. Changed to absolute imports to resolve:
- "attempted relative import beyond top-level package" errors
- Monero and Dogecoin sync status now properly detected
- Payment method availability correctly reflects wallet/node sync status
This restores the intended functionality where crypto payment buttons only
appear when the underlying nodes/wallets are fully synchronized.
- Change Monero is_synced() to simple height > 0 check for remote nodes
- Update get_sync_status() to return appropriate status for remote node setup
- Fixes false "syncing" status when using upstream Monero nodes
- Apply black code formatting for consistency
This preserves the sync status detection functionality while fixing
issues with remote Monero node deployments.
Crypto payment buttons now only appear when nodes are fully synced,
preventing failed payments due to incomplete blockchain synchronization.
- Add is_synced() and get_sync_status() methods to MoneroClient and DogecoinClient
- Add request.monero_synced and request.dogecoin_synced request methods
- Update payment visibility logic to require both enabled AND synced status
- Add helpful sync status messages in checkout template when nodes are syncing
- Prevent customer frustration from unsynchronized payment processing
Payment method visibility now requires:
1. App globally enabled
2. Shop has cold wallet configured
3. RPC accessible and responsive
4. Node/wallet fully synchronized
Auto-sweep functions were not persisting swept_tx_hash to database because
the modified crypto_payment objects weren't being added to the session.
- Add dbsession parameter to auto_sweep_payment functions
- Add explicit dbsession.add(crypto_payment) calls when sweep succeeds
- Update tests to verify swept_tx_hash is set and payment added to session
- Fix existing tests to work with new function signatures
This resolves the issue where swept payments had NULL swept_tx_hash despite
successful fund transfers, which caused infinite processing loops.
- Move CRYPTO.rst to docs/CRYPTO.rst
- Move CRYPTO_PROBLEMS.rst to docs/CRYPTO_PROBLEMS.rst
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add CryptoPayment status constants for cleaner code maintenance
- Add STATUS_EXPIRED_REFUNDED for payments that expired but received funds and were refunded
- Use ACTIVE_STATUSES and TERMINAL_STATUSES for better organization
- Update all status assignments to use constants instead of magic strings
- Fix test expectation for expired_refunded status
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Filter at database level with swept_tx_hash.is_(None) for minimum result set
- Remove artificial "swept" status - keep "confirmed" as final state
- is_swept property derives from swept_tx_hash column, no separate boolean
- Stuck payments with swept_tx_hash set will be automatically excluded
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace non-existent is_unlocked_for_user with get_userproduct_for_user check
- Move overpayment refund logic outside elif branch to apply to both new and stuck payments
- All crypto watcher tests now pass
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Exclude already-swept payments from watcher queue to prevent endless processing
- Mark swept payments as "swept" status to remove from future cycles
- Fix cart deactivation by ensuring shop/user objects are attached to session
- Add debug logging for cart deactivation to track behavior
- Resolves issue where confirmed payments were processed every 20 seconds indefinitely
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Formatted Python code using black for:
- crypto_clients.py
- crypto_watcher.py
- test_crypto_watcher.py
Maintains consistent code style across the codebase.
Added test_finalize_invoice_deactivates_cart to verify that when a
cryptocurrency payment is confirmed, the user's active cart is replaced
with a new empty cart.
The test verifies:
- User starts with a non-empty cart
- After finalize_invoice(), user gets a new empty cart
- New cart has different ID than the old cart
This ensures the UX improvement works correctly - users see an empty
cart after their crypto payment is confirmed.
When a cryptocurrency payment is confirmed in finalize_invoice(),
create a new empty cart for the user, mirroring the behavior of
Stripe payments in save_cart().
This ensures users see an empty cart immediately after their crypto
payment is detected on the blockchain, improving the UX by allowing
them to start shopping again instead of seeing the items they just paid for.
- Added logic to get user's current active cart for the shop
- Create new empty cart only if current cart is not empty
- Added logging for debugging
- Mirrors save_cart() behavior used in Stripe checkout flow
Replace urllib with requests library for DogecoinClient HTTP calls:
- Consistent with MoneroClient implementation
- Cleaner code using requests.post() with Basic auth
- Removed unused _headers method and imports
- Both crypto clients now use the same HTTP library
This provides better reliability and consistency across all RPC clients.
Switch from urllib to requests library for all MoneroClient HTTP requests.
The requests library handles Digest authentication correctly while urllib
has compatibility issues with Monero wallet RPC server implementation.
- Simplified code by removing complex urllib Digest auth workarounds
- Uses requests.post() with HTTPDigestAuth for authenticated requests
- Maintains backward compatibility with no-auth configurations
- Adds requests as a runtime dependency for Monero functionality
Fixes Digest authentication issues that caused '401 digest auth failed' errors.
Small fix to help urllib match credentials correctly by adding
both the full RPC URL and base URL to the password manager.
This may resolve Digest authentication issues with Monero wallet RPC.