feat: Initial code drop
All checks were successful
Build Docker / Build Image (push) Successful in 35s
41
.gitea/workflows/build.yml
Normal 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
@ -0,0 +1,6 @@
|
||||
|
||||
.env
|
||||
|
||||
__pycache__/
|
||||
|
||||
data/
|
17
Dockerfile
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -0,0 +1,7 @@
|
||||
flask
|
||||
python-dotenv
|
||||
gunicorn
|
||||
requests
|
||||
cloudflare
|
||||
apscheduler
|
||||
bcrypt
|
47
server.py
Normal 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
@ -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&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>
|
6
templates/assets/bootstrap/css/bootstrap.min.css
vendored
Normal file
6
templates/assets/bootstrap/js/bootstrap.min.js
vendored
Normal file
1
templates/assets/css/styles.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.tooltip-inner{color:#fff}
|
BIN
templates/assets/img/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
templates/assets/img/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
templates/assets/img/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
templates/assets/img/brands/apple.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
templates/assets/img/brands/facebook.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
templates/assets/img/brands/google.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
templates/assets/img/brands/microsoft.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
templates/assets/img/brands/twitter.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
templates/assets/img/favicon-16x16.png
Normal file
After Width: | Height: | Size: 856 B |
BIN
templates/assets/img/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
templates/assets/img/favicon.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
templates/assets/img/products/1.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
templates/assets/img/products/2.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
templates/assets/img/products/3.jpg
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
templates/assets/img/team/avatar2.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
templates/assets/img/team/avatar4.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
templates/assets/img/team/avatar5.jpg
Normal file
After Width: | Height: | Size: 57 KiB |
1
templates/assets/js/script.min.js
vendored
Normal 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
@ -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&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&data=My%20Payment%20Data&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
@ -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&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
@ -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&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> 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
@ -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&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&cht=qr&chl={{address}}&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>
|