feat: Initial code drop
All checks were successful
Build Docker / Build Image (push) Successful in 35s

This commit is contained in:
Nathan Woodburn 2024-02-14 22:39:37 +11:00
parent 0dc3be9b4c
commit 607e11f8ec
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
37 changed files with 1261 additions and 0 deletions

View File

@ -0,0 +1,41 @@
name: Build Docker
run-name: Build Docker Images
on:
push:
jobs:
Build Image:
runs-on: [ubuntu-latest, amd]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Docker
run : |
apt-get install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce-cli -y
- name: Build Docker image
run : |
echo "${{ secrets.DOCKERGIT_TOKEN }}" | docker login git.woodburn.au -u nathanwoodburn --password-stdin
echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
tag=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}
tag=${tag//\//-}
tag_num=${GITHUB_RUN_NUMBER}
echo "tag_num=$tag_num"
if [[ "$tag" == "main" ]]; then
tag="latest"
else
tag_num="${tag}-${tag_num}"
fi
docker build -t hns-pay:$tag_num .
docker tag hns-pay:$tag_num git.woodburn.au/nathanwoodburn/hns-pay:$tag_num
docker push git.woodburn.au/nathanwoodburn/hns-pay:$tag_num
docker tag hns-pay:$tag_num git.woodburn.au/nathanwoodburn/hns-pay:$tag
docker push git.woodburn.au/nathanwoodburn/hns-pay:$tag

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.env
__pycache__/
data/

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM --platform=$BUILDPLATFORM python:3.10-alpine AS builder
WORKDIR /app
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r requirements.txt
COPY . /app
# Add mount point for data volume
VOLUME /app/data
ENTRYPOINT ["python3"]
CMD ["server.py"]
FROM builder as dev-envs

162
accounts.py Normal file
View File

@ -0,0 +1,162 @@
import json
import os
import requests
import bcrypt
import random
if not os.path.exists('data/accounts.json'):
with open('data/accounts.json', 'w') as f:
json.dump({}, f, indent=4)
cookies = {}
def createAccount(email,password):
# Create an account
if getAccount(email):
return False
accounts = getAccounts()
accounts[email] = {
"email": email,
"password": hashPassword(password)
}
with open('data/accounts.json', 'w') as f:
json.dump(accounts, f, indent=4)
return True
def getAccountFromLink(link):
# Get an account from a link
with open('data/accounts.json', 'r') as f:
accounts = json.load(f)
for email in accounts:
if 'link' in accounts[email] and accounts[email]['link'] == link:
return email
return False
def getAccount(email):
# Get an account
with open('data/accounts.json', 'r') as f:
accounts = json.load(f)
if email in accounts:
return accounts[email]
else:
return False
def getAccountAddress(email):
# Get an account address
account = getAccount(email)
if not account:
return False
if 'address' in account:
return account['address']
else:
return ''
def getAccountLink(email):
# Get an account link
account = getAccount(email)
if not account:
return False
if 'link' in account:
return account['link']
else:
return ''
def getAccountDisplayName(email):
# Get an account display name
account = getAccount(email)
if not account:
return False
if 'displayName' in account:
return account['displayName']
else:
return ''
def getAccountWebhook(email):
# Get an account webhook
account = getAccount(email)
if not account:
return False
if 'webhook' in account:
return account['webhook']
else:
return ''
def setAccountAddress(email,address):
# Set an account address
accounts = getAccounts()
accounts[email]['address'] = address
with open('data/accounts.json', 'w') as f:
json.dump(accounts, f, indent=4)
return True
def setAccountLink(email,link):
# Set an account link
accounts = getAccounts()
accounts[email]['link'] = link
with open('data/accounts.json', 'w') as f:
json.dump(accounts, f, indent=4)
return True
def setAccountDisplayName(email,displayName):
# Set an account display name
accounts = getAccounts()
accounts[email]['displayName'] = displayName
with open('data/accounts.json', 'w') as f:
json.dump(accounts, f, indent=4)
return True
def setAccountWebhook(email,webhook):
# Set an account webhook
accounts = getAccounts()
accounts[email]['webhook'] = webhook
with open('data/accounts.json', 'w') as f:
json.dump(accounts, f, indent=4)
return True
def login(email,password):
# Login
account = getAccount(email)
if not account:
return False
if checkPassword(email, password):
return True
else:
return False
def getAccounts():
# Get all accounts
with open('data/accounts.json', 'r') as f:
accounts = json.load(f)
return accounts
def createCookie(email):
global cookies
# Create a cookie
cookie = str(random.randint(0,2**64))
cookies[cookie] = email
return cookie
def getCookie(cookie):
# Get a cookie
if cookie in cookies:
return cookies[cookie]
else:
return False
def hashPassword(password):
# Hash a password
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
def checkPassword(email,password):
# Check a password
account = getAccount(email)
if not account:
return False
if bcrypt.checkpw(password.encode('utf-8'), account['password'].encode('utf-8')):
return True
else:
return False

5
example.env Normal file
View File

@ -0,0 +1,5 @@
HSD_IP=127.0.0.1
HSD_API_KEY=superlongkey
WALLET=primary
#WALLET_PASS=yourpassword
GLOBAL_WEBHOOK=https://yourwebhookurl.com

193
main.py Normal file
View File

@ -0,0 +1,193 @@
from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory
import os
import dotenv
import requests
import datetime
import json
import payments
import accounts
import threading
app = Flask(__name__)
dotenv.load_dotenv()
# Exchange cache
exchange = {
'timestamp': 0,
'rate': 0
}
#Assets routes
@app.route('/assets/<path:path>')
def send_report(path):
return send_from_directory('templates/assets', path)
@app.route('/sitemap')
@app.route('/sitemap.xml')
def sitemap():
# Remove all .html from sitemap
with open('templates/sitemap.xml') as file:
sitemap = file.read()
sitemap = sitemap.replace('.html', '')
return make_response(sitemap, 200, {'Content-Type': 'application/xml'})
@app.route('/favicon.png')
def faviconPNG():
return send_from_directory('templates/assets/img', 'favicon.png')
@app.route('/.well-known/wallets/<path:path>')
def wallet(path):
return send_from_directory('templates/.well-known/wallets', path, mimetype='text/plain')
#region Account
@app.route('/register', methods=['POST'])
def register():
data = request.form
email = data['email']
password = data['password']
if not email or not password:
return render_template('register.html', error='Email and password are required')
if not accounts.createAccount(email, password):
return render_template('register.html', error='Account already exists')
cookie = accounts.createCookie(email)
response = make_response(redirect('/account'))
response.set_cookie('session', cookie)
return response
@app.route('/login', methods=['POST'])
def login():
data = request.form
email = data['email']
password = data['password']
if not email or not password:
return render_template('login.html', error='Email and password are required')
if not accounts.login(email, password):
return render_template('login.html', error='Invalid email or password')
cookie = accounts.createCookie(email)
response = make_response(redirect('/account'))
response.set_cookie('session', cookie)
return response
@app.route('/account')
def account():
cookie = request.cookies.get('session')
if not cookie:
return redirect('/login')
if not accounts.getCookie(cookie):
return redirect('/login')
account = accounts.getCookie(cookie)
address = accounts.getAccountAddress(account)
name = accounts.getAccountDisplayName(account)
link = accounts.getAccountLink(account)
webhook = accounts.getAccountWebhook(account)
return render_template('account.html', account=account, address=address, link=link, name=name, webhook=webhook)
@app.route('/account/edit', methods=['POST'])
def payout():
cookie = request.cookies.get('session')
if not cookie:
return redirect('/login')
if not accounts.getCookie(cookie):
return redirect('/login')
account = accounts.getCookie(cookie)
data = request.form
address = data['payout']
link = data['link']
name = data['name']
accounts.setAccountAddress(account, address)
accounts.setAccountLink(account, link)
accounts.setAccountDisplayName(account, name)
return redirect('/account')
@app.route('/account/webhook', methods=['POST'])
def webhook():
cookie = request.cookies.get('session')
if not cookie:
return redirect('/login')
if not accounts.getCookie(cookie):
return redirect('/login')
account = accounts.getCookie(cookie)
data = request.form
url = data['url']
accounts.setAccountWebhook(account, url)
return redirect('/account')
#endregion
# Payment routes
@app.route('/p/<path:path>')
def payment(path):
year = datetime.datetime.now().year
account = accounts.getAccountFromLink(path)
if not account:
return redirect('/?error=Invalid%20link')
display_name = accounts.getAccountDisplayName(account)
amount = request.args.get('amount')
data = request.args.get('data')
redirectLink = request.args.get('redirect')
if not data:
data = ''
if not amount:
return render_template('payment.html',year=year,account=account,name=display_name,readonly=False,data=data)
address = payments.generate_payment(account, amount, data)
if not redirectLink:
redirectLink = ""
else:
redirectLink = f'<a class="btn btn-primary" role="button" href="{redirectLink}">Continue</a>'
return render_template('payment2.html',year=year,account=account,name=display_name,amount=amount,data=data,address=address,redirect=redirectLink)
@app.route('/p/<path:path>', methods=['POST'])
def payment_post(path):
year = datetime.datetime.now().year
account = accounts.getAccountFromLink(path)
if not account:
return redirect('/?error=Invalid%20link')
display_name = accounts.getAccountDisplayName(account)
amount = request.form['amount']
data = request.form['data']
if not data:
data = ''
print(data)
address = payments.generate_payment(account, amount, data)
return render_template('payment2.html',year=year,account=account,name=display_name,amount=amount,data=data,address=address)
# Main routes
@app.route('/')
def index():
year = datetime.datetime.now().year
return render_template('index.html',year=year)
@app.route('/<path:path>')
def catch_all(path):
year = datetime.datetime.now().year
# If file exists, load it
if os.path.isfile('templates/' + path):
return render_template(path, year=year)
# Try with .html
if os.path.isfile('templates/' + path + '.html'):
return render_template(path + '.html', year=year)
return redirect('/')
# 404 catch all
@app.errorhandler(404)
def not_found(e):
return redirect('/')
def check_payments():
payments.check_payments()
if __name__ == '__main__':
app.run(debug=True, port=5000, host='0.0.0.0')

162
payments.py Normal file
View File

@ -0,0 +1,162 @@
import json
import os
import requests
import dotenv
import accounts
dotenv.load_dotenv()
HSD_IP = os.getenv('HSD_IP')
HSD_API_KEY = os.getenv('HSD_API_KEY')
WALLET = os.getenv('WALLET')
if not os.path.exists('data/addresses.json'):
with open('data/addresses.json', 'w') as f:
json.dump([], f, indent=4)
def generate_payment(email, amount, data):
# Generate a payment
with open('data/addresses.json', 'r') as f:
addresses = json.load(f)
address = ''
validAddress = False
while not validAddress:
address = generateAddress()
validAddress = True
for a in addresses:
if a["address"] == address:
validAddress = False
addresses.append({
"address": address,
"email": email,
"amount": amount,
"data": data,
"status": "Pending"
})
with open('data/addresses.json', 'w') as f:
json.dump(addresses, f, indent=4)
return address
def check_payments():
# Get all txs
with open('data/addresses.json', 'r') as f:
addresses = json.load(f)
# Select the wallet
url = f"http://x:{HSD_API_KEY}@{HSD_IP}:12039"
data = {
"method": "selectwallet",
"params": [WALLET]
}
resp = requests.post(url, json=data)
if resp.status_code != 200:
print(resp.text)
return False
# Get last 20 transactions
data = {
"method": "listtransactions",
"params": ["default", 20]
}
resp = requests.post(url, json=data)
if resp.status_code != 200:
print(resp.text)
return False
txs = resp.json()
if txs['error'] != None:
print(txs['error'])
return False
for tx in txs['result']:
if tx['confirmations'] < 1:
continue
for address in addresses:
if tx['address'] == address['address']:
address['status'] = 'Confirmed'
finalise_payment(address,tx)
with open('data/addresses.json', 'w') as f:
json.dump(addresses, f, indent=4)
return True
def generateAddress():
# Generate an address
url = f"http://x:{HSD_API_KEY}@{HSD_IP}:12039/wallet/{WALLET}/address"
data = '{"account":"default"}'
resp = requests.post(url, data=data)
if resp.status_code != 200:
print(resp.text)
return False
print("INDEX: ", resp.json()["index"])
return resp.json()["address"]
def finalise_payment(payment,tx):
print(payment)
# Send webhook
email = payment['email']
payout = accounts.getAccountAddress(email)
# Unlock wallet if password is set
password = os.getenv('WALLET_PASS')
if password:
url = f"http://x:{HSD_API_KEY}@{HSD_IP}:12039/wallet/{WALLET}/unlock"
data = {
"passphrase": password,
}
resp = requests.post(url, json=data)
if resp.status_code != 200:
print(resp.text)
# Send payout
url = f"http://x:{HSD_API_KEY}@{HSD_IP}:12039"
data = {
"method": "sendtoaddress",
"params": [payout, tx['amount'],"","" ,True]
}
resp = requests.post(url, json=data)
if resp.status_code != 200:
print(resp.text)
txHash = resp.json()['result']
isEqual = tx['amount'] == payment['amount']
# Send global webhook
globalWebhook = os.getenv('GLOBAL_WEBHOOK')
if globalWebhook:
data = {
"email": email,
"payout": payout,
"amount": tx['amount'],
"data": payment['data'],
"tx": txHash,
"isEqual": isEqual
}
resp = requests.post(globalWebhook, json=data)
if resp.status_code != 200:
print(resp.text)
webhook = accounts.getAccountWebhook(email)
if webhook:
data = {
"amount": tx['amount'],
"data": payment['data'],
"tx": txHash,
"isEqual": isEqual
}
resp = requests.post(webhook, json=data)
if resp.status_code != 200:
print(resp.text)
return True

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
flask
python-dotenv
gunicorn
requests
cloudflare
apscheduler
bcrypt

47
server.py Normal file
View File

@ -0,0 +1,47 @@
import time
from flask import Flask
from main import app
import main
from gunicorn.app.base import BaseApplication
import os
import dotenv
import sys
import json
from apscheduler.schedulers.background import BackgroundScheduler
class GunicornApp(BaseApplication):
def __init__(self, app, options=None):
self.options = options or {}
self.application = app
super().__init__()
def load_config(self):
for key, value in self.options.items():
if key in self.cfg.settings and value is not None:
self.cfg.set(key.lower(), value)
def load(self):
return self.application
if __name__ == '__main__':
scheduler = BackgroundScheduler()
scheduler.add_job(func=main.check_payments, trigger="interval", seconds=10)
scheduler.start()
workers = os.getenv('WORKERS')
threads = os.getenv('THREADS')
if workers is None:
workers = 1
if threads is None:
threads = 2
workers = int(workers)
threads = int(threads)
options = {
'bind': '0.0.0.0:5000',
'workers': workers,
'threads': threads,
}
gunicorn_app = GunicornApp(app, options)
print('Starting server with ' + str(workers) + ' workers and ' + str(threads) + ' threads', flush=True)
gunicorn_app.run()

84
templates/account.html Normal file
View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Account - HNS Pay</title>
<meta name="description" content="Accept payments via your HNS address">
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
</head>
<body style="background: rgb(39,38,46);">
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-md d-flex justify-content-center align-items-center me-2 bs-icon"><img src="/assets/img/favicon.png" width="100%"></span><span>HNS Pay</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
</nav>
<header class="bg-dark">
<div class="container pt-4 pt-xl-5">
<div class="row pt-5">
<div class="col-md-8 col-xl-6 text-center text-md-start mx-auto">
<div class="text-center">
<h1 class="fw-bold">Account</h1>
</div>
</div>
</div>
</div>
</header>
<section>
<div style="max-width: 700px;margin: auto;margin-top: 100px;margin-bottom: 100px;">
<h4>How do you want to receive your payments?</h4>
<form method="post" action="/account/edit"><label class="form-label" for="name">Display Name</label><input class="form-control" type="text" placeholder="Your Name" value="{{name}}" name="name"><label class="form-label" for="payout" style="margin-top: 25px;">Payout Address</label><input class="form-control" type="text" name="payout" placeholder="hs1...." value="{{address}}"><label class="form-label" style="margin-top: 25px;" for="link">Payment link</label>
<div><span>https://pay.hns.au/p/</span><input class="form-control form-control-sm" type="text" name="link" value="{{link}}" placeholder="yourlink" style="display: inline;width: auto;"></div><input class="btn btn-primary" type="submit" value="Save">
</form>
</div>
</section>
<section>
<div style="max-width: 700px;margin: auto;margin-top: 100px;margin-bottom: 100px;">
<h4>Payment webhook</h4>
<p style="margin-bottom: 0px;">Webhook is a post request with the following json data format</p><code>{<br>"amount": 100,<br>"data":"inputtedData",<br>"tx":"hash_of_pending_payout",<br>"isEqual": True<br>}</code>
<p>- amount is the amount paid (not necessarily the amount asked).<br>- isEqual is a bool that indicates if the amount matches the requested amount</p>
<form method="post" action="/account/webhook" style="margin-top: 25px;"><label class="form-label" for="url">URL</label><input class="form-control" type="text" placeholder="https://yourdomain.com/hook" value="{{webhook}}" name="url"><input class="btn btn-primary" type="submit" value="Save" style="margin-top: 25px;"></form>
</div>
</section>
<section>
<div>{{payments|safe}}</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Web Hosting</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Pay</span></div>
<p class="text-muted">Accept payments using HNS</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} HNS Pay</p>
</div>
</div>
</footer>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
templates/assets/css/styles.min.css vendored Normal file
View File

@ -0,0 +1 @@
.tooltip-inner{color:#fff}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

1
templates/assets/js/script.min.js vendored Normal file
View File

@ -0,0 +1 @@
document.addEventListener("DOMContentLoaded",(function(){[].slice.call(document.querySelectorAll("[data-bss-tooltip]")).map((function(e){return new bootstrap.Tooltip(e)}))}),!1),function(){"use strict";!function(){if("requestAnimationFrame"in window&&!/Mobile|Android/.test(navigator.userAgent)){var e=document.querySelectorAll("[data-bss-parallax]");if(e.length){var t,n=[];window.addEventListener("scroll",o),window.addEventListener("resize",o),o()}}function o(){n.length=0;for(var o=0;o<e.length;o++){var r=e[o].getBoundingClientRect(),i=parseFloat(e[o].getAttribute("data-bss-parallax-speed"),10)||.5;r.bottom>0&&r.top<window.innerHeight&&n.push({speed:i,node:e[o]})}cancelAnimationFrame(t),n.length&&(t=requestAnimationFrame(a))}function a(){for(var e=0;e<n.length;e++){var t=n[e].node,o=n[e].speed;t.style.transform="translate3d(0, "+-window.scrollY*o+"px, 0)"}}}()}();

184
templates/index.html Normal file
View File

@ -0,0 +1,184 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>HNS Pay</title>
<meta name="description" content="Accept payments via your HNS address">
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "HNS Pay",
"url": "https://pay.hns.au"
}
</script>
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
</head>
<body style="background: rgb(39,38,46);">
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-md d-flex justify-content-center align-items-center me-2 bs-icon"><img src="/assets/img/favicon.png" width="100%"></span><span>HNS Pay</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav mx-auto"></ul><a class="btn btn-primary shadow" role="button" href="/account">Account</a>
</div>
</div>
</nav>
<header class="bg-dark">
<div class="container pt-4 pt-xl-5">
<div class="row pt-5">
<div class="col-md-8 col-xl-6 text-center text-md-start mx-auto">
<div class="text-center">
<p class="fw-bold text-success mb-2">Easiest way to receive HNS Payments</p>
<h1 class="fw-bold">Easy to use free HNS payment provider</h1>
</div>
</div>
</div>
</div>
</header>
<section>
<div class="container bg-dark py-5" style="background: rgb(39, 38, 46);">
<div class="row">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<p class="fw-bold text-success mb-2">Features</p>
<h3 class="fw-bold">How can you use HNS Pay</h3>
</div>
</div>
<div class="py-5 p-lg-5">
<div class="row row-cols-1 row-cols-md-2 mx-auto" style="max-width: 900px;">
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<div class="bs-icon-lg d-flex justify-content-center align-items-center mb-3 bs-icon" style="top: 1rem;right: 1rem;position: absolute;"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-shop text-success">
<path d="M2.97 1.35A1 1 0 0 1 3.73 1h8.54a1 1 0 0 1 .76.35l2.609 3.044A1.5 1.5 0 0 1 16 5.37v.255a2.375 2.375 0 0 1-4.25 1.458A2.371 2.371 0 0 1 9.875 8 2.37 2.37 0 0 1 8 7.083 2.37 2.37 0 0 1 6.125 8a2.37 2.37 0 0 1-1.875-.917A2.375 2.375 0 0 1 0 5.625V5.37a1.5 1.5 0 0 1 .361-.976l2.61-3.045zm1.78 4.275a1.375 1.375 0 0 0 2.75 0 .5.5 0 0 1 1 0 1.375 1.375 0 0 0 2.75 0 .5.5 0 0 1 1 0 1.375 1.375 0 1 0 2.75 0V5.37a.5.5 0 0 0-.12-.325L12.27 2H3.73L1.12 5.045A.5.5 0 0 0 1 5.37v.255a1.375 1.375 0 0 0 2.75 0 .5.5 0 0 1 1 0M1.5 8.5A.5.5 0 0 1 2 9v6h1v-5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v5h6V9a.5.5 0 0 1 1 0v6h.5a.5.5 0 0 1 0 1H.5a.5.5 0 0 1 0-1H1V9a.5.5 0 0 1 .5-.5M4 15h3v-5H4zm5-5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1zm3 0h-2v3h2z"></path>
</svg></div>
<h5 class="fw-bold card-title">Accept Payments for products</h5>
<p class="text-muted card-text mb-4">Create a selling account. Send your users to your custom url and we will send you a email or webhook when a payment is received</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section>
<div class="container bg-dark py-5" style="background: rgb(39, 38, 46);">
<div class="row">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<p class="fw-bold text-success mb-2">Usage</p>
<h3 class="fw-bold">How to accept payments</h3>
</div>
</div>
<div class="text-center py-5 p-lg-5">
<p style="margin-bottom: 0px;">After you create a account and set your payout address, and link, you can create a link with parameters.</p><code>https://pay.hns.au/p/yourlink?amount=10&amp;data=My%20Payment%20Data&amp;redirect=https://nathan.woodburn.au</code>
<div class="row row-cols-1 row-cols-md-2 text-start mx-auto" style="max-width: 900px;margin-top: 25px;">
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<div class="bs-icon-lg d-flex justify-content-center align-items-center mb-3 bs-icon" style="top: 1rem;right: 1rem;position: absolute;"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-currency-dollar text-success">
<path d="M4 10.781c.148 1.667 1.513 2.85 3.591 3.003V15h1.043v-1.216c2.27-.179 3.678-1.438 3.678-3.3 0-1.59-.947-2.51-2.956-3.028l-.722-.187V3.467c1.122.11 1.879.714 2.07 1.616h1.47c-.166-1.6-1.54-2.748-3.54-2.875V1H7.591v1.233c-1.939.23-3.27 1.472-3.27 3.156 0 1.454.966 2.483 2.661 2.917l.61.162v4.031c-1.149-.17-1.94-.8-2.131-1.718H4zm3.391-3.836c-1.043-.263-1.6-.825-1.6-1.616 0-.944.704-1.641 1.8-1.828v3.495l-.2-.05zm1.591 1.872c1.287.323 1.852.859 1.852 1.769 0 1.097-.826 1.828-2.2 1.939V8.73l.348.086z"></path>
</svg></div>
<h5 class="fw-bold card-title">amount</h5>
<p class="text-muted card-text mb-4">Amount to charge user. If this isn't included the user will be prompted to enter an amount.</p><span>HNS Pay will send you a webhook even if the amount isn't the same as the inputted one</span>
</div>
</div>
</div>
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<div class="bs-icon-lg d-flex justify-content-center align-items-center mb-3 bs-icon" style="top: 1rem;right: 1rem;position: absolute;"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-info-circle-fill text-success">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2"></path>
</svg></div>
<h5 class="fw-bold card-title">data</h5>
<p class="text-muted card-text mb-4">Add custom data that will be sent to you when the payment has been sent to you</p>
</div>
</div>
</div>
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<div class="bs-icon-lg d-flex justify-content-center align-items-center mb-3 bs-icon" style="top: 1rem;right: 1rem;position: absolute;"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-forward-fill text-success">
<path d="m9.77 12.11 4.012-2.953a.647.647 0 0 0 0-1.114L9.771 5.09a.644.644 0 0 0-.971.557V6.65H2v3.9h6.8v1.003c0 .505.545.808.97.557z"></path>
</svg></div>
<h5 class="fw-bold card-title">redirect</h5>
<p class="text-muted card-text mb-4">Include a link on the payment page to your url.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="py-5">
<div class="container">
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<p class="fw-bold text-success mb-2">Contacts</p>
<h2 class="fw-bold">How you can reach us</h2>
</div>
</div>
<div class="row d-flex justify-content-center">
<div class="col-md-4 col-xl-4 d-flex justify-content-center justify-content-xl-start">
<div class="d-flex flex-wrap flex-md-column justify-content-md-start align-items-md-start h-100">
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-discord">
<path d="M13.545 2.907a13.227 13.227 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 0 0-3.658 0 8.258 8.258 0 0 0-.412-.833.051.051 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019c.308-.42.582-.863.818-1.329a.05.05 0 0 0-.01-.059.051.051 0 0 0-.018-.011 8.875 8.875 0 0 1-1.248-.595.05.05 0 0 1-.02-.066.051.051 0 0 1 .015-.019c.084-.063.168-.129.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 0 1 .053.007c.08.066.164.132.248.195a.051.051 0 0 1-.004.085 8.254 8.254 0 0 1-1.249.594.05.05 0 0 0-.03.03.052.052 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.235 13.235 0 0 0 4.001-2.02.049.049 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 0 0-.02-.019Zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"></path>
</svg></div>
<div class="px-2">
<h6 class="fw-bold mb-0">Discord</h6><a href="https://l.woodburn.au/discord" target="_blank">Join Server</a>
</div>
</div>
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-envelope">
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"></path>
</svg></div>
<div class="px-2">
<h6 class="fw-bold mb-0">Email</h6>
<p class="text-muted mb-0">pay@hns.au</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Web Hosting</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Pay</span></div>
<p class="text-muted">Accept payments using HNS</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} HNS Pay</p>
</div>
</div>
</footer>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

86
templates/login.html Normal file
View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Log in - HNS Pay</title>
<meta name="description" content="Accept payments via your HNS address">
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
</head>
<body>
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-md d-flex justify-content-center align-items-center me-2 bs-icon"><img src="/assets/img/favicon.png" width="100%"></span><span>HNS Pay</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav mx-auto"></ul><a class="btn btn-primary shadow" role="button" href="/account">Account</a>
</div>
</div>
</nav>
<section class="py-5">
<div class="container py-5">
<div class="row mb-4 mb-lg-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<p class="fw-bold text-success mb-2">Login</p>
<h2 class="fw-bold">Welcome back</h2>
</div>
</div>
<h1 class="text-center">{{error}}</h1>
<div class="row d-flex justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="card">
<div class="card-body text-center d-flex flex-column align-items-center">
<div class="bs-icon-xl bs-icon-circle bs-icon-primary shadow bs-icon my-4"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-person">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664z"></path>
</svg></div>
<form method="post">
<div class="mb-3"><input class="form-control" type="email" name="email" placeholder="Email"></div>
<div class="mb-3"><input class="form-control" type="password" name="password" placeholder="Password"></div>
<div class="mb-3"><button class="btn btn-primary shadow d-block w-100" type="submit">Log in</button></div>
<p class="text-muted"><a href="/register">Create an account?</a></p>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Web Hosting</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Pay</span></div>
<p class="text-muted">Accept payments using HNS</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} HNS Pay</p>
</div>
</div>
</footer>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

66
templates/payment.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Pay {{name}} - HNS Pay</title>
<meta name="description" content="Accept payments via your HNS address">
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
</head>
<body>
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-md d-flex justify-content-center align-items-center me-2 bs-icon"><img src="/assets/img/favicon.png" width="100%"></span><span>HNS Pay</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
</nav>
<section class="py-5">
<h1 class="text-center">Pay {{name}}</h1>
<div class="container py-5">
<div class="row mx-auto">
<div class="col">
<form class="text-center" method="post"><label class="form-label" for="amount">How much HNS would you like to send?</label>
<div style="margin-bottom: 25px;"><input class="form-control" type="number" name="amount" value="{{amount}}" placeholder="0" style="width: auto;display: inline-block;"><span>&nbsp;HNS</span></div><input class="form-control" type="hidden" name="data" value="{{data}}"><input class="btn btn-primary" type="submit" value="Proceed">
</form>
</div>
</div>
</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Web Hosting</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Pay</span></div>
<p class="text-muted">Accept payments using HNS</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} HNS Pay</p>
</div>
</div>
</footer>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

79
templates/payment2.html Normal file
View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Pay {{name}} - HNS Pay</title>
<meta name="description" content="Accept payments via your HNS address">
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
</head>
<body>
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-md d-flex justify-content-center align-items-center me-2 bs-icon"><img src="/assets/img/favicon.png" width="100%"></span><span>HNS Pay</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
</nav>
<section class="py-5">
<h1 class="text-center">Pay {{name}}</h1>
<div class="container py-5">
<div class="row mx-auto">
<div class="col text-center">
<h2>Send {{amount}} HNS to {{name}}</h2>
<p>If sending from Namebase please pay an extra 1 HNS to cover their fees.</p>
<h3>Address: <code data-bs-toggle="tooltip" data-bss-tooltip="" id="address" title="Click to copy">{{address}}</code><script>
document.getElementById("address").addEventListener("click", function() {
var text = this.innerText;
navigator.clipboard.writeText(text)
.then(function() {
console.log("Copied");
})
.catch(function(err) {
console.error("Failed to copy text: ", err);
});
});
</script></h3>
<p>Or scan the QR to pay</p><img src="https://chart.googleapis.com/chart?chs=300x300&amp;cht=qr&amp;chl={{address}}&amp;choe=UTF-8">
<div class="text-center" style="margin-top: 25px;">{{redirect|safe}}</div>
</div>
</div>
</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Web Hosting</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Pay</span></div>
<p class="text-muted">Accept payments using HNS</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} HNS Pay</p>
</div>
</div>
</footer>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

87
templates/register.html Normal file
View File