make_post_sell/docs/CRYPTO_WATCHER_FLOW.md
Russell Ballestrini c1a936539e deleted: debug_auth_headers.py
deleted:    debug_digest_details.py
	deleted:    debug_monero_auth.py
	renamed:    CRYPTO_WATCHER_FLOW.md -> docs/CRYPTO_WATCHER_FLOW.md
	deleted:    test_digest_auth_local.py
	deleted:    test_dogecoin_client.py
	deleted:    test_monero_auth.py
	deleted:    test_monero_client.py
	deleted:    test_requests_lib.py
	deleted:    test_urllib_variants.py
2025-09-28 15:26:42 -04:00

12 KiB

Crypto Watcher Mechanism - Complete Flow Documentation

This document describes the complete flow of the crypto payment watcher system, including all paths for payment processing, duplicate detection, and refund handling.

flowchart TD
    A[Crypto Watcher Starts] --> B[Load Active Crypto Processors]
    B --> C[For Each Processor: Scan Wallet]
    
    C --> D{New Transactions Found?}
    D -->|No| E[Sleep 20 seconds]
    D -->|Yes| F[Process Each Transaction]
    
    F --> G[Match Transaction to Payment by Subaddress]
    G --> H{Payment Found?}
    
    H -->|No| I[Log: No matching payment]
    H -->|Yes| J[Call process_payment]
    
    J --> K{Payment Status?}
    
    K -->|PENDING| L[First Payment Path]
    K -->|RECEIVED/CONFIRMED| M[Duplicate Detection Path]
    K -->|DOUBLEPAY_REFUND| N[Refund Processing Path]
    
    subgraph FirstPayment [First Payment Path]
        L1[Add transaction amount]
        L2[Update tx_hashes with txid]
        L3[Update confirmations]
        L4{Amount >= Expected AND Confirmations >= Required?}
        L5[Status = CONFIRMED]
        L6[Status = RECEIVED]
        L7[Finalize Invoice - Unlock Products]
        L8[Continue Monitoring]
        
        L1 --> L2
        L2 --> L3
        L3 --> L4
        L4 -->|Yes| L5
        L4 -->|No| L6
        L5 --> L7
        L6 --> L8
        L7 --> L8
    end
    
    subgraph DuplicateDetection [Duplicate Detection Path]
        M1{received_amount > 0 AND new_txids?}
        M2[DUPLICATE PAYMENT DETECTED]
        M3[Create Duplicate Payment Records]
        M4[Set status = STATUS_DOUBLEPAY_REFUND]
        M5[Store tx_hashes with duplicate txid]
        M6[Update original payment confirmations only]
        M7[Scanner will process refunds]
        
        M1 -->|Yes| M2
        M2 --> M3
        M3 --> M4
        M4 --> M5
        M5 --> M6
        M6 --> M7
    end
    
    subgraph RefundProcessing [Refund Processing Path]
        N1{PaymentRescue available?}
        N2[Status = STATUS_NO_REFUND]
        N3[Calculate refund amount with 9% fee]
        N4[Execute refund transaction]
        N5{Refund successful?}
        N6[Store refund_tx_hash]
        N7[Log refund failure]
        N8[Monitor refund confirmations]
        N9[Status remains DOUBLEPAY_REFUND]
        N10{Refund confirmed?}
        N11[Status = STATUS_DOUBLEPAY_REFUND_COMPLETE]
        N12[Continue monitoring]
        
        N1 -->|No| N2
        N1 -->|Yes| N3
        N3 --> N4
        N4 --> N5
        N5 -->|Yes| N6
        N5 -->|No| N7
        N6 --> N8
        N7 --> N9
        N8 --> N10
        N10 -->|Yes| N11
        N10 -->|No| N12
    end
    
    L --> FirstPayment
    M --> DuplicateDetection
    N --> RefundProcessing
    M1 -->|No| L1
    
    E --> C
    L8 --> E
    M7 --> E
    N2 --> E
    N9 --> E
    N11 --> E
    N12 --> E
    I --> E

Complete Payment Status State Machine

stateDiagram-v2
    [*] --> PENDING : Payment created
    
    %% Normal payment flow
    PENDING --> RECEIVED : First transaction received
    RECEIVED --> CONFIRMED : Sufficient confirmations + exact payment
    RECEIVED --> CONFIRMED_OVERPAID : Sufficient confirmations + overpayment
    
    %% Terminal success states
    CONFIRMED --> [*] : Products unlocked
    CONFIRMED_OVERPAID --> CONFIRMED_OVERPAID_REFUNDED : Excess refunded
    CONFIRMED_OVERPAID_REFUNDED --> [*] : Refund confirmed
    
    %% Expiration flow
    PENDING --> EXPIRED : Quote expires (no payment)
    RECEIVED --> EXPIRED : Quote expires (partial payment)
    EXPIRED --> EXPIRED_REFUNDED : Late payment arrives + refund sent
    EXPIRED_REFUNDED --> EXPIRED_REFUNDED_COMPLETE : Refund confirmed
    EXPIRED_REFUNDED_COMPLETE --> [*]
    EXPIRED --> [*] : No late payment
    
    %% Underpayment flow
    RECEIVED --> UNDERPAID_REFUNDED : Quote expires + insufficient funds
    UNDERPAID_REFUNDED --> UNDERPAID_REFUNDED_COMPLETE : Refund confirmed
    UNDERPAID_REFUNDED_COMPLETE --> [*]
    
    %% Duplicate payment flow
    CONFIRMED --> DOUBLEPAY_REFUND : Duplicate payment detected
    CONFIRMED_OVERPAID --> DOUBLEPAY_REFUND : Duplicate payment detected
    RECEIVED --> DOUBLEPAY_REFUND : Duplicate payment detected
    DOUBLEPAY_REFUND --> DOUBLEPAY_REFUND_COMPLETE : Refund confirmed
    DOUBLEPAY_REFUND_COMPLETE --> [*]
    
    %% Out of stock flow
    PENDING --> OUT_OF_STOCK_REFUNDED : Product out of stock + payment received
    RECEIVED --> OUT_OF_STOCK_REFUNDED : Product out of stock
    CONFIRMED --> OUT_OF_STOCK_REFUNDED : Product out of stock after payment
    OUT_OF_STOCK_REFUNDED --> OUT_OF_STOCK_REFUNDED_COMPLETE : Refund confirmed
    OUT_OF_STOCK_REFUNDED_COMPLETE --> [*]
    
    %% No refund scenarios
    EXPIRED --> NO_REFUND : No refund address configured
    UNDERPAID_REFUNDED --> NO_REFUND : Refund failed
    DOUBLEPAY_REFUND --> NO_REFUND : Refund failed
    OUT_OF_STOCK_REFUNDED --> NO_REFUND : Refund failed
    NO_REFUND --> NO_REFUND_COMPLETE : Manual resolution
    NO_REFUND_COMPLETE --> [*]
    
    %% Cancellation flow
    PENDING --> CANCELLED : Payment cancelled by user/system
    RECEIVED --> CANCELLED : Payment cancelled with refund
    CANCELLED --> [*]
    
    %% Status groupings for processing
    state "Active Statuses (Monitored)" as ActiveGroup {
        PENDING
        RECEIVED  
        CONFIRMED
        CONFIRMED_OVERPAID
        NO_REFUND
    }
    
    state "Refund Pending (Confirmation Monitoring)" as RefundGroup {
        EXPIRED_REFUNDED
        UNDERPAID_REFUNDED
        OUT_OF_STOCK_REFUNDED
        DOUBLEPAY_REFUND
    }
    
    state "Terminal Statuses (No Processing)" as TerminalGroup {
        EXPIRED
        EXPIRED_REFUNDED_COMPLETE
        UNDERPAID_REFUNDED_COMPLETE
        CONFIRMED_OVERPAID_REFUNDED
        CANCELLED
        OUT_OF_STOCK_REFUNDED_COMPLETE
        NO_REFUND_COMPLETE
        DOUBLEPAY_REFUND_COMPLETE
    }

Status Categories and Processing Rules

Active Statuses (Actively Monitored)

  • PENDING: Waiting for first payment
  • RECEIVED: Has funds, waiting for confirmations
  • CONFIRMED: Payment complete, products unlocked
  • CONFIRMED_OVERPAID: Overpaid, products unlocked, excess pending refund
  • NO_REFUND: Failed refund scenarios, manual intervention needed

Refund Pending (Confirmation Monitoring)

  • EXPIRED_REFUNDED: Late payment refunded with 9% fee
  • UNDERPAID_REFUNDED: Partial payment refunded with 9% fee
  • OUT_OF_STOCK_REFUNDED: Out of stock, full refund (no fee)
  • DOUBLEPAY_REFUND: Duplicate payment refunded with 9% fee

Terminal Statuses (No Further Processing)

  • EXPIRED: Quote expired, no payment received
  • EXPIRED_REFUNDED_COMPLETE: Late payment refund confirmed
  • UNDERPAID_REFUNDED_COMPLETE: Partial payment refund confirmed
  • CONFIRMED_OVERPAID_REFUNDED: Overpayment excess refund confirmed
  • CANCELLED: Payment cancelled by user/system
  • OUT_OF_STOCK_REFUNDED_COMPLETE: Out of stock refund confirmed
  • NO_REFUND_COMPLETE: Manual resolution complete
  • DOUBLEPAY_REFUND_COMPLETE: Duplicate payment refund confirmed

Key Functions and Their Responsibilities

Main Processing Loop

  • crypto_watcher_main(): Entry point, coordinates scanning and processing
  • process_payment(): Core payment processing logic
  • scan_wallet_for_double_or_late_payments(): Scanner for missed/duplicate transactions

Duplicate Detection

  • _create_duplicate_payment(): Creates separate records for duplicate transactions
  • _should_process_late_payment(): Determines if a transaction should be processed

Payment Processing Paths

1. Normal Payment Path

# New payment to fresh address
status: PENDING  RECEIVED  CONFIRMED
actions: 
- Add amount to received_amount
- Update tx_hashes with transaction IDs
- Update confirmation count
- Finalize invoice when confirmed

2. Duplicate Payment Path (Main Loop)

# Additional payment to already-paid address
if (received_amount > 0 AND new_txids AND status != PENDING):
    # Create separate duplicate records
    for txid in new_txids:
        duplicate = _create_duplicate_payment(original, tx, coin_type)
        duplicate.status = STATUS_DOUBLEPAY_REFUND
        duplicate.tx_hashes = json.dumps([txid])
    
    # Update original payment confirmations only (no new amounts)
    original.confirmations = calculate_from_legitimate_txs_only()

3. Scanner Duplicate Processing

# Scanner finds duplicate transactions
for tx in wallet_transfers:
    payment = find_by_subaddress(tx)
    if payment.received_amount > 0:  # Duplicate
        existing = find_duplicate_by_txid(tx.txid)
        if not existing:
            duplicate = _create_duplicate_payment(payment, tx, coin_type)
        else:
            duplicate = existing
        
        # Process refund
        process_payment(request, duplicate, [tx], client=refund_client)

4. Refund Processing Path

# For payments with STATUS_DOUBLEPAY_REFUND
if payment.status == STATUS_DOUBLEPAY_REFUND:
    if payment_rescue and payment.refund_address:
        refund_amount = received_amount * 0.91  # 9% fee
        result = payment_rescue.execute_refund(payment, refund_amount)
        if result.success:
            payment.refund_tx_hash = result.tx_hash
            # Monitor for refund confirmation
        else:
            # Log failure, retry later
    else:
        payment.status = STATUS_NO_REFUND

Infinite Duplicate Support

The system now supports unlimited duplicate payments to the same address:

  1. Payment #1: Normal processing → CONFIRMED
  2. Payment #2: Main loop creates duplicate record #1 → Scanner processes refund #1
  3. Payment #3: Main loop creates duplicate record #2 → Scanner processes refund #2
  4. Payment #4: Main loop creates duplicate record #3 → Scanner processes refund #3
  5. etc...

Each duplicate gets:

  • Its own CryptoPayment record with unique ID
  • Status: STATUS_DOUBLEPAY_REFUND
  • Individual refund processing
  • Separate entry in crypto payments history

Error Handling and Edge Cases

Double Refund Prevention

  • Scanner checks for existing duplicate records by transaction ID
  • Only one duplicate record created per transaction
  • Refund only processed once per duplicate

Confirmation Handling for Duplicates

  • Original payment confirmations calculated from legitimate transactions only
  • Duplicate transactions don't affect original payment confirmation count
  • Original payment can move RECEIVED → CONFIRMED despite having duplicates

Missing Transaction Scenarios

  • Scanner catches transactions missed by main loop
  • Late payments to expired quotes handled separately
  • Blockchain reorganizations handled through re-scanning

Database Schema Impact

CryptoPayment Table Fields Used

  • id: Unique payment identifier
  • status: Payment status (see status flow above)
  • received_amount: Amount received for this specific payment
  • tx_hashes: JSON array of transaction IDs
  • refund_tx_hash: Transaction ID of refund (for duplicates)
  • refund_address: Where to send refund
  • subaddress_index: Unique address identifier

Query Patterns

-- Find payment by subaddress
SELECT * FROM mps_crypto_payment 
WHERE coin_type = ? AND account_index = ? AND subaddress_index = ?

-- Find duplicate by transaction ID
SELECT * FROM mps_crypto_payment 
WHERE status = 'doublepay-refund' 
AND tx_hashes LIKE '%"transaction-id-here"%'

-- Get payment history
SELECT * FROM mps_crypto_payment 
WHERE user_id = ? 
ORDER BY created_timestamp DESC

This system ensures that every payment (original and all duplicates) is tracked, processed, and appears in the user's payment history while preventing financial loss through proper duplicate detection and single-refund-per-transaction guarantees.