Adds an optional `extra` (JSON) field to a withdraw link. When the link
is claimed, that `extra` is merged onto the payout payment's `extra`, so
a caller can tag the resulting payment with metadata an external listener
keys on — the link is the only place to attach it (the customer-facing
LNURL-withdraw payout otherwise carries just `{tag, withdrawal_link_id}`).
Motivating use: bitSpire cash-in settlements. The operator's spirekeeper
listener fires a `cash_in` settlement (fee split to the platform) only on
an outbound payment stamped `source=bitspire`; before this there was no
way to stamp an LNURL-withdraw payout, so cash-ins never settled. bitSpire
now creates the cash-in link for the NET amount with
`extra={source, type:cash_in, principal_sats, fee_sats, ...}` and the
settlement fires on claim.
- models: `extra: dict | None` on CreateWithdrawData + WithdrawLink.
LNbits' db layer (de)serializes dict columns to/from JSON natively
(same as Payment.extra) — no per-field validator needed.
- migrations_fork.py: `withdraw_link.extra TEXT` under `withdraw_fork`,
keeping the upstream-tracked migrations.py byte-identical for clean
rebases (aiolabs/lnbits#8 pattern).
- views_lnurl: `extra={**(link.extra or {}), "tag": ..., "withdrawal_link_id": ...}`
— the withdraw extension's own keys are written last so a caller cannot
clobber them.
Verified end-to-end on the dev stack: a stamped link's payout carries the
merged extra and drives a spirekeeper cash_in settlement + super-fee payout.
44 lines
1.8 KiB
Python
44 lines
1.8 KiB
Python
"""
|
|
Fork-specific database migrations for the aiolabs withdraw extension.
|
|
|
|
These migrations are tracked separately under `withdraw_fork` in the
|
|
`dbversions` table (loaded by `lnbits/core/helpers.py:migrate_extension_database`),
|
|
so they do not collide with upstream's `m{NNN}_*` numbering in
|
|
`migrations.py`. Keeping the upstream-tracked file untouched means
|
|
`git pull upstream` stays rebase-clean for schema changes.
|
|
|
|
Conventions:
|
|
- Sequential numbering starting from m001.
|
|
- Each migration is `async def m{NNN}_<description>(db)`.
|
|
- DDL must be idempotent: a fresh install runs every migration; an
|
|
install that already carries the column must not crash. Use
|
|
`_alter_add_column_safe` so re-runs are no-ops.
|
|
"""
|
|
|
|
|
|
async def _alter_add_column_safe(db, sql: str) -> None:
|
|
"""ALTER TABLE ADD COLUMN that swallows duplicate-column errors, so a
|
|
re-run on a DB that already has the column is a silent no-op."""
|
|
try:
|
|
await db.execute(sql)
|
|
except Exception as exc:
|
|
msg = str(exc).lower()
|
|
if "duplicate column" in msg or "already exists" in msg:
|
|
return
|
|
raise
|
|
|
|
|
|
async def m001_aio_withdraw_schema(db):
|
|
"""
|
|
Apply every aiolabs schema delta on top of upstream withdraw.
|
|
|
|
`withdraw_link.extra` — arbitrary JSON merged into the payout payment's
|
|
`extra` when the link is claimed (see views_lnurl). Lets a caller tag the
|
|
resulting payment with settlement/attribution metadata an external listener
|
|
can key on — e.g. bitSpire stamps {source, type, principal_sats, fee_sats,
|
|
...} so the spirekeeper cash-in settlement fires off an LNURL-withdraw
|
|
payout. Stored as TEXT; (de)serialized to a dict by the WithdrawLink model.
|
|
"""
|
|
await _alter_add_column_safe(
|
|
db, "ALTER TABLE withdraw.withdraw_link ADD COLUMN extra TEXT"
|
|
)
|