Open Markets Protocol
Payments
Merchant preferences, manual and direct flows, payment requests, receipts, wallet execution, and proof verification.
Scope
Open Markets separates four concerns:
- An offer states a price.
- An order proposes a purchase.
- A payment request states what the merchant will accept for that order.
- A payment receipt carries evidence that the buyer paid.
Payment messages are private unsigned rumors transported with the envelope defined in Orders. This specification preserves Gamma's deployed wire allocation: payment requests are kind 16, type 2; payment receipts are kind 17.
Merchant preferences
A merchant MAY publish one payment_preference tag in their kind 0 profile:
["payment_preference", "manual"]
["payment_preference", "ecash"]
["payment_preference", "lud16"]
| Value | Meaning |
|---|---|
| absent | Use the manual flow |
manual | Wait for a merchant-authored payment request |
ecash | A direct eCash flow may be offered |
lud16 | A direct LNURL/Lightning flow may be offered |
| unknown | Do not guess; fall back to manual |
Open Gamma PR #2 proposed replacing these values with service, lightning, and bitcoin, changing the default, and moving receipts into kind 16. Open Markets incorporates its useful three-flow model but rejects those incompatible wire changes.
Before checkout, a client SHOULD automatically resolve:
- the merchant's kind
0payment preference; - merchant-authored NIP-89 kind
31989application recommendations; - the referenced kind
31990application descriptor; - required payment metadata such as
lud16or preferred Cashu mint information.
The merchant's signature on kind 31989 establishes a recommendation. A standalone application descriptor or client tag does not. Recommendations affect routing and UX; they do not authorize software to sign for either party.
Processing flows
Manual
Manual is the default and most compatible flow:
- Buyer sends a kind
16, type1order. - Merchant MAY acknowledge it as
pending. - Merchant calculates the final amount and sends kind
16, type2. - Buyer validates and pays one option.
- Buyer sends kind
17with payment evidence. - Merchant verifies the proof independently.
- Merchant sends kind
16, type3,confirmed.
Direct
Direct payment is available only when the merchant has published sufficient compatible payment information:
- Buyer resolves
ecashorlud16preference data. - Buyer creates the order and obtains or constructs valid payment material.
- Buyer pays without waiting for a merchant-authored request.
- Buyer sends a normal kind
17receipt. - Merchant verifies payment and sends
confirmed.
direct is a flow classification, not a payment_preference wire value.
Service-assisted
A merchant-recommended application MAY facilitate either flow. The service can calculate totals, construct a payment request, monitor payment, or help construct a receipt. The user remains the author of user-authored rumors unless separate explicit authority exists.
A buyer-authored type 2 rumor is permitted only as service-assisted payment initiation when the merchant has explicitly recommended the service, the kind 31990 handler declares compatible support, and the merchant can independently validate its amount and payment details. It MUST NOT be presented as a merchant-authored request.
Payment request
A payment request is normally authored by the merchant and addressed to the buyer.
Schema
| Tag | Cardinality | Meaning |
|---|---|---|
p | exactly 1 | Buyer public key |
subject | exactly 1 | Recommended value order-payment |
type | exactly 1 | 2 |
order | exactly 1 | Original order identifier |
amount | exactly 1 | Final total in integer satoshis |
payment | 1 or more | Alternative payment option |
expiration | 0 or 1 | Request expiry in Unix seconds |
client | 0 or 1 | Optional NIP-89 attribution |
Supported payment option shapes include:
["payment", "lightning", "<bolt11-invoice-or-lud16>"]
["payment", "bitcoin", "<bitcoin-address>"]
["payment", "ecash", "<cashu-payment-request>"]
["payment", "<medium>", "<medium-specific-request>"]
Repeated payment tags are alternatives. This protocol does not define split payment across them.
Example
{
"id": "<rumor-id>",
"pubkey": "<merchant-pubkey>",
"created_at": 1786953600,
"kind": 16,
"tags": [
["p", "<buyer-pubkey>"],
["subject", "order-payment"],
["type", "2"],
["order", "a47e7fbb3a8f4f98a31d88b3f7703567"],
["amount", "21000"],
["payment", "lightning", "lnbc210u1..."],
["payment", "bitcoin", "bc1q..."],
["expiration", "1786954500"]
],
"content": "Choose one payment option before the request expires."
}
The optional expiration tag is retained from merged Gamma PR #4. A client MUST NOT pay an expired request. When native payment material expires earlier, such as a BOLT11 invoice, the earlier expiry wins.
Request validation
Before presenting or paying a request, a client MUST:
- Validate the NIP-17 envelope, author, recipient, order identity, and tag cardinality.
- Require
amountto be a non-negative base-10 integer in satoshis. - Confirm that the request corresponds to an existing order between the same buyer and merchant.
- Display material differences between the order proposal and payment request.
- Validate each payment option before use.
- Reject expired requests.
For BOLT11, the encoded amount MUST equal amount × 1000 millisatoshis when the invoice specifies an amount. For lud16, the client MUST resolve LNURL-pay and validate the returned invoice. A Lightning address is a destination, not an amount-bound invoice.
A newer payment request does not automatically invalidate every earlier request. Clients SHOULD detect multiple valid requests for one order and require confirmation when their amount or destination differs.
Payment receipt
A receipt is a kind 17 rumor authored by the buyer and addressed to the merchant. It is a payment claim with evidence, not settlement by itself.
Schema
| Tag | Cardinality | Meaning |
|---|---|---|
p | exactly 1 | Merchant public key |
subject | exactly 1 | Recommended value order-receipt |
order | exactly 1 | Original order identifier |
amount | exactly 1 | Amount paid in integer satoshis |
payment | 1 or more | Payment proof |
client | 0 or 1 | Optional NIP-89 attribution |
Proof forms:
["payment", "lightning", "<invoice>", "<preimage>"]
["payment", "bitcoin", "<receiving-address>", "<transaction-id>"]
["payment", "ecash", "<mint-url>", "<token-or-proof>"]
["payment", "fiat", "<provider-reference>", "<provider-proof>"]
["payment", "<medium>", "<medium-reference>", "<proof>"]
{
"id": "<rumor-id>",
"pubkey": "<buyer-pubkey>",
"created_at": 1786953660,
"kind": 17,
"tags": [
["p", "<merchant-pubkey>"],
["subject", "order-receipt"],
["order", "a47e7fbb3a8f4f98a31d88b3f7703567"],
["amount", "21000"],
["payment", "lightning", "lnbc210u1...", "<32-byte-preimage-hex>"]
],
"content": "Lightning payment completed."
}
Receiving a syntactically valid receipt MUST NOT automatically confirm the order. The merchant confirms only after independent verification.
Proof verification
Lightning
The merchant MUST decode the invoice, verify its network and amount, calculate SHA256(preimage), and require it to equal the invoice payment hash. A preimage is stronger evidence than a screenshot or a receipt event alone.
Bitcoin
The merchant MUST verify the transaction on the intended network, destination output, amount, transaction status, and application-defined confirmation depth. A transaction ID without a matching output is insufficient.
Ecash
Cashu material is bearer value. The merchant MUST validate and redeem proofs with the intended mint and MUST prevent replay. Merely receiving an ecash string does not establish settlement.
Fiat
Open Markets defines no universal fiat proof. The merchant MUST verify the provider reference with the provider or another trusted settlement source. Opaque text and screenshots are not machine-verifiable proof.
Nostr Wallet Connect
NIP-47 controls the payer's wallet; it does not replace an Open Markets payment request or receipt.
For Lightning execution:
- Obtain and validate a BOLT11 invoice from the request or LNURL callback.
- Send NWC
pay_invoiceas kind23194using the dedicated connection key. - Validate the kind
23195response and requireresult_type=pay_invoice. - Use the returned preimage in the buyer's kind
17receipt.
NWC uses millisatoshis while Open Markets uses satoshis:
nwc_amount_msat == open_markets_amount_sat × 1000
Clients MUST NOT publish or include the NWC URI, connection secret, wallet authorization, or encrypted NWC payload in an order rumor.
Lightning zaps
NIP-57 MAY provide an invoice and public zap receipt when a merchant's LNURL endpoint supports it. Clients MUST validate the zap receipt signer, embedded request, description hash, amount, recipient, and LNURL.
A kind 9735 zap receipt is supplemental evidence and does not replace kind 17. NIP-57 explicitly notes that a zap receipt is not cryptographic proof that a real invoice was paid. A verified preimage remains preferable.
Client attribution
Payment requests and receipts MAY include the NIP-89 client tuple. Software SHOULD include it when attribution is enabled and MUST offer an opt-out. The tag helps debugging and deep-linking but MUST NOT be used as evidence of authorization, merchant recommendation, or payment validity.
PR #2 compatibility decisions
Open Markets incorporates every compatible intent from Gamma PR #2 while preserving deployed semantics.
| PR #2 proposal | Open Markets decision |
|---|---|
| Direct, manual, and service flows | Adopted as behavioral profiles |
Replace manual with service | Rejected; manual and its default remain |
Add lightning and bitcoin preferences | Not added to the current enum; methods remain payment options |
Move receipts into kind 16, type 2 | Rejected; kind 17 remains independently filterable |
Rename shipping to shipping_option | Rejected for order-message compatibility |
| Make fixed subjects mandatory | Rewritten as recommended conventional subjects |
| Change ecash receipt tuple order | Rejected; mint URL remains before proof |
| Improve flow explanations | Adopted throughout this page |
This is a deliberate reconciliation, not an accidental partial merge. It follows the later PR discussion favoring separate business messages and receipts for retrieval, accountability, and UX.
Security requirements
- Payment material MUST stay inside encrypted rumors.
- Clients MUST validate authorship, recipient role, order identity, amounts, and expiration before payment.
- Receipts MUST be deduplicated by rumor ID and proof identity.
- A receipt,
clienttag, NIP-89 recommendation, or zap receipt MUST NOT be treated as settlement authority. - Implementations MUST prevent eCash replay and protect bearer proofs at rest.
- Wallet operations SHOULD use connection-specific keys and least-privilege budgets.
- Users MUST see the amount, currency conversion, payment destination, and relevant expiry before approval.