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 example) 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 wants to be contacted by phone>, "email": <String (optional), if the customer wants to be contacted by email>, }, "items": [ { "product_id": <String, UUID of the product>, "quantity": <int, how many products the customer is ordering> } ], "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>