forked from nostr-protocol/nips
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NIP-15 Nostr marketplace (nostr-protocol#330)
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com> Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
- Loading branch information
1 parent
18a9ddd
commit b38bf17
Showing
1 changed file
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
NIP-15 | ||
====== | ||
|
||
Nostr Marketplace (for resilient marketplaces) | ||
----------------------------------- | ||
|
||
`draft` `optional` `author:fiatjaf` `author:benarc` `author:motorina0` `author:talvasconcelos` | ||
|
||
> Based on https://github.com/lnbits/Diagon-Alley | ||
> Implemented here https://github.com/lnbits/nostrmarket | ||
## Terms | ||
|
||
- `merchant` - seller of products with NOSTR key-pair | ||
- `customer` - buyer of products with NOSTR key-pair | ||
- `product` - item for sale by the `merchant` | ||
- `stall` - list of products controlled by `merchant` (a `merchant` can have multiple stalls) | ||
- `marketplace` - clientside software for searching `stalls` and purchasing `products` | ||
|
||
## Nostr Marketplace Clients | ||
|
||
### Merchant admin | ||
|
||
Where the `merchant` creates, updates and deletes `stalls` and `products`, as well as where they manage sales, payments and communication with `customers`. | ||
|
||
The `merchant` admin software can be purely clientside, but for `convenience` and uptime, implementations will likely have a server client listening for NOSTR events. | ||
|
||
### Marketplace | ||
|
||
`Marketplace` software should be entirely clientside, either as a stand-alone app, or as a purely frontend webpage. A `customer` subscribes to different merchant NOSTR public keys, and those `merchants` `stalls` and `products` become listed and searchable. The marketplace client is like any other ecommerce site, with basket and checkout. `Marketplaces` may also wish to include a `customer` support area for direct message communication with `merchants`. | ||
|
||
## `Merchant` publishing/updating products (event) | ||
|
||
A merchant can publish these events: | ||
| Kind | | Description | NIP | | ||
|---------|------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------| | ||
| `0 ` | `set_meta` | The merchant description (similar with any `nostr` public key). | [NIP01 ](https://github.com/nostr-protocol/nips/blob/master/01.md) | | ||
| `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | | ||
| `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | | ||
| `4 ` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | [NIP09](https://github.com/nostr-protocol/nips/blob/master/09.md) | | ||
| `5 ` | `delete` | Delete a product or a stall. | [NIP05](https://github.com/nostr-protocol/nips/blob/master/05.md) | | ||
|
||
### Event `30017`: Create or update a stall. | ||
|
||
**Event Content**: | ||
```json | ||
{ | ||
"id": <String, UUID generated by the merchant. Sequential IDs (`0`, `1`, `2`...) are discouraged>, | ||
"name": <String, stall name>, | ||
"description": <String (optional), stall description>, | ||
"currency": <String, currency used>, | ||
"shipping": [ | ||
{ | ||
"id": <String, UUID of the shipping zone, generated by the merchant>, | ||
"name": <String (optional), zone name>, | ||
"cost": <float, cost for shipping. The currency is defined at the stall level>, | ||
"countries": [<String, countries included in this zone>], | ||
} | ||
] | ||
} | ||
``` | ||
|
||
Fields that are not self-explanatory: | ||
- `shipping`: | ||
- an array with possible shipping zones for this stall. The customer MUST choose exactly one shipping zone. | ||
- shipping to different zones can have different costs. For some goods (digital for examle) the cost can be zero. | ||
- the `id` is an internal value used by the merchant. This value must be sent back as the customer selection. | ||
|
||
**Event Tags**: | ||
```json | ||
"tags": [["d", <String, id of stall]] | ||
``` | ||
- the `d` tag is required by [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md). Its value MUST be the same as the stall `id`. | ||
|
||
### Event `30018`: Create or update a product | ||
|
||
**Event Content**: | ||
```json | ||
{ | ||
"id": <String, UUID generated by the merchant.Sequential IDs (`0`, `1`, `2`...) are discouraged>, | ||
"stall_id": <String, UUID of the stall to which this product belong to>, | ||
"name": <String, product name>, | ||
"description": <String (optional), product description>, | ||
"images": <[String], array of image URLs, optional>, | ||
"currency": <String, currency used>, | ||
"price": <float, cost of product>, | ||
"quantity": <int, available items>, | ||
"specs": [ | ||
[ <String, spec key>, <String, spec value>] | ||
] | ||
} | ||
``` | ||
|
||
Fields that are not self-explanatory: | ||
- `specs`: | ||
- an array of key pair values. It allows for the Customer UI to present present product specifications in a structure mode. It also allows comparison between products | ||
- eg: `[["operating_system", "Android 12.0"], ["screen_size", "6.4 inches"], ["connector_type", "USB Type C"]]` | ||
|
||
_Open_: better to move `spec` in the `tags` section of the event? | ||
|
||
**Event Tags**: | ||
```json | ||
"tags": [ | ||
["d", <String, id of product], | ||
["t", <String (optional), product category], | ||
["t", <String (optional), product category], | ||
... | ||
] | ||
``` | ||
|
||
- the `d` tag is required by [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md). Its value MUST be the same as the product `id`. | ||
- the `t` tag is as searchable tag ([NIP12](https://github.com/nostr-protocol/nips/blob/master/12.md)). It represents different categories that the product can be part of (`food`, `fruits`). Multiple `t` tags can be present. | ||
|
||
## Checkout events | ||
|
||
All checkout events are sent as JSON strings using ([NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md)). | ||
|
||
The `merchant` and the `customer` can exchange JSON messages that represent different actions. Each `JSON` message `MUST` have a `type` field indicating the what the JSON represents. Possible types: | ||
|
||
| Message Type | Sent By | Description | | ||
|--------------|----------|---------------------| | ||
| 0 | Customer | New Order | | ||
| 1 | Merchant | Payment Request | | ||
| 2 | Merchant | Order Status Update | | ||
|
||
|
||
### Step 1: `customer` order (event) | ||
The below json goes in content of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). | ||
|
||
```json | ||
{ | ||
"id": <String, UUID generated by the customer>, | ||
"type": 0, | ||
"name": <String (optional), ???>, | ||
"address": <String (optional), for physical goods an address should be provided> | ||
"message": "<String (optional), message for merchant>, | ||
"contact": { | ||
"nostr": <32-bytes hex of a pubkey>, | ||
"phone": <String (optional), if the customer whats to be contacted by phone>, | ||
"email": <String (optional), if the customer whats to be contacted by email>, | ||
}, | ||
"items": [ | ||
{ | ||
"product_id": <String, UUID of the product>, | ||
"quantity": <int, how many products have been ordered> | ||
} | ||
], | ||
"shipping_id": <String, UUID of the shipping zone> | ||
} | ||
|
||
``` | ||
|
||
_Open_: is `contact.nostr` required? | ||
|
||
|
||
### Step 2: `merchant` request payment (event) | ||
|
||
Sent back from the merchant for payment. Any payment option is valid that the merchant can check. | ||
|
||
The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). | ||
|
||
`payment_options`/`type` include: | ||
|
||
- `url` URL to a payment page, stripe, paypal, btcpayserver, etc | ||
- `btc` onchain bitcoin address | ||
- `ln` bitcoin lightning invoice | ||
- `lnurl` bitcoin lnurl-pay | ||
|
||
```json | ||
{ | ||
"id": <String, UUID of the order>, | ||
"type": 1, | ||
"message": <String, message to customer, optional>, | ||
"payment_options": [ | ||
{ | ||
"type": <String, option type>, | ||
"link": <String, url, btc address, ln invoice, etc> | ||
}, | ||
{ | ||
"type": <String, option type>, | ||
"link": <String, url, btc address, ln invoice, etc> | ||
}, | ||
{ | ||
"type": <String, option type>, | ||
"link": <String, url, btc address, ln invoice, etc> | ||
} | ||
] | ||
} | ||
``` | ||
|
||
### Step 3: `merchant` verify payment/shipped (event) | ||
|
||
Once payment has been received and processed. | ||
|
||
The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). | ||
|
||
```json | ||
{ | ||
"id": <String, UUID of the order>, | ||
"type": 2, | ||
"message": <String, message to customer>, | ||
"paid": <Bool, true/false has received payment>, | ||
"shipped": <Bool, true/false has been shipped>, | ||
} | ||
``` | ||
|
||
## Customer support events | ||
|
||
Customer support is handled over whatever communication method was specified. If communicating via nostr, NIP-04 is used https://github.com/nostr-protocol/nips/blob/master/04.md. | ||
|
||
## Additional | ||
|
||
Standard data models can be found here <a href="https://raw.githubusercontent.com/lnbits/nostrmarket/main/models.py">here</a> |