make_post_sell/docs/crypto-payments-state-machine.md
2025-09-30 14:46:47 -04:00

333 lines
14 KiB
Markdown

# 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
```mermaid
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 --> [*] : ✓ 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_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 requests
- **`latepay_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_overpay_refunded_complete`** - Overpaid, customer got product + refund
- **`confirmed_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 detection
- **`cancelled`** - 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 progress
- **`out_of_stock_refunded`** - Out of stock refund in progress
- **Note**: `latepay_refunded` and `doublepay_refunded` are 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 processed
- **`confirmed_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 payments
- `latepay_refunded` - Entry point for late payment objects (created after expiration)
- `doublepay_refunded` - Entry point for duplicate payment objects
### **Rule 4: Invoice Preservation Logic**
```python
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_complete` states
- **Rationale**: Most dangerous operation, requires high confirmations, done last
## Business Logic Flows
### **Normal Payment Flow**
```
pending → received → confirmed ✅
```
Customer pays exact amount, gets product, invoice kept.
### **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
- `confirmed_overpay_refunded_complete` - Overpaid + refunded
- `confirmed_overpay_not_refunded` - Overpaid, no refund wallet
**Failed Terminals** (delete invoice):
- `expired` - Never paid
- `cancelled` - User cancelled
- `*_refunded_complete` - Failed + refunded
- `*_not_refunded` - Failed, no refund wallet
## State Transition Validation
All transitions are validated via `CryptoPayment.is_valid_transition()`:
```python
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,
],
# ... 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
- **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 `status` column containing string constants
- `invoice_id` nullable for proper invoice handling
- `created_timestamp` and `updated_timestamp` for 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**
```python
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**
1. **Invalid `received → expired`**: Check if payment was properly detected on blockchain
2. **Priority conflicts**: Verify processing order matches business requirements
3. **Invoice handling**: Ensure successful payments keep invoices, failed payments delete them
4. **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.