nostr-marketplace-builder
Nostrプロトコル上で、NIP-15の販売所、商品、オークションやNIP-69のP2P注文を活用し、eコマースや商品リスト作成、オークションシステム構築、P2P取引などを実現するアプリケーションを開発するSkill。
📜 元の英語説明(参考)
Build Nostr marketplace applications using NIP-15 stalls, products, auctions, and NIP-69 P2P orders. Use when creating e-commerce on Nostr, building product listings, setting up stalls with shipping zones, implementing auction bidding systems, constructing checkout flows with NIP-04 DMs, creating P2P trading orders, or generating marketplace UI configurations. Covers kind:30017 stalls, kind:30018 products, kind:30020 auctions, kind:1021 bids, kind:1022 bid confirmations, kind:30019 marketplace UI, and kind:38383 P2P orders.
🇯🇵 日本人クリエイター向け解説
Nostrプロトコル上で、NIP-15の販売所、商品、オークションやNIP-69のP2P注文を活用し、eコマースや商品リスト作成、オークションシステム構築、P2P取引などを実現するアプリケーションを開発するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o nostr-marketplace-builder.zip https://jpskill.com/download/9137.zip && unzip -o nostr-marketplace-builder.zip && rm nostr-marketplace-builder.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9137.zip -OutFile "$d\nostr-marketplace-builder.zip"; Expand-Archive "$d\nostr-marketplace-builder.zip" -DestinationPath $d -Force; ri "$d\nostr-marketplace-builder.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
nostr-marketplace-builder.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
nostr-marketplace-builderフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Nostr Marketplace Builder
概要
商用アプリケーション向けの正しい Nostr マーケットプレイスイベントを構築します。このスキルは、わかりにくい部分、つまり、ストール/製品の関係、配送料の計算、オークションのライフサイクルルール、NIP-04 を介したチェックアウトメッセージのシーケンス、および NIP-69 からの P2P 注文タグ構造を処理します。
どのような時に使用するか
- 配送ゾーンを持つマーチャントストールを作成する (kind:30017)
- カテゴリと仕様を持つ製品をリストする (kind:30018)
- 入札によるオークションを設定する (kind:30020, kind:1021, kind:1022)
- チェックアウトフローを構築する (NIP-04 order → payment → status)
- ビットコイントレード用の P2P 売買注文を作成する (NIP-69 kind:38383)
- カスタムマーケットプレイス UI を構成する (kind:30019)
- ゾーンと製品全体の配送料を計算する
以下の場合には使用しないでください:
- 一般的な Nostr イベントを構築する (nostr-event-builder を使用)
- リレー WebSocket ロジックを実装する
- NIP-19 のエンコード/デコードを扱う
ワークフロー
1. 構築するものを特定する
| インテント | Kind(s) | NIP |
|---|---|---|
| マーチャントストールを作成/更新する | 30017 | NIP-15 |
| 販売する製品をリストする | 30018 | NIP-15 |
| マーケットプレイス UI を構成する | 30019 | NIP-15 |
| オークション出品を作成する | 30020 | NIP-15 |
| オークションに入札する | 1021 | NIP-15 |
| 入札を承認/拒否する | 1022 | NIP-15 |
| チェックアウト (order/pay/status) | NIP-04 DMs | NIP-15 |
| P2P 売買注文 | 38383 | NIP-69 |
2. イベントを構築する
ストール (Kind:30017) — アドレス指定可能
ストールはマーチャントのストアです。製品はストールに属します。配送ゾーンは、基本コストとともにストールレベルで定義されます。
{
"kind": 30017,
"tags": [["d", "my-stall-001"]],
"content": "{\"id\":\"my-stall-001\",\"name\":\"Satoshi's Electronics\",\"description\":\"Quality electronics for sats\",\"currency\":\"USD\",\"shipping\":[{\"id\":\"us-domestic\",\"name\":\"US Domestic\",\"cost\":5.99,\"regions\":[\"US\"]},{\"id\":\"worldwide\",\"name\":\"Worldwide\",\"cost\":15.00,\"regions\":[\"EU\",\"AS\",\"SA\"]}]}"
}
ルール:
dタグの値は、コンテンツ JSON のidと一致する必要がありますidは UUID または記述的なスラッグである必要があります — 連番 ID (0,1,2) は推奨されませんcurrencyは文字列です (例:"USD","BTC","EUR")shipping配列はゾーンを定義します。顧客はチェックアウト時に正確に 1 つのゾーンを選択する必要があります- 各ゾーンには、ストールの通貨での基本
costがあります
製品 (Kind:30018) — アドレス指定可能
製品は stall_id を介してストールに属します。ストールの基本配送料に加えて、製品ごとの配送料を追加できます。
{
"kind": 30018,
"tags": [
["d", "product-xyz-789"],
["t", "electronics"],
["t", "gadgets"]
],
"content": "{\"id\":\"product-xyz-789\",\"stall_id\":\"my-stall-001\",\"name\":\"Lightning Node Kit\",\"description\":\"Pre-configured Bitcoin node\",\"images\":[\"https://example.com/node.jpg\"],\"currency\":\"USD\",\"price\":299.99,\"quantity\":25,\"specs\":[[\"processor\",\"ARM Cortex-A72\"],[\"storage\",\"1TB SSD\"],[\"connectivity\",\"Ethernet + WiFi\"]],\"shipping\":[{\"id\":\"us-domestic\",\"cost\":10.00},{\"id\":\"worldwide\",\"cost\":25.00}]}"
}
ルール:
dタグの値は、コンテンツ JSON のidと一致する必要がありますstall_idは、既存のストールのidを参照する必要がありますquantity: 在庫が限られている場合は整数、無制限の場合はnull(デジタル商品/サービス)tタグは検索可能なカテゴリです — 発見可能性を高めるために複数使用しますspecsは、構造化された表示のための[key, value]ペアの配列です- 製品の
shipping[].idは、親ストールのゾーンidと一致する必要があります - 合計配送料 = ストールの基本コスト + (製品配送料 × 数量)
マーケットプレイス UI (Kind:30019) — アドレス指定可能
マーチャントをグループ化するカスタムマーケットプレイス構成:
{
"kind": 30019,
"tags": [["d", "my-marketplace"]],
"content": "{\"name\":\"Bitcoin Bazaar\",\"about\":\"A curated marketplace for Bitcoin products\",\"ui\":{\"picture\":\"https://example.com/logo.png\",\"banner\":\"https://example.com/banner.jpg\",\"theme\":\"dark\",\"darkMode\":true},\"merchants\":[\"pubkey1hex...\",\"pubkey2hex...\"]}"
}
オークション (Kind:30020) — アドレス指定可能
オークションは製品に似ていますが、入札メカニズムがあります。
{
"kind": 30020,
"tags": [["d", "auction-rare-item-001"]],
"content": "{\"id\":\"auction-rare-item-001\",\"stall_id\":\"my-stall-001\",\"name\":\"Signed Hal Finney Print\",\"description\":\"Limited edition signed print\",\"images\":[\"https://example.com/print.jpg\"],\"starting_bid\":50000,\"start_date\":1719391096,\"duration\":86400,\"specs\":[[\"condition\",\"mint\"],[\"authentication\",\"verified\"]],\"shipping\":[{\"id\":\"worldwide\",\"cost\":20.00}]}"
}
ルール:
starting_bidはストールの通貨 (整数) ですstart_dateは Unix タイムスタンプです。開始日が不明な場合は省略しますdurationはオークションが実行される秒数です (延長を除く)- 実際の終了 =
start_date + duration + SUM(確認からのすべての duration_extended) - 最初の入札を受け取った後はオークションを編集できません — 入札は製品 UUID ではなくイベント ID を参照するため、編集すると新しいイベント ID が作成され、すべての入札が失われます
入札 (Kind:1021) — 通常
{
"kind": 1021,
"content": "75000",
"tags": [["e", "<auction-event-id>"]]
}
ルール:
contentは、オークションの通貨での入札額を文字列として表したものですeタグは、製品 UUID ではなく、オークションの イベント ID を参照します- これが入札後にオークションを編集すると、それらの入札が破棄される理由です
入札確認 (Kind:1022) — 通常
マーチャントによって送信され、入札を検証します。
{
"kind": 1022,
"content": "{\"status\":\"accepted\",\"message\":\"Bid received and validated\",\"duration_extended\":300}",
"tags": [
["e", "<bid-event-id>"],
["e", "<auction-event-id>"]
]
}
ステータス値: accepted, rejected, pending, winner
winnerは winnin に送信されます
(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Nostr Marketplace Builder
Overview
Construct correct Nostr marketplace events for commerce applications. This skill handles the non-obvious parts: stall/product relationships, shipping cost calculations, auction lifecycle rules, checkout message sequencing via NIP-04, and P2P order tag structures from NIP-69.
When to Use
- Creating a merchant stall with shipping zones (kind:30017)
- Listing products with categories and specs (kind:30018)
- Setting up auctions with bidding (kind:30020, kind:1021, kind:1022)
- Building checkout flows (NIP-04 order → payment → status)
- Creating P2P buy/sell orders for bitcoin trading (NIP-69 kind:38383)
- Configuring a custom marketplace UI (kind:30019)
- Calculating shipping costs across zones and products
Do NOT use when:
- Building generic Nostr events (use nostr-event-builder)
- Implementing relay WebSocket logic
- Working with NIP-19 encoding/decoding
Workflow
1. Identify What to Build
| Intent | Kind(s) | NIP |
|---|---|---|
| Create/update a merchant stall | 30017 | NIP-15 |
| List a product for sale | 30018 | NIP-15 |
| Configure marketplace UI | 30019 | NIP-15 |
| Create an auction listing | 30020 | NIP-15 |
| Place a bid on an auction | 1021 | NIP-15 |
| Confirm/reject a bid | 1022 | NIP-15 |
| Checkout (order/pay/status) | NIP-04 DMs | NIP-15 |
| P2P buy/sell order | 38383 | NIP-69 |
2. Build the Event
Stall (Kind:30017) — Addressable
The stall is the merchant's store. Products belong to stalls. Shipping zones are defined at the stall level with base costs.
{
"kind": 30017,
"tags": [["d", "my-stall-001"]],
"content": "{\"id\":\"my-stall-001\",\"name\":\"Satoshi's Electronics\",\"description\":\"Quality electronics for sats\",\"currency\":\"USD\",\"shipping\":[{\"id\":\"us-domestic\",\"name\":\"US Domestic\",\"cost\":5.99,\"regions\":[\"US\"]},{\"id\":\"worldwide\",\"name\":\"Worldwide\",\"cost\":15.00,\"regions\":[\"EU\",\"AS\",\"SA\"]}]}"
}
Rules:
dtag value MUST equal theidin content JSONidshould be a UUID or descriptive slug — sequential IDs (0,1,2) are discouragedcurrencyis a string (e.g.,"USD","BTC","EUR")shippingarray defines zones; customer must choose exactly one zone at checkout- Each zone has a base
costin the stall's currency
Product (Kind:30018) — Addressable
Products belong to a stall via stall_id. They can add per-product shipping
costs on top of the stall's base shipping cost.
{
"kind": 30018,
"tags": [
["d", "product-xyz-789"],
["t", "electronics"],
["t", "gadgets"]
],
"content": "{\"id\":\"product-xyz-789\",\"stall_id\":\"my-stall-001\",\"name\":\"Lightning Node Kit\",\"description\":\"Pre-configured Bitcoin node\",\"images\":[\"https://example.com/node.jpg\"],\"currency\":\"USD\",\"price\":299.99,\"quantity\":25,\"specs\":[[\"processor\",\"ARM Cortex-A72\"],[\"storage\",\"1TB SSD\"],[\"connectivity\",\"Ethernet + WiFi\"]],\"shipping\":[{\"id\":\"us-domestic\",\"cost\":10.00},{\"id\":\"worldwide\",\"cost\":25.00}]}"
}
Rules:
dtag value MUST equal theidin content JSONstall_idMUST reference an existing stall'sidquantity: integer for limited stock,nullfor unlimited (digital goods/services)ttags are searchable categories — use multiple for discoverabilityspecsis an array of[key, value]pairs for structured display- Product
shipping[].idMUST match a zoneidfrom the parent stall - Total shipping = stall base cost + (product shipping cost × quantity)
Marketplace UI (Kind:30019) — Addressable
Custom marketplace configuration grouping merchants together:
{
"kind": 30019,
"tags": [["d", "my-marketplace"]],
"content": "{\"name\":\"Bitcoin Bazaar\",\"about\":\"A curated marketplace for Bitcoin products\",\"ui\":{\"picture\":\"https://example.com/logo.png\",\"banner\":\"https://example.com/banner.jpg\",\"theme\":\"dark\",\"darkMode\":true},\"merchants\":[\"pubkey1hex...\",\"pubkey2hex...\"]}"
}
Auction (Kind:30020) — Addressable
Auctions are similar to products but with bidding mechanics:
{
"kind": 30020,
"tags": [["d", "auction-rare-item-001"]],
"content": "{\"id\":\"auction-rare-item-001\",\"stall_id\":\"my-stall-001\",\"name\":\"Signed Hal Finney Print\",\"description\":\"Limited edition signed print\",\"images\":[\"https://example.com/print.jpg\"],\"starting_bid\":50000,\"start_date\":1719391096,\"duration\":86400,\"specs\":[[\"condition\",\"mint\"],[\"authentication\",\"verified\"]],\"shipping\":[{\"id\":\"worldwide\",\"cost\":20.00}]}"
}
Rules:
starting_bidis in the stall's currency (integer)start_dateis a Unix timestamp; omit if start date is unknowndurationis seconds the auction runs (excluding extensions)- Actual end =
start_date + duration + SUM(all duration_extended from confirmations) - Cannot edit auction after receiving first bid — bids reference the event ID (not product UUID), so editing creates a new event ID and loses all bids
Bid (Kind:1021) — Regular
{
"kind": 1021,
"content": "75000",
"tags": [["e", "<auction-event-id>"]]
}
Rules:
contentis the bid amount as a string (in the auction's currency)- The
etag references the event ID of the auction, not the product UUID - This is why editing an auction after bids destroys those bids
Bid Confirmation (Kind:1022) — Regular
Sent by the merchant to validate bids:
{
"kind": 1022,
"content": "{\"status\":\"accepted\",\"message\":\"Bid received and validated\",\"duration_extended\":300}",
"tags": [
["e", "<bid-event-id>"],
["e", "<auction-event-id>"]
]
}
Status values: accepted, rejected, pending, winner
winneris sent to the winning bid after auction endsduration_extended(optional): seconds added to auction duration- Clients must verify the confirmation pubkey matches the merchant's pubkey
3. Checkout Flow (NIP-04 DMs)
Checkout uses encrypted direct messages. See references/checkout-flow.md for full details.
Step 1 — Customer sends order (type:0):
{
"id": "order-uuid-123",
"type": 0,
"name": "Alice",
"address": "123 Bitcoin St, Miami, FL",
"message": "Please ship ASAP",
"contact": {
"nostr": "<customer-pubkey-hex>",
"phone": null,
"email": "alice@example.com"
},
"items": [
{ "product_id": "product-xyz-789", "quantity": 2 }
],
"shipping_id": "us-domestic"
}
Step 2 — Merchant sends payment request (type:1):
{
"id": "order-uuid-123",
"type": 1,
"message": "Payment required within 15 minutes",
"payment_options": [
{ "type": "ln", "link": "lnbc..." },
{ "type": "btc", "link": "bc1q..." },
{ "type": "url", "link": "https://pay.example.com/order-123" }
]
}
Step 3 — Merchant sends status update (type:2):
{
"id": "order-uuid-123",
"type": 2,
"message": "Payment confirmed, shipping tomorrow",
"paid": true,
"shipped": false
}
4. P2P Orders (NIP-69 Kind:38383)
For peer-to-peer bitcoin trading. See references/marketplace-events.md for full tag reference.
{
"kind": 38383,
"tags": [
["d", "order-uuid-456"],
["k", "sell"],
["f", "USD"],
["s", "pending"],
["amt", "100000"],
["fa", "50"],
["pm", "zelle", "cashapp"],
["premium", "3"],
["network", "mainnet"],
["layer", "lightning"],
["name", "SatoshiTrader"],
["bond", "1000"],
["expires_at", "1719391096"],
["expiration", "1719995896"],
["y", "my-platform"],
["z", "order"]
],
"content": ""
}
Required tags: d, k, f, s, amt, fa, pm, premium, network,
layer, expires_at, expiration, y, z
Status flow: pending → in-progress → success | canceled | expired
5. Validate Before Publishing
- [ ]
dtag matches contentid(for kinds 30017, 30018, 30020) - [ ] Product
stall_idreferences a valid stall - [ ] Product shipping zone IDs match stall shipping zone IDs
- [ ]
quantityis integer or null (not string, not undefined) - [ ] Auction
starting_bidis an integer - [ ] Bid
etag references auction event ID (not product UUID) - [ ] Bid confirmation has two
etags (bid + auction) - [ ] Checkout messages use correct
typevalues (0, 1, 2) - [ ] P2P order has
ztag set to"order" - [ ] P2P order
ftag uses ISO 4217 currency code - [ ] P2P order
ktag is"buy"or"sell" - [ ] All timestamps are Unix seconds (not milliseconds)
- [ ] Content is stringified JSON where required
Shipping Cost Calculation
Total shipping for an order:
base_cost = stall.shipping[chosen_zone].cost
per_product = SUM(product.shipping[chosen_zone].cost × quantity) for each item
total_shipping = base_cost + per_product
Example: Stall base shipping $5.99, buying 2 units of a product with $10 extra shipping per unit → total shipping = $5.99 + (2 × $10) = $25.99.
Common Mistakes
| Mistake | Why It Breaks | Fix |
|---|---|---|
d tag doesn't match content id |
Relay can't address the event correctly | Always keep d tag and content id identical |
Sequential IDs (0, 1, 2) |
Collision risk across merchants | Use UUIDs or descriptive slugs |
| Editing auction after bids received | Bids reference event ID which changes on edit | Never edit auctions that have bids |
| Bid references product UUID instead of event ID | Bid won't be associated with the auction | Use the auction's Nostr event ID in the e tag |
Missing z tag on P2P orders |
Clients can't identify the event as an order | Always include ["z", "order"] |
Using buy/sell without ISO 4217 f tag |
Currency is ambiguous | Use standard codes: USD, EUR, BRL, VES |
| Product shipping zone ID doesn't match stall | Shipping calculation breaks | Product shipping IDs must exist in parent stall |
quantity as string "10" instead of int 10 |
Type mismatch in clients | Use integer or null, never string |
Checkout type as string "0" instead of int 0 |
Message routing fails | Use integer type values: 0, 1, 2 |
Bid confirmation missing auction e tag |
Can't link confirmation to auction | Include both bid and auction e tags |
Quick Reference
| Event | Kind | Category | Key Fields |
|---|---|---|---|
| Stall | 30017 | Addressable | id, name, currency, shipping zones |
| Product | 30018 | Addressable | id, stall_id, price, quantity, specs, shipping |
| Marketplace UI | 30019 | Addressable | name, ui config, merchant pubkeys |
| Auction | 30020 | Addressable | id, stall_id, starting_bid, start_date, duration |
| Bid | 1021 | Regular | amount in content, e tag → auction event ID |
| Bid Confirmation | 1022 | Regular | status, e tags → bid + auction |
| P2P Order | 38383 | Addressable | k (buy/sell), f (currency), s (status), amt, fa |
Key Principles
-
IDs must be consistent — The
dtag and theidfield inside content JSON must always match. This is how addressable events are located. -
Shipping is hierarchical — Stalls define base shipping zones and costs. Products add per-unit costs on top. The customer picks one zone at checkout.
-
Auctions are immutable after bids — Because bids reference the event ID (not a UUID), editing an auction creates a new event and orphans existing bids. Design auctions carefully before publishing.
-
Checkout is sequential — Order (type:0) → Payment request (type:1) → Status update (type:2). Each message references the same order ID. All messages are NIP-04 encrypted DMs.
-
P2P orders are self-contained — All trade parameters live in tags, not content. The
ztag must be"order"for client discovery. Status transitions follow: pending → in-progress → success/canceled/expired.