mirror of
https://github.com/Such-Software/xmrcheckout.git
synced 2026-10-17 05:51:34 +02:00
BTCPay: add paid/receipt flow and redirect support
This commit is contained in:
+19
-2
@@ -201,7 +201,13 @@ def startup():
|
||||
connection.execute(
|
||||
text(
|
||||
"ALTER TABLE users "
|
||||
"ADD COLUMN IF NOT EXISTS btcpay_checkout_style VARCHAR DEFAULT 'standard'"
|
||||
"ADD COLUMN IF NOT EXISTS btcpay_checkout_style VARCHAR DEFAULT 'btcpay_classic'"
|
||||
)
|
||||
)
|
||||
connection.execute(
|
||||
text(
|
||||
"ALTER TABLE users "
|
||||
"ALTER COLUMN btcpay_checkout_style SET DEFAULT 'btcpay_classic'"
|
||||
)
|
||||
)
|
||||
connection.execute(
|
||||
@@ -212,10 +218,21 @@ def startup():
|
||||
)
|
||||
connection.execute(
|
||||
text(
|
||||
"UPDATE users SET btcpay_checkout_style = 'standard' "
|
||||
"UPDATE users SET btcpay_checkout_style = 'btcpay_classic' "
|
||||
"WHERE btcpay_checkout_style IS NULL"
|
||||
)
|
||||
)
|
||||
connection.execute(
|
||||
text(
|
||||
"UPDATE users "
|
||||
"SET btcpay_checkout_style = 'btcpay_classic' "
|
||||
"WHERE btcpay_checkout_style = 'standard' "
|
||||
"AND NOT EXISTS ("
|
||||
" SELECT 1 FROM profile_history ph "
|
||||
" WHERE ph.user_id = users.id AND ph.field_name = 'btcpay_checkout_style'"
|
||||
")"
|
||||
)
|
||||
)
|
||||
connection.execute(
|
||||
text(
|
||||
"UPDATE users SET default_confirmation_target = 2 "
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ class User(Base):
|
||||
default_confirmation_target = Column(Integer, nullable=False, server_default="2")
|
||||
default_qr_logo = Column(String, nullable=False, server_default="monero")
|
||||
default_qr_logo_data_url = Column(String, nullable=True)
|
||||
btcpay_checkout_style = Column(String, nullable=False, server_default="standard")
|
||||
btcpay_checkout_style = Column(String, nullable=False, server_default="btcpay_classic")
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
@@ -37,12 +37,24 @@ def ensure_invoice_qr_png(
|
||||
detail="QR storage is not configured",
|
||||
)
|
||||
os.makedirs(storage_dir, exist_ok=True)
|
||||
try:
|
||||
os.chmod(storage_dir, 0o755)
|
||||
except Exception:
|
||||
pass
|
||||
filename = f"{invoice.id}.png"
|
||||
path = os.path.join(storage_dir, filename)
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
os.chmod(path, 0o644)
|
||||
except Exception:
|
||||
pass
|
||||
return path
|
||||
png_bytes = build_invoice_qr_png_bytes(invoice=invoice, settings=settings)
|
||||
_atomic_write(path, png_bytes)
|
||||
try:
|
||||
os.chmod(path, 0o644)
|
||||
except Exception:
|
||||
pass
|
||||
return path
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
import csv
|
||||
import io
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
@@ -239,6 +240,11 @@ def _public_invoice_status_response(
|
||||
response = InvoiceStatusResponse.model_validate(invoice)
|
||||
metadata = invoice.metadata_json or {}
|
||||
update: dict[str, Any] = {}
|
||||
if invoice.total_paid_atomic is not None:
|
||||
total_paid = (Decimal(invoice.total_paid_atomic) / Decimal("1000000000000")).quantize(
|
||||
Decimal("0.000000000001"), rounding=ROUND_DOWN
|
||||
)
|
||||
update["amount_paid_xmr"] = format_xmr_amount(total_paid)
|
||||
btcpay_data = metadata.get("btcpay") if isinstance(metadata, dict) else None
|
||||
if isinstance(btcpay_data, dict):
|
||||
amount = btcpay_data.get("amount")
|
||||
@@ -250,8 +256,30 @@ def _public_invoice_status_response(
|
||||
user = db.query(User).filter(User.id == invoice.user_id).first()
|
||||
if user and user.btcpay_checkout_style:
|
||||
update["btcpay_checkout_style"] = user.btcpay_checkout_style
|
||||
checkout = btcpay_data.get("checkout")
|
||||
if isinstance(checkout, dict):
|
||||
redirect_url = checkout.get("redirectURL")
|
||||
if isinstance(redirect_url, str) and redirect_url.strip():
|
||||
update["btcpay_redirect_url"] = redirect_url.strip()
|
||||
redirect_auto = checkout.get("redirectAutomatically")
|
||||
if isinstance(redirect_auto, bool):
|
||||
update["btcpay_redirect_automatically"] = redirect_auto
|
||||
if isinstance(metadata, dict) and isinstance(metadata.get("quote"), dict):
|
||||
update["quote"] = metadata.get("quote")
|
||||
if isinstance(metadata, dict) and isinstance(metadata.get("posData"), str):
|
||||
try:
|
||||
pos_data = json.loads(metadata.get("posData", ""))
|
||||
except (TypeError, ValueError, json.JSONDecodeError):
|
||||
pos_data = None
|
||||
if isinstance(pos_data, dict):
|
||||
woo = pos_data.get("WooCommerce")
|
||||
if isinstance(woo, dict):
|
||||
order_id = woo.get("Order ID")
|
||||
order_number = woo.get("Order Number")
|
||||
if order_id is not None:
|
||||
update["btcpay_order_id"] = str(order_id)
|
||||
if order_number is not None:
|
||||
update["btcpay_order_number"] = str(order_number)
|
||||
qr = metadata.get("qr") if isinstance(metadata, dict) else None
|
||||
if isinstance(qr, dict):
|
||||
logo = qr.get("logo")
|
||||
|
||||
+6
-1
@@ -126,6 +126,7 @@ class InvoiceStatusResponse(BaseModel):
|
||||
address: str
|
||||
subaddress_index: int | None = None
|
||||
amount_xmr: Decimal
|
||||
amount_paid_xmr: str | None = None
|
||||
status: InvoiceStatus
|
||||
confirmation_target: int
|
||||
confirmations: int = 0
|
||||
@@ -140,6 +141,10 @@ class InvoiceStatusResponse(BaseModel):
|
||||
btcpay_amount: str | None = None
|
||||
btcpay_currency: str | None = None
|
||||
btcpay_checkout_style: BtcpayCheckoutStyle | None = None
|
||||
btcpay_redirect_url: str | None = None
|
||||
btcpay_redirect_automatically: bool | None = None
|
||||
btcpay_order_id: str | None = None
|
||||
btcpay_order_number: str | None = None
|
||||
quote: dict[str, Any] | None = None
|
||||
qr_url: str | None = None
|
||||
|
||||
@@ -156,7 +161,7 @@ class ProfileResponse(BaseModel):
|
||||
default_confirmation_target: conint(ge=0, le=10) = 2
|
||||
default_qr_logo: QrLogoMode = "monero"
|
||||
default_qr_logo_data_url: str | None = None
|
||||
btcpay_checkout_style: BtcpayCheckoutStyle = "standard"
|
||||
btcpay_checkout_style: BtcpayCheckoutStyle = "btcpay_classic"
|
||||
created_at: datetime
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user