diff --git a/.env.example b/.env.example index ecae912..99327b7 100644 --- a/.env.example +++ b/.env.example @@ -11,13 +11,6 @@ VITE_API_KEY=your-api-key-here VITE_LNBITS_DEBUG=false VITE_WEBSOCKET_ENABLED=true -# LNbits Nostr-transport server pubkey (kind-21000 RPC endpoint). -# Logged by the LNbits server at startup: -# `Nostr transport: starting with pubkey ... on N relay(s)` -# Required for the activities ticket scanner; legacy HTTP path still -# works without it. -VITE_LNBITS_NOSTR_TRANSPORT_PUBKEY= - # Lightning Address Domain (optional) # Override the domain used for Lightning Addresses # If not set, domain will be extracted from VITE_LNBITS_BASE_URL diff --git a/CLAUDE.md b/CLAUDE.md index b85fab5..2ef2826 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -714,90 +714,6 @@ VITE_ADMIN_PUBKEYS='["pubkey1","pubkey2"]' VITE_WEBSOCKET_ENABLED=true ``` -## Payment Rails Pattern - -Shared primitives for modules that mix Lightning + fiat (and, future, -cash / internal-wallet) payment rails. Activities is the first -consumer; restaurant + marketplace will adopt the same primitives as -their backends gain fiat support. - -### Vocabulary (canonical — used in code AND UI labels) - -| Term | Meaning | Field | -|---|---|---| -| **Price currency** | unit the price is quoted in | `currency` | -| **Payment method** | the rail used to pay (Lightning / Fiat / Cash / Internal) | `payment_method` | -| **Fiat currency** | currency the fiat provider settles in | `fiat_currency` | -| **Also accept fiat** | the user-facing label for the `allow_fiat` toggle | `allow_fiat` | - -The bare word `Currency` is **banned** in payment-context UI labels — -it always carries a `Price` or `Fiat` qualifier. The literal string -`Pay in fiat` is also banned on buyer-side buttons — each fiat rail -shows as a button labeled with its provider name (`Stripe`, `PayPal`, -`Square`, `SEPA`). Only the degenerate "providers unknown" fallback -shows a generic `Card`. - -### Fiat-provider architecture (LNbits today) - -Fiat providers are configured **globally** by the LNbits admin -(`lnbits/settings.py`). Each provider has an optional `allowed_users` -whitelist; the per-session filtered list is exposed as -`User.fiat_providers: string[]` on `GET /api/v1/auth` (which the -webapp already reads as `currentUser.fiat_providers`). Both organizer -and buyer on the same instance see the same list. - -Per-user provider configuration is a deferred backend feature. Until -then, `useFiatProviders` reads the same `currentUser.fiat_providers` -for both sides. - -### Shared primitives (live in base module) - -``` -src/modules/base/ -├── composables/ -│ ├── useFiatProviders.ts // providers + hasAnyProvider + refresh + providerMeta() -│ └── usePriceConversion.ts // convert() + useLivePreview(); 60s cache; null on failure -└── components/payments/ - ├── PaymentMethodSelector.vue // buyer-side rail picker - ├── FiatToggleField.vue // organizer-side toggle + conditional fiat-currency dropdown - └── PriceConversionPreview.vue // muted "≈ X.XX USD" line -``` - -All three components consume services via DI — never import them -directly across module boundaries. - -### `PaymentMethodSelector` data shape - -```ts -type PaymentRail = 'lightning' | 'fiat' | 'cash' | 'internal' | (string & {}) - -type PaymentMethod = { - id: string // unique v-for key, e.g. 'fiat:stripe' - rail: PaymentRail // sent as payment_method - provider?: string // sent as fiat_provider when present - label: string // 'Lightning' | 'Stripe' | 'SEPA' | 'Cash' | … - icon: Component // lucide icon - available: boolean // false ⇒ rendered disabled with tooltip - unavailableReason?: string // tooltip when disabled - badge?: string // optional secondary hint, e.g. '≈ 1000 sats' -} -``` - -Module usage: -- **Activities** passes `[lightning, ...one entry per organizer provider]`. -- **Restaurant** (future) passes the subset of - `[lightning, cash, internal, ...fiat providers]` enabled by the - restaurant's `accepts_*` flags. - -### Adding a new fiat provider - -1. Backend exposes the provider id in `User.fiat_providers`. -2. Add the id to `KNOWN_PROVIDERS` in `useFiatProviders.ts` with its - display label and icon hint (`'card' | 'bank' | 'wallet'`). -3. Unknown ids fall back to a `Capitalized` label with a `'card'` - icon hint — no code change required just for the buttons to - render, only for nice branding. - ## Mobile Browser File Input & Form Refresh Issues ### **Problem Overview** diff --git a/package.json b/package.json index 1770e19..106d1c5 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "light-bolt11-decoder": "^3.2.0", "lucide-vue-next": "^0.474.0", "ngeohash": "^0.6.3", - "nostr-tools": "^2.23.3", + "nostr-tools": "2.15.0", "pinia": "^2.3.1", "qr-scanner": "^1.4.2", "qrcode": "^1.5.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1a8b7c..8891845 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,8 +57,8 @@ importers: specifier: ^0.6.3 version: 0.6.3 nostr-tools: - specifier: ^2.23.3 - version: 2.23.5(typescript@5.6.3) + specifier: 2.15.0 + version: 2.15.0(typescript@5.6.3) pinia: specifier: ^2.3.1 version: 2.3.1(typescript@5.6.3)(vue@3.5.34(typescript@5.6.3)) @@ -1247,17 +1247,22 @@ packages: resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} engines: {node: '>= 12.13.0'} - '@noble/ciphers@2.1.1': - resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} - engines: {node: '>= 20.19.0'} + '@noble/ciphers@0.5.3': + resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==} - '@noble/curves@2.0.1': - resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} - engines: {node: '>= 20.19.0'} + '@noble/curves@1.1.0': + resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} - '@noble/hashes@2.0.1': - resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} - engines: {node: '>= 20.19.0'} + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/hashes@1.3.1': + resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} + engines: {node: '>= 16'} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -1473,14 +1478,11 @@ packages: '@scure/base@1.1.1': resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} - '@scure/base@2.0.0': - resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==} + '@scure/bip32@1.3.1': + resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} - '@scure/bip32@2.0.1': - resolution: {integrity: sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==} - - '@scure/bip39@2.0.1': - resolution: {integrity: sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==} + '@scure/bip39@1.2.1': + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} @@ -3533,8 +3535,8 @@ packages: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - nostr-tools@2.23.5: - resolution: {integrity: sha512-Fa7ZlUdjfUW1P4E7H3yBexhOHYi18XNyvd2n7eNHkYR085xADX6Y8V8Vm7nT/XQajaFOBrptXmVIGkJ2E4vfVw==} + nostr-tools@2.15.0: + resolution: {integrity: sha512-Jj/+UFbu3JbTAWP4ipPFNuyD4W5eVRBNAP+kmnoRCYp3bLmTrlQ0Qhs5O1xSQJTFpjdZqoS0zZOUKdxUdjc+pw==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -6202,13 +6204,19 @@ snapshots: dependencies: cross-spawn: 7.0.6 - '@noble/ciphers@2.1.1': {} + '@noble/ciphers@0.5.3': {} - '@noble/curves@2.0.1': + '@noble/curves@1.1.0': dependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 1.3.1 - '@noble/hashes@2.0.1': {} + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/hashes@1.3.1': {} + + '@noble/hashes@1.3.2': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -6354,18 +6362,16 @@ snapshots: '@scure/base@1.1.1': {} - '@scure/base@2.0.0': {} - - '@scure/bip32@2.0.1': + '@scure/bip32@1.3.1': dependencies: - '@noble/curves': 2.0.1 - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 + '@noble/curves': 1.1.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 - '@scure/bip39@2.0.1': + '@scure/bip39@1.2.1': dependencies: - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 '@sindresorhus/is@4.6.0': {} @@ -8533,14 +8539,14 @@ snapshots: normalize-url@6.1.0: {} - nostr-tools@2.23.5(typescript@5.6.3): + nostr-tools@2.15.0(typescript@5.6.3): dependencies: - '@noble/ciphers': 2.1.1 - '@noble/curves': 2.0.1 - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 - '@scure/bip32': 2.0.1 - '@scure/bip39': 2.0.1 + '@noble/ciphers': 0.5.3 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + '@scure/bip32': 1.3.1 + '@scure/bip39': 1.2.1 nostr-wasm: 0.1.0 optionalDependencies: typescript: 5.6.3 diff --git a/src/accounting-app/views/BalancePage.vue b/src/accounting-app/views/BalancePage.vue index 8b6492b..1335021 100644 --- a/src/accounting-app/views/BalancePage.vue +++ b/src/accounting-app/views/BalancePage.vue @@ -98,11 +98,8 @@ async function loadData() { totalIncomeSats.value = balanceData.total_income_sats || 0 totalIncomeFiat.value = balanceData.total_income_fiat || {} - // Filter for pending transactions (flag = '!'), excluding voided ones - // (libra convention: voided keeps '!' flag and carries a 'voided' tag). - pendingTransactions.value = txData.entries.filter( - tx => tx.flag === '!' && !tx.tags?.includes('voided') - ) + // Filter for pending transactions (flag = '!') + pendingTransactions.value = txData.entries.filter(tx => tx.flag === '!') } catch (error) { console.error('[BalancePage] Error loading data:', error) toast.error('Failed to load balance data') diff --git a/src/activities-app/App.vue b/src/activities-app/App.vue index ac80413..640608b 100644 --- a/src/activities-app/App.vue +++ b/src/activities-app/App.vue @@ -1,8 +1,7 @@