diff --git a/lnbits/extensions/satspay/README.md b/lnbits/extensions/satspay/README.md deleted file mode 100644 index 5f7511caf..000000000 --- a/lnbits/extensions/satspay/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# SatsPay Server - -Create onchain and LN charges. Includes webhooks! - diff --git a/lnbits/extensions/satspay/__init__.py b/lnbits/extensions/satspay/__init__.py deleted file mode 100644 index 7023f7a9d..000000000 --- a/lnbits/extensions/satspay/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from quart import Blueprint -from lnbits.db import Database - -db = Database("ext_satspay") - - -satspay_ext: Blueprint = Blueprint("satspay", __name__, static_folder="static", template_folder="templates") - - -from .views_api import * # noqa -from .views import * # noqa diff --git a/lnbits/extensions/satspay/config.json b/lnbits/extensions/satspay/config.json deleted file mode 100644 index beb0071cb..000000000 --- a/lnbits/extensions/satspay/config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "SatsPay Server", - "short_description": "Create onchain and LN charges", - "icon": "payment", - "contributors": [ - "arcbtc" - ] -} diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py deleted file mode 100644 index 5779592a8..000000000 --- a/lnbits/extensions/satspay/crud.py +++ /dev/null @@ -1,96 +0,0 @@ -from typing import List, Optional, Union - -#from lnbits.db import open_ext_db -from . import db -from .models import Charges - -from lnbits.helpers import urlsafe_short_hash - -from quart import jsonify -import httpx -from lnbits.core.services import create_invoice, check_invoice_status -from ..watchonly.crud import get_watch_wallet, get_derive_address, get_mempool - -import time - -###############CHARGES########################## - - -async def create_charge(user: str, description: Optional[str] = None, onchainwallet: Optional[str] = None, lnbitswallet: Optional[str] = None, webhook: Optional[str] = None, time: Optional[int] = None, amount: Optional[int] = None) -> Charges: - charge_id = urlsafe_short_hash() - if onchainwallet: - wallet = await get_watch_wallet(onchainwallet) - onchainaddress = await get_derive_address(onchainwallet, wallet[4] + 1) - else: - onchainaddress = None - if lnbitswallet: - payment_hash, payment_request = await create_invoice( - wallet_id=lnbitswallet, - amount=amount, - memo=charge_id) - else: - payment_hash = None - payment_request = None - await db.execute( - """ - INSERT INTO charges ( - id, - user, - description, - onchainwallet, - onchainaddress, - lnbitswallet, - payment_request, - payment_hash, - webhook, - time, - amount, - balance, - paid - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - (charge_id, user, description, onchainwallet, onchainaddress, lnbitswallet, payment_request, payment_hash, webhook, time, amount, 0, False), - ) - return await get_charge(charge_id) - -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 charges SET {q} WHERE id = ?", (*kwargs.values(), wallet_id)) - row = await db.fetchone("SELECT * FROM charges WHERE id = ?", (wallet_id,)) - return Charges.from_row(row) if row else None - - -async def get_charge(charge_id: str) -> Charges: - row = await db.fetchone("SELECT * FROM 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 charges WHERE user = ?", (user,)) - return [Charges.from_row(row) for row in rows] - - -async def delete_charge(charge_id: str) -> None: - await db.execute("DELETE FROM charges WHERE id = ?", (charge_id,)) - -async def check_address_balance(charge_id: str) -> List[Charges]: - charge = await get_charge(charge_id) - if charge.onchainaddress: - mempool = await get_mempool(charge.user) - try: - async with httpx.AsyncClient() as client: - r = await client.get(mempool.endpoint + "/api/address/" + charge.onchainaddress) - respAmount = r.json()['chain_stats']['funded_txo_sum'] - if (charge.balance + respAmount) >= charge.balance: - return await update_charge(charge_id = charge_id, balance = (charge.balance + respAmount), paid = True) - else: - return await update_charge(charge_id = charge_id, balance = (charge.balance + respAmount), paid = False) - except Exception: - pass - if charge.lnbitswallet: - invoice_status = await check_invoice_status(charge.lnbitswallet, charge.payment_hash) - if invoice_status.paid: - return await update_charge(charge_id = charge_id, balance = charge.balance, paid = True) - row = await db.fetchone("SELECT * FROM charges WHERE id = ?", (charge_id,)) - return Charges.from_row(row) if row else None diff --git a/lnbits/extensions/satspay/migrations.py b/lnbits/extensions/satspay/migrations.py deleted file mode 100644 index 044eed13b..000000000 --- a/lnbits/extensions/satspay/migrations.py +++ /dev/null @@ -1,26 +0,0 @@ -async def m001_initial(db): - """ - Initial wallet table. - """ - - - await db.execute( - """ - CREATE TABLE IF NOT EXISTS 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, - time INTEGER, - amount INTEGER, - balance INTEGER DEFAULT 0, - paid BOOLEAN, - timestamp TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now')) - ); - """ - ) \ No newline at end of file diff --git a/lnbits/extensions/satspay/models.py b/lnbits/extensions/satspay/models.py deleted file mode 100644 index 013dc1d7f..000000000 --- a/lnbits/extensions/satspay/models.py +++ /dev/null @@ -1,22 +0,0 @@ -from sqlite3 import Row -from typing import NamedTuple - -class Charges(NamedTuple): - id: str - user: str - description: str - onchainwallet: str - onchainaddress: str - lnbitswallet: str - payment_request: str - payment_hash: str - webhook: str - time: str - amount: int - balance: int - paid: bool - timestamp: int - - @classmethod - def from_row(cls, row: Row) -> "Payments": - return cls(**dict(row)) \ No newline at end of file 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 b4bc201ea..000000000 --- a/lnbits/extensions/satspay/templates/satspay/_api_docs.html +++ /dev/null @@ -1,156 +0,0 @@ - - -

SatsPay: Create Onchain/LN charges. Includes webhooks!
- - Created by, Ben Arc -

-
- - - - - - - - GET /pay/api/v1/links -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- [<pay_link_object>, ...] -
Curl example
- curl -X GET {{ request.url_root }}pay/api/v1/links -H "X-Api-Key: {{ - g.user.wallets[0].inkey }}" - -
-
-
- - - - GET - /pay/api/v1/links/<pay_id> -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 201 CREATED (application/json) -
- {"lnurl": <string>} -
Curl example
- curl -X GET {{ request.url_root }}pay/api/v1/links/<pay_id> -H - "X-Api-Key: {{ g.user.wallets[0].inkey }}" - -
-
-
- - - - POST /pay/api/v1/links -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
- {"description": <string> "amount": <integer>} -
- Returns 201 CREATED (application/json) -
- { - "deliveryId": <string>, - "description": <string>, - "webhookId": <string>, - "originalDeliveryId": <string>, - "isRedelivery": <boolean>, - "type": <string>, - "timestamp": <int>, - "paytime": <int>, - "storeId": <string>, - "invoiceId": <string>, - "manuallyMarked": <boolean>, - "overPaid": <boolean>, - "afterExpiration": <boolean>, - "partiallyPaid": <boolean> - }
"type" can be InvoiceReceivedPayment, InvoicePaidInFull, InvoiceExpired, InvoiceConfirmed, and InvoiceInvalid
-
Curl example
- curl -X POST {{ request.url_root }}pay/api/v1/links -d - '{"description": <string>, "amount": <integer>}' -H - "Content-type: application/json" -H "X-Api-Key: {{ - g.user.wallets[0].adminkey }}" - -
-
-
- - - - PUT - /pay/api/v1/links/<pay_id> -
Headers
- {"X-Api-Key": <admin_key>}
-
Body (application/json)
- {"description": <string>, "amount": <integer>} -
- Returns 200 OK (application/json) -
- {"lnurl": <string>} -
Curl example
- curl -X PUT {{ request.url_root }}pay/api/v1/links/<pay_id> -d - '{"description": <string>, "amount": <integer>}' -H - "Content-type: application/json" -H "X-Api-Key: {{ - g.user.wallets[0].adminkey }}" - -
-
-
- - - - DELETE - /pay/api/v1/links/<pay_id> -
Headers
- {"X-Api-Key": <admin_key>}
-
Returns 204 NO CONTENT
- -
Curl example
- curl -X DELETE {{ request.url_root }}pay/api/v1/links/<pay_id> - -H "X-Api-Key: {{ g.user.wallets[0].adminkey }}" - -
-
-
-
diff --git a/lnbits/extensions/satspay/templates/satspay/display.html b/lnbits/extensions/satspay/templates/satspay/display.html deleted file mode 100644 index c0773e33e..000000000 --- a/lnbits/extensions/satspay/templates/satspay/display.html +++ /dev/null @@ -1,74 +0,0 @@ -{% extends "public.html" %} {% block page %} - -
- - -
Our Changing Planet
-
by John Doe
-
- - - - - - - - - - - -
- Copy address -
-
- - - -
- Copy address -
-
-
-
-
- - - - - - -{% 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 a4a2a4318..000000000 --- a/lnbits/extensions/satspay/templates/satspay/index.html +++ /dev/null @@ -1,643 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - -{% raw %} - New charge - - - - - -
-
-
Charges
-
-
- - - - -
-
- - - - - - {% endraw %} - - - - - -
-
- - - - -
- - - -
- - -
- LNbits satspay Extension -
-
- - - - {% include "satspay/_api_docs.html" %} - - -
-
- - - - - - - - - - - - -
-
-
- -
-
- - - Watch-Only extension MUST be activated and have a wallet - - - -
-
-
- -
- -
-
-
- - - -
- - - -
-
- - -
-
- Update Paylink - Create Paylink - Cancel -
-
-
-
- - -
-{% 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 1be0d2669..000000000 --- a/lnbits/extensions/satspay/views.py +++ /dev/null @@ -1,21 +0,0 @@ -from quart import g, abort, render_template -from http import HTTPStatus - -from lnbits.decorators import check_user_exists, validate_uuids - -from . import satspay_ext -from .crud import get_charge - - -@satspay_ext.route("/") -@validate_uuids(["usr"], required=True) -@check_user_exists() -async def index(): - return await render_template("satspay/index.html", user=g.user) - - -@satspay_ext.route("/") -async def display(charge_id): - charge = get_charge(charge_id) or abort(HTTPStatus.NOT_FOUND, "Charge link does not exist.") - - return await render_template("satspay/display.html", charge=charge) \ No newline at end of file diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py deleted file mode 100644 index b99ab6322..000000000 --- a/lnbits/extensions/satspay/views_api.py +++ /dev/null @@ -1,112 +0,0 @@ -import hashlib -from quart import g, jsonify, url_for -from http import HTTPStatus -import httpx - - -from lnbits.core.crud import get_user -from lnbits.decorators import api_check_wallet_key, api_validate_post_request - -from lnbits.extensions.satspay import satspay_ext -from .crud import ( - create_charge, - get_charge, - get_charges, - delete_charge, - check_address_balance, -) - -#############################CHARGES########################## -@satspay_ext.route("/api/v1/charges/balance/", methods=["GET"]) -@api_check_wallet_key("invoice") -async def api_charges_balance(charge_id): - - charge = await check_address_balance(charge_id) - if not charge: - return ( - jsonify(""), - HTTPStatus.OK - ) - else: - return jsonify(charge._asdict()), HTTPStatus.OK - -@satspay_ext.route("/api/v1/charges", methods=["GET"]) -@api_check_wallet_key("invoice") -async def api_charges_retrieve(): - - charges = await get_charges(g.wallet.user) - if not charges: - return ( - jsonify(""), - HTTPStatus.OK - ) - else: - return jsonify([charge._asdict() for charge in charges]), HTTPStatus.OK - - -@satspay_ext.route("/api/v1/charge/", methods=["GET"]) -@api_check_wallet_key("invoice") -async def api_charge_retrieve(charge_id): - charge = get_charge(charge_id) - - if not charge: - return jsonify({"message": "charge does not exist"}), HTTPStatus.NOT_FOUND - - return jsonify({charge}), HTTPStatus.OK - - -@satspay_ext.route("/api/v1/charge", methods=["POST"]) -@satspay_ext.route("/api/v1/charge/", methods=["PUT"]) -@api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "onchainwallet": {"type": "string"}, - "lnbitswallet": {"type": "string"}, - "description": {"type": "string", "empty": False, "required": True}, - "webhook": {"type": "string", "empty": False, "required": True}, - "time": {"type": "integer", "min": 1, "required": True}, - "amount": {"type": "integer", "min": 1, "required": True}, - } -) -async def api_charge_create_or_update(charge_id=None): - - if not charge_id: - charge = await create_charge(user = g.wallet.user, **g.data) - return jsonify(charge), HTTPStatus.CREATED - else: - charge = await update_charge(user = g.wallet.user, **g.data) - return jsonify(charge), HTTPStatus.OK - - -@satspay_ext.route("/api/v1/charge/", methods=["DELETE"]) -@api_check_wallet_key("invoice") -async def api_charge_delete(charge_id): - charge = await get_watch_wallet(charge_id) - - if not charge: - return jsonify({"message": "Wallet link does not exist."}), HTTPStatus.NOT_FOUND - - await delete_watch_wallet(charge_id) - - return "", HTTPStatus.NO_CONTENT - -#############################MEMPOOL########################## - -@satspay_ext.route("/api/v1/mempool", methods=["PUT"]) -@api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "endpoint": {"type": "string", "empty": False, "required": True}, - } -) -async def api_update_mempool(): - mempool = await update_mempool(user=g.wallet.user, **g.data) - return jsonify(mempool._asdict()), HTTPStatus.OK - -@satspay_ext.route("/api/v1/mempool", methods=["GET"]) -@api_check_wallet_key("invoice") -async def api_get_mempool(): - mempool = await get_mempool(g.wallet.user) - if not mempool: - mempool = await create_mempool(user=g.wallet.user) - return jsonify(mempool._asdict()), HTTPStatus.OK \ No newline at end of file