bug fixes with payments and donations

legacy
Steve L. Nyemba 4 years ago
parent 2b0209f004
commit 37845eb43b

@ -10,7 +10,7 @@
The store must be prefixed with an authentication module, because customers must be signed in before making a purchase. The store must be prefixed with an authentication module, because customers must be signed in before making a purchase.
""" """
from __future__ import division from __future__ import division
from flask import Flask, request, session, render_template, Response from flask import Flask, request, session, render_template, Response,redirect
from flask_session import Session from flask_session import Session
from flask_cors import CORS from flask_cors import CORS
from flask_pymongo import PyMongo from flask_pymongo import PyMongo
@ -54,12 +54,12 @@ CORS(app)
SYS_STORE = CONFIG['store'] SYS_STORE = CONFIG['store']
@app.route("/donate") # @app.route("/donate")
def donate() : # def _donate() :
print (request.args['amount'])
amount = float(request.args['amount']) if 'amount' in request.args else 5.0 # amount = float(request.args['amount']) if 'amount' in request.args else 5.0
args = {'amount':amount,'context':CONTEXT} # args = {'amount':amount,'context':CONTEXT}
return render_template('donate.html',**args) # return render_template('donate.html',**args)
@app.route("/") @app.route("/")
def index(): def index():
mystore = store.factory.instance(name='music') mystore = store.factory.instance(name='music')
@ -91,7 +91,7 @@ def to_session():
if info : if info :
for key in info : for key in info :
session[key] = info[key] session[key] = info[key]
return "{}" return "{}",200
@app.route("/signup",methods=['GET','POST']) @app.route("/signup",methods=['GET','POST'])
def signup(): def signup():
""" """
@ -137,7 +137,8 @@ def signup():
</script> </script>
""".replace(":uri",uri) """.replace(":uri",uri)
@app.route("/ui/signup/<id>",methods=['GET','PUT']) # @app.route("/ui/signup/<id>",methods=['GET','PUT'])
@app.route("/<id>/signup",methods=['GET','PUT'])
def signup_form(id): def signup_form(id):
""" """
This function loads the signup form with cloud-view authentication support This function loads the signup form with cloud-view authentication support
@ -154,7 +155,71 @@ def signup_form(id):
args['cloud-view'] = CONFIG['cloud-view'] args['cloud-view'] = CONFIG['cloud-view']
return render_template("signup.html",**args) return render_template("signup.html",**args)
@app.route("/ui/<id>") @app.route("/me")
def me ():
#
#{'user': {'uid': 'nyemba@gmail.com', 'uii': 'Steve Nyemba', 'usage': {'used': 1.012175691, 'size': 4.02653184, 'units': 'GB'}, 'sid': 'dropbox'}, 'key': 'nsfCKKUWunUAAAAAAAAAAUMzU6oR6qp9TK7t-_zop-sBOcEaM3eb1mRi_PG8bM69', 'features': {}}
# mystore = store.factory.instance(name = session['product']['name'])
# mystore.init(email=session['auth']['user']['uid'])
try:
email = session['auth']['user']['uid']
name = session['product']['name']
mystore = store.factory.instance(name=name,email=email)
payments = mystore.user.invoices()
cards = mystore.user.card.html() if mystore.user.card.exists() else None
_args = {'context':CONTEXT,"user":session['auth']['user'],"product":session['product'],"plan":session['plan'],"payments":payments,"cards":cards}
return render_template('me.html',**_args)
except Exception as e:
pass
return redirect("https://the-phi.com")
@app.route("/me/card",methods=['POST'])
def me_card ():
if 'auth' in session :
email = session['auth']['user']['uid']
name = session['product']['name']
mystore = store.factory.instance(name=name,email = email)
body = request.json
try:
mystore.user.card.add(card=body)
except Exception as e:
print (e)
return "0",200
return "1",200
# pricing = mystore.get.checkout() #-- pricing
# pricing = [{"unit_amount":500,"currency":"usd"},{"unit_amount":1000,"currency":"usd"},{"unit_amount":2500,"currency":"usd"}]
# args = {"product":session['product']}
else:
return "0",403
return render_template("card.html")
@app.route("/<id>/cancel",methods=['POST'])
def cancel(id):
#
# This function will cancel the user's current plan
STATUS = "1"
try:
if 'auth' in session :
email = session['auth']['user']['uid']
mystore = store.factory.instance(name=id,email=email)
_id = mystore.user.plan.sub_id()
mystore.cancel(_id)
session['plan'] = None
session['payments'] = mystore.user.invoices()
else:
STATUS = "0"
except Exception as e:
print (e)
STATUS = "0"
pass
return STATUS,200
# @app.route("/ui/<id>")
@app.route("/<id>/plans")
def get_plans_ui(id): def get_plans_ui(id):
""" """
This function loads the form for signup and information about the various plans for a given product. This function loads the form for signup and information about the various plans for a given product.
@ -167,9 +232,10 @@ def get_plans_ui(id):
description = mystore.product['metadata']['about'] if 'about' in mystore.product['metadata'] else '' description = mystore.product['metadata']['about'] if 'about' in mystore.product['metadata'] else ''
label = mystore.product['statement_descriptor'] label = mystore.product['statement_descriptor']
args = {"product":id,"label":label,"description":description,"context":CONTEXT,"plans":mystore.get.plans(),"now":datetime.now().year} args = {"product":id,"label":label,"description":description,"context":CONTEXT,"plans":mystore.get.plans(),"now":datetime.now().year}
args['product'] = mystore.product
args['theme'] = 'theme-clouds' args['theme'] = 'theme-clouds'
# print (mystore.product.keys()) # print (mystore.product.keys())
session['cloud-view'] = CONFIG['cloud-view'] session['cloud-view'] = CONFIG['cloud-view'].replace(':id',id)
session['product'] = mystore.product session['product'] = mystore.product
return render_template('plans.html',**args) return render_template('plans.html',**args)
@app.route("/goto/<id>",methods=['POST','GET']) @app.route("/goto/<id>",methods=['POST','GET'])
@ -193,7 +259,7 @@ def redirect(id ) :
return "0",403 return "0",403
pass pass
@app.route("/<id>/donate") @app.route("/<id>/donate")
def payme (id) : def donate (id) :
""" """
This will charge a given user/card for a given product. i.e it is designed for a one-time payment This will charge a given user/card for a given product. i.e it is designed for a one-time payment
""" """
@ -203,7 +269,18 @@ def payme (id) :
args = {"product":mystore.product,"plans":mystore.get.plans(),"context":CONTEXT,"pricing":pricing} args = {"product":mystore.product,"plans":mystore.get.plans(),"context":CONTEXT,"pricing":pricing}
return render_template("card.html",**args) return render_template("card.html",**args)
@app.route("/ui/dialog",methods=['POST']) @app.route("/<id>/pay")
def pay(id):
email = session['auth']['user']['uid']
fname = session['auth']['user']['uii'].split(' ')[0] if 'uii' in session['auth']['user'] else ''
lname = " ".join(session['auth']['user']['uii'].split(' ')[1:]) if 'uii' in session['auth']['user'] else ''
mystore = store.factory.instance(name=id,email=email) #--
amount = session['plan']['amount']
# cards = mystore.user.card.html() if mystore.user.card.exists() else None
_args = {"product":mystore.product,"pricing":None,"context":CONTEXT,"email":email,"lname":lname,"fname":fname,"amount":amount} #,"cards":cards}
return render_template("card.html",**_args)
@app.route("/html/dialog",methods=['POST'])
def get_dialog(): def get_dialog():
info = request.get_json() info = request.get_json()
@ -213,33 +290,61 @@ def get_dialog():
@app.route('/<id>/pay',methods=['POST','PUT']) @app.route('/<id>/pay',methods=['POST','PUT'])
def _payme(id) : def _payme(id) :
info = request.get_json() info = request.get_json()
mystore = store.factory.instance(name=id,email=info['email']) mystore = store.factory.instance(name=id,email=info['email'])
user = mystore.user user = mystore.user
# #
# ... Let us massage the data so it can go through properly # ... Let us massage the data so it can go through properly
_args = {}
if 'has_card' not in info and not info['has_card'] :
card = {"number":info['number'],"cvc":info['cvc']} card = {"number":info['number'],"cvc":info['cvc']}
card['exp_month'] = info['exp'].split("/")[0] card['exp_month'] = info['exp'].split("/")[0]
card['exp_year'] = info['exp'].split("/")[1] card['exp_year'] = info['exp'].split("/")[1]
card['name'] = " ".join([info["fname"],info["lname"]]) card['name'] = " ".join([info["fname"],info["lname"]])
_args = {"card":card,"email":info['email'],"amount":info['amount'],"currency":"usd"} _args = {"email":info['email'],"amount":info['amount'],"currency":"usd"}
else:
#
# Let us make a payment using the source we already have
card = {}
_args['card'] = card
# #
# if a plan information is provided we are running the card for the plan ? # if a plan information is provided we are running the card for the plan ?
# #
if 'plan' in info : if 'plan' in session :
#
# We are dealing with a subscription here and we should run this through the subscription engine
# and NOT charge the user immediately (handled by stripe)
#
# _args['amount'] = info['plan']['amount']
plan = session['plan']
_args['metadata'] = {"payment":plan['nickname'],"product":id}
_args['plan'] = plan
if user.plan.info() :
r = mystore.plan.upgrade(plan['id'],info['email'])
else:
r = mystore.plan.subscribe(id=plan['id'],email=info['email'])
pass pass
else: else:
# #
# For now we consider this a donation # For now we consider this a donation or other payment
_args['metadata'] = {"payment":"donation","product":id} _args['metadata'] = {"payment":"charge","product":id}
_args['description'] = " ".join([id,_args['metadata']['payment']]) _args['statement_descriptor'] = " ".join([str(info['amount']),id])
r = user.charge(**_args) r = user.charge(**_args)
if not r : if not r :
msg,status = "Problem enountered with card",403 msg,status = "Payment error enountered",403
else : else :
msg,status = "Payment accepted, Thank you", 200 msg,status = "Payment accepted, Thank you", 200
return Response(msg,status=status) return Response(msg,status=status)
@app.route("/me/logout",methods=["POST","GET"])
def logout():
session.clear()
return "1",200
@app.route("/init/<product>",methods=['POST']) @app.route("/init/<product>",methods=['POST'])
def init(product): def init(product):
@ -337,7 +442,8 @@ def get_plans(product) :
else: else:
return [] return []
@app.route('/features/<product>') # @app.route('/features/<product>')
@app.route('/<product>/features')
def features(product): def features(product):
""" """
This function returns the plan/features of a user for a given application if provided a uid (email) This function returns the plan/features of a user for a given application if provided a uid (email)
@ -361,96 +467,97 @@ def features(product):
# #
# formatting plans for the output # formatting plans for the output
# #
return json.dumps(plans) return json.dumps(plans),{"content-type":"application/json"}
""" # """
This function returns a user's plans/status for an application # This function returns a user's plans/status for an application
@header uid user's email address # @header uid user's email address
""" # """
@app.route('/status/<app_name>') # @app.route('/status/<app_name>')
def status(app_name): # def status(app_name):
uid = request.headers['uid']
couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
handler = Domain.User(couchdb.dbase,stripe)
handler.initialize(uid)
#lsub = handler.subscriptions()
plans = handler.plans()
return json.dumps(plans)
"""
This endpoint is the signup form for a given application,
It will allow user's to be able to signup for various plans
@pre 'uid' in request.headers
"""
@app.route('/signup/<product>')
def _signup(product) :
apikey = CONFIG['stripe']['pub'].strip()
# # uid = request.headers['uid']
# This function returns the plans for a given application
# We assume the application name is the prefix of the plan identifier in stripe
#
if 'user-info' in session :
_info = session['user-info']
session['uid'] = _info['uid']
uid = session['uid'] if 'uid' in session else ''
can_purchase = True
if uid is None :
uid = request.args.get('uid') if 'uid' in request.args else None
plans = get_plans(product)
# # couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
# @TODO: Mark the plans the current user is signed up for # handler = Domain.User(couchdb.dbase,stripe)
# # handler.initialize(uid)
if 'themes' in CONFIG: # #lsub = handler.subscriptions()
theme = CONFIG['themes'][product] if product in CONFIG['themes'] else CONFIG['theme']['default'] # plans = handler.plans()
else: # return json.dumps(plans)
theme = '' # """
platform='web' if 'platform' not in request.args else request.args['platform'] # This endpoint is the signup form for a given application,
alias = plans[0]['product_alias'] # It will allow user's to be able to signup for various plans
if 'user-plan' in session : # @pre 'uid' in request.headers
user_plans = [item['id'] for item in session['user-plan']] # """
else : # @app.route('/signup/<product>')
user_plans = [] # @app.route('/<product>/signup')
# active_plan = session['active-plans'] if 'active-plans' in session else [] # def _signup(product) :
args = {"context":CONTEXT,"theme":theme,"uid":uid,"alias":alias,"platform":platform,"app_name":product,"apikey":apikey,"plans":plans} # apikey = CONFIG['stripe']['pub'].strip()
args['active_plans'] = user_plans
return render_template('subscribe.html',**args) #context=CONTEXT,uid=uid,alias=alias,platform=platform,app_name=product,plans=plans,apikey=apikey) # #
# # This function returns the plans for a given application
# # We assume the application name is the prefix of the plan identifier in stripe
# #
# if 'user-info' in session :
# _info = session['user-info']
# session['uid'] = _info['uid']
# uid = session['uid'] if 'uid' in session else ''
# can_purchase = True
# if uid is None :
# uid = request.args.get('uid') if 'uid' in request.args else None
# plans = get_plans(product)
@app.route('/subscribe/<name>',methods=['DELETE']) # #
def cancel_subscribe(name) : # # @TODO: Mark the plans the current user is signed up for
pass # #
""" # if 'themes' in CONFIG:
This function defines if a given user is a customer or not # theme = CONFIG['themes'][product] if product in CONFIG['themes'] else CONFIG['theme']['default']
We should be able to tell by how we create customers # else:
""" # theme = ''
@app.route('/checkout/<app_name>',methods=['GET']) # platform='web' if 'platform' not in request.args else request.args['platform']
def is_customer (app_name): # alias = plans[0]['product_alias']
uid = request.args.get('uid') # if 'user-plan' in session :
pid = request.args.get('pid') # user_plans = [item['id'] for item in session['user-plan']]
couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False) # else :
r = couchdb.view('federation/uid_map',key=uid) # user_plans = []
id = r[0]['value'] # # active_plan = session['active-plans'] if 'active-plans' in session else []
couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=id,create=False) # args = {"context":CONTEXT,"theme":theme,"uid":uid,"alias":alias,"platform":platform,"app_name":product,"apikey":apikey,"plans":plans}
info = couchdb.read() # args['active_plans'] = user_plans
lsub = info['subscriptions']
# return render_template('subscribe.html',**args) #context=CONTEXT,uid=uid,alias=alias,platform=platform,app_name=product,plans=plans,apikey=apikey)
plans = [dict(dict(item['plan'],**{"status":item['status']}),**{"subscription":item['id']}) for item in lsub]
if pid is not None: # @app.route('/subscribe/<name>',methods=['DELETE'])
plans = [item for item in plans if item['id'] == pid] # def cancel_subscribe(name) :
# # pass
# Caching the subscription identifiers so we can create an invoice later on (if need be) # """
# @TODO Improve this process later on by allowing user's to pay for what they can (not everything) # This function defines if a given user is a customer or not
# # We should be able to tell by how we create customers
session['plans'] = plans # """
# @app.route('/checkout/<app_name>',methods=['GET'])
# def is_customer (app_name):
# uid = request.args.get('uid')
# pid = request.args.get('pid')
# couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
# r = couchdb.view('federation/uid_map',key=uid)
# id = r[0]['value']
# couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=id,create=False)
# info = couchdb.read()
# lsub = info['subscriptions']
# plans = [dict(dict(item['plan'],**{"status":item['status']}),**{"subscription":item['id']}) for item in lsub]
# if pid is not None:
# plans = [item for item in plans if item['id'] == pid]
# #
# # Caching the subscription identifiers so we can create an invoice later on (if need be)
# # @TODO Improve this process later on by allowing user's to pay for what they can (not everything)
# #
# session['plans'] = plans
session['plans'] # session['plans']
amount = sum([item['amount'] for item in plans]) # amount = sum([item['amount'] for item in plans])
apikey = CONFIG['stripe']['pub'].strip() # apikey = CONFIG['stripe']['pub'].strip()
amount = amount / 100 # amount = amount / 100
return render_template('bill.html',context=CONTEXT,apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount) # return render_template('bill.html',context=CONTEXT,apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount)
if __name__ == '__main__' : if __name__ == '__main__' :
# #

@ -3,9 +3,14 @@ var cards = {
"^4\\d{3}(| |-)(?:\\d{4}\\1){2}\\d{4}$":"visa","^6(?:011|5\\d\\d)(| |-)(?:\\d{4}\\1){2}\\d{4}$":"discover"} "^4\\d{3}(| |-)(?:\\d{4}\\1){2}\\d{4}$":"visa","^6(?:011|5\\d\\d)(| |-)(?:\\d{4}\\1){2}\\d{4}$":"discover"}
var payment = {} var payment = {}
payment.setup = function(context,pricing){ payment.setup = function(context,pricing,product_name){
sessionStorage.store_context = context sessionStorage.store_context = context
if (pricing != null){
sessionStorage.pricing = pricing.constructor == String ? pricing: JSON.stringify(pricing) sessionStorage.pricing = pricing.constructor == String ? pricing: JSON.stringify(pricing)
}else{
sessionStorage.pricing = "[]"
}
sessionStorage.product_name = product_name
sessionStorage.customer = JSON.stringify({info:{}}) sessionStorage.customer = JSON.stringify({info:{}})
$('.payment-amount > input').on('focusin',function(){ $('.payment-amount > input').on('focusin',function(){
@ -27,7 +32,10 @@ payment.setup = function(context,pricing){
} }
payment.init = function(index){ payment.init = function(index){
var p = JSON.parse(sessionStorage.pricing) var p = JSON.parse(sessionStorage.pricing)
console.log(p)
if(p.length == 0){
return ;
}
var _item = p[index] var _item = p[index]
var amount = (_item.unit_amount/100).toFixed(2) var amount = (_item.unit_amount/100).toFixed(2)
$('.amount').val( amount) $('.amount').val( amount)
@ -39,8 +47,11 @@ payment.init = function(index){
} }
payment.validate = function(e){ payment.validate = function(e){
var id = e.target var id = e.target
console.log([id,e])
customer = JSON.parse(sessionStorage.customer) customer = JSON.parse(sessionStorage.customer)
if($(id).attr('class').match(/number|cvc|exp/) && sessionStorage.has_card){
return 1 ;
}
$(id).removeClass('error') $(id).removeClass('error')
var pattern = $(id).attr('data-pattern') var pattern = $(id).attr('data-pattern')
var field = $(id).attr('class') var field = $(id).attr('class')
@ -78,6 +89,7 @@ payment.validate = function(e){
sessionStorage.customer = JSON.stringify(customer) sessionStorage.customer = JSON.stringify(customer)
return 1 return 1
}else{ }else{
// id.className = id.className + ' error'
$(id).addClass('error') $(id).addClass('error')
return 0 return 0
} }
@ -86,7 +98,7 @@ payment.validate = function(e){
} }
payment.dialog = function(title,message,type){ payment.dialog = function(title,message,type){
ICONS = {'WARN':'fas fa-exclamation-triangle','CHECK':'fas fa-check fa-3x','PROGRESS':'fas fa-cog fa-spin'} ICONS = {'WARN':'fas fa-exclamation-triangle','CHECK':'fas fa-check fa-3x','PROGRESS':'fas fa-cog fa-spin'}
var uri = ([sessionStorage.store_context,'/ui/dialog']).join('') var uri = ([sessionStorage.store_context,'/html/dialog']).join('')
var info = {title:title,message:message,icon:ICONS[type],type:type} var info = {title:title,message:message,icon:ICONS[type],type:type}
var httpclient = HttpClient.instance() var httpclient = HttpClient.instance()
@ -96,22 +108,24 @@ payment.dialog = function(title,message,type){
jx.modal.show({html:x.responseText,id:'payment-dialog'}) jx.modal.show({html:x.responseText,id:'payment-dialog'})
}) })
} }
payment.proceed = function(){ payment.proceed = function(pointer){
jx.utils.patterns.visitor($('input'),function(_input){ var values = jx.utils.patterns.visitor($('input'),function(_input){
// //
// re-running validation // re-running validation
if ($(_input).attr('class') != 'amount'){ return payment.validate({target:_input})
$(_input).keyup() // if ($(_input).attr('class') != 'amount'){
}else { // $(_input).keyup()
$(_input).change() // }else {
}
// $(_input).change()
// }
}) })
var N = $('input').length var N = $('input').length
info = JSON.parse(sessionStorage.customer).info info = JSON.parse(sessionStorage.customer).info
var n = jx.utils.keys(info).length var n = jx.utils.keys(info).length
if (n != N){ if (n != jx.math.sum(values) && ! sessionStorage.has_card){
// //
// Nothing to be done here because the user interface would have lit-up // Nothing to be done here because the user interface would have lit-up
// //
@ -123,10 +137,13 @@ payment.proceed = function(){
payment.dialog('Preparing payment','Please wait ...','PROGRESS') payment.dialog('Preparing payment','Please wait ...','PROGRESS')
info.amount = info.amount * 100 info.amount = info.amount * 100
$('.dialog > .message').text('Processing card, please wait ...') $('.dialog > .message').text('Processing card, please wait ...')
var uri = ([sessionStorage.store_context,'/{{product.name|safe}}/pay']).join('/') var uri = ([sessionStorage.store_context,sessionStorage.product_name,'pay']).join('/')
var http = HttpClient.instance() var http = HttpClient.instance()
http.setData(JSON.stringify(info)) if(sessionStorage.has_card == "1"){
http.setHeader('content-type','application/json') info.has_card = true
}
console.log(info)
http.setData(info,"application/json")
http.post(uri,function(x){ http.post(uri,function(x){
if(x.status == 200){ if(x.status == 200){
@ -143,9 +160,38 @@ payment.proceed = function(){
TITLE='.::' TITLE='.::'
TYPE='WARN' TYPE='WARN'
} }
$('.jxmodal')[$('.jxmodal')].remove() $('.jxmodal')[$('.jxmodal').length -1 ].remove()
payment.dialog(TITLE,x.responseText,TYPE) payment.dialog(TITLE,x.responseText,TYPE)
if (payment.do_post !=null){
payment.do_post()
}
}) })
} }
} }
payment.card_onfile = function(){
var name = $('.card_onfile > i').attr('class')
if (name.match(/square/i)){
$('.card_onfile > i').attr('class','fas fa-check')
$('.number').slideUp( function(){
$('.expire').slideUp()
})
var pointer = function(){}
sessionStorage.has_card = 1
}else{
delete sessionStorage.has_card
$('.card_onfile > i').attr('class','far fa-square')
$('.number').slideDown( function(){
$('.expire').slideDown()
})
pointer = function(){
payment.validate(event)
}
}
jx.utils.patterns.visitor(['.number','.exp','.cvc'],function(id){
$(id).click = pointer
})
}

@ -28,12 +28,21 @@ class User :
""" """
try: try:
card = dict(_args['card'] ) card = dict(_args['card'] )
values = card['exp'].split('/') if 'exp' in card else None,None
if 'exp' in card :
card['exp_month'] = card['exp'].split('/')[0]
card['exp_year'] = int(card['exp'].split('/')[1])
del card['exp']
for field in ['name','email'] : for field in ['name','email'] :
if field in card : if field in card :
self.me['customer'][field] = card[field]
del card[field] del card[field]
# # tok_1HPyukEUWsmgY81ALQuKGBmT
# address information should be added to the card # address information should be added to the card
# #
# save_card = _args['save'] if 'save' in _args else 0 # save_card = _args['save'] if 'save' in _args else 0
@ -47,14 +56,32 @@ class User :
del card['currency'] del card['currency']
else: else:
currency = 'usd' currency = 'usd'
r = stripe.source.Source.create(type='card',currency=currency,card=card) for item in self.me['payments'] :
stripe.customer.Customer.delete_source(self.me['customer']['id'],item['id'])
#
#
owner = {"email":self.me['customer']['email'],"name":self.me['customer']['name']}
# r = stripe.source.Source.create(type='card',currency=currency,card=card)
r = stripe.token.Token.create(card=card)
o = stripe.source.Source.create(type='card',currency=currency,owner=owner,token=r.id,usage='reusable')
o = stripe.customer.Customer.create_source(self.me['customer']['id'],source=o.id)
# r = stripe.source.Source.create(type='card',currency=currency,card=card)
self.me['payments'] = [r.card.to_dict_recursive()]+self.me['payments'] self.me['payments'] = [r.card.to_dict_recursive()]+self.me['payments']
self.init(self.me["customer"]['email']) self.init(self.me["customer"]['email'])
return r return r
except Exception as error : except Exception as error :
print (error) print ([' ** ',error])
return 0 return 0
def invoices(self):
# r = stripe.charge.Charge.list(customer=self.me['customer']['id']).data
r = []
_logs = stripe.invoice.Invoice.list(customer=self.me['customer']['id'])
for item in _logs.auto_paging_iter() :
r += [{"id":item.id,"date":item.date*1000,"amount":item.amount_paid,"paid":item.paid,"status":item.status,"label":item.lines.data[0].description,"invoice":item.id,"pdf":item.invoice_pdf,"hosted":item.hosted_invoice_url} ]
# r += [{"id":item.id,"date":item.created*1000,"amount":item.amount,"paid":item.paid,"status":item.status,"label":item.statement_descriptor if item.statement_descriptor else item.calculated_statement_descriptor,"invoice":item.invoice} for item in logs]
return r
def charge(self,**args) : def charge(self,**args) :
""" """
This function will charge a user for a service (given a plan id) This function will charge a user for a service (given a plan id)
@ -118,7 +145,7 @@ class User :
self.card.exists = lambda: len(self.me["payments"]) > 0 self.card.exists = lambda: len(self.me["payments"]) > 0
self.card.add = self._add_card self.card.add = self._add_card
self.card.charge = self.charge self.card.charge = self.charge
self.card.html = lambda: [{} for card in self.me["payments"]] self.card.html = lambda: [{"number":card['last4'],"type":card['brand']} for card in self.me["payments"]]
self.card.delete = lambda index: stripe.customer.Customer.delete_source(self.me["customer"]['id'],self.me["payment"][index]) self.card.delete = lambda index: stripe.customer.Customer.delete_source(self.me["customer"]['id'],self.me["payment"][index])
def init(self,email): def init(self,email):
@ -154,7 +181,7 @@ class User :
if 'sources' in customer and 'data' in customer['sources'] : if 'sources' in customer and 'data' in customer['sources'] :
if customer['sources']['data'] : if customer['sources']['data'] :
# self.payment = [ card.to_dict_recursive() for card in customers['sources']['data'] ] # self.payment = [ card.to_dict_recursive() for card in customers['sources']['data'] ]
self.me['payments'] = [ card.to_dict_recursive() for card in customer['sources']['data'] ] self.me['payments'] = [ card.card.to_dict_recursive() if 'card' in card else card.card.to_dict_recursive() for card in customer['sources']['data'] ]
# if not self._has_plan(customer) and free_plan: # if not self._has_plan(customer) and free_plan:
# free_plan = free_plan[0] # free_plan = free_plan[0]
# self.subscribe(free_plan['id'],email) # self.subscribe(free_plan['id'],email)
@ -291,15 +318,18 @@ class Plans(Store) :
_item['amount'] = item['unit_amount'] _item['amount'] = item['unit_amount']
if item.recurring == None : if item.recurring == None :
self.checkout.append(_item) self.checkout.append(_item)
elif 'features' in item.metadata : else:
_item['metadata']['features'] = json.loads(item.metadata['features'])
self.plans.append(_item) self.plans.append(_item)
if 'features' in item.metadata :
_item['metadata']['features'] = json.loads(item.metadata['features'])
else:
_item['metadata']['features'] = {}
# self.plans.append(_item)
# index = self.plans.index(item) # index = self.plans.index(item)
# self.plans[index] = item.to_dict_recursive() # self.plans[index] = item.to_dict_recursive()
# if 'features' in self.plans[index]['metadata'] : # if 'features' in self.plans[index]['metadata'] :
# self.plans[index]['metadata']['features'] = json.loads(self.plans[index]['metadata']['features']) # self.plans[index]['metadata']['features'] = json.loads(self.plans[index]['metadata']['features'])
# elif self.plans # elif self.plans
self.plans.sort(key=lambda item: item['unit_amount']) self.plans.sort(key=lambda item: item['unit_amount'])
self.checkout.sort(key=lambda item:item['unit_amount']) self.checkout.sort(key=lambda item:item['unit_amount'])
if 'email' in args : if 'email' in args :
@ -373,7 +403,7 @@ class Plans(Store) :
self.user.init(email) self.user.init(email)
# #
# We must insure the user is one of our customers i.e perhaps # We must insure the user is one of our customers i.e perhaps
if amount == 0 or self.payment : if amount == 0 or self.payment and plan:
r = stripe.subscription.Subscription.create( r = stripe.subscription.Subscription.create(
customer = self.user.info()['id'], customer = self.user.info()['id'],
items = [plan] items = [plan]

@ -3,16 +3,24 @@
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<link rel="shortcut icon" href="{{product.images[0]}}"> <link rel="shortcut icon" href="{{product.images[0]}}">
<script type="text/javascript" src="{{ context }}/static/js/jquery/jquery.min.js"></script>
<link rel="stylesheet" href="{{context}}/static/css/borders.css" type="text/css"> <link rel="stylesheet" href="{{context}}/static/css/borders.css" type="text/css">
<link rel="stylesheet" href="{{context}}/static/css/fa/css/all.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/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/utils.js"></script>
<script src="{{context}}/static/js/jx/dom.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/rpc.js"></script>
<script src="{{context}}/static/js/jx/ext/modal.js"></script> <script src="{{context}}/static/js/jx/ext/modal.js"></script>
<script src="{{context}}/static/js/jx/ext/math.js"></script>
<script src="{{context}}/static/js/payment.js"></script> <script src="{{context}}/static/js/payment.js"></script>
<title>
{% if not product.metadata.label %}
{{product.name}}
{% else %}
{{product.metadata.label}}
{% endif %}
</title>
<style> <style>
@ -75,7 +83,8 @@
height:48px; height:48px;
border:2px solid transparent; border:2px solid transparent;
padding:6px; padding:6px;
font-weight:lighter; font-weight:normal;
color:#000000;
background-color:#f3f3f3; background-color:#f3f3f3;
} }
.fa-exclamation-triangle{ .fa-exclamation-triangle{
@ -195,9 +204,11 @@
</style> </style>
<script > <script >
$(document).ready(function(){ $(document).ready(function(){
$('head').append('<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">') $('head').append('<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">')
payment.setup("{{context|safe}}",{{pricing|tojson}},"{{product.name|safe}}")
payment.dialog('','Loading ...','PROGRESS') payment.dialog('','Loading ...','PROGRESS')
payment.setup("{{context|safe}}",{{pricing|tojson}})
setTimeout(function(){ setTimeout(function(){
jx.modal.close() jx.modal.close()
@ -229,9 +240,15 @@
<div class="payment-pane"> <div class="payment-pane">
<div class="frame"> <div class="frame">
<div class="caption title" align="center" style="display:grid; gap:2px; grid-template-columns: auto 32px; align-items:center; justify-items:center"> <div class="caption title" align="center" style="display:grid; gap:2px; grid-template-columns: auto 32px; align-items:center; justify-items:center; text-transform:capitalize">
<div class="bold">{{product.name}}</div> <div class="bold">
{% if not product.metadata.label %}
{{product.name}}
{% else %}
{{product.metadata.label}}
{% endif %}
</div>
<div class="active close-dialog" onclick="jx.modal.close()"><i class="fa fa-times"></i></div> <div class="active close-dialog" onclick="jx.modal.close()"><i class="fa fa-times"></i></div>
</div> </div>
@ -243,21 +260,29 @@
</div> </div>
<div class="description"> <div class="description" align="center">
<!--
@TODO: if a plan is being loaded it should display here
-->
{{product.metadata.about}} {{product.metadata.about}}
</div> </div>
</div> </div>
<div class="card" style="vertical-align:middle;"> <div class="card" style="vertical-align:middle;">
<div style="margin-top:2px"> <div style="margin-top:2px">
{% if email %}
<input value="{{email}}" type="text" placeholder="Email@domain.com" class="email" data-pattern="^[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)*@[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)+$" readonly />
{% else %}
<input type="text" placeholder="Email@domain.com" class="email" data-pattern="^[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)*@[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)+$" onkeyup="payment.validate(event)"/> <input type="text" placeholder="Email@domain.com" class="email" data-pattern="^[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)*@[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)+$" onkeyup="payment.validate(event)"/>
{% endif %}
</div> </div>
<div style="display:grid; grid-template-columns:50% 50%; gap:2px; margin-top:2px;"> <div style="display:grid; grid-template-columns:50% 50%; gap:2px; margin-top:2px;">
<div> <div>
<input type="text" placeholder="First Name" class="fname" data-pattern="^[a-z,A-Z,-]{2,}$" onkeyup="payment.validate(event)"/> <input value="{{fname|safe}}" type="text" placeholder="First Name" class="fname black" data-pattern="^[a-z,A-Z,-]{2,}$" onkeyup="payment.validate(event)"/>
</div> </div>
<div> <div>
<input type="text" placeholder="Last Name" class="lname" data-pattern="^[a-z,A-Z,-]{2,}$" onkeyup="payment.validate(event)"/> <input value="{{lname|safe}}" type="text" placeholder="Last Name" class="lname black" data-pattern="^[a-z,A-Z,-]{2,}$" onkeyup="payment.validate(event)"/>
</div> </div>
</div> </div>
<div class="card-info" style="margin-top:2px"> <div class="card-info" style="margin-top:2px">
@ -266,18 +291,23 @@
<div class="" style="display:grid; grid-template-columns:50% auto; gap:2px"> <div class="" style="display:grid; grid-template-columns:50% auto; gap:2px">
<div class="payment-amount"> <div class="payment-amount">
{% if pricing %}
<input type="text" id="amount" class="amount" readonly="true" title="Amount" placeholder="00.00" onchange="payment.validate(event)" style="text-align:right"/> <input type="text" id="amount" class="amount" readonly="true" title="Amount" placeholder="00.00" onchange="payment.validate(event)" style="text-align:right"/>
<div class="border payment-list" style="margin:4px"> <div class="border payment-list" style="margin:4px">
<div style="margin:4px;">Choose an amount</div> <div style="margin:4px;">Choose an amount</div>
{%for item in pricing %} {%for item in pricing %}
<div class="item active" style="font-weight:lighter" onclick="payment.init({{loop.index -1 }})"> <div class="item active" style="font-weight:lighter" onclick="payment.init({{loop.index -1 }})">
{{item.unit_amount/100}} <span style="text-transform: uppercase">{{item.currency}}</span> {{'%0.2f'| format(item.unit_amount/100|float) }} <span style="text-transform: uppercase">{{item.currency}}</span>
</div> </div>
{% endfor %} {% endfor %}
<div style="margin:4px"></div> <div style="margin:4px"></div>
</div> </div>
{% else %}
<input type="text" value= "{{'%0.2f'| format(amount/100|float)}}" readonly id="amount" class="amount" title="Amount" placeholder="00.00" />
{% endif %}
</div> </div>
<div class="currency bold" style="padding:4px; display:grid; align-items:center" align="left"></div> <div class="currency bold" style="padding:4px; display:grid; align-items:center" align="left"></div>
@ -305,14 +335,28 @@
<div class="pay-frame"> <div class="pay-frame">
{% if not pricing %}
<div class="active" onclick="payment.card_onfile()" style="padding:8px; margin-bottom:4px; align-items:center; display:grid; grid-template-columns:28px auto; gap:2px; font-size:14">
<div class="card_onfile" >
<i class="far fa-square"></i>
</div>
<div>
Use Card On File
</div>
</div>
{% endif %}
<div class="active " align="center" style=" background-color:#f3f3f3; color:#4682b4; display:grid; ;align-items:center; height:48px; padding:4px;" <div class="active " align="center" style=" background-color:#f3f3f3; color:#4682b4; display:grid; ;align-items:center; height:48px; padding:4px;"
onclick="payment.proceed()" onclick="payment.proceed()"
> >
<div style="font-size:18px" class="bold"> <div style="font-size:18px" class="bold">
{% if not pricing %}
Pay Now</div> Signup
{% else %}
Make Payment
{% endif %}
</div>
</div> </div>
</div> </div>

@ -37,8 +37,9 @@
display:grid; display:grid;
align-items:center; align-items:center;
padding:8px; padding:8px;
font-size:14px;
font-weight:normal ; font-weight:normal ;
color:#4682B4 ; color:#000000 ;
} }
.dialog-frame .fa-exclamation-triangle{color:#ff6500 ;} .dialog-frame .fa-exclamation-triangle{color:#ff6500 ;}
.dialog-frame .fa-cog {color:gray} .dialog-frame .fa-cog {color:gray}

@ -1,9 +1,15 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<link rel="shortcut icon" href="{{context}}/static/img/logo-0.png"> {% if product.images|length > 0 %}
<link rel="shortcut icon" href="{{product.images[0]}}">
{% else %}
<link rel="shortcut icon" href="{{context}}/static/img/logo-0.png">
{% endif %}
<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css"> <link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">
<link rel="stylesheet" href="{{context}}/static/css/dialog.css" type="text/css"> <link rel="stylesheet" href="{{context}}/static/css/dialog.css" type="text/css">
<link rel="stylesheet" href="{{context}}/static/css/themes.css" type="text/css">
<link rel="stylesheet" href="{{context}}/static/css/plans.css" type="text/css">
<script src="https://kit.fontawesome.com/ab15160345.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/ab15160345.js" crossorigin="anonymous"></script>
<!-- <!--
@ -16,17 +22,27 @@
<script type="text/javascript" src="{{ context }}/static/js/jx/utils.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/dom.js"></script>
<script type="text/javascript" src="{{ context }}/static/js/jx/ext/modal.js"></script> <script type="text/javascript" src="{{ context }}/static/js/jx/ext/modal.js"></script>
<script type="text/javascript" src="{{ context }}/static/js/jx/ext/math.js"></script>
<script type="text/javascript" src="{{ context }}/static/js/jx/ext/cloud-view.js"></script> <script type="text/javascript" src="{{ context }}/static/js/jx/ext/cloud-view.js"></script>
<script type="text/javascript" src="{{ context }}/static/js/payment.js"></script>
<title style="text-transform: capitalize">
{% if product.metadata.label %}
{{product.metadata.label}}
{% else %}
{{product.name.replace('-',' ')}}
{% endif %}
</title>
<style>
<title style="text-transform: capitalize">{{product.replace('-',' ')}}</title> </style>
<script> <script>
sessionStorage.context = "{{context|safe}}" sessionStorage.context = "{{context|safe}}"
var url = {{ session['cloud-view'] |tojson}} var url = "{{ session['cloud-view'] |safe}}"
var signup_form = function(index,caller){ var signup_form = function(index,caller){
var url = '{{context}}/ui/signup/{{product}}?index=:index'.replace(/:index/,index) var url = '{{context}}/{{product.name|safe}}/signup?index=:index'.replace(/:index/,index)
var httpclient = HttpClient.instance() var httpclient = HttpClient.instance()
httpclient.get(url,function(x){ httpclient.get(url,function(x){
var html = x.responseText ; var html = x.responseText ;
@ -40,8 +56,14 @@
var price = parseFloat(data.unit_amount / 100 ).toFixed(2) var price = parseFloat(data.unit_amount / 100 ).toFixed(2)
price = ([price, data.recurring.interval]).join(' / ') price = ([price, data.recurring.interval]).join(' / ')
} }
jx.dom.set.value('plan-price',price) jx.dom.set.value('plan-price',price)
var image = jx.dom.get.attribute('product-logo','src')
jx.dom.set.attribute('plan-image','src',image)
//jx.dom.set.focus('email') //jx.dom.set.focus('email')
bind (data) bind (data)
//bind( JSON.parse( $(caller).attr('data')) ) //bind( JSON.parse( $(caller).attr('data')) )
@ -52,13 +74,14 @@
var http = HttpClient.instance() var http = HttpClient.instance()
http.get('{{context}}/card.html') http.get('{{context}}/card.html')
} }
console.log("{{ session['cloud-view']}}")
/* /*
* Binding buttons to oauth 2.0 interface * Binding buttons to oauth 2.0 interface
*/ */
var bind = function(data){ var bind = function(data){
var buttons = $('.signup-button') var buttons = $('.signup-button') ;
jx.utils.patterns.visitor(buttons,function(button){ jx.utils.patterns.visitor(buttons,function(button){
$(button).attr('data',data) $(button).attr('data',data)
$(button).click(function(event){ $(button).click(function(event){
@ -68,7 +91,10 @@
var host = window.location.href.match(/^.+\/\/([a-z,-,.]+)\/.+$/)[1] var host = window.location.href.match(/^.+\/\/([a-z,-,.]+)\/.+$/)[1]
var key = ([protocol,'://',host,'/cloud-view/oauth']).join('') var key = ([protocol,'://',host,'/cloud-view/oauth']).join('')
//jx.cloudview.init(host,protocol) //jx.cloudview.init(host,protocol)
$('.auth').slideUp(function(){
$('.dialog').slideDown()
jx.dom.set.value('message','Waiting for <b>'+id+'</b>')
})
jx.cloudview.init("{{session['cloud-view']|safe}}") jx.cloudview.init("{{session['cloud-view']|safe}}")
jx.cloudview.oauth.init(id,key,function(p){ jx.cloudview.oauth.init(id,key,function(p){
@ -77,12 +103,19 @@
// At this point we need to provide a screen to provide credit card information // At this point we need to provide a screen to provide credit card information
// And or redirect to application framework // And or redirect to application framework
// //
if (p == null){
$('.dialog').slideUp(function(){
$('.auth').slideDown()
})
return
}
var info = {"auth":p,"plan":data} var info = {"auth":p,"plan":data}
var httpclient = HttpClient.instance() var httpclient = HttpClient.instance()
httpclient.setHeader("Content-Type","application/json") httpclient.setHeader("Content-Type","application/json")
httpclient.setData(JSON.stringify(info) ) httpclient.setData(JSON.stringify(info) )
httpclient.post('{{context|safe}}/store',function(x){}) httpclient.post('{{context|safe}}/store',function(x){})
if (data.unit_amount == 0){
if (data.unit_amount == 0|| data.amount == 0){
// //
// We should not be prompting the user but automatically logging her into her session // We should not be prompting the user but automatically logging her into her session
// //
@ -90,47 +123,7 @@
}else{ }else{
pay_now(p,data) pay_now(p,data)
/*var http = HttpClient.instance()
http.get('{{context}}/signup',function(x){
x = JSON.parse(x.responseText)
var form = jx.dom.get.instance('FORM')
form.id = data.id
form.method = 'POST'
form.action = '{{context}}/signup'
var script = jx.dom.get.instance('SCRIPT')
var input = jx.dom.get.instance('INPUT')
input.type = 'submit'
input.type = 'stripe-button'
script.setAttribute('data-key', x['data-key'].trim())
script.setAttribute('data-email', p.user.uid)
script.setAttribute('data-name', data.nickname)
script.setAttribute('data-amount', data.unit_amount)
script.setAttribute('data-description','{{description|safe}} ' + data.nickname)
script.src = 'https://checkout.stripe.com/checkout.js'
script.setAttribute('data-amount',data.unit_amount)
script.setAttribute('data-image','https://s3.amazonaws.com/stripe-uploads/acct_15kiA1EUWsmgY81Amerchant-icon-1493912370912-logo-0.png')
script.setAttribute('data-label',(['Pay $',data.unit_amount,data.interval]).join(' '))
script.className = 'stripe-button'
form.appendChild(script)
form.appendChild(input)
//jx.dom.set.value('form','')
//jx.dom.append('form',form)
jx.modal.close('signup')
$('#form').html('')
$('#form').append(form)
setTimeout(function(){
console.log('###')
$('.stripe-button-el').click()
},500)
})*/
} }
@ -143,7 +136,25 @@
/** /**
*/ */
var pay_now = function(auth,plan){ var pay_now = function(auth,plan){
console.log("oO") jx.modal.close()
var http = HttpClient.instance()
var body ={auth:auth,plan:plan}
http.setData(body)
http.post('{{context}}/store',function(x){
var _http = HttpClient.instance()
_http.get('{{context}}/{{product.name}}/pay',function(x){
jx.modal.show({html:x.responseText,id:'dialog'})
payment.do_post = redirect_now
payment.setup("{{context|safe}}",[],"{{product.name}}")
})
})
}
var _pay_now = function(auth,plan){
var http = HttpClient.instance() var http = HttpClient.instance()
http.get('{{context}}/signup',function(x){ http.get('{{context}}/signup',function(x){
x = JSON.parse(x.responseText) x = JSON.parse(x.responseText)
@ -195,20 +206,22 @@ var redirect_now = function(auth,plan){
var http = HttpClient.instance() var http = HttpClient.instance()
http.setHeader('Content-Type','application/json') http.setHeader('Content-Type','application/json')
http.setData(JSON.stringify(info)) http.setData(JSON.stringify(info))
http.post('{{context|safe}}/goto/{{product|safe}}',function(x){ http.post('{{context|safe}}/goto/{{product.name|safe}}',function(x){
var uri = x.getResponseHeader('location') //var uri = x.getResponseHeader('location')
var uri = '{{context}}/me'
if (uri != null){ if (uri != null){
var icon= '<i class="fas fa-cog fa-spin fa-3x"></i>'
var msg = 'Redirecting to {{product|safe}}' var msg = 'Redirecting to <b>{{product.name|safe}}</b>'
}else{ }else{
var icon= '<i class="fas fa-times fa-3x"></i>' var icon= '<i class="fas fa-times fa-3x"></i>'
var msg = "Error found, please try again" var msg = "Error found, please try again"
} }
jx.dom.set.value('icon',icon) if(jx.dom.exists('message')){
jx.dom.set.value('message',msg) jx.dom.set.value('message',msg)
}
$('.auth').slideUp(function(){ $('.auth').slideUp(function(){
$('.dialog').slideDown() $('.dialog').slideDown()
@ -229,30 +242,34 @@ var redirect_now = function(auth,plan){
}) })
} }
$(document).ready(function(){
jx.cloudview.init( "{{session['cloud-view']}}")
})
</script> </script>
<body > <body >
<div class="header border-bottom">
<div class="logo">
<div>
<img src="{{context}}/static/img/logo-0.png" align="left" style="margin:4px">
</div>
<div>
<div class="caption">The Phi Technology</div>
</div>
</div>
</div>
<div class="pricing-frame"> <div class="pricing-frame">
<div align="center" class="" style="padding:8px"> <div class="header">
<div align="right">
<img id='product-logo' src="{{product.images[0]}}">
</div>
<div class="caption" align="left">
{% if product.metadata.label %}
{{product.metadata.label}}
{% else %}
{{product.name.replace('-',' ')}}
{% endif %}
</div>
<div align="center" style="text-transform:capitalize;grid-row:2; grid-column:1 /span 2;">{{description}}</div>
<p><div class="caption">{{label}}</div>
<div class="" style="text-transform:capitalize">{{description}}</div>
</p>
</div> </div>
<div class="pricing"> <div class="pricing">
@ -273,6 +290,7 @@ var redirect_now = function(auth,plan){
{%else%} {%else%}
<div class="default" > <div class="default" >
<span class="bold">$ {{ item.unit_amount/100 }}</span> <span class="bold">$ {{ item.unit_amount/100 }}</span>
/ <span class="small">{{item.recurring.interval[:]}}</span> / <span class="small">{{item.recurring.interval[:]}}</span>
@ -298,10 +316,10 @@ var redirect_now = function(auth,plan){
<div align="right" class="border-top"> <div align="right" class="border-top">
<p> <p>
<div class="button " data='{{item|tojson|safe}}' onclick="signup_form( {{loop.index-1}} , this )"> <div align="center" style="width:50%; margin-right:25%;" class="button action" data='{{item|tojson|safe}}' onclick="signup_form( {{loop.index-1}} , this )">
<div>Signup Now</div> <div>Signup Now</div>
{% if item.unit_amount == 0%} {% if item.unit_amount == 0 %}
<div class="small text"> <div class="small text">
<span class="bold">Free</span> <span class="bold">Free</span>
</div> </div>
@ -329,7 +347,7 @@ var redirect_now = function(auth,plan){
<div id="form" class="border-top" style="display:none"> <div id="form" class="border-top" style="display:none">
</div> </div>
<div class="small footer"> <div class="small black footer">
<div>support@the-phi.com</div> <div>support@the-phi.com</div>
<div> <div>
all rights reserved &copy; {{ now }}, The Phi Technologys all rights reserved &copy; {{ now }}, The Phi Technologys

@ -2,7 +2,9 @@
<meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1"> <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"> <link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">
<link href="{{context}}/static/css/fa/css/font-awesome.css" rel="stylesheet" type="text/css"> <link rel="stylesheet" href="{{context}}/static/css/borders.css" type="text/css">
<link href="{{context}}/static/css/fa/css/all.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/css/fa/js/all.js" ></script>
<script type="text/javascript" src="{{ context }}/static/js/jquery/jquery.min.js"></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/rpc.js"></script>
@ -12,22 +14,17 @@
<title style="text-transform: capitalize">Signup {{product.replace('-',' ')}}</title> <title style="text-transform: capitalize">Signup {{product.replace('-',' ')}}</title>
<style> <style>
.pane { .pane {
/*display:grid; display:grid;
align-content: center; align-content: center;
grid-gap:1px; grid-gap:1px;
grid-template-columns: 50% auto; grid-template-columns: 25% auto;
*/min-width:500px; min-width:500px;
font-size:16px; font-size:16px;
} }
.border-round {
border-radius:8px;
-webkit-border-radius:8px;
-moz-border-radius:8px;
padding:8px;
}
.border-none {border:1px solid transparent}
.signup-button { .signup-button {
display:flex; display:flex;
align-items:center; align-items:center;
@ -64,6 +61,7 @@
grid-gap:2px; grid-gap:2px;
grid-template-columns: 32px auto; grid-template-columns: 32px auto;
align-items: center; align-items: center;
} }
.input-form input[type=text] { .input-form input[type=text] {
@ -84,12 +82,19 @@
<body > <body >
<div class="pane"> <div class="pane">
<div class="" style="display:grid; grid-template-columns:auto 48px; gap:1px"> <div class="" style="grid-column:1 /span 2; display:grid; grid-template-columns:auto 48px; gap:1px">
<div class="bold border-bottom">Signup <span id="signup-plan" style="text-transform:capitalize"></span></div> <div class="bold border-bottom">Signup <span id="signup-plan" style="text-transform:capitalize"></span></div>
<div align="center" class="active" onclick="jx.modal.close()"><i class="fa fa-times" style="font-size:18px;"></i></div> <div align="center" class="active" onclick="jx.modal.close()"><i class="fa fa-times" style="font-size:18px;"></i></div>
</div> </div>
<div align="center" style="margin-top:22px;">
<div class="bold"><i class="fa fa-check"></i> <span id="plan-price" class="small"></span></div> <div class="bold"><i class="fa fa-check"></i> <span id="plan-price" class="small"></span></div>
<div align="center">
<img id='plan-image' src='' style="width:96px; margin:4px">
</div>
</div>
<div class="auth"> <div class="auth">
<p> <p>
<div align="center" style="display:grid; align-content:center; justify-content:center" > <div align="center" style="display:grid; align-content:center; justify-content:center" >
@ -121,18 +126,20 @@
</p> </p>
</div> </div>
</div>
<div class="dialog"> <div class="dialog">
<div style="display:grid; grid-template-columns:48px auto; gap:2px; grid-gap:2px; align-items:center; margin-left:25%; width:50%"> <br>
<div id="icon"> <div style="display:grid; grid-template-columns:80px auto; gap:2px; grid-gap:2px; align-items:center; ">
<i class="fas fa-cog fa-3x fa-spin"></i> <div id="icon" align="center">
<i class="fas fa-cog fa-spin" style="color:darkgray; font-size:48px"></i>
<i class="fas fa-cog fa-spin" style="color:#4682B4; font-size:26px; margin-left:-10px;"></i>
</div> </div>
<div id="message"> <div id="message">
Redirecting Redirecting
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- <!--
<div class="border-round {{theme}}"> <div class="border-round {{theme}}">
<div class="border-bottom" align="right" style="padding:4px;"> <div class="border-bottom" align="right" style="padding:4px;">

@ -11,7 +11,7 @@ install(){
} }
start(){ start(){
python3 api/index.py --path $PWD/config.json --port 8084 --context $STORE_CONTEXT & > out.log python3 api/index.py --path $PWD/config.json --port 8084 --context $STORE_CONTEXT
} }
stop(){ stop(){

Loading…
Cancel
Save