feat: merge a link's extra into the payout payment (v1.2.2-aio.2) #3
5 changed files with 66 additions and 2 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "Withdraw Links",
|
"name": "Withdraw Links",
|
||||||
"short_description": "Make LNURL withdraw links",
|
"short_description": "Make LNURL withdraw links",
|
||||||
"tile": "/withdraw/static/image/lnurl-withdraw.png",
|
"tile": "/withdraw/static/image/lnurl-withdraw.png",
|
||||||
"version": "1.2.2",
|
"version": "1.2.2-aio.2",
|
||||||
"min_lnbits_version": "1.3.0",
|
"min_lnbits_version": "1.3.0",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
1
crud.py
1
crud.py
|
|
@ -32,6 +32,7 @@ async def create_withdraw_link(
|
||||||
webhook_headers=data.webhook_headers,
|
webhook_headers=data.webhook_headers,
|
||||||
webhook_body=data.webhook_body,
|
webhook_body=data.webhook_body,
|
||||||
custom_url=data.custom_url,
|
custom_url=data.custom_url,
|
||||||
|
extra=data.extra,
|
||||||
number=0,
|
number=0,
|
||||||
)
|
)
|
||||||
await db.insert("withdraw.withdraw_link", withdraw_link)
|
await db.insert("withdraw.withdraw_link", withdraw_link)
|
||||||
|
|
|
||||||
44
migrations_fork.py
Normal file
44
migrations_fork.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
"""
|
||||||
|
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"
|
||||||
|
)
|
||||||
10
models.py
10
models.py
|
|
@ -16,6 +16,12 @@ class CreateWithdrawData(BaseModel):
|
||||||
webhook_body: str = Query(None)
|
webhook_body: str = Query(None)
|
||||||
custom_url: str = Query(None)
|
custom_url: str = Query(None)
|
||||||
enabled: bool = Query(True)
|
enabled: bool = Query(True)
|
||||||
|
# Arbitrary JSON merged into the payout payment's `extra` when this 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.
|
||||||
|
extra: dict | None = None
|
||||||
|
|
||||||
|
|
||||||
class WithdrawLink(BaseModel):
|
class WithdrawLink(BaseModel):
|
||||||
|
|
@ -37,6 +43,10 @@ class WithdrawLink(BaseModel):
|
||||||
webhook_headers: str = Query(None)
|
webhook_headers: str = Query(None)
|
||||||
webhook_body: str = Query(None)
|
webhook_body: str = Query(None)
|
||||||
custom_url: str = Query(None)
|
custom_url: str = Query(None)
|
||||||
|
# Persisted as TEXT (JSON); merged into the payout payment's `extra` on
|
||||||
|
# claim. LNbits' db layer (de)serializes dict-typed columns to/from JSON
|
||||||
|
# natively (same as Payment.extra) — no per-field validator needed.
|
||||||
|
extra: dict | None = None
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
enabled: bool = Query(True)
|
enabled: bool = Query(True)
|
||||||
lnurl: str | None = Field(
|
lnurl: str | None = Field(
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,16 @@ async def api_lnurl_callback(
|
||||||
wallet_id=link.wallet,
|
wallet_id=link.wallet,
|
||||||
payment_request=pr,
|
payment_request=pr,
|
||||||
max_sat=link.max_withdrawable,
|
max_sat=link.max_withdrawable,
|
||||||
extra={"tag": "withdraw", "withdrawal_link_id": link.id},
|
# Merge the link's caller-supplied `extra` onto the payout so an
|
||||||
|
# external listener can key on it (e.g. bitSpire cash-in
|
||||||
|
# settlements via spirekeeper). The withdraw extension's own
|
||||||
|
# `tag`/`withdrawal_link_id` are written last so a caller cannot
|
||||||
|
# clobber them.
|
||||||
|
extra={
|
||||||
|
**(link.extra or {}),
|
||||||
|
"tag": "withdraw",
|
||||||
|
"withdrawal_link_id": link.id,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
await increment_withdraw_link(link)
|
await increment_withdraw_link(link)
|
||||||
# If the payment succeeds, delete the record with the unique_hash.
|
# If the payment succeeds, delete the record with the unique_hash.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue