Compare commits
14 commits
05ebf042ac
...
16e50d67f9
| Author | SHA1 | Date | |
|---|---|---|---|
| 16e50d67f9 | |||
| 83e9660ae5 | |||
| 3208e89e19 | |||
| e2fb28e90e | |||
| 8606dce908 | |||
| 1b39744daa | |||
| f85cbaa65e | |||
| e0fdada15c | |||
| 1fd01f8c06 | |||
| 61c2db8e80 | |||
| 0fec454006 | |||
| 7245123e49 | |||
| 7c7d6c7953 | |||
| 631675d2a0 |
6 changed files with 38 additions and 133 deletions
17
README.md
17
README.md
|
|
@ -1,13 +1,3 @@
|
||||||
<a href="https://lnbits.com" target="_blank" rel="noopener noreferrer">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://i.imgur.com/QE6SIrs.png">
|
|
||||||
<img src="https://i.imgur.com/fyKPgVT.png" alt="LNbits" style="width:280px">
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
[](./LICENSE)
|
|
||||||
[](https://github.com/lnbits/lnbits)
|
|
||||||
|
|
||||||
# Nostr Market ([NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md)) - <small>[LNbits](https://github.com/lnbits/lnbits) extension</small>
|
# Nostr Market ([NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md)) - <small>[LNbits](https://github.com/lnbits/lnbits) extension</small>
|
||||||
|
|
||||||
<small>For more about LNBits extension check [this tutorial](https://github.com/lnbits/lnbits/wiki/LNbits-Extensions).</small>
|
<small>For more about LNBits extension check [this tutorial](https://github.com/lnbits/lnbits/wiki/LNbits-Extensions).</small>
|
||||||
|
|
@ -157,10 +147,3 @@ Stall and product are _Parameterized Replaceable Events_ according to [NIP-33](h
|
||||||
Order placing, invoicing, payment details and order statuses are handled over Nostr using [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md).
|
Order placing, invoicing, payment details and order statuses are handled over Nostr using [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md).
|
||||||
|
|
||||||
Customer support is handled over whatever communication method was specified. If communicationg via nostr, [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) is used.
|
Customer support is handled over whatever communication method was specified. If communicationg via nostr, [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) is used.
|
||||||
|
|
||||||
## Powered by LNbits
|
|
||||||
|
|
||||||
[LNbits](https://lnbits.com) is a free and open-source lightning accounts system.
|
|
||||||
|
|
||||||
[](https://shop.lnbits.com/)
|
|
||||||
[](https://my.lnbits.com/login)
|
|
||||||
|
|
|
||||||
20
config.json
20
config.json
|
|
@ -1,15 +1,12 @@
|
||||||
{
|
{
|
||||||
"id": "nostrmarket",
|
|
||||||
"version": "1.1.0",
|
|
||||||
"name": "Nostr Market",
|
"name": "Nostr Market",
|
||||||
"repo": "https://github.com/lnbits/nostrmarket",
|
"version": "1.1.0",
|
||||||
"short_description": "Nostr Webshop/market on LNbits",
|
"short_description": "Nostr Webshop/market on LNbits",
|
||||||
"description": "",
|
"tile": "/nostrmarket/static/images/nostr-market.png",
|
||||||
"tile": "/nostrmarket/static/images/bitcoin-shop.png",
|
|
||||||
"min_lnbits_version": "1.4.0",
|
"min_lnbits_version": "1.4.0",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{
|
{
|
||||||
"name": "Vlad Stan",
|
"name": "motorina0",
|
||||||
"uri": "https://github.com/motorina0",
|
"uri": "https://github.com/motorina0",
|
||||||
"role": "Contributor"
|
"role": "Contributor"
|
||||||
},
|
},
|
||||||
|
|
@ -22,11 +19,6 @@
|
||||||
"name": "talvasconcelos",
|
"name": "talvasconcelos",
|
||||||
"uri": "https://github.com/talvasconcelos",
|
"uri": "https://github.com/talvasconcelos",
|
||||||
"role": "Developer"
|
"role": "Developer"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "BenGWeeks",
|
|
||||||
"uri": "https://github.com/BenGWeeks",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"images": [
|
"images": [
|
||||||
|
|
@ -52,9 +44,5 @@
|
||||||
],
|
],
|
||||||
"description_md": "https://raw.githubusercontent.com/lnbits/nostrmarket/main/description.md",
|
"description_md": "https://raw.githubusercontent.com/lnbits/nostrmarket/main/description.md",
|
||||||
"terms_and_conditions_md": "https://raw.githubusercontent.com/lnbits/nostrmarket/main/toc.md",
|
"terms_and_conditions_md": "https://raw.githubusercontent.com/lnbits/nostrmarket/main/toc.md",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"paid_features": "",
|
|
||||||
"tags": ["Nostr", "Marketplace"],
|
|
||||||
"donate": "",
|
|
||||||
"hidden": false
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
Buy and sell products over Nostr using the NIP-15 marketplace protocol.
|
> IMPORTANT: Nostr market needs the nostr-client extension installed.
|
||||||
|
|
||||||
Its functions include:
|
Buy and sell things over Nostr, using NIP15 https://github.com/nostr-protocol/nips/blob/master/15.md
|
||||||
|
|
||||||
- Managing products, sales, and customer communication as a merchant
|
Nostr was partly based on the the previous version of this extension "Diagon Alley", so lends itself very well to buying and sellinng over Nostr.
|
||||||
- Browsing and ordering products as a customer
|
|
||||||
- Tracking order status and delivery
|
|
||||||
- Communicating via NIP-17 private direct messages (NIP-44 encryption + NIP-59 gift wrapping)
|
|
||||||
|
|
||||||
A decentralized commerce solution for merchants and buyers who want to trade goods and services over Nostr with end-to-end encrypted communication.
|
The Nostr Market extension includes:
|
||||||
|
|
||||||
|
- A merchant client to manage products, sales and communication with customers.
|
||||||
|
- A customer client to find and order products from merchants, communicate with merchants and track status of ordered products.
|
||||||
|
|
||||||
|
All communication happens over NIP-17 private direct messages (NIP-44 encryption + NIP-59 gift wrapping).
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,7 @@ window.app.component('merchant-tab', {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
marketClientUrl: function () {
|
marketClientUrl: function () {
|
||||||
if (!this.publicKey) {
|
return '/nostrmarket/market'
|
||||||
return '/nostrmarket/market'
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL('/nostrmarket/market', window.location.origin)
|
|
||||||
url.searchParams.set('merchant', this.publicKey)
|
|
||||||
return url.pathname + url.search
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,5 @@
|
||||||
var NostrTools = window.NostrTools
|
var NostrTools = window.NostrTools
|
||||||
|
|
||||||
;(function ensureRandomUUID() {
|
|
||||||
if (!globalThis.crypto) {
|
|
||||||
globalThis.crypto = {}
|
|
||||||
}
|
|
||||||
if (!globalThis.crypto.randomUUID) {
|
|
||||||
globalThis.crypto.randomUUID = function () {
|
|
||||||
const getRandomValues = globalThis.crypto.getRandomValues
|
|
||||||
if (getRandomValues) {
|
|
||||||
const bytes = new Uint8Array(16)
|
|
||||||
getRandomValues.call(globalThis.crypto, bytes)
|
|
||||||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
|
||||||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
|
||||||
const hex = Array.from(bytes, b =>
|
|
||||||
b.toString(16).padStart(2, '0')
|
|
||||||
).join('')
|
|
||||||
return (
|
|
||||||
hex.slice(0, 8) +
|
|
||||||
'-' +
|
|
||||||
hex.slice(8, 12) +
|
|
||||||
'-' +
|
|
||||||
hex.slice(12, 16) +
|
|
||||||
'-' +
|
|
||||||
hex.slice(16, 20) +
|
|
||||||
'-' +
|
|
||||||
hex.slice(20)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let d = Date.now()
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
||||||
const r = (d + Math.random() * 16) % 16 | 0
|
|
||||||
d = Math.floor(d / 16)
|
|
||||||
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
var defaultRelays = [
|
var defaultRelays = [
|
||||||
'wss://relay.damus.io',
|
'wss://relay.damus.io',
|
||||||
'wss://relay.snort.social',
|
'wss://relay.snort.social',
|
||||||
|
|
@ -82,24 +44,13 @@ function confirm(message) {
|
||||||
|
|
||||||
|
|
||||||
async function hash(string) {
|
async function hash(string) {
|
||||||
const subtle = globalThis.crypto && globalThis.crypto.subtle
|
const utf8 = new TextEncoder().encode(string)
|
||||||
if (subtle && subtle.digest) {
|
const hashBuffer = await crypto.subtle.digest('SHA-256', utf8)
|
||||||
const utf8 = new TextEncoder().encode(string)
|
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||||
const hashBuffer = await subtle.digest('SHA-256', utf8)
|
const hashHex = hashArray
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
.map(bytes => bytes.toString(16).padStart(2, '0'))
|
||||||
return hashArray.map(bytes => bytes.toString(16).padStart(2, '0')).join('')
|
.join('')
|
||||||
}
|
return hashHex
|
||||||
|
|
||||||
// Fallback for non-secure contexts where crypto.subtle is unavailable.
|
|
||||||
return fallbackHash(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fallbackHash(string) {
|
|
||||||
let hash = 5381
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
hash = ((hash << 5) + hash) + string.charCodeAt(i)
|
|
||||||
}
|
|
||||||
return (hash >>> 0).toString(16).padStart(8, '0')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJson(str) {
|
function isJson(str) {
|
||||||
|
|
|
||||||
|
|
@ -9,26 +9,29 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-mb-md q-gutter-sm">
|
<div class="row q-mb-md q-gutter-sm">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="primary"
|
||||||
|
@click="showEditProfileDialog = true"
|
||||||
|
icon="edit"
|
||||||
|
>Edit</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="primary"
|
||||||
|
icon="qr_code"
|
||||||
|
@click="showKeysDialog = true"
|
||||||
|
>
|
||||||
|
<q-tooltip>Show Keys</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
split
|
split
|
||||||
outline
|
outline
|
||||||
color="primary"
|
color="primary"
|
||||||
icon="vpn_key"
|
icon="swap_horiz"
|
||||||
label="Keys"
|
label="Switch"
|
||||||
>
|
>
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item clickable v-close-popup @click="showKeysDialog = true">
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-icon name="vpn_key" color="primary"></q-icon>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>View Keys</q-item-label>
|
|
||||||
<q-item-label caption
|
|
||||||
>Show public/private keys</q-item-label
|
|
||||||
>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-item-label header>Saved Profiles</q-item-label>
|
<q-item-label header>Saved Profiles</q-item-label>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
|
|
@ -66,13 +69,6 @@
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="primary"
|
|
||||||
@click="showEditProfileDialog = true"
|
|
||||||
icon="edit"
|
|
||||||
label="Edit Profile"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
split
|
split
|
||||||
outline
|
outline
|
||||||
|
|
@ -125,15 +121,6 @@
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="primary"
|
|
||||||
icon="storefront"
|
|
||||||
label="Marketplace"
|
|
||||||
:href="marketClientUrl"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue