refactor: add status column to apipayments (#2537)
* refactor: add status column to apipayments keep track of the payment status with an enum and persist it as string to db. `pending`, `success`, `failed`. - database migration - remove deleting of payments, failed payments stay
This commit is contained in:
parent
b14d36a0aa
commit
8f761dfd0f
14 changed files with 301 additions and 258 deletions
|
|
@ -19,7 +19,7 @@ from lnbits.core.crud import (
|
|||
get_user,
|
||||
update_payment_status,
|
||||
)
|
||||
from lnbits.core.models import CreateInvoice
|
||||
from lnbits.core.models import CreateInvoice, PaymentState
|
||||
from lnbits.core.services import update_wallet_balance
|
||||
from lnbits.core.views.payment_api import api_payments_create_invoice
|
||||
from lnbits.db import DB_TYPE, SQLITE, Database
|
||||
|
|
@ -199,7 +199,9 @@ async def fake_payments(client, adminkey_headers_from):
|
|||
"/api/v1/payments", headers=adminkey_headers_from, json=invoice.dict()
|
||||
)
|
||||
assert response.is_success
|
||||
await update_payment_status(response.json()["checking_id"], pending=False)
|
||||
await update_payment_status(
|
||||
response.json()["checking_id"], status=PaymentState.SUCCESS
|
||||
)
|
||||
|
||||
params = {"time[ge]": ts, "time[le]": time()}
|
||||
return fake_data, params
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ import pytest
|
|||
|
||||
from lnbits import bolt11
|
||||
from lnbits.core.crud import get_standalone_payment, update_payment_details
|
||||
from lnbits.core.models import CreateInvoice, Payment
|
||||
from lnbits.core.models import CreateInvoice, Payment, PaymentState
|
||||
from lnbits.core.services import fee_reserve_total, get_balance_delta
|
||||
from lnbits.core.views.payment_api import api_payment
|
||||
from lnbits.wallets import get_funding_source
|
||||
|
||||
from ..helpers import is_fake, is_regtest
|
||||
|
|
@ -88,11 +87,12 @@ async def test_create_real_invoice(client, adminkey_headers_from, inkey_headers_
|
|||
return
|
||||
assert found_checking_id
|
||||
|
||||
task = asyncio.create_task(listen())
|
||||
await asyncio.sleep(1)
|
||||
pay_real_invoice(invoice["payment_request"])
|
||||
await asyncio.wait_for(task, timeout=10)
|
||||
async def pay():
|
||||
await asyncio.sleep(3)
|
||||
pay_real_invoice(invoice["payment_request"])
|
||||
|
||||
await asyncio.gather(listen(), pay())
|
||||
await asyncio.sleep(3)
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
)
|
||||
|
|
@ -127,10 +127,11 @@ async def test_pay_real_invoice_set_pending_and_check_state(
|
|||
assert len(invoice["checking_id"]) > 0
|
||||
|
||||
# check the payment status
|
||||
response = await api_payment(
|
||||
invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
)
|
||||
assert response["paid"]
|
||||
payment_status = response.json()
|
||||
assert payment_status["paid"]
|
||||
|
||||
# make sure that the backend also thinks it's paid
|
||||
funding_source = get_funding_source()
|
||||
|
|
@ -140,22 +141,9 @@ async def test_pay_real_invoice_set_pending_and_check_state(
|
|||
# get the outgoing payment from the db
|
||||
payment = await get_standalone_payment(invoice["payment_hash"])
|
||||
assert payment
|
||||
assert payment.success
|
||||
assert payment.pending is False
|
||||
|
||||
# set the outgoing invoice to pending
|
||||
await update_payment_details(payment.checking_id, pending=True)
|
||||
|
||||
payment_pending = await get_standalone_payment(invoice["payment_hash"])
|
||||
assert payment_pending
|
||||
assert payment_pending.pending is True
|
||||
|
||||
# check the outgoing payment status
|
||||
await payment.check_status()
|
||||
|
||||
payment_not_pending = await get_standalone_payment(invoice["payment_hash"])
|
||||
assert payment_not_pending
|
||||
assert payment_not_pending.pending is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skipif(is_fake, reason="this only works in regtest")
|
||||
|
|
@ -229,9 +217,11 @@ async def test_pay_hold_invoice_check_pending_and_fail(
|
|||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# payment should not be in database anymore
|
||||
# payment should be in database as failed
|
||||
payment_db_after_settlement = await get_standalone_payment(invoice_obj.payment_hash)
|
||||
assert payment_db_after_settlement is None
|
||||
assert payment_db_after_settlement
|
||||
assert payment_db_after_settlement.pending is False
|
||||
assert payment_db_after_settlement.failed is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -272,15 +262,10 @@ async def test_pay_hold_invoice_check_pending_and_fail_cancel_payment_task_in_me
|
|||
payment_db_after_settlement = await get_standalone_payment(invoice_obj.payment_hash)
|
||||
assert payment_db_after_settlement is not None
|
||||
|
||||
# status should still be available and be False
|
||||
# payment is failed
|
||||
status = await payment_db.check_status()
|
||||
assert not status.paid
|
||||
|
||||
# now the payment should be gone after the status check
|
||||
# payment_db_after_status_check = await get_standalone_payment(
|
||||
# invoice_obj.payment_hash
|
||||
# )
|
||||
# assert payment_db_after_status_check is None
|
||||
assert status.failed
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -304,10 +289,11 @@ async def test_receive_real_invoice_set_pending_and_check_state(
|
|||
)
|
||||
assert response.status_code < 300
|
||||
invoice = response.json()
|
||||
response = await api_payment(
|
||||
invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
)
|
||||
assert not response["paid"]
|
||||
payment_status = response.json()
|
||||
assert not payment_status["paid"]
|
||||
|
||||
async def listen():
|
||||
found_checking_id = False
|
||||
|
|
@ -317,14 +303,17 @@ async def test_receive_real_invoice_set_pending_and_check_state(
|
|||
return
|
||||
assert found_checking_id
|
||||
|
||||
task = asyncio.create_task(listen())
|
||||
await asyncio.sleep(1)
|
||||
pay_real_invoice(invoice["payment_request"])
|
||||
await asyncio.wait_for(task, timeout=10)
|
||||
response = await api_payment(
|
||||
invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
|
||||
async def pay():
|
||||
await asyncio.sleep(3)
|
||||
pay_real_invoice(invoice["payment_request"])
|
||||
|
||||
await asyncio.gather(listen(), pay())
|
||||
await asyncio.sleep(3)
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
)
|
||||
assert response["paid"]
|
||||
payment_status = response.json()
|
||||
assert payment_status["paid"]
|
||||
|
||||
# get the incoming payment from the db
|
||||
payment = await get_standalone_payment(invoice["payment_hash"], incoming=True)
|
||||
|
|
@ -332,32 +321,15 @@ async def test_receive_real_invoice_set_pending_and_check_state(
|
|||
assert payment.pending is False
|
||||
|
||||
# set the incoming invoice to pending
|
||||
await update_payment_details(payment.checking_id, pending=True)
|
||||
await update_payment_details(payment.checking_id, status=PaymentState.PENDING)
|
||||
|
||||
payment_pending = await get_standalone_payment(
|
||||
invoice["payment_hash"], incoming=True
|
||||
)
|
||||
assert payment_pending
|
||||
assert payment_pending.pending is True
|
||||
|
||||
# check the incoming payment status
|
||||
await payment.check_status()
|
||||
|
||||
payment_not_pending = await get_standalone_payment(
|
||||
invoice["payment_hash"], incoming=True
|
||||
)
|
||||
assert payment_not_pending
|
||||
assert payment_not_pending.pending is False
|
||||
|
||||
# verify we get the same result if we use the checking_id to look up the payment
|
||||
payment_by_checking_id = await get_standalone_payment(
|
||||
payment_not_pending.checking_id, incoming=True
|
||||
)
|
||||
|
||||
assert payment_by_checking_id
|
||||
assert payment_by_checking_id.pending is False
|
||||
assert payment_by_checking_id.bolt11 == payment_not_pending.bolt11
|
||||
assert payment_by_checking_id.payment_hash == payment_not_pending.payment_hash
|
||||
assert payment_pending.success is False
|
||||
assert payment_pending.failed is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue