mirror of
https://github.com/Such-Software/xmrcheckout.git
synced 2026-10-17 05:51:34 +02:00
Fix BTCPay webhook events and delivery
This commit is contained in:
@@ -37,7 +37,9 @@ router = APIRouter()
|
|||||||
BTCPAY_PAYMENT_METHOD = "XMR-CHAIN"
|
BTCPAY_PAYMENT_METHOD = "XMR-CHAIN"
|
||||||
BTCPAY_PAYMENT_METHOD_ALIASES = {BTCPAY_PAYMENT_METHOD, "XMR", "XMR_CHAIN"}
|
BTCPAY_PAYMENT_METHOD_ALIASES = {BTCPAY_PAYMENT_METHOD, "XMR", "XMR_CHAIN"}
|
||||||
BTCPAY_WEBHOOK_EVENTS = {
|
BTCPAY_WEBHOOK_EVENTS = {
|
||||||
|
"InvoiceCreated",
|
||||||
"InvoiceReceivedPayment",
|
"InvoiceReceivedPayment",
|
||||||
|
"InvoicePaidInFull",
|
||||||
"InvoicePaymentSettled",
|
"InvoicePaymentSettled",
|
||||||
"InvoiceProcessing",
|
"InvoiceProcessing",
|
||||||
"InvoiceExpired",
|
"InvoiceExpired",
|
||||||
@@ -323,6 +325,7 @@ def create_invoice(
|
|||||||
except HTTPException:
|
except HTTPException:
|
||||||
pass
|
pass
|
||||||
dispatch_webhooks(db, str(user.id), "invoice.created", invoice)
|
dispatch_webhooks(db, str(user.id), "invoice.created", invoice)
|
||||||
|
dispatch_btcpay_webhooks(db, str(user.id), "InvoiceCreated", invoice)
|
||||||
status_name, additional_status = _btcpay_status(invoice)
|
status_name, additional_status = _btcpay_status(invoice)
|
||||||
amount, currency, _ = _btcpay_amount_currency(invoice)
|
amount, currency, _ = _btcpay_amount_currency(invoice)
|
||||||
expiration_time = _epoch_seconds(invoice.expires_at)
|
expiration_time = _epoch_seconds(invoice.expires_at)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from requests import RequestException
|
from requests import RequestException
|
||||||
@@ -16,6 +17,8 @@ from .security import decrypt_secret
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_REDIRECT_STATUSES = {301, 302, 303, 307, 308}
|
||||||
|
|
||||||
|
|
||||||
def dispatch_btcpay_webhooks(
|
def dispatch_btcpay_webhooks(
|
||||||
db: Session,
|
db: Session,
|
||||||
@@ -51,8 +54,25 @@ def dispatch_btcpay_webhooks(
|
|||||||
headers = {
|
headers = {
|
||||||
"BTCPay-Sig": f"sha256={signature}",
|
"BTCPay-Sig": f"sha256={signature}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "xmrcheckout-btcpay-webhook/1.0",
|
||||||
}
|
}
|
||||||
requests.post(hook.url, data=body, headers=headers, timeout=5)
|
response = _post_with_redirects(
|
||||||
|
hook.url,
|
||||||
|
data=body,
|
||||||
|
headers=headers,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if response is None:
|
||||||
|
continue
|
||||||
|
if response.status_code >= 400:
|
||||||
|
logger.warning(
|
||||||
|
"BTCPay webhook delivered non-success status",
|
||||||
|
extra={
|
||||||
|
"webhook_id": str(hook.id),
|
||||||
|
"event": event_type,
|
||||||
|
"http_status": response.status_code,
|
||||||
|
},
|
||||||
|
)
|
||||||
except RequestException as exc:
|
except RequestException as exc:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"BTCPay webhook delivery failed",
|
"BTCPay webhook delivery failed",
|
||||||
@@ -109,3 +129,29 @@ def _after_expiration(invoice: Invoice) -> bool:
|
|||||||
|
|
||||||
def _sign_payload(body: bytes, secret: str) -> str:
|
def _sign_payload(body: bytes, secret: str) -> str:
|
||||||
return hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
|
return hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def _post_with_redirects(
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
data: bytes,
|
||||||
|
headers: dict[str, str],
|
||||||
|
timeout: int,
|
||||||
|
max_redirects: int = 3,
|
||||||
|
) -> requests.Response | None:
|
||||||
|
current_url = url
|
||||||
|
for _ in range(max_redirects + 1):
|
||||||
|
response = requests.post(
|
||||||
|
current_url,
|
||||||
|
data=data,
|
||||||
|
headers=headers,
|
||||||
|
timeout=timeout,
|
||||||
|
allow_redirects=False,
|
||||||
|
)
|
||||||
|
if response.status_code not in _REDIRECT_STATUSES:
|
||||||
|
return response
|
||||||
|
location = response.headers.get("Location")
|
||||||
|
if not location:
|
||||||
|
return response
|
||||||
|
current_url = urljoin(current_url, location)
|
||||||
|
return None
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ def _reconcile_invoices(service: MoneroWalletService) -> None:
|
|||||||
dispatch_btcpay_webhooks(
|
dispatch_btcpay_webhooks(
|
||||||
db, str(user.id), "InvoiceReceivedPayment", invoice
|
db, str(user.id), "InvoiceReceivedPayment", invoice
|
||||||
)
|
)
|
||||||
|
dispatch_btcpay_webhooks(db, str(user.id), "InvoicePaidInFull", invoice)
|
||||||
dispatch_btcpay_webhooks(
|
dispatch_btcpay_webhooks(
|
||||||
db, str(user.id), "InvoiceProcessing", invoice
|
db, str(user.id), "InvoiceProcessing", invoice
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user