.. | ||
static/images | ||
templates/market | ||
__init__.py | ||
config.json | ||
crud.py | ||
migrations.py | ||
models.py | ||
notifier.py | ||
README.md | ||
tasks.py | ||
views.py | ||
views_api.py |
Nostr Diagon Alley protocol (for resilient marketplaces)
Original protocol https://github.com/lnbits/Diagon-Alley
The concepts around resilience in Diagon Alley helped influence the creation of the NOSTR protocol, now we get to build Diagon Alley on NOSTR!
In Diagon Alley, merchant
and customer
communicate via NOSTR relays, so loss of money, product information, and reputation become far less likely if attacked.
A merchant
and customer
both have a NOSTR key-pair that are used to sign notes and subscribe to events.
For further information about NOSTR, see https://github.com/nostr-protocol/nostr
Terms
merchant
- seller of products with NOSTR key-paircustomer
- buyer of products with NOSTR key-pairproduct
- item for sale by themerchhant
stall
- list of products controlled bymerchant
marketplace
- clientside software for searchingstalls
an buyingproducts
Diagon Alley 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 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)
NIP-01 https://github.com/nostr-protocol/nips/blob/master/01.md uses the basic NOSTR event type.
The merchant
event that publishes and updates product lists
ALL fields are optional apart from the timestamp
. Data from newer events should replace data from older events.
action
types (used to indicate changes):
update
element has chnageddelete
element should be deletedsuspend
element is suspendedunsuspend
element is unsuspended
{
"name": <String, name of merchant>,
"description": <String, description of merchant>,
"timestamp": <String, unix timestamp>,
"action": <String, optional action>,
"stalls": [
{
"id": <UUID dervied from merchant public-key>,
"name": <String, stall name>,
"description": <String, stall description>,
"categories": <String, CSV of voluntary categories>,
"currency": <Str, currency used>,
"action": <String, optional action>,
"products": [
{
"id": <String, UUID derived from stall ID>,
"name": <String, name of product>,
"description": <String, product description>,
"categories": <String, CSV of voluntary categories>,
"amount": <Int, number of units>,
"price": <Int, cost per unit>,
"action": <String, optional action>,
},
{
"id": <String, UUID derived from stall ID>,
"name": <String, name of product>,
"description": <String, product description>,
"categories": <String, CSV of voluntary categories>,
"amount": <Int, number of units>,
"price": <Int, cost per unit>,
"action": <String, optional action>,
},
]
},
{
"id": <UUID dervied from merchant public_key>,
"name": <String, stall name>,
"description": <String, stall description>,
"categories": <String, CSV of voluntary categories>,
"currency": <Str, currency used>,
"action": <String, optional action>,
"products": [
{
"id": <String, UUID derived from stall ID>,
"name": <String, name of product>,
"categories": <String, CSV of voluntary categories>,
"amount": <Int, number of units>,
"price": <Int, cost per unit>,
"action": <String, optional action>,
}
]
}
]
}
Checkout events
NIP-04 https://github.com/nostr-protocol/nips/blob/master/04.md, all checkout events are encrypted
Step 1: customer
order (event)
ALL fields are optional apart from timestamp
.
{
"id": <String, UUID derived from sum of product ids + timestamp>,
"name": <String, name of customer>,
"description": <String, description of customer>,
"address": <String, postal address>,
"message": <String, special request>,
"timestamp": <String, unix timestamp>,
"contact": [
"nostr": <String, NOSTR pubkey>,
"phone": <String, phone number>,
"email": <String, email address>
],
"items": [
{
"id": <String, product ID>,
"quantity": <String, stall name>,
"message": <String, special request>
},
{
"id": <String, product ID>,
"quantity": <String, stall name>,
"message": <String, special request>
},
{
"id": <String, product ID>,
"quantity": <String, stall name>,
"message": <String, special request>
}
}
Merchant should verify the sum of product ids + timestamp.
Step 2: merchant
request payment (event)
Sent back from the merchant for payment. Any payment option is valid that the merchant can check.
payment_options
/type
include:
url
URL to a payment page, stripe, paypal, btcpayserver, etcbtc
onchain bitcoin addressln
bitcoin lightning invoicelnurl
bitcoin lnurl-pay
{
"id": <String, UUID derived from sum of product ids + timestamp>,
"message": <String, message to customer>,
"timestamp": <String, unix timestamp>,
"payment_options": [
{
"type": <String, option type>,
"link": <String, url, btc adress, ln invoice, etc>
},
{
"type": <String, option type>,
"link": <String, url, btc adress, ln invoice, etc>
},
{
"type": <String, option type>,
"link": <String, url, btc adress, ln invoice, etc>
}
}
Step 3: merchant
verify payment/shipped (event)
Once payment has been received and processed.
{
"id": <String, UUID derived from sum of product ids + timestamp>,
"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 handle over whatever communication method was specified. If communicationg via nostr, NIP-04 is used https://github.com/nostr-protocol/nips/blob/master/04.md.