Receive payments

Get a receive-only NWC code

A NWC code allows you to securely receive payments with OpenReceive.

NWC is built on the NIP-47 open source standard.

Choose from any provider below or build your own NWC service.

Recommended

Rizful #

Rizful is free and has simple onboarding.

Rizful menu showing the NWC option
Open the Rizful menu and choose NWC.
Rizful modal showing Copy receive-only code
Choose Receive-only code, then copy the connection code.
  1. Open Rizful and tap the menu.
  2. Tap NWC.
  3. Tap Receive-only NWC Code.
  4. Tap Copy receive-only code and paste it into OpenReceive.

Tutorial: Get a read-only NWC code from Rizful

Recommended

Alby Hub #

Alby Hub is self-custodial and is therefore recommended for larger merchants.

  1. Open Alby Hub and go to Connections.
  2. Choose Add Connection.
  3. Select the Read Only connection type and press Next.
  4. Copy the nostr+walletconnect:// URL or scan the QR code.

Links: Alby Hub guide

For teams

Build your own NWC service #

NWC is an open standard, so a team can run its own wallet service, choose its relays, and define the exact receive-only behavior it wants.

This requires significant work and is better suited for enterprise deployments or dedicated engineering teams.

A wallet service is an always-on process that controls a Lightning wallet or node, listens on one or more Nostr relays, and answers the encrypted requests OpenReceive sends it. To work with OpenReceive it must implement two specifications.

Required

NIP-47: the NWC core

The connection string, the info event, the encrypted request and response envelope, and make_invoice.

Read NIP-47
Required

NWC-05: transaction history

list_transactions, which OpenReceive uses to confirm that every payment really settled in your wallet.

Read NWC-05

NWC-02 notifications are strongly recommended. NWC-05 is an optional extension in the NWC repository; OpenReceive does not treat it as optional.

Methods OpenReceive calls #

  • make_invoice NIP-47 Required

    Creates one BOLT11 invoice per payment attempt. Sends amount in millisatoshis, description or description_hash, expiry, and optionally metadata. The response must include invoice and a 64-character hex payment_hash; amount, created_at and expires_at are read when present.

  • list_transactions NWC-05 Required

    Confirms payments. Called with type: "incoming", limit: 20, offset, from, until, and unpaid: true on some passes. Return incoming invoices newest first, honor offset, and give every row its payment_hash plus a state or settled_at.

  • get_info NIP-47 Recommended

    Read at startup for this connection's own methods list. Without it, OpenReceive falls back to the service-wide info event, which can list methods this connection is not allowed to use.

  • payment_received notification NWC-02 Recommended

    Marks an order paid the moment the payment lands. Publish kind 23197 (NIP-44), and also 23196 only if you support NIP-04. Without notifications, payments are still found through list_transactions, just later.

  • lookup_invoice, get_balance NIP-47 Not used

    OpenReceive never calls them. Implement them only if other clients need them.

Protocol checklist #

  • Connection string. nostr+walletconnect://<wallet-service-pubkey>?relay=wss://<relay>&secret=<client-secret>. Use a separate wallet-service key for every connection, do not keep the client secret after handing it over, and let the merchant revoke a connection.
  • Info event. A replaceable kind 13194 event on every relay in the connection string. Its content lists the methods, space-separated, for example make_invoice list_transactions get_info. Tag it with encryption (nip44_v2), notifications (payment_received) and extensions (02 05).
  • Requests and responses. Requests arrive as kind 23194 with a p tag naming your key. Answer each with kind 23195, tagged p with the client's key and e with the request id. Set result_type to the method name and error to null, or to a NIP-47 error code and message. Act only on requests signed by the connection's client key, and ignore any request past its expiration tag.
  • Encryption. NIP-44 v2 (nip44_v2). OpenReceive picks it whenever you advertise it; NIP-04 is a deprecated fallback for older wallets.
  • Settlement truth. OpenReceive treats an invoice as paid only when your service reports settled_at or state: "settled", so report either one only when the payment is final in your wallet. A held (accepted) payment is not settled, and a preimage alone is not proof of payment.

Running it in production #

  • Keep the wallet service and the Lightning node behind it online around the clock. Checkout cannot create an invoice while the service is down, and a payment cannot arrive while the node is offline.
  • Make sure the node has inbound liquidity for the payment sizes you expect.
  • Pick relays that keep idle WebSocket connections open and accept events of at least 64 KB, the size NWC-05 asks for transaction pages. Running your own relay with authentication removes a third party from the path.
  • Put the connection string in your app's NWC_URI and run npx openreceive doctor. It connects over your relays and checks the method list, the encryption, and that no spend method is advertised.
  • Then pay a real invoice end to end and watch the order turn paid.
Mobile wallet

Zeus #

Zeus can issue NWC strings from its Nostr Wallet Connect service on recent versions.

  1. Go to Tools, then Nostr Wallet Connect service.
  2. Create a new connection.
  3. Enable make_invoice and list_transactions, and get_info if offered: OpenReceive needs the first two to create invoices and confirm payments.
  4. Leave pay_invoice and keysend permissions off, then copy the connection string.

Links: Zeus docs

Self-hosted

LNbits NWCProvider #

LNbits can mint scoped NWC connections through the NWCProvider extension.

  1. Install and configure the nostrclient extension.
  2. Enable the NWCProvider extension.
  3. Create a connection for the wallet you want to receive into.
  4. Grant make_invoice and list_transactions, and get_info if offered; do not grant pay_invoice.

Links: NWCProvider repo LNbits overview

How NWC travels

What is a Nostr relay? #

A relay is a small server that passes signed messages between Nostr keys over WebSockets. Your OpenReceive server and your wallet service never connect to each other: both connect out to the same relays, and each relay forwards every message to whoever subscribed to it.

Built for real time

  • A subscription is a filter held on an open WebSocket. A matching message is pushed the moment it arrives; nothing polls.
  • Both sides dial out, so your server receives wallet messages without a public URL, an open port or its own certificate, even behind a firewall or on a laptop.
  • A message can go to several relays at once, so one relay going down does not cut the line.

Security properties

  • Every message is signed. OpenReceive accepts responses and notifications only from the wallet service's key, so a relay cannot forge a payment or change an amount.
  • End-to-end encrypted. NIP-44 encrypts content between the two keys. A relay sees event kinds, keys and timestamps, never invoices or amounts.
  • No secrets on the relay. There is no API key or signing secret to steal. A hostile relay can drop or delay messages, not read or fake them.
  • One key per connection. It is nobody's Nostr identity, and revoking it cuts off that one connection.

Why it beats webhooks #

Webhooks Nostr relay
Where your server listens A public HTTPS endpoint anyone can find and probe. Nowhere. It opens an outbound connection.
Proving who sent it A shared secret both sides must store, rotate and verify correctly. A public-key signature on every message. No shared secret.
Privacy in transit TLS protects each hop, but the payload is readable wherever TLS ends: load balancers, proxies, request logs. Encrypted end to end. Relays and proxies only see ciphertext.
Local development Needs a tunnel to reach a laptop. Works anywhere that can open a WebSocket.
Redundancy One URL. If it is down, the sender's retry policy decides what happens. The same message goes to several relays at once.
Latency A new HTTP request per event, often queued. Pushed on a connection that is already open.
Relays do not retry. NWC messages are not stored, so a listener that is offline when a notification goes out never sees it. OpenReceive is built for that: it checks every open payment against the wallet with list_transactions, so a missed notification only delays a confirmation.