2024-08-30 15:25:35 -04:00
NIP-69: Nostr Offer STRings
===========================
2024-08-26 22:54:45 -04:00
Nostr Offer STRings
-------------------
`wip` `optional`
2024-08-30 15:25:35 -04:00
This NIP proposes a format for static payment codes in Nostr as a successor to LNURL-Pay, enabling users to initiate Lightning Network payments by scanning or clicking a string.
2024-08-26 22:54:45 -04:00
## Motivation
Reliance on LNURL has led to centralization via custodial solutions due to the legacy baggage of IP4-NAT/Domain/SSL requirements. Nostr's use-cases are already centered around Lightning, whether as a wallet connector, zap receipts, or its overlapping network effects, rendering Nostr as a de facto "3rd layer" for Lightning and a natural successor to LNURL.
When layered over kind 21000, NWC, or other potential Lightning RPCs, this specification enables a seamless user experience similar to legacy LNURL-Pay, without the domain and SSL requirements. Additionally, the signed nature of Nostr communications eliminates the trust requirements inherent in LNURL when a node must outsource the serving of web requests.
## Specification
### Static Payment Code Format
The static payment code is a bech32 (per NIP-19) encoded string prefixed with `noffer` . The encoded string will include the following TLV (Type-Length-Value) items:
2024-08-30 15:25:35 -04:00
- `0` : The 32 bytes of the receiving service's public key, encoded in hex.
- `1` : The relay URL where the receiving service subscribes to payment requests.
- `2` : The offer string.
2024-08-26 22:54:45 -04:00
2024-08-30 16:02:49 -04:00
### NIP-05 Integration
To support trust-optimized Lightning Addresses, services can add a `nip69` field in the NIP-05 content. This field will provide the offer on name-based lookups.
2024-08-30 15:25:35 -04:00
### Process Flow
2024-08-26 22:54:45 -04:00
1. **Payer Scans or Clicks the Static Payment Code**
2024-08-30 15:25:35 -04:00
2. **Payer's Wallet Decodes the Payment Code**
3. **Payer's Wallet Sends a Nostr Event to the Specified Relay**
- Addressed to the receiver's public key, containing the identifier string.
2024-08-26 22:54:45 -04:00
- Event Type: Ephemeral Kind 21001 | NIP-44 Encrypted
2024-08-30 15:25:35 -04:00
- Optional: Include additional payer data.
2024-08-26 22:54:45 -04:00
4. **Receiver Responds with Lightning Invoice**
2024-08-30 15:25:35 -04:00
- Generates a Lightning invoice and responds with a Nostr event containing the invoice details.
2024-08-26 22:54:45 -04:00
- Event Type: Ephemeral Kind 21001 | NIP-44 Encrypted
2024-08-30 15:25:35 -04:00
- Optional: Include additional purchase data.
2024-08-26 22:54:45 -04:00
5. **Payer Pays the Invoice**
2024-08-30 15:25:35 -04:00
- Completes the payment by settling the Lightning invoice.
2024-08-26 22:54:45 -04:00
6. **Optional: Receiver Emits Payment Receipt**
- This step is out of scope for this NIP, but potential receipt considerations include:
2024-08-30 15:25:35 -04:00
- Nostr Event as Receipt: Emitting a public Nostr event as a "zap" receipt for verifiable record-keeping.
- Lightning Pre-Image: Sharing the Lightning pre-image as proof of payment.
- External / WebHook: The receiver may finalize the flow out-of-band.
2024-08-26 22:54:45 -04:00
### Nostr Event
This NIP specifies the use of event kind `21001` with the following structure:
2024-08-30 15:25:35 -04:00
- `content` : NIP-44 encrypted payment details.
2024-08-26 22:54:45 -04:00
- `tags` :
2024-08-30 16:02:49 -04:00
- `p` : Receiver's public key (hex).
2024-08-26 22:54:45 -04:00
Example event:
2024-08-30 15:25:35 -04:00
2024-08-26 22:54:45 -04:00
```json
{
"id": "< event_id > ",
"pubkey": "< sender_pubkey > ",
"created_at": 1234567890,
"kind": 21001,
"tags": [
["p", "< receiver_pubkey > "],
["e", "< event_id > "] // used only in response by the receiver to identify the original payment request
],
2024-08-30 15:25:35 -04:00
"content": "< NIP-44 encrypted offer and optional payer data > ",
2024-08-26 22:54:45 -04:00
"sig": "< signature > "
}
2024-08-30 16:06:40 -04:00
```
2024-08-26 22:54:45 -04:00
2024-08-30 16:02:49 -04:00
The `content` field, after NIP-44 decryption, should contain an object with the following structure:
2024-08-26 22:54:45 -04:00
2024-08-30 16:06:40 -04:00
```json
2024-08-26 22:54:45 -04:00
{
2024-08-30 15:25:35 -04:00
"offer": "< offer_string > ",
"payer_data": "< optional_payer_data > "
2024-08-26 22:54:45 -04:00
}
```
## Client Behavior
2024-08-30 15:25:35 -04:00
### Mandatory
2024-08-26 22:54:45 -04:00
Clients implementing this NIP must:
1. Decode and validate the structure of the `noffer` bech32-encoded static payment code.
2. Use NIP-44 encryption for all communication between payer and receiver.
3. Generate and send kind 21001 events as specified in this NIP.
4. Parse the `content` field of received events by decrypting and JSON-parsing the stringified content.
5. Handle potential errors gracefully, providing clear feedback to users.
2024-08-30 15:25:35 -04:00
6. Respect the relay URL specified in the static payment code for sending payment request events.
### Optional
2024-08-26 22:54:45 -04:00
Clients implementing this NIP may:
1. Support both this protocol and LNURL for backward compatibility during the transition period.
2. Implement additional features such as multi-recipient payments or integration with other Lightning-related NIPs.
2024-08-30 16:02:49 -04:00
3. **Use Addressable Events** : Implement addressable events to allow for updates to the recipient key or relay URL associated with the offer.
4. **Attempt Payments by Name** : Use the combination of NIP-05 and NIP-69 to attempt payments by name-based lookups.
2024-08-26 22:54:45 -04:00
2024-08-30 15:25:35 -04:00
### Prohibited
2024-08-26 22:54:45 -04:00
Clients implementing this NIP MUST NOT:
1. Send unencrypted sensitive information in event content or tags.
2. Ignore or modify any fields from the encoded static payment code.
2024-08-30 16:02:49 -04:00
## Service Behavior
### Optional
Services implementing this NIP may:
1. Use an account identifier as a default offer string for spontaneous payments without explicit user setup.
2. **NIP-05 Integration** : Add a `nip69` field in the NIP-05 content to provide the offer on name-based lookups for trust-optimized Lightning Addresses.
2024-08-30 15:25:35 -04:00
## Error Handling
To ensure consistent error handling across implementations, this NIP defines the following error responses, similar to other NIPs:
1. **Invalid Offer** : When the offer ID is invalid or no longer available.
2. **Temporary Failure** : When the receiver is temporarily unable to process the request.
3. **Expired Offer** : When the offer has expired and is no longer valid.
4. **Unsupported Feature** : When the receiver doesn't support a feature requested by the payer.
Error responses should be sent as kind 21001 events with the following structure in the encrypted content:
```json
{
"error": "< error_message > ",
"code": "< error_code > "
}
```
Error codes:
- 1: Invalid Offer
- 2: Temporary Failure
- 3: Expired Offer
- 4: Unsupported Feature
Clients MUST handle these error responses gracefully and display appropriate messages to users.
2024-08-26 22:54:45 -04:00
2024-08-30 15:25:35 -04:00
## Transition from LNURL
2024-08-26 22:54:45 -04:00
LNURL services wishing to migrate to Nostr Offer STRings should consider adding a `noffer` tag to their responses.
2024-08-30 15:25:35 -04:00
## Future Considerations
2024-08-26 22:54:45 -04:00
Future versions of this NIP may consider additional features such as:
2024-08-30 15:25:35 -04:00
1. Multi-recipient payments.
2. Integration with other Lightning-related NIPs.
3. Extended metadata for more complex payment scenarios.
2024-08-30 16:02:49 -04:00
4. **Addressable Events** : In Nostr apps where the offer is used, consider using addressable events to allow for updates to the recipient key or relay URL associated with the offer.
2024-08-26 22:54:45 -04:00
## Reference Implementation
Lightning.Pub / ShockWallet offers branch