mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 10:34:20 +01:00
Mint: fix Blink multiple failed attempts (#440)
* fix: blink multiple failed attempts * fix line length again
This commit is contained in:
@@ -24,6 +24,8 @@ from .base import (
|
||||
|
||||
# according to https://github.com/GaloyMoney/galoy/blob/7e79cc27304de9b9c2e7d7f4fdd3bac09df23aac/core/api/src/domain/bitcoin/index.ts#L59
|
||||
BLINK_MAX_FEE_PERCENT = 0.5
|
||||
DIRECTION_SEND = "SEND"
|
||||
DIRECTION_RECEIVE = "RECEIVE"
|
||||
|
||||
|
||||
class BlinkWallet(LightningBackend):
|
||||
@@ -35,7 +37,11 @@ class BlinkWallet(LightningBackend):
|
||||
wallet_ids: Dict[Unit, str] = {}
|
||||
endpoint = "https://api.blink.sv/graphql"
|
||||
invoice_statuses = {"PENDING": None, "PAID": True, "EXPIRED": False}
|
||||
payment_execution_statuses = {"SUCCESS": True, "ALREADY_PAID": None}
|
||||
payment_execution_statuses = {
|
||||
"SUCCESS": True,
|
||||
"ALREADY_PAID": None,
|
||||
"FAILURE": False,
|
||||
}
|
||||
payment_statuses = {"SUCCESS": True, "PENDING": None, "FAILURE": False}
|
||||
|
||||
def __init__(self):
|
||||
@@ -78,10 +84,12 @@ class BlinkWallet(LightningBackend):
|
||||
)
|
||||
|
||||
balance = 0
|
||||
for wallet_dict in resp["data"]["me"]["defaultAccount"]["wallets"]:
|
||||
if wallet_dict["walletCurrency"] == "USD":
|
||||
for wallet_dict in (
|
||||
resp.get("data", {}).get("me", {}).get("defaultAccount", {}).get("wallets")
|
||||
):
|
||||
if wallet_dict.get("walletCurrency") == "USD":
|
||||
self.wallet_ids[Unit.usd] = wallet_dict["id"]
|
||||
elif wallet_dict["walletCurrency"] == "BTC":
|
||||
elif wallet_dict.get("walletCurrency") == "BTC":
|
||||
self.wallet_ids[Unit.sat] = wallet_dict["id"]
|
||||
balance = wallet_dict["balance"]
|
||||
|
||||
@@ -137,9 +145,13 @@ class BlinkWallet(LightningBackend):
|
||||
|
||||
resp = r.json()
|
||||
assert resp, "invalid response"
|
||||
payment_request = resp["data"]["lnInvoiceCreateOnBehalfOfRecipient"]["invoice"][
|
||||
"paymentRequest"
|
||||
]
|
||||
payment_request = (
|
||||
resp.get("data", {})
|
||||
.get("lnInvoiceCreateOnBehalfOfRecipient", {})
|
||||
.get("invoice", {})
|
||||
.get("paymentRequest")
|
||||
)
|
||||
assert payment_request, "payment request not found"
|
||||
checking_id = payment_request
|
||||
|
||||
return InvoiceResponse(
|
||||
@@ -178,6 +190,7 @@ class BlinkWallet(LightningBackend):
|
||||
r = await self.client.post(
|
||||
url=self.endpoint,
|
||||
data=json.dumps(data),
|
||||
timeout=None,
|
||||
)
|
||||
r.raise_for_status()
|
||||
except Exception as e:
|
||||
@@ -186,9 +199,14 @@ class BlinkWallet(LightningBackend):
|
||||
|
||||
resp: dict = r.json()
|
||||
paid = self.payment_execution_statuses[
|
||||
resp["data"]["lnInvoicePaymentSend"]["status"]
|
||||
resp.get("data", {}).get("lnInvoicePaymentSend", {}).get("status")
|
||||
]
|
||||
fee = resp["data"]["lnInvoicePaymentSend"]["transaction"]["settlementFee"]
|
||||
fee = (
|
||||
resp.get("data", {})
|
||||
.get("lnInvoicePaymentSend", {})
|
||||
.get("transaction", {})
|
||||
.get("settlementFee")
|
||||
)
|
||||
checking_id = quote.request
|
||||
|
||||
return PaymentResponse(
|
||||
@@ -221,12 +239,15 @@ class BlinkWallet(LightningBackend):
|
||||
logger.error(f"Blink API error: {str(e)}")
|
||||
return PaymentStatus(paid=None)
|
||||
resp: dict = r.json()
|
||||
if resp["data"]["lnInvoicePaymentStatus"]["errors"]:
|
||||
if resp.get("data", {}).get("lnInvoicePaymentStatus", {}).get("errors"):
|
||||
logger.error(
|
||||
"Blink Error", resp["data"]["lnInvoicePaymentStatus"]["errors"]
|
||||
"Blink Error",
|
||||
resp.get("data", {}).get("lnInvoicePaymentStatus", {}).get("errors"),
|
||||
)
|
||||
return PaymentStatus(paid=None)
|
||||
paid = self.invoice_statuses[resp["data"]["lnInvoicePaymentStatus"]["status"]]
|
||||
paid = self.invoice_statuses[
|
||||
resp.get("data", {}).get("lnInvoicePaymentStatus", {}).get("status")
|
||||
]
|
||||
return PaymentStatus(paid=paid)
|
||||
|
||||
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||
@@ -267,19 +288,49 @@ class BlinkWallet(LightningBackend):
|
||||
|
||||
resp: dict = r.json()
|
||||
# no result found
|
||||
if not resp["data"]["me"]["defaultAccount"]["walletById"][
|
||||
"transactionsByPaymentHash"
|
||||
]:
|
||||
if (
|
||||
not resp.get("data", {})
|
||||
.get("me", {})
|
||||
.get("defaultAccount", {})
|
||||
.get("walletById", {})
|
||||
.get("transactionsByPaymentHash")
|
||||
):
|
||||
return PaymentStatus(paid=None)
|
||||
|
||||
paid = self.payment_statuses[
|
||||
resp["data"]["me"]["defaultAccount"]["walletById"][
|
||||
"transactionsByPaymentHash"
|
||||
][0]["status"]
|
||||
]
|
||||
fee = resp["data"]["me"]["defaultAccount"]["walletById"][
|
||||
"transactionsByPaymentHash"
|
||||
][0]["settlementFee"]
|
||||
all_payments_with_this_hash = (
|
||||
resp.get("data", {})
|
||||
.get("me", {})
|
||||
.get("defaultAccount", {})
|
||||
.get("walletById", {})
|
||||
.get("transactionsByPaymentHash")
|
||||
)
|
||||
|
||||
# Blink API edge case: for a failed payment attempt, it returns two payments with the same hash
|
||||
# if there are two payments with the same hash with "direction" == "SEND" and "RECEIVE"
|
||||
# it means that the payment previously failed and we can ignore the attempt and return
|
||||
# PaymentStatus(paid=None)
|
||||
if len(all_payments_with_this_hash) == 2 and all(
|
||||
p["direction"] in [DIRECTION_SEND, DIRECTION_RECEIVE]
|
||||
for p in all_payments_with_this_hash
|
||||
):
|
||||
return PaymentStatus(paid=None)
|
||||
|
||||
# if there is only one payment with the same hash, it means that the payment might have succeeded
|
||||
# we only care about the payment with "direction" == "SEND"
|
||||
payment = next(
|
||||
(
|
||||
p
|
||||
for p in all_payments_with_this_hash
|
||||
if p.get("direction") == DIRECTION_SEND
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not payment:
|
||||
return PaymentStatus(paid=None)
|
||||
|
||||
# we read the status of the payment
|
||||
paid = self.payment_statuses[payment["status"]]
|
||||
fee = payment["settlementFee"]
|
||||
|
||||
return PaymentStatus(
|
||||
paid=paid,
|
||||
@@ -312,22 +363,30 @@ class BlinkWallet(LightningBackend):
|
||||
r = await self.client.post(
|
||||
url=self.endpoint,
|
||||
data=json.dumps(data),
|
||||
timeout=None,
|
||||
)
|
||||
r.raise_for_status()
|
||||
except Exception as e:
|
||||
logger.error(f"Blink API error: {str(e)}")
|
||||
return PaymentResponse(ok=False, error_message=str(e))
|
||||
raise e
|
||||
resp: dict = r.json()
|
||||
|
||||
if resp.get("data", {}).get("lnInvoiceFeeProbe", {}).get("errors"):
|
||||
raise Exception(
|
||||
resp["data"]["lnInvoiceFeeProbe"]["errors"][0].get("message")
|
||||
or "Unknown error"
|
||||
)
|
||||
invoice_obj = decode(bolt11)
|
||||
assert invoice_obj.amount_msat, "invoice has no amount."
|
||||
|
||||
amount_msat = int(invoice_obj.amount_msat)
|
||||
|
||||
fees_response_msat = int(resp["data"]["lnInvoiceFeeProbe"]["amount"]) * 1000
|
||||
fees_response_msat = (
|
||||
int(resp.get("data", {}).get("lnInvoiceFeeProbe", {}).get("amount")) * 1000
|
||||
)
|
||||
# we either take fee_msat_response or the BLINK_MAX_FEE_PERCENT, whichever is higher
|
||||
fees_msat = max(
|
||||
fees_response_msat, math.ceil(amount_msat / 100 * BLINK_MAX_FEE_PERCENT)
|
||||
fees_response_msat,
|
||||
max(math.ceil(amount_msat / 100 * BLINK_MAX_FEE_PERCENT), 1000),
|
||||
)
|
||||
|
||||
fees = Amount(unit=Unit.msat, amount=fees_msat)
|
||||
|
||||
@@ -498,6 +498,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
|
||||
payment_quote = await self.backends[method][unit].get_payment_quote(
|
||||
melt_quote.request
|
||||
)
|
||||
assert payment_quote.checking_id, "quote has no checking id"
|
||||
|
||||
quote = MeltQuote(
|
||||
quote=random_hash(),
|
||||
|
||||
@@ -110,7 +110,11 @@ async def test_blink_get_payment_status():
|
||||
"defaultAccount": {
|
||||
"walletById": {
|
||||
"transactionsByPaymentHash": [
|
||||
{"status": "SUCCESS", "settlementFee": 10}
|
||||
{
|
||||
"status": "SUCCESS",
|
||||
"settlementFee": 10,
|
||||
"direction": "SEND",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user