parent
2acf10f7d7
commit
073b105c21
@ -1,10 +1,364 @@
|
||||
<body>
|
||||
<input type="text" disabled/>
|
||||
<input type="text" placeholder="card number"/>
|
||||
<div>
|
||||
<div>
|
||||
{% for i in range(12) %}
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="cache-control" content="no-cache">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
|
||||
<link rel="shortcut icon" href="{{product.images[0]}}">
|
||||
<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">
|
||||
<link rel="stylesheet" href="{{context}}/static/css/fa/css/all.css" type="text/css">
|
||||
<script type="text/javascript" src="{{ context }}/static/css/fa/js/all.js"></script>
|
||||
<script type="text/javascript" src="{{ context }}/static/js/jquery/jquery.min.js"></script>
|
||||
<script src="{{context}}/static/js/jx/utils.js"></script>
|
||||
<script src="{{context}}/static/js/jx/dom.js"></script>
|
||||
<script src="{{context}}/static/js/jx/rpc.js"></script>
|
||||
<script src="{{context}}/static/js/jx/ext/modal.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family:sans-serif;
|
||||
margin:0px;
|
||||
|
||||
}
|
||||
.frame {
|
||||
font-size:16px;
|
||||
font-weight:lighter;
|
||||
width:800px;
|
||||
|
||||
display:grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
gap:2px;
|
||||
align-items:center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
}
|
||||
.product-info {
|
||||
display:grid;
|
||||
grid-template-rows: 48px 250px auto;
|
||||
gap:2px;
|
||||
align-items:center;
|
||||
border-right:1px solid #CAD5E0;
|
||||
padding:32px;
|
||||
|
||||
}
|
||||
.product-info .description {
|
||||
padding:4px;
|
||||
|
||||
}
|
||||
.product-info .caption {
|
||||
font-size:28px;
|
||||
padding:8px;
|
||||
text-align:center;
|
||||
}
|
||||
.product-info img {
|
||||
width:150px;
|
||||
margin:4px; }
|
||||
.card-info {
|
||||
padding:4px;
|
||||
|
||||
|
||||
}
|
||||
.card-info .amount {text-align:right;}
|
||||
|
||||
.payment-amount .menu {display:none; position:absolute; z-index:9; background-color:#ffffff; padding:4px;}
|
||||
.menu .item {padding:4px}
|
||||
.card-info input {width:100%;}
|
||||
|
||||
.email { width:100%;}
|
||||
.fname { width:100% }
|
||||
.lname {width:100%}
|
||||
|
||||
input {
|
||||
height:48px;
|
||||
border:2px solid transparent;
|
||||
padding:6px;
|
||||
font-weight:lighter;
|
||||
background-color:#f3f3f3;
|
||||
}
|
||||
.fa-exclamation-triangle{
|
||||
color:#ff6500 ;
|
||||
}
|
||||
input:focus {
|
||||
/*border-left: 2px solid #ff6500 ;*/
|
||||
border-left: 2px solid #4682B4;
|
||||
}
|
||||
.error {
|
||||
border-color: maroon;
|
||||
}
|
||||
.location{padding:4px; display:none; grid-template-columns:50% 50%; gap:2px;}
|
||||
.location input {width:100%}
|
||||
.gray {color:#d3d3d3}
|
||||
.steel-blue{color:#4682B4;}
|
||||
.dialog-frame {
|
||||
min-width:320px;
|
||||
min-height:125px;
|
||||
|
||||
}
|
||||
/*
|
||||
.dialog {
|
||||
display:grid;
|
||||
padding:4px;
|
||||
display:grid;
|
||||
grid-template-columns: 25% auto;
|
||||
gap:2px;
|
||||
}
|
||||
.dialog-frame .close {display:grid; gap:2px; grid-template-columns:auto 32px;
|
||||
text-align:right; ;
|
||||
background-color:#f3f3f3;
|
||||
}
|
||||
.dialog .icon { font-size:42px;justify-items:center; display:grid; align-items:center}
|
||||
.dialog .message {
|
||||
display:grid;
|
||||
align-items:center;
|
||||
padding:8px;
|
||||
font-weight:bold ;
|
||||
color:#4682B4 ;
|
||||
} */
|
||||
</style>
|
||||
<script>
|
||||
sessionStorage.context = "{{context|safe}}"
|
||||
sessionStorage.pricing = JSON.stringify({{pricing|tojson}})
|
||||
sessionStorage.customer = JSON.stringify({info:{}})
|
||||
var cards = {
|
||||
"^3[47]\\d{1,2}(| |-)\\d{6}\\1\\d{6}$":"amex","^5[1-5]\\d{2}(| |-)(?:\\d{4}\\1){2}\\d{4}$":"mastercard",
|
||||
"^4\\d{3}(| |-)(?:\\d{4}\\1){2}\\d{4}$":"visa","^6(?:011|5\\d\\d)(| |-)(?:\\d{4}\\1){2}\\d{4}$":"discover"}
|
||||
|
||||
var checkout = {}
|
||||
checkout.init = function(index){
|
||||
var p = JSON.parse(sessionStorage.pricing)
|
||||
var _item = p[index]
|
||||
var amount = (_item.unit_amount/100).toFixed(2)
|
||||
$('.amount').val( amount)
|
||||
//$('.amount-value').text(amount)
|
||||
$('.currency').text(_item.currency.toUpperCase())
|
||||
$('.amount').change()
|
||||
sessionStorage.product = JSON.parse(sessionStorage.customer)
|
||||
|
||||
}
|
||||
checkout.validate = function(e){
|
||||
var id = e.target
|
||||
customer = JSON.parse(sessionStorage.customer)
|
||||
|
||||
$(id).removeClass('error')
|
||||
var pattern = $(id).attr('data-pattern')
|
||||
var field = $(id).attr('class')
|
||||
var value = $(id).val().trim()
|
||||
|
||||
$(id).val(value)
|
||||
if (customer.info[field] != null){
|
||||
delete customer.info[field]
|
||||
}
|
||||
if (pattern == 'card'){
|
||||
var r = jx.utils.patterns.visitor(jx.utils.keys(cards),function(key){
|
||||
|
||||
if(value.match(key)){
|
||||
return cards[key]
|
||||
}
|
||||
})
|
||||
if(r.length > 0){
|
||||
//
|
||||
// @TODO: Generalize this to a function call (poor programming)
|
||||
customer.info[field] = value
|
||||
sessionStorage.customer = JSON.stringify(customer)
|
||||
var link = ([sessionStorage.context,'/static/img/cards/',r[0]+'.svg']).join('')
|
||||
jx.dom.set.attribute('pay-icon','src',link)
|
||||
return 1
|
||||
}else{
|
||||
|
||||
jx.dom.set.attribute('pay-icon','src','')
|
||||
$(id).addClass('error')
|
||||
return 0
|
||||
}
|
||||
|
||||
}else{
|
||||
if(value.match(pattern)){
|
||||
customer.info[field] = value
|
||||
sessionStorage.customer = JSON.stringify(customer)
|
||||
return 1
|
||||
}else{
|
||||
$(id).addClass('error')
|
||||
return 0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
checkout.dialog = function(title,message,type){
|
||||
ICONS = {'WARN':'fas fa-exclamation-triangle','CHECK':'fas fa-check fa-2x','PROGRESS':'fas fa-cog fa-spin'}
|
||||
var uri = ([sessionStorage.context,'/ui/dialog']).join('')
|
||||
var info = {title:title,message:message,icon:ICONS[type],type:type}
|
||||
var httpclient = HttpClient.instance()
|
||||
httpclient.setData( JSON.stringify(info))
|
||||
httpclient.setHeader('content-type','application/json')
|
||||
httpclient.post(uri,function(x){
|
||||
jx.modal.show({html:x.responseText,id:'dialog'})
|
||||
})
|
||||
}
|
||||
checkout.proceed = function(){
|
||||
checkout.dialog('Preparing payment','Please wait ...','PROGRESS')
|
||||
jx.utils.patterns.visitor($('input'),function(_input){
|
||||
//
|
||||
// re-running validation
|
||||
if ($(_input).attr('class') != 'amount'){
|
||||
$(_input).keyup()
|
||||
}else {
|
||||
$(_input).change()
|
||||
}
|
||||
})
|
||||
|
||||
var N = $('input').length
|
||||
info = JSON.parse(sessionStorage.customer).info
|
||||
var n = jx.utils.keys(info).length
|
||||
if (n != N){
|
||||
//
|
||||
// We can not submit this (trash)
|
||||
/*jx.utils.patterns.visitor(['fa fa-cog fa-3x fa-spin gray','fa fa-cog fa-5x fa-spin steel-blue','fa fa-cog fa-2x fa-spin gray'],function(name){
|
||||
var i = jx.dom.get.instance('I')
|
||||
i.className = name
|
||||
$('.dialog > .icon').append(i)
|
||||
})
|
||||
*/
|
||||
|
||||
setTimeout( function(){
|
||||
jx.modal.close()
|
||||
},2000)
|
||||
/*$('.card').slideUp( function(){
|
||||
$('.dialog-frame').show()
|
||||
var icon = jx.dom.get.instance('I')
|
||||
icon.className = 'fas fa-exclamation-triangle'
|
||||
$('.dialog > .icon').append(icon)
|
||||
$('.dialog > .message').text('Invalid entries founds')
|
||||
console.log(info)
|
||||
|
||||
})*/
|
||||
}else{
|
||||
//
|
||||
// Submit the charge to be processed
|
||||
//
|
||||
info.amount = info.amount * 100
|
||||
$('.dialog > .message').text('Processing card, please wait ...')
|
||||
var uri = ([sessionStorage.context,'/{{product.name|safe}}/pay']).join('/')
|
||||
var http = HttpClient.instance()
|
||||
http.setData(JSON.stringify(info))
|
||||
http.setHeader('content-type','application/json')
|
||||
http.post(uri,function(x){
|
||||
|
||||
if(x.status == 200){
|
||||
TYPE='CHECK'
|
||||
TITLE = ''
|
||||
}else{
|
||||
TITLE='.::'
|
||||
TYPE='WARN'
|
||||
}
|
||||
jx.modal.close()
|
||||
checkout.dialog(TITLE,x.responseText,TYPE)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$('.payment-amount > input').on('focusin',function(){
|
||||
$('.payment-amount > .menu').slideDown('fast')
|
||||
})
|
||||
$('.payment-amount > input').on('focusout',function(){
|
||||
$('.payment-amount > .menu').slideUp('slow')
|
||||
})
|
||||
checkout.dialog('','Loading ...','PROGRESS')
|
||||
checkout.init(0)
|
||||
// jx.dom.set.focus('amount')
|
||||
var d = new Date()
|
||||
$('#copyright').text(d.getFullYear())
|
||||
|
||||
setTimeout(function(){
|
||||
jx.modal.close()
|
||||
},1700)
|
||||
})
|
||||
</script>
|
||||
<div style="display:grid; justify-items:center; align-items:center; height:99%;">
|
||||
<div class="frame">
|
||||
<div class="product-info">
|
||||
<div class="caption">{{product.name}}</div>
|
||||
<div align="center">
|
||||
<div class="border-round border" style="background-color: #f3f3f3; padding:32px; width:256px;">
|
||||
<img src="{{product.images[0]}}">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="description">
|
||||
{{product.metadata.about}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" style="vertical-align:middle; padding:4px;">
|
||||
|
||||
<div style="margin:4px;">
|
||||
<input type="text" placeholder="Email@domain.com" class="email" data-pattern="^[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)*@[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)+$" onkeyup="checkout.validate(event)"/>
|
||||
</div>
|
||||
<div style="margin:4px; display:grid; grid-template-columns:50% auto; gap:2px;">
|
||||
<input type="text" placeholder="First Name" class="fname" data-pattern="^[a-z,A-Z,-]{2,}$" onkeyup="checkout.validate(event)"/>
|
||||
<input type="text" placeholder="Last Name" class="lname" data-pattern="^[a-z,A-Z,-]{2,}$" onkeyup="checkout.validate(event)"/>
|
||||
</div>
|
||||
<div class="card-info">
|
||||
|
||||
|
||||
|
||||
<div class="" style="display:grid; grid-template-columns:50% auto; gap:2px">
|
||||
<div class="payment-amount">
|
||||
<input type="text" id="amount" class="amount" readonly="true" title="Amount" placeholder="00.00" onchange="checkout.validate(event)" style="text-align:right"/>
|
||||
<div class="border menu" style="margin:4px">
|
||||
<div style="margin:4px;">Choose an amount</div>
|
||||
{%for item in pricing %}
|
||||
<div class="item active" style="font-weight:lighter" onclick="checkout.init({{loop.index -1 }})">
|
||||
|
||||
{{item.unit_amount/100}} <span style="text-transform: uppercase">{{item.currency}}</span>
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div style="margin:4px"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="currency" style="display:grid; align-items:center" align="left"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="number" style="margin-top:2px; margin-bottom:2px; display:grid; gap:2px; grid-template-columns:64px auto;">
|
||||
<div class="" style="background:#f3f3f3; display:grid; align-items:center; justify-items:center"><img id="pay-icon" src="" style="width:32px; vertical-align:middle"/></div>
|
||||
<input type="text" placeholder="Card Number" class="number" data-pattern="card" onkeyup="checkout.validate(event)"/>
|
||||
</div>
|
||||
|
||||
<div class="expire" style="display:grid; grid-template-columns:50% auto; gap:2px;">
|
||||
<input type="text" placeholder="MM/YYYY" class="exp" data-pattern="^(0[1-9]|1[0-2])/202[0-9]$" onkeyup="checkout.validate(event)"/>
|
||||
<input type="text" placeholder="CVC" class="cvc" data-pattern="[0-9]{3,}" onkeyup="checkout.validate(event)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="location">
|
||||
<div class="state">
|
||||
<_input type="text" placeholder="State" data-pattern="[A-Z]+" value="USA" onkeyup="checkout.validate(event)"/>
|
||||
</div>
|
||||
<div class="zip">
|
||||
<_input type="text" placeholder="Zip Code" data-pattern="[0-9]{5,}" onkeyup="checkout.validate(event)"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style="padding:4px; margin-top:48px">
|
||||
<div class="active " align="center" style=" background-color:#f3f3f3; color:#4682b4; display:grid; ;align-items:center; height:48px; padding:4px;"
|
||||
onclick="checkout.proceed()"
|
||||
>
|
||||
|
||||
<div style="font-size:18px">
|
||||
|
||||
|
||||
Pay Now</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="small border-top" align="center" style="margin-top:32px; grid-column: 1 / span 2; font-weight:lighter; font-family:sans-serif">
|
||||
<p>The Phi Technology
|
||||
|
||||
- {{product.name}} © <span id="copyright"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
@ -0,0 +1,2 @@
|
||||
{"email": "nyemba@gmail.com", "fname": "steve", "lname": "nyemba", "amount": 2500, "number": "5555555555554444", "exp": "02/2024", "cvc": "666"}
|
||||
|
@ -0,0 +1,71 @@
|
||||
<!--
|
||||
*
|
||||
* This page is intended to perform a donate operation against payment backend
|
||||
|
||||
-->
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="cache-control" content="no-cache">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
|
||||
<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">
|
||||
<script src="https://kit.fontawesome.com/ab15160345.js" crossorigin="anonymous"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ context }}/static/js/jquery/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="{{ context }}/static/js/jx/rpc.js"></script>
|
||||
<script type="text/javascript" src="{{ context }}/static/js/jx/utils.js"></script>
|
||||
<script type="text/javascript" src="{{ context }}/static/js/jx/dom.js"></script>
|
||||
<script type="text/javascript" src="{{ context }}/static/js/jx/ext/modal.js"></script>
|
||||
<style>
|
||||
|
||||
.dialog {
|
||||
display:grid;
|
||||
padding:4px;
|
||||
display:grid;
|
||||
grid-template-columns: 25% auto;
|
||||
gap:2px;
|
||||
}
|
||||
.dialog-frame .title {display:grid; gap:2px; grid-template-columns:auto 32px;
|
||||
min-height:32px;
|
||||
border-top-left-radius: 6px;;
|
||||
border-top-right-radius: 6px;;
|
||||
color:#4682b4;
|
||||
align-items: center;
|
||||
padding:4px;
|
||||
}
|
||||
.dialog { margin-top:4px;}
|
||||
.dialog .icon { font-size:42px;justify-items:center; display:grid; align-items:center}
|
||||
.dialog .message {
|
||||
display:grid;
|
||||
align-items:center;
|
||||
padding:8px;
|
||||
font-weight:normal ;
|
||||
color:#4682B4 ;
|
||||
}
|
||||
.fa-exclamation-triangle{color:#ff6500 ;}
|
||||
.fa-cog {color:gray}
|
||||
.gray {color:#d3d3d3}
|
||||
.steel-blue{color:#4682B4;}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<div class="dialog-frame ">
|
||||
<div class="">
|
||||
<div class="title">
|
||||
<div class="bold" style="padding:4px; display:grid; align-items:center;">{{title}}</div>
|
||||
{% if type != 'PROGRESS' %}
|
||||
<div class="active" align="center" onclick="jx.modal.close()">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="dialog">
|
||||
<div class="icon">
|
||||
<i class="{{icon|safe}}"></i>
|
||||
</div>
|
||||
<div class="message">{{message|safe}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
Loading…
Reference in new issue