generated at
NIP-15
> Nostr Marketplace (for resilient marketplaces)
※2023/4/9まで同じ番号で存在していた別の仕様についてはこちら→NIP-15 End of Stored Events Notice

Nostrクライアント同士で商店を開けるようになる仕組み。
https://github.com/lnbits/Diagon-Alley のアイデアを実現するもの。
この仕様を用いた実装: NostrMarket, Plebian Market

用語
出店者(merchant) - Nostr鍵ペアを持つ、商品の売り手
顧客(customer) - Nostr鍵ペアを持つ、商品の買い手
商品(product) - 出店者が販売するもの
商店(stall) - 出店者の管理下にある商品のリスト(1人の出店者が複数の商店を出店できる)
マーケットプレイス(marketplace) - 商店を検索し、商品を購入するためのクライアントソフト

Nostrマーケットプレイス クライアント
出店者向け管理画面(Merchant admin)
出店者が商店や商品を作成・更新・削除したり、売上・支払・顧客とのやり取りを行うための場所。
出店者向け管理ソフトは純粋なクライアントサイド実装であってもよいが、利便性と動作可能時間(の向上)の目的で、Nostrイベントをlistenするサーバを持つことになるだろう。

マーケットプレイス
マーケットプレイスソフトは100%クライアントサイド実装(スタンドアロンアプリ、またはフロントエンドのみで成立するWebページ)となるだろう。顧客が出店者のNostr公開鍵を使って出店者・商店・商品をリストアップ・検索できるようにする。マーケットプレイスクライアントは、買い物かごや精算機能を持つ他のEコマースサイトに似たものとなる。マーケットプレイスは、出店者とのダイレクトメッセージを使ったやり取りを行う顧客サポート機能を持つことが望ましい。

出店者による商品の公開・更新
出店者は、以下のイベントを発行できる:
出店関連イベント
kind説明
0set_meta出店者の説明]
30017set_stall商店の作成・更新
30018set_product商品の作成・更新
4direct_message顧客とのやり取り メッセージはプレーンテキストまたはJSON
5delete商品や商店の削除

イベント 30017: 商店の作成・更新
Content:
30017_content.json
{ "id": <String, UUID(出店者が生成)。連続的なID(`0`, `1`, `2`...)は推奨されない>, "name": <String, 商店の名前>, "description": <String (任意), 商店の説明>, "currency": <String, 通用する通貨>, "shipping": [ { "id": <String, 配送区分のUUID。出店者が生成する>, "name": <String (任意), 配送区分の名前>, "cost": <float, 基本送料。 通貨は商店レベルで定義>, "regions": [<String, この配送区分に含まれる地域>], } ] }

要説明のフィールド:
shipping
商店が対応している配送区分の配列。
顧客はちょうど1つの配送区分を選択しなければならない(MUST)
区分ごとに送料は異なる。送料無料の商品もある(例: デジタルなもの)
id は出店者が使う内部的な値。この値を顧客の選択として(出店者に)送り返すこと。
各配送区分は、その区分への注文に対する基本送料を持つが、特定の商品の送料が基本送料よりも高額になる場合、商品ごとの送料を設定することもできる。

Tags:
30017_tags.json
"tags": [["d", <String, 商店のID>]]

d タグが必須。この値は( content 内の)商店の id と同じでなければならない(MUST)。


イベント 30018: 商品の作成・更新
Content:
30018_content.json
{ "id": <String, UUID(出店者が生成)。連続的なID(`0`, `1`, `2`...)は推奨されない>, "stall_id": <String, この商品が陳列される商店のUUID>, "name": <String, 商品名>, "description": <String (任意), 商品の説明>, "images": <[String], 画像URLの配列。任意>, "currency": <String, 通貨>, "price": <float, 商品の価格>, "quantity": <int, 商品の数量>, "specs": [ [ <String, 仕様キー>, <String, 仕様値>] ], "shipping": [ { "id": <String, 配送区分のUUID。商店(イベント)で定義された地域のどれか1つに一致しなければならない>, "cost": <float, 追加送料。通貨は商店(イベント)で定義される> } ] }

要説明のフィールド:
specs
key-valueペアの配列(任意)。製品仕様を構造化された形で表示できるようにする。商品間の比較も可能となる。
例: [["operating_system", "Android 12.0"], ["screen_size", "6.4 inches"], ["connector_type", "USB Type C"]]
shipping :
配送区分ごとの追加送料(任意)。商店(イベント)で決められた基本送料に追加で特別な配送コストが必要となる商品のみに対し使われる
id は商店の shipping フィールドで定義された配送区分のIDのどれかに一致しなければならない
ユーザは精算時に配送方法を選択する。その後、ある注文に対する送料の合計を計算するにあたり、クライアントは以下のコストを考慮しなければならない:
選択された配送方法における、商店の基本送料
商品に対し設定された(追加)送料に購入数を掛けた結果(あれば)

Tags:
30018_tags.json
"tags": [ ["d", <String, 商品ID>], ["t", <String (任意), 商品のカテゴリ>], ["t", <String (任意), 商品のカテゴリ>], ... ]

d タグが必須。この値は( content 内の)商品の id と同じでなければならない(MUST)。
t タグは検索用のタグ(NIP-12)。商品のカテゴリを表す( food , fruits など)。複数の t タグを含めることができる

精算イベント(Checkout events)
全ての精算イベントは、NIP-04を用いてJSON文字列として送信される。
出店者と顧客は、様々なアクションを表現するJSONメッセージを交換する。それぞれのJSONメッセージは、それが何を表現するかを表す type フィールドを持たなければならない(MUST)。
精算イベントのtype
type送信者説明
0顧客新しい注文
1出店者支払い請求
2出店者注文のステータス更新

ステップ1: 顧客による注文
以下のJSONをNIP-04メッセージの content に含める。
order.json
{ "id": <String, UUID(顧客が生成)>, "type": 0, "name": <String (任意), ???>, "address": <String (任意), 物理的な商品の場合は住所(配送先?)を含める> "message": <String (任意), 出店者へのメッセージ>, "contact": { "nostr": <(顧客の)公開鍵(32バイト・16進文字列)>, "phone": <String (任意), 顧客が電話での連絡を希望する場合>, "email": <String (任意), 顧客がEメールでの連絡を希望する場合>, }, "items": [ { "product_id": <String, 商品のUUID>, "quantity": <int, 注文数量> } ], "shipping_id": <String, 配送区分のUUID> }

ステップ2: 出店者による支払い請求
支払い請求のために出店者から折返し送信される。出店者が確認できる限り、任意の支払い手段が有効。
以下のJSONをNIP-04メッセージの content に含める。

payment_options の要素の type :
url : 支払いページ(stripe, paypal, btcpayserverなど)のURL
btc : オンチェーンビットコインアドレス
ln : ビットコインライトニングインボイス
lnurl : ビットコインLNURL-Pay

request_payment.json
{ "id": <String, 注文のUUID>, "type": 1, "message": <String, 顧客へのメッセージ, 任意>, "payment_options": [ { "type": <String, 支払い手段>, "link": <String, URL、BTCアドレス、LNインボイス ほか> }, { "type": <String, 支払い手段>, "link": <String, URL、BTCアドレス、LNインボイス ほか> } ] }

ステップ3: 出店者による支払い確認・出荷
出店者が支払いを領収・処理した時点で送信。
以下のJSONをNIP-04メッセージの content に含める。

verify_payment.json
{ "id": <String, 注文のUUID>, "type": 2, "message": <String, 顧客へのメッセージ>, "paid": <Bool, true/false 支払い領収済みか>, "shipped": <Bool, true/false 出荷済みか>, }


顧客サポートイベント(Customer support events)
顧客サポートに用いる連絡手段は何でもよい。Nostrを使ってやり取りする場合は、NIP-04を用いる。

追加情報
標準データモデルはこちら