Commit graph

317 commits

Author SHA1 Message Date
b91e49b642 feat(v2): wire bitSpire invoice listener + settlement landing (P1a)
Replaces the no-op tasks.py stub with a real invoice listener that lands
bitSpire settlements idempotently into dca_settlements.

Architecture: satmachineadmin runs *inside* the LNbits process, so it
plugs into LNbits' canonical extension hook (register_invoice_listener
from lnbits.tasks) instead of going through the Nostr transport layer.
External clients like bitSpire use Nostr; internal extensions consume
the resulting Payment objects directly. One invoice_listener queue per
extension, dispatched by invoice_callback_dispatcher.

Flow:
  bitSpire ATM (Nostr kind-21000)
    → LNbits nostr_transport handler
    → core Payment system (create_invoice + status=SUCCESS on settle)
    → invoice_callback_dispatcher
    → satmachineadmin's invoice_queue
    → _handle_payment filters by wallet_id → active machine
    → bitspire.parse_settlement reads Payment.extra (or back-derives)
    → create_settlement_idempotent (keyed on payment_hash UNIQUE)

The parser (new bitspire.py module) is bitSpire-specific:

- Happy path (post-aiolabs/lamassu-next#44): Payment.extra carries
  {source:"bitspire", net_sats, fee_sats, fee_pct, exchange_rate,
   currency, txid, machine_npub, bills, cassettes}. Read directly,
  zero back-derivation.
- Fallback path (pre-#44): extra is absent. Back-derive the split
  using machine.fallback_commission_pct with the Lamassu-style
  formula (calculations.calculate_commission), mark
  used_fallback_split=true, log a WARNING that namechecks the
  upstream issue so it's findable in logs.

Two-stage commission split (super first, operator remainder) is
computed at land time so the audit row is complete:
  platform_fee_sats = round(commission_sats * super_fee_pct)
  operator_fee_sats = commission_sats - platform_fee_sats

The actual payout (LP DCA legs + super-fee leg + operator-split legs)
happens in a separate settlement-processor task in P2. P1 only LANDS
the settlement with status='pending'.

Smoke-tested both paths against real LNbits 1.4 (nostr-transport venv):
  happy:    266800 gross → 258835 net + 7965 commission
            (2390 super @ 30%, 5575 operator)
  fallback: 266800 gross → 254095 net + 12705 commission @ 5% default

Also adds crud.get_active_machine_by_wallet_id, the lookup that gates
inbound payments to known machine wallets.

Refs: aiolabs/satmachineadmin#9, aiolabs/lamassu-next#44

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:48:44 +02:00
cba327d0f0 fix(v2): use payment_hash as settlement idempotency key
The initial m005 made bitspire_event_id the UNIQUE idempotency key on
dca_settlements, but settlements arriving through LNbits' invoice
listener (the canonical path per nostr-transport-branch architecture)
don't carry a Nostr event id at the Payment level — that's the
underlying transport's concern, not exposed to extensions.

The natural unique key is payment_hash:
- every LN invoice has a globally unique payment_hash
- subscription replays / dispatcher double-fires dedup via UNIQUE
- it's always present on the Payment object the invoice_listener delivers

Reshape the dca_settlements column constraints:
- payment_hash: TEXT NOT NULL UNIQUE (was: NOT NULL + separate index)
- bitspire_event_id: TEXT (was: NOT NULL UNIQUE) — kept nullable for
  a future path where we subscribe to raw kind-21000 Nostr events
  directly, bypassing the Payment system

Also rename the CRUD helper: get_settlement_by_event_id →
get_settlement_by_payment_hash, and update create_settlement_idempotent
to dedup on payment_hash. CreateDcaSettlementData / DcaSettlement
adjust accordingly.

The schema is unshipped (v2-bitspire branch is local only) — fixing
m005 in-place is appropriate. The separate dca_telemetry path for
kind-30078/30079 events already uses (machine_id, beacon_received_at)
semantics, so the UNIQUE-by-Nostr-event-id pattern isn't needed there
either.

Caught during P1a design before subscribing to register_invoice_listener.

Refs: aiolabs/satmachineadmin#9, aiolabs/lamassu-next#44

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:46:08 +02:00
937749f149 feat(v2): operator-scoped CRUD + stub legacy entry points
Replaces v1's super-only single-config CRUD with the v2 operator-scoped data
layer that matches the m005 schema:

- Machines: create/get/get_by_npub/list_for_operator/update/delete
- Clients: scoped per (machine, user). Adds list_for_operator (across an
  operator's fleet) and list_for_user (LP cross-operator view), plus
  get_flow_mode_clients_for_machine for the distribution algorithm.
- Deposits: now carry machine_id and creator_user_id; per-operator listing.
- Settlements: create_settlement_idempotent treats bitspire_event_id as the
  uniqueness key, returning the existing row on replay so subscription
  re-delivery is safe by construction. mark_settlement_status drives the
  pending → processed/partial/refunded/errored lifecycle.
- Commission splits: replace_commission_splits is an atomic per-scope
  replace; the SetCommissionSplitsData model already validates legs sum
  to 1.0 at the boundary. get_effective_commission_splits handles the
  per-machine-override-or-operator-default precedence.
- Payments: leg-typed (dca / super_fee / operator_split / settlement /
  autoforward / refund) with helpers for settlement/client/operator scopes.
- Balance summary: sums confirmed deposits minus completed dca legs.
- Telemetry: upsert_beacon_snapshot uses COALESCE so today's sparse
  kind-30078 payload doesn't clobber post-#43 fields when they start
  arriving. upsert_fleet_snapshot stores raw JSON until lamassu-next#42
  fixes the kind-30079 schema.
- Super config: singleton get/update.

Also stubs three legacy entry points so __init__.py imports cleanly while
the rest of P0/P1 is in flight:

- tasks.py: no-op stubs for wait_for_paid_invoices + hourly_transaction_polling.
  Real Nostr subscription manager lands in P1.
- views_api.py: a single /api/v1/dca/{...} catch-all returns 503 with a
  precise message. v2 endpoints land in P1+.
- views.py: drops the super-only check on the index page (v2 is
  operator-installable); platform-fee config moves to a super-only API in P1.

transaction_processor.py is left untouched but is now orphaned (no one
imports it) — gets a full rewrite in P1.

Refs: plan at ~/.claude/plans/snug-gliding-shamir.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:37:48 +02:00
013e3d5f6b feat(v2): rewrite models.py for v2 schema
Replaces the Lamassu-era data models (LamassuConfig, StoredLamassuTransaction,
single-config CRUD carriers) with the v2 Pydantic surface matching m005:

- Machine / CreateMachineData / UpdateMachineData: per-operator multi-machine
  registry keyed by Nostr npub. Replaces single-row LamassuConfig + SSH fields.
- DcaClient now scoped per (machine_id, user_id). Includes autoforward fields
  for satmachineadmin#8 (best-effort LN-address forwarding for LPs).
- DcaDeposit gains machine_id + creator_user_id (audit trail finding from v1).
- DcaSettlement: idempotency carrier for bitSpire kind-21000 events. Carries
  platform_fee_sats + operator_fee_sats as absolute ints (v1 hook for the v2
  customer-discount engine — see plan).
- CommissionSplitLeg / CommissionSplit / SetCommissionSplitsData: operator's
  remainder-distribution rules. SetCommissionSplitsData validates legs sum
  to 1.0 at the boundary so crud.py only sees valid sets.
- DcaPayment dropped transaction_type in favor of leg_type discriminator
  (dca | super_fee | operator_split | settlement | autoforward | refund).
  Gains settlement_id + machine_id + operator_user_id + destination_*.
- TelemetrySnapshot: sparse beacon + fleet snapshot fields, all nullable so
  we degrade gracefully against today's minimal kind-30078 payload.
- SuperConfig / UpdateSuperConfigData: super-only platform-fee carrier.
- PartialDispenseData (satmachineadmin#3), SettleBalanceData (satmachineadmin#4):
  operator UX action carriers.

Stays on pydantic v1 @validator pattern + Optional[X] hints to match the
rest of the codebase. The UP045 / N805 lint noise is pre-existing tech debt
across the repo, not introduced here.

Refs: plan at ~/.claude/plans/snug-gliding-shamir.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:33:16 +02:00
ae4e241d1c feat(v2): add m005 satmachine_v2 schema for bitSpire + multi-tenant
Breaking redesign. Drops the v1 Lamassu-era tables (lamassu_config,
lamassu_transactions, plus the singular-config dca_clients/deposits/payments)
and creates the v2 schema:

- dca_machines: per-operator multi-machine registry, keyed by Nostr npub.
  Replaces the single-row lamassu_config pattern.
- dca_settlements: bitSpire kind-21000 idempotency. platform_fee_sats and
  operator_fee_sats stored as absolute BIGINT — v1 hook so the v2 customer-
  discount engine can record who-forgave-what without a migration.
- dca_commission_splits: operator-defined remainder rules (per-machine or
  default; sum-to-1.0 invariant enforced at write).
- dca_payments: leg-typed (dca | super_fee | operator_split | settlement |
  autoforward | refund). Drops the old transaction_type field.
- dca_clients: now scoped per (machine_id, user_id) so an LP can hold
  positions across machines/operators on the same instance.
- dca_deposits: gains machine_id + creator_user_id for audit.
- dca_telemetry: sparse kind-30078 / kind-30079 snapshots; post-#43 fields
  nullable until lamassu-next enriches the beacon.
- super_config: singleton row for super_fee_pct + super_fee_wallet_id.

No backwards compatibility — operators on the previous schema must wipe and
re-onboard. Old migrations m001-m004 remain so fresh installs still walk the
versioned path; m005 drops their tables before creating the v2 schema.

Incidental: stripped trailing whitespace in m004 (W291/W293 hygiene).

Refs: plan at ~/.claude/plans/snug-gliding-shamir.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:30:45 +02:00
28241e70c3 feat: add deposit edit and delete for pending deposits
Add PUT /api/v1/dca/deposits/{id} endpoint to update amount, currency,
and notes on pending deposits. Add DELETE endpoint to remove deposits
not yet inserted into the machine. Both endpoints reject confirmed
deposits. Frontend now shows edit/delete buttons only for pending rows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 16:00:04 +02:00
6eb076d5f6 chore: bump version to 0.0.4 2026-01-11 16:10:48 +01:00
545a0284a7 fix: cap DCA allocations when ATM cash exceeds tracked balances
When there's a sync mismatch (more cash in ATM than tracked client
balances), cap each client's allocation to their remaining fiat
balance equivalent in sats. Orphan sats stay in the source wallet.

This prevents over-allocation when deposits haven't been recorded
yet or when there's a timing mismatch between ATM transactions
and balance tracking.

- Detect sync mismatch: total_confirmed_deposits < fiat_amount
- In sync mismatch mode: allocate based on client balance, not tx amount
- Track orphan_sats that couldn't be distributed
- Normal mode unchanged: proportional distribution using calculate_distribution()

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 15:54:48 +01:00
49f3670bac fix: cast amount to float for LNbits create_invoice API
LNbits create_invoice expects amount as float, not int. Added
explicit float() cast to both DCA distribution and commission
payment invoice creation calls.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 15:40:17 +01:00
397fd4b002 feat: add unit tests for DCA calculations with empirical Lamassu data
- Extract pure calculation functions to calculations.py (no lnbits deps)
- transaction_processor.py now imports from calculations.py (DRY)
- Add 22 tests covering commission, distribution, and fiat round-trip
- Include real Lamassu transaction data (8.75%, 5.5% commission rates)
- Test edge cases: discounts (90%, 100%), zero commission, small amounts

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 14:14:18 +01:00
8d94dcc2b7 fix: reset correct loading state after manual transaction processing
The finally block was resetting runningTestTransaction instead of
processingSpecificTransaction, causing the button to stay in loading
state after processing.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 20:57:53 +01:00
ab4e3d6b12 fix: use check_user_exists for LNbits 1.4 compatibility
LNbits 1.4 changed check_super_user to return Account (no wallets)
instead of User (with wallets). This broke the template rendering
because LNbits.map.user() requires the wallets property.

Switch to check_user_exists (returns User with wallets) and manually
check user.super_user for access control. This follows the same
pattern used by LNbits core admin pages.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 12:14:48 +01:00
25be6cff87 Fix LNbits 1.4 compatibility: add null guards for g.user.wallets
LNbits 1.4 changed g.user initialization (PR #3615), moving it from
windowMixin to base.html. This means g.user can be null during initial
Vue template evaluation.

- Use optional chaining g.user?.wallets || [] in template
- Add null guard before accessing this.g.user.wallets in JS

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 11:11:19 +01:00
cd0d958c2c consolidate docs 2025-11-03 22:23:10 +01:00
1b7374fa70 Removes test transaction UI button
Removes the test transaction button from the admin UI.

The test transaction endpoint is still available in the API for development and debugging purposes.
2025-11-03 22:23:10 +01:00
fe38e08d4e Adds manual transaction processing feature
Implements functionality to manually process specific Lamassu transactions by ID, bypassing dispense checks.

This allows administrators to handle transactions that may have failed due to dispense issues or were settled manually outside of the automated process.

The feature includes a new UI dialog for entering the transaction ID and an API endpoint to fetch and process the transaction, crediting wallets and distributing funds according to the DCA configuration.
2025-11-03 22:23:08 +01:00
230beccc37 Improve balance verification in LamassuTransactionProcessor: Added rounding to two decimal places for balance and fiat amounts to ensure precision in comparisons. Enhanced logging for insufficient balance scenarios, improving clarity in transaction processing and error reporting. 2025-11-03 22:23:04 +01:00
077e097fc2 Refactor transaction data handling in LamassuTransactionProcessor: Improved consistency in processing None and empty values during data ingestion. Defaulted numeric fields to 0 and percentage fields to 0.0 for better error handling. Ensured clean extraction of transaction details, enhancing reliability in transaction processing. 2025-11-03 22:23:04 +01:00
4843b43147 FIX: exclude flow_clients remaining_balance values less than 0.01 2025-07-06 02:04:11 +02:00
5d41e0c50e Refine distribution logic in transaction processing: Updated the LamassuTransactionProcessor to only create distributions for clients with positive allocated sats. Enhanced logging to indicate when clients are skipped due to zero amounts, improving clarity and accuracy in transaction reporting. 2025-07-06 01:15:26 +02:00
bca39b91cd Enhance commission memo generation in transaction processing: Added discount handling to the commission memo in LamassuTransactionProcessor, allowing for the calculation of effective commission percentages when discounts are applied. This improves clarity in transaction details by providing a more accurate representation of commissions after discounts. 2025-07-06 00:54:19 +02:00
a864f285e4 Refactor m004_convert_to_gtq_storage migration: Streamlined the conversion of centavo amounts to GTQ by detecting the database type (PostgreSQL or SQLite) and applying appropriate data type changes and updates. This enhances clarity and ensures proper handling of data conversions across relevant tables. 2025-07-06 00:24:49 +02:00
c83ebf43ab 01 Refactor currency handling to store amounts in GTQ: Removed currency conversion utilities, updated models and API endpoints to directly handle GTQ amounts, and modified transaction processing logic for consistency. Enhanced frontend to reflect these changes, ensuring accurate display and submission of GTQ values across the application.
Refactor GTQ storage migration: Moved the conversion logic for centavo amounts to GTQ into a new migration function, m004_convert_to_gtq_storage, ensuring proper data type changes and updates across relevant tables. This enhances clarity and maintains the integrity of the migration process.
2025-07-06 00:13:03 +02:00
aa71321c84 00 Add currency conversion utilities and update models for GTQ handling: Introduced currency conversion functions for GTQ and centavos, updated API and database models to handle GTQ amounts directly, and modified API endpoints to streamline deposit and balance summary responses. Adjusted frontend to reflect changes in currency representation, ensuring consistency across the application. 2025-07-05 23:38:23 +02:00
7b40fcef21 Change order of transaction confirmation sorting: Updated the SQL query in the LamassuTransactionProcessor to sort confirmed transactions in ascending order by confirmation date, improving the retrieval of older transactions. 2025-07-05 18:31:59 +02:00
d701b7c770 Implement final balance verification in distribution process: Added checks to ensure clients have positive balances before finalizing distributions and making payments. Enhanced logging for rejected clients and balance sufficiency, improving transaction reliability and transparency. 2025-07-05 18:31:20 +02:00
f0f38f73bf Update currency handling in frontend and backend: Adjusted amount formatting to display GTQ instead of centavos in the UI. Modified data submission logic to convert GTQ to centavos for backend processing, ensuring consistency in currency representation across the application. 2025-07-05 17:35:11 +02:00
1943da1fc3 Enhance DCA distribution logic: Improved the proportional distribution algorithm to include precise calculations with banker's rounding and handle remainder allocation fairly among clients. Added verification to ensure total distributed amount matches the base allocation, enhancing accuracy and reliability in transaction processing. 2025-07-05 16:57:04 +02:00
e222922a6a Update fiat amount handling in models and transaction processor: Modified models to store fiat amounts in centavos for precision. Adjusted transaction processing logic to calculate and display fiat values in centavos, ensuring consistency across the application. Enhanced comments for clarity on the new storage format. 2025-07-05 16:57:04 +02:00
7af0e47d48 Add max_daily_limit_gtq field to lamassu_config: Updated database schema and models to include max_daily_limit_gtq for configurable client limits. Enhanced API and frontend to support this new field, ensuring proper handling and validation in the user interface. 2025-07-05 16:57:04 +02:00
ae50db10c7 Normalize transaction_time to UTC in transaction processing: Enhanced the LamassuTransactionProcessor to ensure transaction_time is consistently converted to UTC if present, improving accuracy in time handling across transactions. 2025-07-05 16:55:44 +02:00
3e4effb76f Refactor logging and transaction time normalization: Updated the get_client_balance_summary function to use loguru for improved logging clarity. Enhanced transaction processing to normalize transaction_time to UTC, ensuring consistency and accuracy in time handling across transactions. 2025-07-05 16:50:31 +02:00
5988a25983 Refine client balance summary logic: Updated the get_client_balance_summary function to filter payments based on transaction_time for improved temporal accuracy. Enhanced logging to clarify balance calculations with respect to the cutoff time, ensuring better transparency in reported balances. 2025-07-05 16:41:41 +02:00
59e4d53215 Enhance timestamp parsing in transaction processing: Improved handling of PostgreSQL timestamp formats to ensure consistency in UTC. Added error logging for invalid timestamps and implemented a fallback mechanism to use the current UTC time when parsing fails, enhancing robustness in transaction time management. 2025-07-05 16:06:00 +02:00
c5227100b8 Enhance transaction time handling: Updated the transaction processing logic to ensure all timestamps are normalized to UTC for consistency. Added logging to warn about timezone-naive timestamps and to inform about conversions from other timezones. Adjusted the client balance summary logging to include timezone information when as_of_time is used, improving clarity in balance reporting. 2025-07-05 15:49:42 +02:00
7e39bb160a Enhance client balance summary retrieval: Updated the get_client_balance_summary function to accept an optional as_of_time parameter for temporal accuracy in balance calculations. Adjusted SQL queries to filter results based on the specified time, ensuring accurate balance summaries. Added logging for cases where as_of_time is utilized. Updated transaction processing to leverage this new functionality for improved accuracy in client balance calculations. 2025-07-05 15:49:42 +02:00
8871f24cec Refactor DCA API endpoints to use superuser authentication: Updated all relevant DCA-related API endpoints to require check_super_user instead of require_admin_key, enhancing security. Adjusted client-side API calls to remove wallet admin key usage, ensuring session-based superuser authentication is utilized. Updated documentation in CLAUDE.md to reflect these changes. 2025-06-26 13:36:29 +02:00
dfc2dd695c Remove test client creation functionality from admin extension
Client registration will now be handled by the DCA client extension.
The admin extension focuses solely on:
- Reading existing clients
- Managing deposits (pending → confirmed workflow)
- Monitoring DCA activity

Test client creation code preserved in 'feature/test-client-creation' branch.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-26 12:04:55 +02:00
76807663db Update database references from 'satmachineadmin' to 'satoshimachine': Modified all relevant CRUD operations and migration scripts to reflect the new database name, ensuring consistency across the application. 2025-06-26 10:14:49 +02:00
4d3e6a4b37 Add SSH configuration handling in transaction processor: Introduced a temporary SSH config file for secure connections, including strict settings and proper key handling. Enhanced error handling for cleanup of SSH key and config files. Updated connection methods to utilize the new SSH config structure for improved security and maintainability. 2025-06-23 00:20:37 +02:00
3a2f10f949 Add transaction_time field to DCA payments: Updated the database schema to include a new transaction_time column in the dca_payments table, modified the CreateDcaPaymentData model to accept transaction_time, and adjusted the create_dca_payment function to store the original ATM transaction time. Updated transaction processing to capture this new field. 2025-06-22 16:50:27 +02:00
18323dc0c8 move to parent folder 2025-06-22 11:51:45 +02:00
466d2c74e3 Refactor DCA API endpoints to require admin key for access: Updated wallet dependency in multiple DCA-related endpoints to use require_admin_key instead of require_invoice_key, enhancing security and access control. Cleaned up code formatting for improved readability.
Update DCA API calls to use admin key: Changed references from `inkey` to `adminkey` in multiple DCA-related API requests to ensure proper access control and security compliance.
2025-06-22 11:19:18 +02:00
931ae1308f remove helper bash script 2025-06-22 10:59:49 +02:00
628d5b4254 update CLAUDE.md 2025-06-20 22:32:54 +02:00
2569fbdf0d update manifest 2025-06-20 22:24:18 +02:00
6db94179cc Replace [mM]yextension with [sS]at[mM]achine[aA]dmin 2025-06-20 22:16:58 +02:00
896ca5f3ca Refactor DCA Admin migration script: Simplify and consolidate database schema creation for Dollar Cost Averaging administration, including DCA clients, deposits, payments, and Lamassu configuration. Remove legacy migration functions and enhance clarity with updated table structures and comments. 2025-06-20 22:10:16 +02:00
75dd03b15a Refactor MyExtension to DCA Admin: Update extension name and description in config.json, remove legacy MyExtension CRUD operations and related API endpoints, and adjust router tags. Clean up unused files and methods to streamline the codebase for DCA administration functionality.
Refactor DCA Admin page endpoints: Update description, remove unused CRUD operations and API endpoints related to MyExtension, and streamline the code for improved clarity and functionality.

Remove QR Code dialog from MyExtension index.html: streamline the UI by eliminating unused dialog components, enhancing code clarity and maintainability.
2025-06-20 22:00:41 +02:00
edca91579c Update README.md to provide comprehensive documentation for DCA Admin Extension: include detailed overview, features, installation instructions, configuration management, usage guidelines for administrators and end users, technical details, and troubleshooting tips. Enhance clarity and usability for developers integrating with Lamassu ATM transactions.
fix
2025-06-20 02:00:21 +02:00