The chain rail: money that arrives at an address the store watches.
Correlation is by address — charging opens one, the invoice records it, and
settlement is read back from the wallet for that address. A settlement notice
is only ever a prompt to re-read; its body is never evidence. That is what
makes this rail safe over a gateway that signs nothing, and it is why the
profile declares :server-confirmed.
Any IChainWallet drives it: a monero-wallet-rpc the store runs, a MoneroPay
instance watching on its behalf, or the fake. The rail cannot tell, which is
the point.
The chain rail: money that arrives at an address the store watches. Correlation is by address — charging opens one, the invoice records it, and settlement is read back from the wallet for that address. A settlement notice is only ever a prompt to re-read; its body is never evidence. That is what makes this rail safe over a gateway that signs nothing, and it is why the profile declares `:server-confirmed`. Any `IChainWallet` drives it: a monero-wallet-rpc the store runs, a MoneroPay instance watching on its behalf, or the fake. The rail cannot tell, which is the point.
The hosted-checkout rail: the customer pays on the processor's page.
Correlation is by session id, and the money is read back from the session
itself. What differs between processors — which event names mean settled,
where the amount sits in the body, how a notice is authenticated — is a
READER, supplied as data. This namespace holds the behaviour every hosted
checkout shares; payments.stripe is one reader over it, and a second
processor is a second reader, not a second rail.
The hosted-checkout rail: the customer pays on the processor's page. Correlation is by session id, and the money is read back from the session itself. What differs between processors — which event names mean settled, where the amount sits in the body, how a notice is authenticated — is a READER, supplied as data. This namespace holds the behaviour every hosted checkout shares; `payments.stripe` is one reader over it, and a second processor is a second reader, not a second rail.
Operator-asserted payment. The rail for cash, bank transfer, a donation platform with no API, and comps.
Its profile declares :provider/webhook-auth :none, which is what keeps it
off the HTTP settlement surface entirely: interpret grants on nothing but
the invoice's own amount, so reaching it from the wire would be a way to be
paid by asking. It is reachable only from an operator session.
Operator-asserted payment. The rail for cash, bank transfer, a donation platform with no API, and comps. Its profile declares `:provider/webhook-auth :none`, which is what keeps it off the HTTP settlement surface entirely: `interpret` grants on nothing but the invoice's own amount, so reaching it from the wire would be a way to be paid by asking. It is reachable only from an operator session.
Payment rails as data plus a protocol for their effects. The DIP seam.
A ProviderProfile carries the measured behaviour of a rail — its currency,
how many confirmations it needs, how much underpayment it tolerates, whether
it can be polled, how its notices can be authenticated. settle reads that
profile and never branches on which rail it is, so a new rail is a new entry
in a registry and no edit anywhere below.
The registry is a VALUE, built at boot and carried in deps. Nothing here is global: two stores in one process, or a test and the system it tests, hold their own.
Payment rails as data plus a protocol for their effects. The DIP seam. A ProviderProfile carries the measured behaviour of a rail — its currency, how many confirmations it needs, how much underpayment it tolerates, whether it can be polled, how its notices can be authenticated. `settle` reads that profile and never branches on which rail it is, so a new rail is a new entry in a registry and no edit anywhere below. The registry is a VALUE, built at boot and carried in deps. Nothing here is global: two stores in one process, or a test and the system it tests, hold their own.
Stripe as a READER over the hosted-checkout rail.
Everything Stripe-specific is here and it is all data plus one signature
check: which event names mean settled, where the invoice id is replayed, and
how a notice authenticates itself. The rail behaviour lives in
payments.hosted; adding a second processor means writing another reader
this size, not another rail.
The signature is verified with javax.crypto rather than the Stripe SDK, so
a notice can be authenticated by a build that carries no vendor jar at all —
the SDK is confined to the gateway adapter that opens sessions.
Stripe as a READER over the hosted-checkout rail. Everything Stripe-specific is here and it is all data plus one signature check: which event names mean settled, where the invoice id is replayed, and how a notice authenticates itself. The rail behaviour lives in `payments.hosted`; adding a second processor means writing another reader this size, not another rail. The signature is verified with `javax.crypto` rather than the Stripe SDK, so a notice can be authenticated by a build that carries no vendor jar at all — the SDK is confined to the gateway adapter that opens sessions.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |