Compare commits

..

8 commits

Author SHA1 Message Date
f85cbaa65e Refactors type hints for clarity
Some checks failed
CI / lint (push) Has been cancelled
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled
Updates type hints in `crud.py`, `helpers.py`, and `models.py` for improved readability and maintainability.

Replaces `Merchant | None` with `Optional[Merchant]` and `list[X]` with `List[X]` for consistency with standard Python typing practices.
2025-12-31 12:07:51 +01:00
e0fdada15c Improves Nostr message handling and error logging
Enhances the processing of Nostr messages by adding more robust error handling and logging, providing better insights into potential issues.

Specifically:
- Improves the checks on the websocket connection to log errors and debug information.
- Implements more comprehensive error logging for failed product quantity checks.
- Enhances logging and validation of EVENT messages to prevent potential errors.
- Implements a more robust merchant lookup logic to avoid double processing of events.
- Implements a more lenient time window for direct message subscriptions.
2025-12-31 12:06:07 +01:00
1fd01f8c06 Adds order discovery analysis document
Adds a document analyzing the order discovery mechanism in Nostrmarket.

The document identifies the reasons merchants need to manually refresh to see new orders, instead of receiving them automatically. It analyzes timing window issues, connection stability, subscription state management, and event processing delays. It proposes solutions such as enhanced persistent subscriptions, periodic auto-refresh, WebSocket health monitoring, and event gap detection.
2025-12-31 12:06:07 +01:00
61c2db8e80 Enhances websocket connection robustness
Improves websocket connection reliability by predefining the websocket URL and handling potential queueing errors.

This change also updates the websocket close message for clarity.
2025-12-31 12:06:07 +01:00
0fec454006 TEMP: set merchant active to True by default 2025-12-31 12:06:07 +01:00
7245123e49 Improve merchant creation with automatic keypair generation
Enhance the merchant creation process by automatically generating Nostr keypairs
for users who don't have them, and streamline the API interface.

Changes:
- Add CreateMerchantRequest model to simplify merchant creation API
- Auto-generate Nostr keypairs for users without existing keys
- Update merchant creation endpoint to use user account keypairs
- Improve error handling and validation in merchant creation flow
- Clean up frontend JavaScript for merchant creation

This ensures all merchants have proper Nostr keypairs for marketplace
functionality without requiring manual key management from users.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-31 12:04:22 +01:00
7c7d6c7953 interactive rebase commit, clean logs 2025-12-31 11:55:28 +01:00
631675d2a0 add DEBUG logs 2025-12-31 11:55:28 +01:00
6 changed files with 38 additions and 133 deletions

View file

@ -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: MIT](https://img.shields.io/badge/License-MIT-success?logo=open-source-initiative&logoColor=white)](./LICENSE)
[![Built for LNbits](https://img.shields.io/badge/Built%20for-LNbits-4D4DFF?logo=lightning&logoColor=white)](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.
[![Visit LNbits Shop](https://img.shields.io/badge/Visit-LNbits%20Shop-7C3AED?logo=shopping-cart&logoColor=white&labelColor=5B21B6)](https://shop.lnbits.com/)
[![Try myLNbits SaaS](https://img.shields.io/badge/Try-myLNbits%20SaaS-2563EB?logo=lightning&logoColor=white&labelColor=1E40AF)](https://my.lnbits.com/login)

View file

@ -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
} }

View file

@ -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-04 encrypted direct messages
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 NIP04 encrypted DMs.

View file

@ -31,13 +31,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: {

View file

@ -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) {

View file

@ -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>
@ -85,13 +88,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
@ -144,15 +140,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>