# Payment State Machine & UNKNOWN — Lab M11

## 01. Bài toán
A timed-out broadcast can be mined even though the HTTP caller saw an exception.

## 02. Vấn đề
Without durable state, crashes, duplicates or reorgs can silently corrupt payment records.

## 03. Giải pháp
Track transaction hash and UNKNOWN state; reconcile with on-chain receipt before settling.

## 04. Lý thuyết
- Distinguish blockchain consensus from your database transaction boundary.
- Make replay and crash recovery deterministic, using stable identifiers.
- For production use PostgreSQL transactions, unique constraints, Kafka delivery semantics, and RPC provider failover.

## 05. Lab từng bước
1. Open `solution/PaymentDemo.java`; identify persistent state and failure transition.
2. Run `cd solution && bash test.sh` (requires JDK 21).
3. Observe the printed PASS assertion and use `java -ea` to enable Java assertions.
4. Change the failure scenario and verify the invariant fails as expected.
5. Implement the same algorithm in `starter/` without looking at solution.

## 06. Expected output
See `expected-output.md`; a PASS line proves only the educational in-memory scenario.

## 07. Exercises
Add idempotency key and a finality-aware CONFIRMED stage. Define replacement and dropped-transaction policies.

## 08. Interview
- Why is at-least-once delivery insufficient without idempotency?
- What state must be persisted atomically to survive crashes?
- How does a blockchain reorganization affect this component?
- Which assumptions of the in-memory simulation fail in production?

## Production extension
Replace the in-memory store with PostgreSQL and test process restart, transaction rollback, concurrent workers, and a real Anvil RPC. This extension is **not implemented** in the reference demo.
