Commit graph

84 commits

Author SHA1 Message Date
9f116ff1f8 feat(signer): stop reading account.prvkey in merchant provision/rotate paths (#5)
Pre-cascade prerequisite for aiolabs/lnbits#17 (signer abstraction
phase 1), which lands an m002 startup job that fail-closed NULLs the
legacy `accounts.prvkey` column. This commit migrates the two sites
in `views_api.py` that read `account.prvkey` so they no longer
silently undo m002, and fail-closed cleanly when prvkey is missing.

Scope intentionally narrow — this is the prvkey-elimination subset
of aiolabs/nostrmarket#5. The full phase A (envelope-encrypt
`merchants.private_key` → `signer_blob`) and phase B (route
`Merchant.sign_hash` through core's `NostrSigner`) work remains
tracked under that issue.

## What changed

### views_api.py — `_auto_create_merchant`

Was: lazy fallback that, if `account.prvkey` was missing, generated
a fresh keypair and wrote it back into the account (lines 112-118).
After m002 NULLs `accounts.prvkey`, this regenerate-and-write-back
path would silently undo the migration AND change the user's
Nostr pubkey out from under them.

Now: no longer touches the account. Asserts `account.prvkey` is
present (matching the existing pubkey assertion) with a clear
fail-closed message pointing at aiolabs/nostrmarket#5 for the
phase A/B fix. For accounts that still carry a plaintext prvkey
(pre-m002, FakeWallet local dev, etc.) the auto-provision path
continues to work unchanged. For migrated accounts, the assertion
fires fast with an actionable error.

Removed the regenerate block entirely. Dropped now-unused imports:
`update_account`, `generate_keypair`.

### views_api.py — `api_migrate_merchant_keys`

Was: same `account and account.pubkey and account.prvkey` assertion
with the generic message "Account has no Nostr keypair".

Now: assertion updated with the same bridge-state framing — points
at aiolabs/nostrmarket#5 for the phase A/B fix.

## Acceptance

- [x] regenerate-and-write-back block removed (would undo m002)
- [x] `account.prvkey` references in views_api.py are assertions only
      (fail-closed guards, not data reads)
- [x] unused imports dropped (`update_account`, `generate_keypair`)
- [x] error messages reference aiolabs/nostrmarket#5 for the
      phase A/B fix path

Manual smoke / version bump / tag / catalog entry deferred until
the lnbits cascade lands AND phase A's schema migration ships;
this commit alone doesn't change the on-disk merchants table.

## Out of scope (per aiolabs/nostrmarket#5)

- Phase A: envelope-encrypting `merchants.private_key` column.
- Phase B (full): refactoring `Merchant.sign_hash` /
  `helpers.sign_message_hash` through core's `NostrSigner`.
- Phase C: NIP-46 bunker + NIP-26 delegation variants.
- Re-enabling `_create_default_merchant` on the lnbits core side.

## Cross-references

- aiolabs/nostrmarket#5 — issue this is a partial step toward
- aiolabs/lnbits#17 — the cascading signer-abstraction PR whose
  m002 fail-closed NULLs `accounts.prvkey`
- aiolabs/lnbits#21 — umbrella audit (5 affected extensions)
- aiolabs/events#23 / aiolabs/tasks#3 / aiolabs/restaurant#11 —
  sister migrations already on signer-abstraction branches

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 08:58:31 +02:00
05ebf042ac Extract provision_merchant() service for shared use
Some checks failed
ci.yml / Extract provision_merchant() service for shared use (pull_request) Failing after 0s
ci.yml / Extract provision_merchant() service for shared use (push) Failing after 0s
Both _auto_create_merchant (lazy GET fallback in views_api) and
LNbits' _create_default_merchant (eager signup hook) used to
reimplement merchant + zone + stall creation independently. Moves the
canonical implementation to services.provision_merchant() so both
call sites stay in lockstep — future changes (NIP-17 kind 10050 relay
list, additional default zones, etc.) only happen in one place.

- services.provision_merchant(user_id, wallet_id, public_key,
  private_key, display_name, config): creates merchant if absent,
  default 'Online' zone, default '<username>'s Store' stall, and
  publishes the kind 30017 stall event. Idempotent on the merchant
  pubkey: returns the existing merchant unchanged if one exists.
- views_api._auto_create_merchant: now a 10-line wrapper that loads
  the account, generates fallback keys if missing, then delegates.

The LNbits-side hook (lnbits/core/services/users.py:_create_default_merchant)
will be updated in a companion commit to also call this service.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 16:59:03 +02:00
e481c9179d Auto-create + publish default stall, republish stall on product publish
Two complementary fixes for the "Unknown Stall" bug, where a customer
sees a product on the relay but the parent stall is missing.

1. _auto_create_merchant() now creates a default "<username>'s Store"
   stall and publishes its kind 30017 event before returning. New users
   land with a fully-published merchant identity, so the very first
   product they create has a known parent stall on relays.

2. POST /api/v1/product (api_create_product) now republishes the parent
   stall before publishing the product. NIP-33 parameterized replaceable
   events make this idempotent, but it self-heals every existing case
   where the stall publish failed or never happened (transient relay
   issues, accounts that pre-date the auto-publish flow, manual stall
   creation that didn't reach all relays).

This complements the LNbits-side fix in core/services/users.py
(_create_default_merchant publishes the stall on signup) and the
webapp self-heal in useMarketStallSelfHeal.ts. With all three layers,
"Unknown Stall" should disappear from the customer view.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 16:59:03 +02:00
25023df8bd Add keypair rotation detection and migration feature
When a user rotates their Nostr keypair in account settings, the
merchant still holds the old key. This adds:

- key_mismatch flag on MerchantConfig (runtime, not persisted) -
  detected on each GET /api/v1/merchant by comparing account vs
  merchant pubkey
- POST /api/v1/merchant/{id}/migrate-keys endpoint that updates
  the merchant keys, republishes all stalls/products under the new
  identity, and resubscribes
- Warning banner in the UI with a "Migrate Keys" button and
  confirmation dialog
- update_merchant_keys() crud function

Orders and DM history are preserved since they reference customer
pubkeys. Old stall/product events on relays become orphaned.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 16:59:03 +02:00
5c38947fc6 Auto-provision merchant from account keypair on first access
The LNbits user account IS the merchant identity. GET /api/v1/merchant
now auto-creates the merchant record using the account's existing Nostr
keypair if one doesn't exist yet, so the extension is immediately
usable without any setup screen.

- Extract _auto_create_merchant() helper used by both GET and POST
- Remove welcome/key-generation screen (replaced with loading spinner)
- Remove dead frontend code (generateKeys, importKeys dialogs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 16:59:03 +02:00
725944ae9c Replace NIP-04 messaging with NIP-17 (NIP-44 + NIP-59 gift wrapping)
Modernize the entire customer-merchant communication layer from deprecated
NIP-04 (kind 4, AES-256-CBC) to NIP-17 private direct messages using
NIP-44 v2 encryption (ChaCha20 + HMAC-SHA256) and NIP-59 gift wrapping
(rumor/seal/gift-wrap protocol). No backwards compatibility retained.

New modules:
- nostr/nip44.py: NIP-44 v2 encryption verified against official spec vectors
- nostr/nip59.py: NIP-59 gift wrap with wrap/unwrap convenience functions
- tests/: 44 unit tests for NIP-44 and NIP-59

Key changes:
- Subscription filters: kind 4 → kind 1059 gift wraps
- Message handler: _handle_nip04_message → _handle_gift_wrap (unwrap + route)
- send_dm/reply_to_structured_dm: NIP-59 gift wrap to recipient + self-archive
- Merchant model: removed NIP-04 crypto methods (decrypt/encrypt/build_dm_event)
- helpers.py: removed NIP-04 functions, kept Schnorr signing + key normalization
- views_api.py: consolidated DM sending through send_dm() service function

Reliability improvements:
- Event deduplication via bounded LRU set in NostrClient
- Subscription health monitor (resubscribes after 120s of silence)
- Preserved 5-minute lenient time window from prior work

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 16:59:03 +02:00
319d5eeb04 Refactors type hints for clarity
Some checks failed
CI / lint (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.
2026-04-24 01:59:27 -04:00
a8eeace36d 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>
2026-04-24 01:59:27 -04:00
Ben Weeks
7aec14854c feat: multi-profile support and UI improvements
- Remove backend restriction on one merchant per user
- Add "Generate New Key" dialog with npub/nsec display
- Add "Import Existing Key" option with duplicate check
- Change "Save" to "Save & Publish" in edit profile dialog
- Remove standalone Publish button (now part of Save)
- Add trash icon to saved profile for removal
- Show display_name in saved profiles dropdown
- Hide nsec by default with eye toggle in generate dialog

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 14:05:18 +00:00
Ben Weeks
a21b5289c1 feat: add merchant profile edit, keys dialog, and publish to Nostr
- Add PATCH endpoint for updating merchant profile config
- Add website field to MerchantProfile model
- Fix to_nostr_event to include all profile fields (display_name, banner, website, nip05, lud16)
- Always publish merchant profile (kind 0) when publishing to Nostr
- Extract edit-profile-dialog and nostr-keys-dialog into separate components
- Fix profile avatar alignment to match original design
- Simplify keys dialog to show only npub QR code (no nsec QR)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 12:42:03 +00:00
dni ⚡
3a8c16d155
chore: add uv, add ci, fix some issues (#113)
in preparation of removing secp
2025-10-30 10:11:18 +01:00
Vlad Stan
de7fe059b8
V1 (#106)
Some checks failed
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled
2024-11-06 11:50:21 +02:00
dni ⚡
3325626777
fix: properly start/stop tasks (#100)
Some checks failed
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled
* fix: properly start/stop tasks

https://github.com/lnbits/lnbits/issues/2411
2024-05-27 12:47:39 +03:00
dni ⚡
49b4ae73c5
chore: rename websocketUpdater (#99)
Some checks failed
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled
* chore: rename `websocketUpdater`
https://github.com/lnbits/lnbits/pull/2377
2024-04-12 12:12:19 +02:00
Vlad Stan
0a36bd7d42 fix: receipt on invoice re-issue 2023-09-26 14:32:46 +03:00
Vlad Stan
1a840ac007
feat: refresh merchant from nostr (#85) 2023-09-20 12:06:36 +03:00
Vlad Stan
97ee1c8f49
Fix high load (#84)
* chore: testing

* fix: resubscribe when merchant added/removed

* chore: code clean-up

* fix: temp subscribe when new merchant is added

* fix: new customer profile
2023-09-20 09:17:01 +03:00
Vlad Stan
f2773e0b44
Fix subscription errors (#81)
* pref: merge filters in one

* chore: load nit

* feat: restore individual order
2023-09-12 15:03:37 +03:00
Vlad Stan
89a1a0045e feat: allow to restore all products at once 2023-07-18 16:40:43 +02:00
Vlad Stan
bf056ab616 fix: ui testing 2023-07-18 16:40:43 +02:00
Vlad Stan
2b46dbc139 fix: error message 2023-07-05 09:08:09 +03:00
Vlad Stan
df5e894b8c feat: update shipping zone when re-issuing invoice 2023-07-04 17:15:20 +02:00
Vlad Stan
dacf318ee4 feat: allow invoice re-issue 2023-07-04 16:20:08 +02:00
Vlad Stan
e47c6ec7f4 feat: show order details for customer 2023-07-04 08:40:49 +02:00
Vlad Stan
0ce4d1ebce fix: notification for orders 2023-07-04 08:40:49 +02:00
Vlad Stan
a3c221aca8 feat: activate/deactivate merchant 2023-07-04 08:40:49 +02:00
Vlad Stan
da284ba768 feat: prepare for merchant active/restoring state 2023-07-04 08:40:49 +02:00
Vlad Stan
503ee3cd3a fix: default zone ID 2023-06-30 13:15:35 +03:00
Vlad Stan
33adae672d
Product delete (#64)
* feat: restore stalls from `nostr` as pending

* feat: stall and prod last update time

* feat: restore products and stalls as `pending`

* feat: show pending stalls

* feat: restore stall

* feat: restore a stall from nostr

* feat: add  blank `Restore Product` button

* fix: handle no talls to restore case

* feat: show restore dialog

* feat: allow query for pending products

* feat: restore products

* chore: code clean-up

* fix: last dm and last order query

* chore: code clean-up

* fix: subscribe for stalls and products on merchant create/restore

* feat: add message type to orders

* feat: simplify messages; code format

* feat: add type to DMs; restore DMs from nostr

* fix: parsing ints

* fix: hide copy button if invoice not present

* fix: do not generate invoice if product not found

* feat: order restore: first version

* refactor: move some logic into `services`

* feat: improve restore UX

* fix: too many calls to customer DMs

* fix: allow `All` customers filter

* fix: ws reconnect on server restart

* fix: query for customer profiles only one

* fix: unread messages per customer per merchant

* fix: disable `user-profile-events`

* fix: customer profile is optional

* fix: get customers after new message debounced

* chore: code clean-up

* feat: auto-create zone

* feat: fixed ID for default zone

* feat: notify order paid
2023-06-30 13:12:56 +03:00
Vlad Stan
3138027c7e chore: code format 2023-05-04 12:50:22 +03:00
Vlad Stan
81a9cc4358 fix: use relative import 2023-05-04 12:49:39 +03:00
Vlad Stan
dcb98ba982 fix: use singular for for customer entity 2023-05-04 12:22:27 +03:00
Vlad Stan
19f67e474a feat: allow to add test public key 2023-05-04 12:15:45 +03:00
Vlad Stan
af780a3080 feat: add reconnect to nostrclient 2023-04-11 19:03:41 +03:00
Vlad Stan
4996a6b7fd feat: add more images for product 2023-04-03 18:36:14 +03:00
Vlad Stan
ef49983e10 feat: dinamically refresh orders 2023-03-30 13:58:14 +03:00
Vlad Stan
021326e773 feat: filter orders 2023-03-30 13:58:14 +03:00
Vlad Stan
87c0477b33 feat: keep track of new messages 2023-03-30 13:58:14 +03:00
Vlad Stan
afb21b100f feat: show customer names 2023-03-30 13:58:14 +03:00
Vlad Stan
330a734932 feat: republish to nostr 2023-03-24 14:52:34 +02:00
Vlad Stan
b483fc2bac feat: part: republish to nostr 2023-03-23 14:33:05 +02:00
Vlad Stan
dd4d677ce1 feat: allow delete merchant from nostr 2023-03-23 11:14:34 +02:00
Vlad Stan
9d63d2f5d8 feat: link order to DMs 2023-03-17 10:51:20 +02:00
Vlad Stan
3d41954c32 feat: show DMs for customers 2023-03-16 18:34:56 +02:00
Vlad Stan
da5490f581 feat: stop websocket when extension is un-installed 2023-03-16 16:32:46 +02:00
Vlad Stan
62e54322f5 feat: extract nostr_client class 2023-03-16 16:01:14 +02:00
Vlad Stan
0265b481d9 fix: catch AssertionErrors 2023-03-16 15:27:07 +02:00
Vlad Stan
7ce4fd76d0 feat: update product quantity when invoice paid 2023-03-16 11:59:38 +02:00
Vlad Stan
aeebae863e fix: do not allow two merchants with the same public key 2023-03-16 09:56:30 +02:00
Vlad Stan
c976c9918a fix: create invoice for fresh merchant 2023-03-15 23:08:37 +02:00