diff options
| author | Arc <33088785+arcbtc@users.noreply.github.com> | 2023-04-13 11:13:04 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-13 07:13:04 -0300 |
| commit | bf0a0da6a48b96467172414d8e41dc72b0ca379c (patch) | |
| tree | 6c09aac40cf91ee1032aabb153064b74b51add19 /15.md | |
| parent | fb39455804cf772337616072a24dc5598ebefcbe (diff) | |
NIP-15 Nostr marketplace (#330)
Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
Diffstat (limited to '15.md')
| -rw-r--r-- | 15.md | 214 |
1 files changed, 214 insertions, 0 deletions
| @@ -0,0 +1,214 @@ | |||
| 1 | NIP-15 | ||
| 2 | ====== | ||
| 3 | |||
| 4 | Nostr Marketplace (for resilient marketplaces) | ||
| 5 | ----------------------------------- | ||
| 6 | |||
| 7 | `draft` `optional` `author:fiatjaf` `author:benarc` `author:motorina0` `author:talvasconcelos` | ||
| 8 | |||
| 9 | > Based on https://github.com/lnbits/Diagon-Alley | ||
| 10 | |||
| 11 | > Implemented here https://github.com/lnbits/nostrmarket | ||
| 12 | |||
| 13 | ## Terms | ||
| 14 | |||
| 15 | - `merchant` - seller of products with NOSTR key-pair | ||
| 16 | - `customer` - buyer of products with NOSTR key-pair | ||
| 17 | - `product` - item for sale by the `merchant` | ||
| 18 | - `stall` - list of products controlled by `merchant` (a `merchant` can have multiple stalls) | ||
| 19 | - `marketplace` - clientside software for searching `stalls` and purchasing `products` | ||
| 20 | |||
| 21 | ## Nostr Marketplace Clients | ||
| 22 | |||
| 23 | ### Merchant admin | ||
| 24 | |||
| 25 | Where the `merchant` creates, updates and deletes `stalls` and `products`, as well as where they manage sales, payments and communication with `customers`. | ||
| 26 | |||
| 27 | The `merchant` admin software can be purely clientside, but for `convenience` and uptime, implementations will likely have a server client listening for NOSTR events. | ||
| 28 | |||
| 29 | ### Marketplace | ||
| 30 | |||
| 31 | `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`. | ||
| 32 | |||
| 33 | ## `Merchant` publishing/updating products (event) | ||
| 34 | |||
| 35 | A merchant can publish these events: | ||
| 36 | | Kind | | Description | NIP | | ||
| 37 | |---------|------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------| | ||
| 38 | | `0 ` | `set_meta` | The merchant description (similar with any `nostr` public key). | [NIP01 ](https://github.com/nostr-protocol/nips/blob/master/01.md) | | ||
| 39 | | `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | | ||
| 40 | | `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | | ||
| 41 | | `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) | | ||
| 42 | | `5 ` | `delete` | Delete a product or a stall. | [NIP05](https://github.com/nostr-protocol/nips/blob/master/05.md) | | ||
| 43 | |||
| 44 | ### Event `30017`: Create or update a stall. | ||
| 45 | |||
| 46 | **Event Content**: | ||
| 47 | ```json | ||
| 48 | { | ||
| 49 | "id": <String, UUID generated by the merchant. Sequential IDs (`0`, `1`, `2`...) are discouraged>, | ||
| 50 | "name": <String, stall name>, | ||
| 51 | "description": <String (optional), stall description>, | ||
| 52 | "currency": <String, currency used>, | ||
| 53 | "shipping": [ | ||
| 54 | { | ||
| 55 | "id": <String, UUID of the shipping zone, generated by the merchant>, | ||
| 56 | "name": <String (optional), zone name>, | ||
| 57 | "cost": <float, cost for shipping. The currency is defined at the stall level>, | ||
| 58 | "countries": [<String, countries included in this zone>], | ||
| 59 | } | ||
| 60 | ] | ||
| 61 | } | ||
| 62 | ``` | ||
| 63 | |||
| 64 | Fields that are not self-explanatory: | ||
| 65 | - `shipping`: | ||
| 66 | - an array with possible shipping zones for this stall. The customer MUST choose exactly one shipping zone. | ||
| 67 | - shipping to different zones can have different costs. For some goods (digital for examle) the cost can be zero. | ||
| 68 | - the `id` is an internal value used by the merchant. This value must be sent back as the customer selection. | ||
| 69 | |||
| 70 | **Event Tags**: | ||
| 71 | ```json | ||
| 72 | "tags": [["d", <String, id of stall]] | ||
| 73 | ``` | ||
| 74 | - 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`. | ||
| 75 | |||
| 76 | ### Event `30018`: Create or update a product | ||
| 77 | |||
| 78 | **Event Content**: | ||
| 79 | ```json | ||
| 80 | { | ||
| 81 | "id": <String, UUID generated by the merchant.Sequential IDs (`0`, `1`, `2`...) are discouraged>, | ||
| 82 | "stall_id": <String, UUID of the stall to which this product belong to>, | ||
| 83 | "name": <String, product name>, | ||
| 84 | "description": <String (optional), product description>, | ||
| 85 | "images": <[String], array of image URLs, optional>, | ||
| 86 | "currency": <String, currency used>, | ||
| 87 | "price": <float, cost of product>, | ||
| 88 | "quantity": <int, available items>, | ||
| 89 | "specs": [ | ||
| 90 | [ <String, spec key>, <String, spec value>] | ||
| 91 | ] | ||
| 92 | } | ||
| 93 | ``` | ||
| 94 | |||
| 95 | Fields that are not self-explanatory: | ||
| 96 | - `specs`: | ||
| 97 | - 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 | ||
| 98 | - eg: `[["operating_system", "Android 12.0"], ["screen_size", "6.4 inches"], ["connector_type", "USB Type C"]]` | ||
| 99 | |||
| 100 | _Open_: better to move `spec` in the `tags` section of the event? | ||
| 101 | |||
| 102 | **Event Tags**: | ||
| 103 | ```json | ||
| 104 | "tags": [ | ||
| 105 | ["d", <String, id of product], | ||
| 106 | ["t", <String (optional), product category], | ||
| 107 | ["t", <String (optional), product category], | ||
| 108 | ... | ||
| 109 | ] | ||
| 110 | ``` | ||
| 111 | |||
| 112 | - 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`. | ||
| 113 | - 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. | ||
| 114 | |||
| 115 | ## Checkout events | ||
| 116 | |||
| 117 | All checkout events are sent as JSON strings using ([NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md)). | ||
| 118 | |||
| 119 | 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: | ||
| 120 | |||
| 121 | | Message Type | Sent By | Description | | ||
| 122 | |--------------|----------|---------------------| | ||
| 123 | | 0 | Customer | New Order | | ||
| 124 | | 1 | Merchant | Payment Request | | ||
| 125 | | 2 | Merchant | Order Status Update | | ||
| 126 | |||
| 127 | |||
| 128 | ### Step 1: `customer` order (event) | ||
| 129 | The below json goes in content of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). | ||
| 130 | |||
| 131 | ```json | ||
| 132 | { | ||
| 133 | "id": <String, UUID generated by the customer>, | ||
| 134 | "type": 0, | ||
| 135 | "name": <String (optional), ???>, | ||
| 136 | "address": <String (optional), for physical goods an address should be provided> | ||
| 137 | "message": "<String (optional), message for merchant>, | ||
| 138 | "contact": { | ||
| 139 | "nostr": <32-bytes hex of a pubkey>, | ||
| 140 | "phone": <String (optional), if the customer whats to be contacted by phone>, | ||
| 141 | "email": <String (optional), if the customer whats to be contacted by email>, | ||
| 142 | }, | ||
| 143 | "items": [ | ||
| 144 | { | ||
| 145 | "product_id": <String, UUID of the product>, | ||
| 146 | "quantity": <int, how many products have been ordered> | ||
| 147 | } | ||
| 148 | ], | ||
| 149 | "shipping_id": <String, UUID of the shipping zone> | ||
| 150 | } | ||
| 151 | |||
| 152 | ``` | ||
| 153 | |||
| 154 | _Open_: is `contact.nostr` required? | ||
| 155 | |||
| 156 | |||
| 157 | ### Step 2: `merchant` request payment (event) | ||
| 158 | |||
| 159 | Sent back from the merchant for payment. Any payment option is valid that the merchant can check. | ||
| 160 | |||
| 161 | The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). | ||
| 162 | |||
| 163 | `payment_options`/`type` include: | ||
| 164 | |||
| 165 | - `url` URL to a payment page, stripe, paypal, btcpayserver, etc | ||
| 166 | - `btc` onchain bitcoin address | ||
| 167 | - `ln` bitcoin lightning invoice | ||
| 168 | - `lnurl` bitcoin lnurl-pay | ||
| 169 | |||
| 170 | ```json | ||
| 171 | { | ||
| 172 | "id": <String, UUID of the order>, | ||
| 173 | "type": 1, | ||
| 174 | "message": <String, message to customer, optional>, | ||
| 175 | "payment_options": [ | ||
| 176 | { | ||
| 177 | "type": <String, option type>, | ||
| 178 | "link": <String, url, btc address, ln invoice, etc> | ||
| 179 | }, | ||
| 180 | { | ||
| 181 | "type": <String, option type>, | ||
| 182 | "link": <String, url, btc address, ln invoice, etc> | ||
| 183 | }, | ||
| 184 | { | ||
| 185 | "type": <String, option type>, | ||
| 186 | "link": <String, url, btc address, ln invoice, etc> | ||
| 187 | } | ||
| 188 | ] | ||
| 189 | } | ||
| 190 | ``` | ||
| 191 | |||
| 192 | ### Step 3: `merchant` verify payment/shipped (event) | ||
| 193 | |||
| 194 | Once payment has been received and processed. | ||
| 195 | |||
| 196 | The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). | ||
| 197 | |||
| 198 | ```json | ||
| 199 | { | ||
| 200 | "id": <String, UUID of the order>, | ||
| 201 | "type": 2, | ||
| 202 | "message": <String, message to customer>, | ||
| 203 | "paid": <Bool, true/false has received payment>, | ||
| 204 | "shipped": <Bool, true/false has been shipped>, | ||
| 205 | } | ||
| 206 | ``` | ||
| 207 | |||
| 208 | ## Customer support events | ||
| 209 | |||
| 210 | 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. | ||
| 211 | |||
| 212 | ## Additional | ||
| 213 | |||
| 214 | Standard data models can be found here <a href="https://raw.githubusercontent.com/lnbits/nostrmarket/main/models.py">here</a> | ||