-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathviews_lnurl.py
156 lines (142 loc) · 5.07 KB
/
views_lnurl.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import base64
from http import HTTPStatus
import bolt11
from fastapi import APIRouter, Query, Request
from lnbits.core.crud import get_wallet
from lnbits.core.services import pay_invoice
from lnbits.lnurl import LnurlErrorResponseHandler
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
from starlette.exceptions import HTTPException
from .crud import (
delete_atm_payment_link,
get_fossa,
get_fossa_payment,
update_fossa_payment,
)
from .helpers import register_atm_payment, xor_decrypt
fossa_lnurl_router = APIRouter(prefix="/api/v1/lnurl")
fossa_lnurl_router.route_class = LnurlErrorResponseHandler
@fossa_lnurl_router.get(
"/{fossa_id}",
status_code=HTTPStatus.OK,
name="fossa.lnurl_params",
)
async def fossa_lnurl_params(
request: Request,
fossa_id: str,
p: str = Query(None),
):
fossa = await get_fossa(fossa_id)
if not fossa:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"fossa {fossa_id} not found on this server",
)
if len(p) % 4 > 0:
p += "=" * (4 - (len(p) % 4))
data = base64.urlsafe_b64decode(p)
try:
_, amount_in_cent = xor_decrypt(fossa.key.encode(), data)
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Invalid payload."
) from exc
price_msat = (
await fiat_amount_as_satoshis(float(amount_in_cent) / 100, fossa.currency)
if fossa.currency != "sat"
else amount_in_cent
)
if price_msat is None:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Price fetch error."
)
fossa_payment, price_msat = await register_atm_payment(fossa, p)
if not fossa_payment:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Payment already claimed."
)
return {
"tag": "withdrawRequest",
"callback": str(
request.url_for("fossa.lnurl_callback", payment_id=fossa_payment.id)
),
"k1": fossa_payment.payload,
"minWithdrawable": price_msat,
"maxWithdrawable": price_msat,
"defaultDescription": f"{fossa.title} ID: {fossa_payment.id}",
}
@fossa_lnurl_router.get(
"/cb/{payment_id}",
status_code=HTTPStatus.OK,
name="fossa.lnurl_callback",
)
async def lnurl_callback(
payment_id: str,
pr: str = Query(None),
k1: str = Query(None),
):
fossa_payment = await get_fossa_payment(payment_id)
if not fossa_payment:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="fossa_payment not found.",
)
if not pr:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="No payment request.",
)
fossa = await get_fossa(fossa_payment.fossa_id)
if not fossa:
await delete_atm_payment_link(payment_id)
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="fossa not found.",
)
if fossa_payment.payload == fossa_payment.payment_hash:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Payment already claimed."
)
if fossa_payment.payment_hash == "pending":
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Pending. If you are unable to withdraw contact vendor",
)
invoice = bolt11.decode(pr)
if not invoice.payment_hash:
await delete_atm_payment_link(payment_id)
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Not valid payment request."
)
wallet = await get_wallet(fossa.wallet)
assert wallet
if wallet.balance_msat < (int(fossa_payment.sats / 1000) + 100):
await delete_atm_payment_link(payment_id)
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Not enough funds."
)
if fossa_payment.payload != k1:
await delete_atm_payment_link(payment_id)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Bad K1")
if fossa_payment.payment_hash != "payment_hash":
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Payment already claimed."
)
try:
fossa_payment.payment_hash = "pending"
fossa_payment = await update_fossa_payment(fossa_payment)
await pay_invoice(
wallet_id=fossa.wallet,
payment_request=pr,
max_sat=int(fossa_payment.sats) + 100,
extra={"tag": "fossa_withdraw"},
)
fossa_payment.payment_hash = fossa_payment.payload
fossa_payment = await update_fossa_payment(fossa_payment)
return {"status": "OK"}
except HTTPException as e:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e)) from e
except Exception as e:
fossa_payment.payment_hash = "payment_hash"
fossa_payment = await update_fossa_payment(fossa_payment)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e)) from e