diff options
| author | Semisol <45574030+Semisol@users.noreply.github.com> | 2023-11-19 01:45:41 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-19 01:45:41 +0100 |
| commit | da19c078ab892b578a5c35968443205c9e8ac27f (patch) | |
| tree | 17a9f4f3105acdae234d3bc67e42571aed261fa2 /15.md | |
| parent | 4d709d1804de45bab3739ce814d4b0c0b211c273 (diff) | |
| parent | 5dcfe85306434f21ecb1e7a47edd92b2e3e64f9a (diff) | |
Merge branch 'master' into clarify-json-serialization
Diffstat (limited to '15.md')
| -rw-r--r-- | 15.md | 232 |
1 files changed, 140 insertions, 92 deletions
| @@ -1,14 +1,14 @@ | |||
| 1 | NIP-15 | 1 | NIP-15 |
| 2 | ====== | 2 | ====== |
| 3 | 3 | ||
| 4 | Nostr Marketplace (for resilient marketplaces) | 4 | Nostr Marketplace |
| 5 | ----------------------------------- | 5 | ----------------- |
| 6 | 6 | ||
| 7 | `draft` `optional` `author:fiatjaf` `author:benarc` `author:motorina0` `author:talvasconcelos` | 7 | `draft` `optional` |
| 8 | 8 | ||
| 9 | > Based on https://github.com/lnbits/Diagon-Alley | 9 | Based on https://github.com/lnbits/Diagon-Alley. |
| 10 | 10 | ||
| 11 | > Implemented here https://github.com/lnbits/nostrmarket | 11 | Implemented in [NostrMarket](https://github.com/lnbits/nostrmarket) and [Plebeian Market](https://github.com/PlebeianTech/plebeian-market). |
| 12 | 12 | ||
| 13 | ## Terms | 13 | ## Terms |
| 14 | 14 | ||
| @@ -33,84 +33,109 @@ The `merchant` admin software can be purely clientside, but for `convenience` an | |||
| 33 | ## `Merchant` publishing/updating products (event) | 33 | ## `Merchant` publishing/updating products (event) |
| 34 | 34 | ||
| 35 | A merchant can publish these events: | 35 | A merchant can publish these events: |
| 36 | | Kind | | Description | NIP | | 36 | | Kind | | Description | |
| 37 | |---------|------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------| | 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) | | 38 | | `0` | `set_meta` | The merchant description (similar with any `nostr` public key). | |
| 39 | | `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | | 39 | | `30017` | `set_stall` | Create or update a stall. | |
| 40 | | `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | | 40 | | `30018` | `set_product` | Create or update a product. | |
| 41 | | `4 ` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md) | | 41 | | `4` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | |
| 42 | | `5 ` | `delete` | Delete a product or a stall. | [NIP09](https://github.com/nostr-protocol/nips/blob/master/09.md) | | 42 | | `5` | `delete` | Delete a product or a stall. | |
| 43 | 43 | ||
| 44 | ### Event `30017`: Create or update a stall. | 44 | ### Event `30017`: Create or update a stall. |
| 45 | 45 | ||
| 46 | **Event Content**: | 46 | **Event Content** |
| 47 | |||
| 47 | ```json | 48 | ```json |
| 48 | { | 49 | { |
| 49 | "id": <String, UUID generated by the merchant. Sequential IDs (`0`, `1`, `2`...) are discouraged>, | 50 | "id": <string, id generated by the merchant. Sequential IDs (`0`, `1`, `2`...) are discouraged>, |
| 50 | "name": <String, stall name>, | 51 | "name": <string, stall name>, |
| 51 | "description": <String (optional), stall description>, | 52 | "description": <string (optional), stall description>, |
| 52 | "currency": <String, currency used>, | 53 | "currency": <string, currency used>, |
| 53 | "shipping": [ | 54 | "shipping": [ |
| 54 | { | 55 | { |
| 55 | "id": <String, UUID of the shipping zone, generated by the merchant>, | 56 | "id": <string, id of the shipping zone, generated by the merchant>, |
| 56 | "name": <String (optional), zone name>, | 57 | "name": <string (optional), zone name>, |
| 57 | "cost": <float, cost for shipping. The currency is defined at the stall level>, | 58 | "cost": <float, base cost for shipping. The currency is defined at the stall level>, |
| 58 | "countries": [<String, countries included in this zone>], | 59 | "regions": [<string, regions included in this zone>], |
| 59 | } | 60 | } |
| 60 | ] | 61 | ] |
| 61 | } | 62 | } |
| 62 | ``` | 63 | ``` |
| 63 | 64 | ||
| 64 | Fields that are not self-explanatory: | 65 | Fields that are not self-explanatory: |
| 65 | - `shipping`: | 66 | - `shipping`: |
| 66 | - an array with possible shipping zones for this stall. The customer MUST choose exactly one shipping zone. | 67 | - an array with possible shipping zones for this stall. |
| 68 | - the customer MUST choose exactly one of those shipping zones. | ||
| 67 | - shipping to different zones can have different costs. For some goods (digital for example) the cost can be zero. | 69 | - shipping to different zones can have different costs. For some goods (digital for example) 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. | 70 | - the `id` is an internal value used by the merchant. This value must be sent back as the customer selection. |
| 71 | - each shipping zone contains the base cost for orders made to that shipping zone, but a specific shipping cost per | ||
| 72 | product can also be specified if the shipping cost for that product is higher than what's specified by the base cost. | ||
| 73 | |||
| 74 | **Event Tags** | ||
| 69 | 75 | ||
| 70 | **Event Tags**: | ||
| 71 | ```json | 76 | ```json |
| 72 | "tags": [["d", <String, id of stall]] | 77 | { |
| 78 | "tags": [["d", <string, id of stall]], | ||
| 79 | ... | ||
| 80 | } | ||
| 73 | ``` | 81 | ``` |
| 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`. | 82 | - the `d` tag is required, its value MUST be the same as the stall `id`. |
| 75 | 83 | ||
| 76 | ### Event `30018`: Create or update a product | 84 | ### Event `30018`: Create or update a product |
| 77 | 85 | ||
| 78 | **Event Content**: | 86 | **Event Content** |
| 87 | |||
| 79 | ```json | 88 | ```json |
| 80 | { | 89 | { |
| 81 | "id": <String, UUID generated by the merchant.Sequential IDs (`0`, `1`, `2`...) are discouraged>, | 90 | "id": <string, id generated by the merchant (sequential ids are discouraged)>, |
| 82 | "stall_id": <String, UUID of the stall to which this product belong to>, | 91 | "stall_id": <string, id of the stall to which this product belong to>, |
| 83 | "name": <String, product name>, | 92 | "name": <string, product name>, |
| 84 | "description": <String (optional), product description>, | 93 | "description": <string (optional), product description>, |
| 85 | "images": <[String], array of image URLs, optional>, | 94 | "images": <[string], array of image URLs, optional>, |
| 86 | "currency": <String, currency used>, | 95 | "currency": <string, currency used>, |
| 87 | "price": <float, cost of product>, | 96 | "price": <float, cost of product>, |
| 88 | "quantity": <int, available items>, | 97 | "quantity": <int or null, available items>, |
| 89 | "specs": [ | 98 | "specs": [ |
| 90 | [ <String, spec key>, <String, spec value>] | 99 | [<string, spec key>, <string, spec value>] |
| 91 | ] | 100 | ], |
| 101 | "shipping": [ | ||
| 102 | { | ||
| 103 | "id": <string, id of the shipping zone (must match one of the zones defined for the stall)>, | ||
| 104 | "cost": <float, extra cost for shipping. The currency is defined at the stall level>, | ||
| 105 | } | ||
| 106 | ] | ||
| 92 | } | 107 | } |
| 93 | ``` | 108 | ``` |
| 94 | 109 | ||
| 95 | Fields that are not self-explanatory: | 110 | Fields that are not self-explanatory: |
| 111 | - `quantity` can be null in the case of items with unlimited abailability, like digital items, or services | ||
| 96 | - `specs`: | 112 | - `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 | 113 | - an optional array of key pair values. It allows for the Customer UI to 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"]]` | 114 | - eg: `[["operating_system", "Android 12.0"], ["screen_size", "6.4 inches"], ["connector_type", "USB Type C"]]` |
| 99 | 115 | ||
| 100 | _Open_: better to move `spec` in the `tags` section of the event? | 116 | _Open_: better to move `spec` in the `tags` section of the event? |
| 117 | |||
| 118 | - `shipping`: | ||
| 119 | - an _optional_ array of extra costs to be used per shipping zone, only for products that require special shipping costs to be added to the base shipping cost defined in the stall | ||
| 120 | - the `id` should match the id of the shipping zone, as defined in the `shipping` field of the stall | ||
| 121 | - to calculate the total cost of shipping for an order, the user will choose a shipping option during checkout, and then the client must consider this costs: | ||
| 122 | - the `base cost from the stall` for the chosen shipping option | ||
| 123 | - the result of multiplying the product units by the `shipping costs specified in the product`, if any. | ||
| 124 | |||
| 125 | **Event Tags** | ||
| 101 | 126 | ||
| 102 | **Event Tags**: | ||
| 103 | ```json | 127 | ```json |
| 104 | "tags": [ | 128 | "tags": [ |
| 105 | ["d", <String, id of product], | 129 | ["d", <string, id of product], |
| 106 | ["t", <String (optional), product category], | 130 | ["t", <string (optional), product category], |
| 107 | ["t", <String (optional), product category], | 131 | ["t", <string (optional), product category], |
| 108 | ... | 132 | ... |
| 109 | ] | 133 | ], |
| 134 | ... | ||
| 110 | ``` | 135 | ``` |
| 111 | 136 | ||
| 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`. | 137 | - the `d` tag is required, 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. | 138 | - the `t` tag is as searchable tag, it represents different categories that the product can be part of (`food`, `fruits`). Multiple `t` tags can be present. |
| 114 | 139 | ||
| 115 | ## Checkout events | 140 | ## Checkout events |
| 116 | 141 | ||
| @@ -130,29 +155,29 @@ The below json goes in content of [NIP04](https://github.com/nostr-protocol/nips | |||
| 130 | 155 | ||
| 131 | ```json | 156 | ```json |
| 132 | { | 157 | { |
| 133 | "id": <String, UUID generated by the customer>, | 158 | "id": <string, id generated by the customer>, |
| 134 | "type": 0, | 159 | "type": 0, |
| 135 | "name": <String (optional), ???>, | 160 | "name": <string (optional), ???>, |
| 136 | "address": <String (optional), for physical goods an address should be provided> | 161 | "address": <string (optional), for physical goods an address should be provided> |
| 137 | "message": "<String (optional), message for merchant>, | 162 | "message": "<string (optional), message for merchant>, |
| 138 | "contact": { | 163 | "contact": { |
| 139 | "nostr": <32-bytes hex of a pubkey>, | 164 | "nostr": <32-bytes hex of a pubkey>, |
| 140 | "phone": <String (optional), if the customer wants to be contacted by phone>, | 165 | "phone": <string (optional), if the customer wants to be contacted by phone>, |
| 141 | "email": <String (optional), if the customer wants to be contacted by email>, | 166 | "email": <string (optional), if the customer wants to be contacted by email>, |
| 142 | }, | 167 | }, |
| 143 | "items": [ | 168 | "items": [ |
| 144 | { | 169 | { |
| 145 | "product_id": <String, UUID of the product>, | 170 | "product_id": <string, id of the product>, |
| 146 | "quantity": <int, how many products the customer is ordering> | 171 | "quantity": <int, how many products the customer is ordering> |
| 147 | } | 172 | } |
| 148 | ], | 173 | ], |
| 149 | "shipping_id": <String, UUID of the shipping zone> | 174 | "shipping_id": <string, id of the shipping zone> |
| 150 | } | 175 | } |
| 151 | 176 | ||
| 152 | ``` | 177 | ``` |
| 153 | 178 | ||
| 154 | _Open_: is `contact.nostr` required? | 179 | _Open_: is `contact.nostr` required? |
| 155 | 180 | ||
| 156 | 181 | ||
| 157 | ### Step 2: `merchant` request payment (event) | 182 | ### Step 2: `merchant` request payment (event) |
| 158 | 183 | ||
| @@ -169,23 +194,23 @@ The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/ni | |||
| 169 | 194 | ||
| 170 | ```json | 195 | ```json |
| 171 | { | 196 | { |
| 172 | "id": <String, UUID of the order>, | 197 | "id": <string, id of the order>, |
| 173 | "type": 1, | 198 | "type": 1, |
| 174 | "message": <String, message to customer, optional>, | 199 | "message": <string, message to customer, optional>, |
| 175 | "payment_options": [ | 200 | "payment_options": [ |
| 176 | { | 201 | { |
| 177 | "type": <String, option type>, | 202 | "type": <string, option type>, |
| 178 | "link": <String, url, btc address, ln invoice, etc> | 203 | "link": <string, url, btc address, ln invoice, etc> |
| 179 | }, | 204 | }, |
| 180 | { | 205 | { |
| 181 | "type": <String, option type>, | 206 | "type": <string, option type>, |
| 182 | "link": <String, url, btc address, ln invoice, etc> | 207 | "link": <string, url, btc address, ln invoice, etc> |
| 183 | }, | 208 | }, |
| 184 | { | 209 | { |
| 185 | "type": <String, option type>, | 210 | "type": <string, option type>, |
| 186 | "link": <String, url, btc address, ln invoice, etc> | 211 | "link": <string, url, btc address, ln invoice, etc> |
| 187 | } | 212 | } |
| 188 | ] | 213 | ] |
| 189 | } | 214 | } |
| 190 | ``` | 215 | ``` |
| 191 | 216 | ||
| @@ -197,13 +222,36 @@ The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/ni | |||
| 197 | 222 | ||
| 198 | ```json | 223 | ```json |
| 199 | { | 224 | { |
| 200 | "id": <String, UUID of the order>, | 225 | "id": <string, id of the order>, |
| 201 | "type": 2, | 226 | "type": 2, |
| 202 | "message": <String, message to customer>, | 227 | "message": <string, message to customer>, |
| 203 | "paid": <Bool, true/false has received payment>, | 228 | "paid": <bool: has received payment>, |
| 204 | "shipped": <Bool, true/false has been shipped>, | 229 | "shipped": <bool: has been shipped>, |
| 205 | } | 230 | } |
| 206 | ``` | 231 | ``` |
| 232 | ## Customize Marketplace | ||
| 233 | Create a customized user experience using the `naddr` from [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md#shareable-identifiers-with-extra-metadata). The use of `naddr` enables easy sharing of marketplace events while incorporating a rich set of metadata. This metadata can include relays, merchant profiles, and more. Subsequently, it allows merchants to be grouped into a market, empowering the market creator to configure the marketplace's user interface and user experience, and share that marketplace. This customization can encompass elements such as market name, description, logo, banner, themes, and even color schemes, offering a tailored and unique marketplace experience. | ||
| 234 | |||
| 235 | ### Event `30019`: Create or update marketplace UI/UX | ||
| 236 | |||
| 237 | **Event Content** | ||
| 238 | |||
| 239 | ```json | ||
| 240 | { | ||
| 241 | "name": <string (optional), market name>, | ||
| 242 | "about": <string (optional), market description>, | ||
| 243 | "ui": { | ||
| 244 | "picture": <string (optional), market logo image URL>, | ||
| 245 | "banner": <string (optional), market logo banner URL>, | ||
| 246 | "theme": <string (optional), market theme>, | ||
| 247 | "darkMode": <bool, true/false> | ||
| 248 | }, | ||
| 249 | "merchants": [array of pubkeys (optional)], | ||
| 250 | ... | ||
| 251 | } | ||
| 252 | ``` | ||
| 253 | |||
| 254 | This event leverages naddr to enable comprehensive customization and sharing of marketplace configurations, fostering a unique and engaging marketplace environment. | ||
| 207 | 255 | ||
| 208 | ## Customer support events | 256 | ## Customer support events |
| 209 | 257 | ||
| @@ -211,4 +259,4 @@ Customer support is handled over whatever communication method was specified. If | |||
| 211 | 259 | ||
| 212 | ## Additional | 260 | ## Additional |
| 213 | 261 | ||
| 214 | Standard data models can be found here <a href="https://raw.githubusercontent.com/lnbits/nostrmarket/main/models.py">here</a> | 262 | Standard data models can be found <a href="https://raw.githubusercontent.com/lnbits/nostrmarket/main/models.py">here</a> |