diff --git a/lnbits/extensions/satspay/README.md b/lnbits/extensions/satspay/README.md deleted file mode 100644 index 4fb249806..000000000 --- a/lnbits/extensions/satspay/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# SatsPay Server - -## Create onchain and LN charges. Includes webhooks! - -Easilly create invoices that support Lightning Network and on-chain BTC payment. - -1. Create a "NEW CHARGE"\ - ![new charge](https://i.imgur.com/fUl6p74.png) -2. Fill out the invoice fields - - set a descprition for the payment - - the amount in sats - - the time, in minutes, the invoice is valid for, after this period the invoice can't be payed - - set a webhook that will get the transaction details after a successful payment - - set to where the user should redirect after payment - - set the text for the button that will show after payment (not setting this, will display "NONE" in the button) - - select if you want onchain payment, LN payment or both - - depending on what you select you'll have to choose the respective wallets where to receive your payment\ - ![charge form](https://i.imgur.com/F10yRiW.png) -3. The charge will appear on the _Charges_ section\ - ![charges](https://i.imgur.com/zqHpVxc.png) -4. Your customer/payee will get the payment page - - they can choose to pay on LN\ - ![offchain payment](https://i.imgur.com/4191SMV.png) - - or pay on chain\ - ![onchain payment](https://i.imgur.com/wzLRR5N.png) -5. You can check the state of your charges in LNbits\ - ![invoice state](https://i.imgur.com/JnBd22p.png) diff --git a/lnbits/extensions/satspay/__init__.py b/lnbits/extensions/satspay/__init__.py deleted file mode 100644 index 8f115a3cf..000000000 --- a/lnbits/extensions/satspay/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -import asyncio - -from fastapi import APIRouter -from fastapi.staticfiles import StaticFiles - -from lnbits.db import Database -from lnbits.helpers import template_renderer -from lnbits.tasks import catch_everything_and_restart - -db = Database("ext_satspay") - - -satspay_ext: APIRouter = APIRouter(prefix="/satspay", tags=["satspay"]) - -satspay_static_files = [ - { - "path": "/satspay/static", - "app": StaticFiles(directory="lnbits/extensions/satspay/static"), - "name": "satspay_static", - } -] - - -def satspay_renderer(): - return template_renderer(["lnbits/extensions/satspay/templates"]) - - -from .tasks import wait_for_paid_invoices -from .views import * # noqa: F401,F403 -from .views_api import * # noqa: F401,F403 - - -def satspay_start(): - loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/satspay/config.json b/lnbits/extensions/satspay/config.json deleted file mode 100644 index 6104d3609..000000000 --- a/lnbits/extensions/satspay/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "SatsPay Server", - "short_description": "Create onchain and LN charges", - "tile": "/satspay/static/image/satspay.png", - "contributors": ["arcbtc"] -} diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py deleted file mode 100644 index c13d0a4b8..000000000 --- a/lnbits/extensions/satspay/crud.py +++ /dev/null @@ -1,181 +0,0 @@ -import json -from typing import List, Optional - -from loguru import logger - -from lnbits.core.services import create_invoice -from lnbits.core.views.api import api_payment -from lnbits.helpers import urlsafe_short_hash - -from ..watchonly.crud import get_config, get_fresh_address # type: ignore -from . import db -from .helpers import fetch_onchain_balance -from .models import Charges, CreateCharge, SatsPayThemes - - -async def create_charge(user: str, data: CreateCharge) -> Charges: - data = CreateCharge(**data.dict()) - charge_id = urlsafe_short_hash() - if data.onchainwallet: - config = await get_config(user) - assert config - data.extra = json.dumps( - {"mempool_endpoint": config.mempool_endpoint, "network": config.network} - ) - onchain = await get_fresh_address(data.onchainwallet) - if not onchain: - raise Exception(f"Wallet '{data.onchainwallet}' can no longer be accessed.") - onchainaddress = onchain.address - else: - onchainaddress = None - if data.lnbitswallet: - payment_hash, payment_request = await create_invoice( - wallet_id=data.lnbitswallet, - amount=data.amount, - memo=charge_id, - extra={"tag": "charge"}, - ) - else: - payment_hash = None - payment_request = None - await db.execute( - """ - INSERT INTO satspay.charges ( - id, - "user", - description, - onchainwallet, - onchainaddress, - lnbitswallet, - payment_request, - payment_hash, - webhook, - completelink, - completelinktext, - time, - amount, - balance, - extra, - custom_css - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - charge_id, - user, - data.description, - data.onchainwallet, - onchainaddress, - data.lnbitswallet, - payment_request, - payment_hash, - data.webhook, - data.completelink, - data.completelinktext, - data.time, - data.amount, - 0, - data.extra, - data.custom_css, - ), - ) - charge = await get_charge(charge_id) - assert charge, "Newly created charge does not exist" - return charge - - -async def update_charge(charge_id: str, **kwargs) -> Optional[Charges]: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - await db.execute( - f"UPDATE satspay.charges SET {q} WHERE id = ?", (*kwargs.values(), charge_id) - ) - row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,)) - return Charges.from_row(row) if row else None - - -async def get_charge(charge_id: str) -> Optional[Charges]: - row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,)) - return Charges.from_row(row) if row else None - - -async def get_charges(user: str) -> List[Charges]: - rows = await db.fetchall( - """SELECT * FROM satspay.charges WHERE "user" = ? ORDER BY "timestamp" DESC """, - (user,), - ) - return [Charges.from_row(row) for row in rows] - - -async def delete_charge(charge_id: str) -> None: - await db.execute("DELETE FROM satspay.charges WHERE id = ?", (charge_id,)) - - -async def check_address_balance(charge_id: str) -> Optional[Charges]: - charge = await get_charge(charge_id) - assert charge - - if not charge.paid: - if charge.onchainaddress: - try: - respAmount = await fetch_onchain_balance(charge) - if respAmount > charge.balance: - await update_charge(charge_id=charge_id, balance=respAmount) - except Exception as e: - logger.warning(e) - if charge.lnbitswallet: - invoice_status = await api_payment(charge.payment_hash) - - if invoice_status["paid"]: - return await update_charge(charge_id=charge_id, balance=charge.amount) - return await get_charge(charge_id) - - -################## SETTINGS ################### - - -async def save_theme(data: SatsPayThemes, css_id: Optional[str]): - # insert or update - if css_id: - await db.execute( - """ - UPDATE satspay.themes SET custom_css = ?, title = ? WHERE css_id = ? - """, - (data.custom_css, data.title, css_id), - ) - else: - css_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO satspay.themes ( - css_id, - title, - "user", - custom_css - ) - VALUES (?, ?, ?, ?) - """, - ( - css_id, - data.title, - data.user, - data.custom_css, - ), - ) - return await get_theme(css_id) - - -async def get_theme(css_id: str) -> Optional[SatsPayThemes]: - row = await db.fetchone("SELECT * FROM satspay.themes WHERE css_id = ?", (css_id,)) - return SatsPayThemes.from_row(row) if row else None - - -async def get_themes(user_id: str) -> List[SatsPayThemes]: - rows = await db.fetchall( - """SELECT * FROM satspay.themes WHERE "user" = ? ORDER BY "title" DESC """, - (user_id,), - ) - return [SatsPayThemes.from_row(row) for row in rows] - - -async def delete_theme(theme_id: str) -> None: - await db.execute("DELETE FROM satspay.themes WHERE css_id = ?", (theme_id,)) diff --git a/lnbits/extensions/satspay/helpers.py b/lnbits/extensions/satspay/helpers.py deleted file mode 100644 index 1967c79d9..000000000 --- a/lnbits/extensions/satspay/helpers.py +++ /dev/null @@ -1,61 +0,0 @@ -import httpx -from loguru import logger - -from .models import Charges - - -def public_charge(charge: Charges): - c = { - "id": charge.id, - "description": charge.description, - "onchainaddress": charge.onchainaddress, - "payment_request": charge.payment_request, - "payment_hash": charge.payment_hash, - "time": charge.time, - "amount": charge.amount, - "balance": charge.balance, - "paid": charge.paid, - "timestamp": charge.timestamp, - "time_elapsed": charge.time_elapsed, - "time_left": charge.time_left, - "custom_css": charge.custom_css, - } - - if charge.paid: - c["completelink"] = charge.completelink - c["completelinktext"] = charge.completelinktext - - return c - - -async def call_webhook(charge: Charges): - async with httpx.AsyncClient() as client: - try: - assert charge.webhook - r = await client.post( - charge.webhook, - json=public_charge(charge), - timeout=40, - ) - return { - "webhook_success": r.is_success, - "webhook_message": r.reason_phrase, - "webhook_response": r.text, - } - except Exception as e: - logger.warning(f"Failed to call webhook for charge {charge.id}") - logger.warning(e) - return {"webhook_success": False, "webhook_message": str(e)} - - -async def fetch_onchain_balance(charge: Charges): - endpoint = ( - f"{charge.config.mempool_endpoint}/testnet" - if charge.config.network == "Testnet" - else charge.config.mempool_endpoint - ) - assert endpoint - assert charge.onchainaddress - async with httpx.AsyncClient() as client: - r = await client.get(endpoint + "/api/address/" + charge.onchainaddress) - return r.json()["chain_stats"]["funded_txo_sum"] diff --git a/lnbits/extensions/satspay/migrations.py b/lnbits/extensions/satspay/migrations.py deleted file mode 100644 index 488754699..000000000 --- a/lnbits/extensions/satspay/migrations.py +++ /dev/null @@ -1,64 +0,0 @@ -async def m001_initial(db): - """ - Initial wallet table. - """ - - await db.execute( - f""" - CREATE TABLE satspay.charges ( - id TEXT NOT NULL PRIMARY KEY, - "user" TEXT, - description TEXT, - onchainwallet TEXT, - onchainaddress TEXT, - lnbitswallet TEXT, - payment_request TEXT, - payment_hash TEXT, - webhook TEXT, - completelink TEXT, - completelinktext TEXT, - time INTEGER, - amount {db.big_int}, - balance {db.big_int} DEFAULT 0, - timestamp TIMESTAMP NOT NULL DEFAULT """ - + db.timestamp_now - + """ - ); - """ - ) - - -async def m002_add_charge_extra_data(db): - """ - Add 'extra' column for storing various config about the charge (JSON format) - """ - await db.execute( - """ALTER TABLE satspay.charges - ADD COLUMN extra TEXT DEFAULT '{"mempool_endpoint": "https://mempool.space", "network": "Mainnet"}'; - """ - ) - - -async def m003_add_themes_table(db): - """ - Themes table - """ - - await db.execute( - """ - CREATE TABLE satspay.themes ( - css_id TEXT NOT NULL PRIMARY KEY, - "user" TEXT, - title TEXT, - custom_css TEXT - ); - """ - ) - - -async def m004_add_custom_css_to_charges(db): - """ - Add custom css option column to the 'charges' table - """ - - await db.execute("ALTER TABLE satspay.charges ADD COLUMN custom_css TEXT;") diff --git a/lnbits/extensions/satspay/models.py b/lnbits/extensions/satspay/models.py deleted file mode 100644 index c9da401a2..000000000 --- a/lnbits/extensions/satspay/models.py +++ /dev/null @@ -1,91 +0,0 @@ -import json -from datetime import datetime, timedelta -from sqlite3 import Row -from typing import Optional - -from fastapi.param_functions import Query -from pydantic import BaseModel - -DEFAULT_MEMPOOL_CONFIG = ( - '{"mempool_endpoint": "https://mempool.space", "network": "Mainnet"}' -) - - -class CreateCharge(BaseModel): - onchainwallet: str = Query(None) - lnbitswallet: str = Query(None) - description: str = Query(...) - webhook: str = Query(None) - completelink: str = Query(None) - completelinktext: str = Query(None) - custom_css: Optional[str] - time: int = Query(..., ge=1) - amount: int = Query(..., ge=1) - extra: str = DEFAULT_MEMPOOL_CONFIG - - -class ChargeConfig(BaseModel): - mempool_endpoint: Optional[str] - network: Optional[str] - webhook_success: Optional[bool] = False - webhook_message: Optional[str] - - -class Charges(BaseModel): - id: str - description: Optional[str] - onchainwallet: Optional[str] - onchainaddress: Optional[str] - lnbitswallet: Optional[str] - payment_request: Optional[str] - payment_hash: Optional[str] - webhook: Optional[str] - completelink: Optional[str] - completelinktext: Optional[str] = "Back to Merchant" - custom_css: Optional[str] - extra: str = DEFAULT_MEMPOOL_CONFIG - time: int - amount: int - balance: int - timestamp: int - - @classmethod - def from_row(cls, row: Row) -> "Charges": - return cls(**dict(row)) - - @property - def time_left(self): - now = datetime.utcnow().timestamp() - start = datetime.fromtimestamp(self.timestamp) - expiration = (start + timedelta(minutes=self.time)).timestamp() - return (expiration - now) / 60 - - @property - def time_elapsed(self): - return self.time_left < 0 - - @property - def paid(self): - if self.balance >= self.amount: - return True - else: - return False - - @property - def config(self) -> ChargeConfig: - charge_config = json.loads(self.extra) - return ChargeConfig(**charge_config) - - def must_call_webhook(self): - return self.webhook and self.paid and self.config.webhook_success is False - - -class SatsPayThemes(BaseModel): - css_id: str = Query(None) - title: str = Query(None) - custom_css: str = Query(None) - user: Optional[str] - - @classmethod - def from_row(cls, row: Row) -> "SatsPayThemes": - return cls(**dict(row)) diff --git a/lnbits/extensions/satspay/static/image/satspay.png b/lnbits/extensions/satspay/static/image/satspay.png deleted file mode 100644 index 827914075..000000000 Binary files a/lnbits/extensions/satspay/static/image/satspay.png and /dev/null differ diff --git a/lnbits/extensions/satspay/static/js/utils.js b/lnbits/extensions/satspay/static/js/utils.js deleted file mode 100644 index 5317673f0..000000000 --- a/lnbits/extensions/satspay/static/js/utils.js +++ /dev/null @@ -1,36 +0,0 @@ -const sleep = ms => new Promise(r => setTimeout(r, ms)) -const retryWithDelay = async function (fn, retryCount = 0) { - try { - await sleep(25) - // Do not return the call directly, use result. - // Otherwise the error will not be cought in this try-catch block. - const result = await fn() - return result - } catch (err) { - if (retryCount > 100) throw err - await sleep((retryCount + 1) * 1000) - return retryWithDelay(fn, retryCount + 1) - } -} - -const mapCharge = (obj, oldObj = {}) => { - const charge = {...oldObj, ...obj} - - charge.progress = obj.time_left < 0 ? 1 : 1 - obj.time_left / obj.time - charge.time = minutesToTime(obj.time) - charge.timeLeft = minutesToTime(obj.time_left) - - charge.displayUrl = ['/satspay/', obj.id].join('') - charge.expanded = oldObj.expanded || false - charge.pendingBalance = oldObj.pendingBalance || 0 - charge.extra = charge.extra ? JSON.parse(charge.extra) : charge.extra - return charge -} - -const mapCSS = (obj, oldObj = {}) => { - const theme = _.clone(obj) - return theme -} - -const minutesToTime = min => - min > 0 ? new Date(min * 1000).toISOString().substring(14, 19) : '' diff --git a/lnbits/extensions/satspay/tasks.py b/lnbits/extensions/satspay/tasks.py deleted file mode 100644 index 2c636351b..000000000 --- a/lnbits/extensions/satspay/tasks.py +++ /dev/null @@ -1,42 +0,0 @@ -import asyncio -import json - -from loguru import logger - -from lnbits.core.models import Payment -from lnbits.helpers import get_current_extension_name -from lnbits.tasks import register_invoice_listener - -from .crud import check_address_balance, get_charge, update_charge -from .helpers import call_webhook - - -async def wait_for_paid_invoices(): - invoice_queue = asyncio.Queue() - register_invoice_listener(invoice_queue, get_current_extension_name()) - - while True: - payment = await invoice_queue.get() - await on_invoice_paid(payment) - - -async def on_invoice_paid(payment: Payment) -> None: - - if payment.extra.get("tag") != "charge": - # not a charge invoice - return - - assert payment.memo - charge = await get_charge(payment.memo) - if not charge: - logger.error("this should never happen", payment) - return - - await payment.set_pending(False) - charge = await check_address_balance(charge_id=charge.id) - assert charge - - if charge.must_call_webhook(): - resp = await call_webhook(charge) - extra = {**charge.config.dict(), **resp} - await update_charge(charge_id=charge.id, extra=json.dumps(extra)) diff --git a/lnbits/extensions/satspay/templates/satspay/_api_docs.html b/lnbits/extensions/satspay/templates/satspay/_api_docs.html deleted file mode 100644 index 2adab8c1f..000000000 --- a/lnbits/extensions/satspay/templates/satspay/_api_docs.html +++ /dev/null @@ -1,29 +0,0 @@ - - -

- SatsPayServer, create Onchain/LN charges.
WARNING: If using with the - WatchOnly extension, we highly reccomend using a fresh extended public Key - specifically for SatsPayServer!
- - Created by, - Ben Arc, - motorina0 -

-
-
- Swagger REST API Documentation -
-
diff --git a/lnbits/extensions/satspay/templates/satspay/display.html b/lnbits/extensions/satspay/templates/satspay/display.html deleted file mode 100644 index 3cde24c39..000000000 --- a/lnbits/extensions/satspay/templates/satspay/display.html +++ /dev/null @@ -1,479 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
-
- -
-
- -
-
-
-
-
- Time elapsed -
-
- Charge paid -
-
- - - - Awaiting payment... - - {% raw %} {{ charge.timeLeft }} {% endraw %} - - - -
-
-
-
-
-
-
Charge Id:
-
- -
-
-
-
Total to pay:
-
- - sat - -
-
-
-
Amount paid:
-
- - - sat -
-
-
-
Amount pending:
-
- - sat - -
-
-
-
Amount due:
-
- - - sat - - - none -
-
-
-
- -
-
-
-
- - - bitcoin lightning payment method not available - - - - pay with lightning - -
-
- - - bitcoin onchain payment method not available - - - - pay onchain - -
-
- -
-
-
- - -
-
-
-
- -
-
- -
-
- -
-
-
-
-
-
- Pay this lightning-network invoice: -
-
- - - - - - -
-
- Copy invoice -
-
-
-
-
-
-
-
- - -
-
- - -
-
-
-
-
-
- -
-
- -
-
- -
-
-
-
-
-
- Send - - - sats to this onchain address -
-
- - - - - - -
-
- Copy address -
-
-
-
-
-
-
-
-
-
-
-{% endblock %} {% block styles %} - - -{% endblock %} {% block scripts %} - - - - -{% endblock %} diff --git a/lnbits/extensions/satspay/templates/satspay/index.html b/lnbits/extensions/satspay/templates/satspay/index.html deleted file mode 100644 index 74b3d2ccc..000000000 --- a/lnbits/extensions/satspay/templates/satspay/index.html +++ /dev/null @@ -1,1011 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - - {% raw %} - New charge - - - New CSS Theme - - New CSS Theme - For security reason, custom css is only available to server - admins. - - - - - -
-
-
Charges
-
- -
- - - -
-
- - - - - Export to CSV - - - - -
-
- - - - - {% endraw %} - -
-
- - - -
-
-
Themes
-
-
- - {% raw %} - - - - {% endraw %} - -
-
-
- -
- - -
- {{SITE_TITLE}} satspay Extension -
-
- - - {% include "satspay/_api_docs.html" %} - -
-
- - - - - - - - - - -
-
-
- -
-
- - - Onchain Wallet (watch-only) extension MUST be activated and - have a wallet - - -
-
-
-
- -
-
-
- -
- -
- - - - -
-
- - - - - - - - - -
-
-
- Create Charge - Cancel -
-
-
-
- - - - - - - -
- Update CSS theme - Save CSS theme - Cancel -
-
-
-
- - - - - -
- Close -
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(user) }} - - - - - - -{% endblock %} diff --git a/lnbits/extensions/satspay/views.py b/lnbits/extensions/satspay/views.py deleted file mode 100644 index 175b00bd4..000000000 --- a/lnbits/extensions/satspay/views.py +++ /dev/null @@ -1,49 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, HTTPException, Request, Response -from fastapi.templating import Jinja2Templates -from starlette.responses import HTMLResponse - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists - -from . import satspay_ext, satspay_renderer -from .crud import get_charge, get_theme -from .helpers import public_charge - -templates = Jinja2Templates(directory="templates") - - -@satspay_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): - return satspay_renderer().TemplateResponse( - "satspay/index.html", - {"request": request, "user": user.dict(), "admin": user.admin}, - ) - - -@satspay_ext.get("/{charge_id}", response_class=HTMLResponse) -async def display_charge(request: Request, charge_id: str): - charge = await get_charge(charge_id) - if not charge: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Charge link does not exist." - ) - - return satspay_renderer().TemplateResponse( - "satspay/display.html", - { - "request": request, - "charge_data": public_charge(charge), - "mempool_endpoint": charge.config.mempool_endpoint, - "network": charge.config.network, - }, - ) - - -@satspay_ext.get("/css/{css_id}") -async def display_css(css_id: str): - theme = await get_theme(css_id) - if theme: - return Response(content=theme.custom_css, media_type="text/css") - return None diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py deleted file mode 100644 index 200773fb8..000000000 --- a/lnbits/extensions/satspay/views_api.py +++ /dev/null @@ -1,180 +0,0 @@ -import json -from http import HTTPStatus - -from fastapi import Depends, HTTPException, Query -from loguru import logger - -from lnbits.decorators import ( - WalletTypeInfo, - check_admin, - get_key_type, - require_admin_key, - require_invoice_key, -) - -from . import satspay_ext -from .crud import ( - check_address_balance, - create_charge, - delete_charge, - delete_theme, - get_charge, - get_charges, - get_theme, - get_themes, - save_theme, - update_charge, -) -from .helpers import call_webhook, public_charge -from .models import CreateCharge, SatsPayThemes - - -@satspay_ext.post("/api/v1/charge") -async def api_charge_create( - data: CreateCharge, wallet: WalletTypeInfo = Depends(require_invoice_key) -): - try: - charge = await create_charge(user=wallet.wallet.user, data=data) - assert charge - return { - **charge.dict(), - **{"time_elapsed": charge.time_elapsed}, - **{"time_left": charge.time_left}, - **{"paid": charge.paid}, - } - except Exception as ex: - logger.debug(f"Satspay error: {str}") - raise HTTPException( - status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex) - ) - - -@satspay_ext.put( - "/api/v1/charge/{charge_id}", dependencies=[Depends(require_admin_key)] -) -async def api_charge_update( - data: CreateCharge, - charge_id: str, -): - charge = await update_charge(charge_id=charge_id, data=data) - assert charge - return charge.dict() - - -@satspay_ext.get("/api/v1/charges") -async def api_charges_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)): - try: - return [ - { - **charge.dict(), - **{"time_elapsed": charge.time_elapsed}, - **{"time_left": charge.time_left}, - **{"paid": charge.paid}, - **{"webhook_message": charge.config.webhook_message}, - } - for charge in await get_charges(wallet.wallet.user) - ] - except: - return "" - - -@satspay_ext.get("/api/v1/charge/{charge_id}", dependencies=[Depends(get_key_type)]) -async def api_charge_retrieve(charge_id: str): - charge = await get_charge(charge_id) - - if not charge: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist." - ) - - return { - **charge.dict(), - **{"time_elapsed": charge.time_elapsed}, - **{"time_left": charge.time_left}, - **{"paid": charge.paid}, - } - - -@satspay_ext.delete("/api/v1/charge/{charge_id}", dependencies=[Depends(get_key_type)]) -async def api_charge_delete(charge_id: str): - charge = await get_charge(charge_id) - - if not charge: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist." - ) - - await delete_charge(charge_id) - return "", HTTPStatus.NO_CONTENT - - -#############################BALANCE########################## - - -@satspay_ext.get("/api/v1/charges/balance/{charge_ids}") -async def api_charges_balance(charge_ids): - charge_id_list = charge_ids.split(",") - charges = [] - for charge_id in charge_id_list: - charge = await api_charge_balance(charge_id) - charges.append(charge) - return charges - - -@satspay_ext.get("/api/v1/charge/balance/{charge_id}") -async def api_charge_balance(charge_id): - charge = await check_address_balance(charge_id) - - if not charge: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist." - ) - - if charge.must_call_webhook(): - resp = await call_webhook(charge) - extra = {**charge.config.dict(), **resp} - await update_charge(charge_id=charge.id, extra=json.dumps(extra)) - - return {**public_charge(charge)} - - -#############################THEMES########################## - - -@satspay_ext.post("/api/v1/themes", dependencies=[Depends(check_admin)]) -@satspay_ext.post("/api/v1/themes/{css_id}", dependencies=[Depends(check_admin)]) -async def api_themes_save( - data: SatsPayThemes, - wallet: WalletTypeInfo = Depends(require_admin_key), - css_id: str = Query(...), -): - - if css_id: - theme = await save_theme(css_id=css_id, data=data) - else: - data.user = wallet.wallet.user - theme = await save_theme(data=data, css_id="no_id") - return theme - - -@satspay_ext.get("/api/v1/themes") -async def api_themes_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)): - try: - return await get_themes(wallet.wallet.user) - except HTTPException: - logger.error("Error loading satspay themes") - logger.error(HTTPException) - return "" - - -@satspay_ext.delete("/api/v1/themes/{theme_id}", dependencies=[Depends(get_key_type)]) -async def api_theme_delete(theme_id): - theme = await get_theme(theme_id) - - if not theme: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Theme does not exist." - ) - - await delete_theme(theme_id) - return "", HTTPStatus.NO_CONTENT