- Convert all state names from underscore to dash format in markdown documentation - Ensures consistency across all documentation formats (dot, svg, markdown) - All payment states now use dashes: confirmed-complete, underpaid-refunded, etc. - Maintains consistency with the source dot file which is the canonical reference This completes the documentation naming convention standardization.
14 KiB
Crypto Payments State Machine
This document visualizes the complete state machine for cryptocurrency payments in the make-post-sell system, including semantic state groups, business logic rules, and implementation details.
State Machine Diagram
stateDiagram-v2
[*] --> pending
[*] --> doublepay-refunded : Duplicate payment detected
[*] --> latepay-refunded: Late payment detected
%% Main payment flow
pending --> received : Payment detected in mempool
pending --> expired : Payment timeout (never received)
pending --> cancelled : User cancellation
%% From received state - multiple possible outcomes
%% NOTE: received payments CANNOT expire (detected in mempool, confirmations tracking)
received --> confirmed : Sufficient payment + confirmations
received --> confirmed-overpay : Overpayment detected
received --> underpaid-refunded : Underpayment detected
received --> out-of-stock-refunded : Product unavailable
%% Successful payment paths
confirmed --> confirmed-complete : Swept to cold storage
confirmed-complete --> [*] : ✓ Terminal Success
%% Overpayment refund flow
confirmed-overpay --> confirmed-overpay-refunded : Initiate refund
confirmed-overpay-refunded --> confirmed-overpay-refunded-complete : Refund confirmed
confirmed-overpay-refunded --> confirmed-overpay-not-refunded : No refund wallet configured
confirmed-overpay-refunded-complete --> [*] : ✓ Terminal Success
confirmed-overpay-not-refunded --> [*] : ✓ Terminal Success (Not Refunded)
%% Expired payment handling (terminal - late payments create new objects)
expired --> [*] : ✓ Terminal Failed (Expired)
%% Late payment objects (created separately for payments after expiration)
latepay-refunded --> latepay-refunded-complete : Refund confirmed
latepay-refunded --> latepay-not-refunded : No refund wallet configured
latepay-refunded-complete --> [*] : ✓ Terminal Failed (Refund Complete)
latepay-not-refunded --> [*] : ✓ Terminal Failed (Not Refunded)
%% Underpayment refund flow
underpaid-refunded --> underpaid-refunded-complete : Refund confirmed
underpaid-refunded --> underpaid-not-refunded : No refund wallet configured
underpaid-refunded-complete --> [*] : ✓ Terminal Failed (Refund Complete)
underpaid-not-refunded --> [*] : ✓ Terminal Failed (Not Refunded)
%% Out of stock refund flow
out-of-stock-refunded --> out-of-stock-refunded-complete : Refund confirmed
out-of-stock-refunded --> out-of-stock-not-refunded : No refund wallet configured
out-of-stock-refunded-complete --> [*] : ✓ Terminal Failed (Refund Complete)
out-of-stock-not-refunded --> [*] : ✓ Terminal Failed (Not Refunded)
%% Double payment refund flow
doublepay-refunded --> doublepay-refunded-complete : Refund confirmed
doublepay-refunded --> doublepay-not-refunded : No refund wallet configured
doublepay-refunded-complete --> [*] : ✓ Terminal Failed (Refund Complete)
doublepay-not-refunded --> [*] : ✓ Terminal Failed (Not Refunded)
%% User cancellation (always terminal, only from pending)
cancelled --> [*] : ✓ Terminal Failed (Cancelled)
%% Style the states by semantic category
classDef successState fill:#d4edda,stroke:#155724,color:#155724
classDef initialWaitingState fill:#e7f3ff,stroke:#0056b3,color:#0056b3
classDef refundState fill:#fff3cd,stroke:#856404,color:#856404
classDef failedState fill:#f8d7da,stroke:#721c24,color:#721c24
classDef processingState fill:#cce5ff,stroke:#004085,color:#004085
%% Successful payments (customer received product)
class confirmed,confirmed-complete,confirmed-overpay-refunded-complete,confirmed-overpay-not-refunded successState
%% Initial/waiting states (entry points that don't come from 'received')
class pending,latepay-refunded,doublepay-refunded initialWaitingState
%% Active refund processing states
class confirmed-overpay-refunded,underpaid-refunded,out-of-stock-refunded refundState
%% Failed payments (customer did not receive product)
class expired,cancelled,latepay-refunded-complete,latepay-not-refunded,underpaid-refunded-complete,underpaid-not-refunded,out-of-stock-refunded-complete,out-of-stock-not-refunded,doublepay-refunded-complete,doublepay-not-refunded failedState
%% Processing states
class received,confirmed-overpay processingState
Semantic State Groups
The state machine uses semantic groups to categorize states by business logic purpose. These groups determine invoice handling, priority processing, and system behavior.
🔵 Initial/Waiting States (Light Blue)
Entry point states that don't transition from received - they represent the start of payment flows:
pending- Initial state for new payment requestslatepay-refunded- Initial state for late payment objects (payments received after expiration)doublepay-refunded- Initial state for duplicate payment objects (separate payment instances)
Business Logic: These states represent separate payment flows and are processed with Priority 0-2 depending on their nature.
🟢 Successful Payment States (Green)
Customer received their product - invoices are preserved:
confirmed- Normal successful payment (exact amount, confirmed)confirmed-complete- Confirmed payment that has been swept to cold storageconfirmed-overpay-refunded-complete- Overpaid, customer got product + refundconfirmed-overpay-not-refunded- Overpaid, customer got product, no refund wallet configured
Business Logic: is_successful_payment() = True, should_keep_invoice() = True
🔴 Failed Payment States (Red)
Customer did not receive product - invoices are deleted:
expired- Payment window expired before any blockchain detectioncancelled- User cancelled payment (only from pending)*-refunded-complete- Failed payments with completed refunds*-not-refunded- Failed payments with no refund wallet configured
Business Logic: is_failed_payment() = True, should_keep_invoice() = False
🟡 Refund Processing States (Yellow)
Active refund workflows - intermediate states:
confirmed-overpay-refunded- Overpayment refund in progress (customer got product)underpaid-refunded- Underpayment refund in progressout-of-stock-refunded- Out of stock refund in progress- Note:
latepay-refundedanddoublepay-refundedare Initial/Waiting states, not regular refund processing
Business Logic: Priority 0 processing (highest), actively monitored for confirmation
🔵 Processing States (Blue)
Active payment processing states:
received- Payment detected on blockchain, being processedconfirmed-overpay- Overpayment confirmed, deciding refund action
Business Logic: Priority 1-3 processing, confirmation monitoring
Critical State Machine Rules
Rule 1: Blockchain Detection is Irreversible
- ❌ INVALID:
received → expired - ✅ VALID:
pending → expired
Rationale: Once a payment is detected on the blockchain (received), it cannot "expire" - it already exists. Only payments that never arrive can expire.
Rule 2: Past-Tense Naming Convention
All status constants use past-tense naming for consistency:
- ✅
STATUS_DOUBLEPAY_REFUNDED(correct) - ❌
STATUS_DOUBLEPAY_REFUND(incorrect - not past tense)
Rule 3: Entry Points vs Transitions
Some states are entry points for new payment objects, not transitions from existing payments:
pending- Entry point for new paymentslatepay-refunded- Entry point for late payment objects (created after expiration)doublepay-refunded- Entry point for duplicate payment objects
Rule 4: Invoice Preservation Logic
should_keep_invoice() = is_successful_payment()
- Keep: Successful payments (customer got product)
- Delete: Failed payments (customer did not get product)
Priority-Based Processing System
The crypto watcher processes payments by priority to ensure proper fund flow and customer service:
Priority 0 (Highest): Customer Refunds
doublepay-refunded,latepay-refunded,underpaid-refunded,out-of-stock-refunded- Rationale: Customer service is highest priority
Priority 1: New Incoming Payments
received- Rationale: Process new money quickly for customer experience
Priority 2: Other Processing
pending, monitoring states, intermediate states- Rationale: General processing tasks
Priority 3: Auto-Sweep to Shop Owner
confirmed,confirmed-overpay- Rationale: Move confirmed funds to shop owner
Priority 4 (Lowest): Restocking Fee Sweeps
*-refunded-completestates- Rationale: Most dangerous operation, requires high confirmations, done last
Business Logic Flows
Normal Payment Flow
pending → received → confirmed → confirmed-complete ✅
Customer pays exact amount, gets product, invoice kept, funds swept to cold storage.
Overpayment Flow
pending → received → confirmed-overpay → confirmed-overpay-refunded → confirmed-overpay-refunded-complete ✅
Customer overpays, gets product, gets refund, invoice kept.
Late Payment Flow
Original: pending → expired ❌
New object: latepay-refunded → latepay-refunded-complete ❌
Original payment expires. Late payment creates new object, gets refunded, invoice deleted.
Underpayment Flow
pending → received → underpaid-refunded → underpaid-refunded-complete ❌
Customer pays too little, gets refund, no product, invoice deleted.
Duplicate Payment Flow
Original: pending → received → confirmed ✅
Duplicate: doublepay-refunded → doublepay-refunded-complete ❌
First payment succeeds, duplicate creates separate object and gets refunded.
Out of Stock Flow
pending → received → out-of-stock-refunded → out-of-stock-refunded-complete ❌
Product unavailable, customer gets refund, no product, invoice deleted.
Cancellation Flow
pending → cancelled ❌
User cancels before payment detected, invoice deleted.
Terminal States Analysis
Successful Terminals (keep invoice):
confirmed- Normal success (awaiting sweep)confirmed-complete- Normal success + swept to cold storageconfirmed-overpay-refunded-complete- Overpaid + refundedconfirmed-overpay-not-refunded- Overpaid, no refund wallet
Failed Terminals (delete invoice):
expired- Never paidcancelled- User cancelled*-refunded-complete- Failed + refunded*-not-refunded- Failed, no refund wallet
State Transition Validation
All transitions are validated via CryptoPayment.is_valid_transition():
VALID_TRANSITIONS = {
STATUS_PENDING: [STATUS_RECEIVED, STATUS_EXPIRED, STATUS_CANCELLED],
STATUS_RECEIVED: [
STATUS_CONFIRMED,
STATUS_CONFIRMED_OVERPAY,
# NOTE: STATUS_EXPIRED removed - received payments cannot expire
STATUS_UNDERPAID_REFUNDED,
STATUS_DOUBLEPAY_REFUNDED,
STATUS_OUT_OF_STOCK_REFUNDED,
],
STATUS_CONFIRMED: [STATUS_CONFIRMED_COMPLETE], # Can transition to complete after sweep
STATUS_CONFIRMED_COMPLETE: [], # Terminal - confirmed and swept
# ... additional transitions
}
Property Testing: The complete test suite validates:
- ✅ All valid transitions work correctly
- ❌ Invalid transitions are properly rejected
- 🔄 Priority system assignments are consistent
- 📊 Graph analysis confirms no orphaned states or cycles
- 🧪 Property-based testing validates invariants
- 🛣️ All payment lifecycles reach valid terminal states
Implementation Details
Core Files
-
Model:
make_post_sell/models/crypto_payment.py- Line 55:
INITIAL_WAITING_STATUSES- New semantic group - Line 62:
SUCCESSFUL_PAYMENT_STATUSES- Keep invoices - Line 70:
FAILED_PAYMENT_STATUSES- Delete invoices - Line 386:
VALID_TRANSITIONS- State transition rules - Line 488:
is_successful_payment()- Business logic helper - Line 522:
is_initial_waiting_state()- New helper method
- Line 55:
-
Tests:
make_post_sell/tests/test_crypto_payment_transitions.py- 21 comprehensive test methods
- Property-based validation
- Graph analysis and reachability testing
- Priority system validation
-
Watcher:
make_post_sell/lib/crypto_watcher.py- Priority-based processing engine
- State transition enforcement
- Invoice deletion logic
-
Views:
make_post_sell/views/crypto.py- Status display logic
- User-facing state information
Database Schema
- Payments stored with
statuscolumn containing string constants invoice_idnullable for proper invoice handlingcreated_timestampandupdated_timestampfor audit trails
Testing Coverage
- 155 total crypto payment tests all passing
- Unit tests for individual state transitions
- Integration tests for payment flows
- Property-based tests for state machine invariants
- Functional tests for end-to-end payment processing
Debugging and Monitoring
Log Messages
All state transitions generate structured logs:
Payment {id} state transition: {old_status} → {new_status} (context: {context})
Invalid Transition Handling
if not payment.is_valid_transition(new_status):
logger.error(f"Invalid state transition for payment {payment.id}: {payment.status} → {new_status}")
return False
Common Issues
- Invalid
received → expired: Check if payment was properly detected on blockchain - Priority conflicts: Verify processing order matches business requirements
- Invoice handling: Ensure successful payments keep invoices, failed payments delete them
- Naming inconsistency: All status constants must use past-tense naming
This state machine provides a robust, well-tested foundation for cryptocurrency payment processing with clear semantic boundaries and validated state transitions.