Skip to content

Commit

Permalink
top-up mostly working
Browse files Browse the repository at this point in the history
  • Loading branch information
james-stevens committed Feb 20, 2023
1 parent de18de6 commit 32a5671
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 62 deletions.
2 changes: 1 addition & 1 deletion bin/run_init
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ mkdir /run/nginx; chown nginx: /run/nginx

perm="${BASE}/storage/perm"
sigs="${BASE}/storage/shared/signals"
mkdir -p ${BASE}/storage ${perm} ${perm}/spooler ${perm}/mail_error ${perm}/postfix
mkdir -p ${BASE}/storage ${perm} ${perm}/spooler ${perm}/mail_error ${perm}/postfix ${perm}/payments
mkdir -p ${BASE}/storage/shared ${sigs}
chown daemon: ${sigs} ${perm}/spooler ${perm}/mail_error
chmod 770 ${sigs}
Expand Down
69 changes: 65 additions & 4 deletions htdocs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<script src="/lib.js"></script>
<script src="/emojis.js"></script>
<script src="/countries.js"></script>
<script src="/paypal.js"></script>
</head>
<style type='text/css'>.msgPop { visibility: hidden;}</style>
<script language="Javascript">
Expand Down Expand Up @@ -47,6 +46,7 @@
"userSpace","output","topSpan","search","businessName"
];

payments = {};
elm = {};
gbl = {
"theme": window.localStorage.theme,
Expand Down Expand Up @@ -173,6 +173,7 @@
if (name=="AddRRwin") e = document.getElementById("rr_name");
if (name=="AddTLSAwin") e = document.getElementById("tlsa_o");
if (name=="emojiPopup") e = document.getElementById("emojiSearch");
if (name=="TopUpAcct") e = document.getElementById("top_up_amount");
if (e) { e.focus(); e.select(); }

return false;
Expand Down Expand Up @@ -526,6 +527,10 @@
gbl.config.ok_tlds = {};
for(let item of gbl.config.zones) gbl.config.ok_tlds[item.name] = true;

if (gbl.config.payment) {
for(let i in gbl.config.payment) add_payment_script(i);
}

let my_icon = policy("icon",null);
let site_name = policy("business_name","Registrar");
document.title = site_name;
Expand All @@ -551,8 +556,6 @@
});
} else { loggedOut(); check_router_path(); }

if (gbl.config.paypal_client_id)
add_paypal_script(gbl.config.paypal_client_id,gbl.config.currency.iso);
});
}

Expand Down Expand Up @@ -1310,6 +1313,52 @@



function top_up_account()
{
let f = document.top_up_form;
if (!f.top_up_method.value) {
errMsg("Please select a payment method","TopUpAcct");
f.top_up_method.focus();
return false;
}

if (!f.top_up_amount.value) {
errMsg("Please enter a top-up amount","TopUpAcct");
f.top_up_amount.focus();
return false;
}

let x = `<center><h3>Top-Up Amount: ${gbl.config.currency.symbol}${f.top_up_amount.value}</h3></center>`;
x += "<div id='payment-whole'></div>";
elm.userSpace.innerHTML = x;

payments[f.top_up_method.value].single(`${policy("business_name","Registrar")}: Top-Up`,f.top_up_amount.value);

return false;
}


function top_up_account_form()
{
let x = "<form name=top_up_form onSubmit='return top_up_account();' action=# method=post><table>";
x += "<tr><td class=formPrompt>Top-Up Amount:</td>";
x += `<td>${gbl.config.currency.symbol}<input id='top_up_amount' style="width: 250px;"></td></tr>`;
x += "<tr><td class=formPrompt>Select method:</td>";
x += "<td><select id=top_up_method><option value=''>--- Select Method ---"
for(let i in payments) {
if ((i in gbl.config.payment)&&(payments[i].single))
x += `<option value='${i}'>${payments[i].desc}`;
}
x += "</select></td></tr>";
x += "<tr><td colspan=2 class=btmBtnBar>"
x += btn("hideAllPopUp()","Cancel","Cancel this top-up",80);
x += "<input class=myBtn type=submit value='Top-Up' title='Make top-up payment now' style='width: 120px;'></td></tr>";

return x +"</form></table>";
}



function remove_payment(provider,provider_tag)
{
callApi("payments/delete",(ok,reply) => {
Expand All @@ -1326,15 +1375,27 @@
callApi("payments/list",(ok,reply) => {
let x="<table width=75% align=center cellspacing=1 cellpadding=0 border=0>";
x += "<tr><td class=btmBtnBar>";

x += generic_popup_btn({
"name": "AddPayment",
"label": "Add Payment",
"width": 100,
"width": 120,
"timeout": null,
"title": "Add a payment method",
"internal": add_payment_form,
"style": "overflow: auto; margin-left: -250;"
});

x += generic_popup_btn({
"name": "TopUpAcct",
"label": "Top-Up Account",
"width": 120,
"timeout": null,
"title": "Add a credit to your account for future payments",
"internal": top_up_account_form,
"style": "overflow: auto; margin-left: -250;"
});

x += "</td></tr></table>";
x += "<div id='add_pay_div'>"+gbl.gap;
if (reply.length>0) {
Expand Down
19 changes: 19 additions & 0 deletions htdocs/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,22 @@ function generic_popup_btn(config)
x += config["internal"](config["param"]);
return x + `</span></div>`;
}



function add_payment_script(module) {
let s = document.createElement('script');
s.setAttribute("src", "/"+module+".js" );
s.setAttribute("type", 'text/javascript');
s.onload = () => { eval(module+"_startup()"); };
document.body.appendChild( s );
}



function rand_tag(want_char)
{
if (!want_char) want_char = 30
let myar = new Uint8Array(10);
return btoa(window.crypto.getRandomValues(myar)).slice(0,want_char);
}
69 changes: 47 additions & 22 deletions htdocs/paypal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@

// PayPal Payment plug-in JS

function paypal_startup() // {module}_startup() is a mandatory func in a JS payment module
{
if ((gbl.config.payment)&&(gbl.config.payment.paypal)&&(gbl.config.payment.paypal.client_id))
add_paypal_script(gbl.config.payment.paypal.client_id,gbl.config.currency.iso);

payments["paypal"] = {
"desc": "PayPal",
"single": paypal_single_payment
};
}


function add_paypal_script(client_id, currency) {
let s = document.createElement('script');
Expand All @@ -9,15 +22,34 @@ function add_paypal_script(client_id, currency) {
}


function show_paypal()

function paypal_single_payment(description, amount)
{
callApi("payments/single",(ok,reply)=> {
if (!ok) return def_errMsg("Failed to create single use token",reply,"payment-whole");

let x = "<table width=100%>"
x += "<colgroup><col width=70%/><col/></colgroup>";
x += "<tr><td align=center>Make payment by PayPal</td><td><span id='payment-button'></span></td></tr></table>";

let e = document.getElementById("payment-whole");
e.innerHTML = x;

initPayPalButton(description, amount, reply.provider_tag);
},{ json:{ "provider":"paypal"}})
}


function test_single_paypal() // this is for debugging
{
show_one_space("userSpace")
elm.userSpace.innerHTML = '<div id="paypal-button-container"></div>';
initPayPalButton("Test Description",12.99,"USD");
elm.userSpace.innerHTML = "<div id='payment-whole'></div>";
payments.paypal.single("Test Description",18.99);
}


function initPayPalButton(description, custom_id, amount, currency) {

function initPayPalButton(description, amount, custom_id) {
paypal.Buttons({
style: {
shape: 'rect',
Expand All @@ -31,11 +63,11 @@ function initPayPalButton(description, custom_id, amount, currency) {
return actions.order.create({
purchase_units: [
{
"description":description,
"description": description,
"custom_id": custom_id,
"amount":{
"currency_code":currency,
"value":amount
"currency_code": gbl.config.currency.iso,
"value": amount
}
}
]
Expand All @@ -44,22 +76,15 @@ function initPayPalButton(description, custom_id, amount, currency) {

onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {

// Full available details
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));

// Show a success message within this page, e.g.
const element = document.getElementById('paypal-button-container');
element.innerHTML = '';
element.innerHTML = '<h3>Thank you for your payment!</h3>';

// Or go to another URL: actions.redirect('thank_you.html');

// console.log(orderData);
let e = document.getElementById("payment-whole");
let x = '<center><h3>Thank you for your payment!</h3><br>'
x += "Your payment will be processed<br>when PayPal notifies us they have completed the transfer</center>";
e.innerHTML = x;
});
},

onError: function(err) {
console.log(err);
}
}).render('#paypal-button-container');
onError: function(err) { errMsg(err,"payment-whole"); }

}).render('#payment-button');
}
3 changes: 1 addition & 2 deletions python/cronjob/run_cronjobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@


def remove_old_one_time_payment_keys():
tout = "7 day"
query = f"delete from payments where single_use and amended_dt < date_sub(now(), interval {tout})"
query = "delete from payments where single_use and amended_dt < date_sub(now(), interval 7 day)"
return sql.sql_exec(query)


Expand Down
20 changes: 18 additions & 2 deletions python/payments/libpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Alternative license arrangements possible, contact me for more information
""" handles plug-in modules for domain interfaces needed by the UI """

import os
import json

from librar.log import log, debug, init as log_init
Expand Down Expand Up @@ -35,8 +36,23 @@ def config():
return all_config


def process_webhook(webhook):
return True
def process_webhook(pay_module, sent_data):
save_dir = os.path.join(os.environ["BASE"],"payments")
with tempfile.NamedTemporaryFile("w+", encoding="utf-8", dir=save_dir, delete=False, prefix=pay_module + "_") as fd:
fd.write(sent_data)

if (func := pay_handler.run(pay_mod, "webhook")) is None:
return False, f"Call to Webhook for '{pay_mod}' failed"

if func(sent_data):
return True, "Processed"
else:
return False, f"Webhook for'{pay_mod}' module is not set up"

return False, f"Unknown failure for pay module '{pay_mod}'"


return True, True


def main():
Expand Down
31 changes: 28 additions & 3 deletions python/payments/plugins/paypal.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,39 @@ def paypal_startup():
return True


def paypal_process_webhook():
# CODE
def check_web_hook_data(sent_data):
if len(sent_data["purchase_units"] != 1:
log("ERROR: PayPal record does have one 'purchase_units'")
return False

pay_unit = sent_data["purchase_units"][0]
if "amount" not in pay_unit or not isinstance(pay_unit["amount"],dict):
log("ERROR: PayPal 'amount' record missing or invalid")
return False

currency = policy.policy("currency")
if "currency" not in pay_unit["amount"] or pay_unit["amount"]["currency"] != currency["iso"]:
log(f"ERROR: PayPal currency missing or doesn't match '{currency['iso']}'")
return False

if "value" not in pay_unit["amount"]:
log(f"ERROR: PayPal amount has no 'value' field")
return False

if "custom_id" not in pay_unit:
log(f"ERROR: PayPal purchase_unit has no 'custom_id' field")



def paypal_process_webhook(sent_data):
if not check_web_hook_data(sent_data):
return False
return True


pay_handler.add_plugin(THIS_MODULE, {
"desc": "PayPal",
"config": paypal_config,
"startup": paypal_startup,
"process": paypal_process_webhook,
"webhook": paypal_process_webhook,
})
Loading

0 comments on commit 32a5671

Please sign in to comment.