Replay And Freshness¶
The facilitator protects exact-pay-per-request flows in two layers:
- Redis replay markers keyed by both the challenge reference and signed-blob hash
- XRPL ledger-window checks in public-gateway mode
Replay Keys¶
Every XRPL-backed payment attempt resolves to two replay identifiers:
- the challenge reference carried in XRPL
InvoiceID blob_hash
For MPP charge, the signed XRPL transaction must carry the challenge invoiceId in InvoiceID.
For MPP session open and session top_up, the signed XRPL transaction must carry the challenge sessionId in InvoiceID.
If the signed transaction omits that reference or uses a different value, the facilitator rejects the payment before settlement.
That means two requests collide if they reuse either:
- the same XRPL
InvoiceID - the same signed transaction blob
Charge And Session Settlement¶
For charge, middleware forwards the decoded MPP credential to POST /charge, and the facilitator:
- verifies the challenge binding and expiry
- validates the signed XRPL payment and reserves both replay keys as
pending - submits the transaction to XRPL
- either converts the reservation to
processedor releases it on failure - returns a
PaymentReceipt
For session, the facilitator uses POST /session:
openandtop_upreuse the same replay reservation and XRPL settlement path, keyed bysessionIduseandclosemutate Redis-backed session state without submitting a new XRPL transaction
This keeps replay protection and settlement atomic while matching the current MPP HTTP API surface.
Redis State¶
Replay markers for charge, session open, and session top_up are stored in Redis as:
pending:<reservation_id>while a settlement is in progressprocessedafter a successful settlement path
Defaults:
- processed TTL:
REPLAY_PROCESSED_TTL_SECONDS, default604800seconds - pending TTL:
max(VALIDATION_TIMEOUT + 60, 300)
If either replay key already exists, the facilitator rejects the payment with:
Transaction already processed (replay attack)
Settlement Mode Effects¶
validated¶
In validated mode, the facilitator:
- submits the transaction
- polls XRPL for up to
VALIDATION_TIMEOUTseconds - waits for
tx.result.validated - checks the delivered amount against the required exact amount
- returns
status="validated"
If validation never arrives in time, the pending reservation is released and settlement fails.
optimistic¶
In optimistic mode, the facilitator:
- submits the transaction
- marks the replay reservation processed immediately
- returns
status="submitted"
This mode lowers latency, but it shifts more validation responsibility to the surrounding system.
Freshness Rules In redis_gateways Mode¶
When GATEWAY_AUTH_MODE=redis_gateways, the facilitator additionally requires bounded XRPL timing:
- the signed transaction must include
LastLedgerSequence LastLedgerSequencemust be greater than the latest validated ledgerLastLedgerSequencemust not exceedcurrent_validated_ledger + MAX_PAYMENT_LEDGER_WINDOW
If any of those checks fail, the facilitator rejects the payment before settlement.
This is why public-gateway mode is stricter than single_token: it assumes third-party sellers may retry or relay traffic, so the facilitator enforces a narrow ledger window for safer exact-payment handling.
Practical Guidance¶
- generate a fresh signed transaction per paid request
- use the challenge
invoiceIdorsessionIdas the XRPLInvoiceIDwhen you sign manually - leave XRPL autofill enabled, or set
LastLedgerSequenceyourself, when usingredis_gateways - prefer
validatedsettlement for internet-facing deployments
For the on-the-wire request/response format, continue to Header Contract.