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.
"""
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_cors import CORS
from flask_pymongo import PyMongo
@ -54,12 +54,12 @@ CORS(app)
SYS_STORE = CONFIG['store']
@app.route("/donate")
def donate() :
print (request.args['amount'])
amount = float(request.args['amount']) if 'amount' in request.args else 5.0
args = {'amount':amount,'context':CONTEXT}
return render_template('donate.html',**args)
# @app.route("/donate")
# def _donate() :
# amount = float(request.args['amount']) if 'amount' in request.args else 5.0
# args = {'amount':amount,'context':CONTEXT}
# return render_template('donate.html',**args)
@app.route("/")
def index():
mystore = store.factory.instance(name='music')
@ -91,7 +91,7 @@ def to_session():
if info :
for key in info :
session[key] = info[key]
return "{}"
return "{}",200
@app.route("/signup",methods=['GET','POST'])
def signup():
"""
@ -137,7 +137,8 @@ def signup():
</script>
""".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):
"""
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']
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):
"""
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 ''
label = mystore.product['statement_descriptor']
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'
# print (mystore.product.keys())
session['cloud-view'] = CONFIG['cloud-view']
session['cloud-view'] = CONFIG['cloud-view'].replace(':id',id)
session['product'] = mystore.product
return render_template('plans.html',**args)
@app.route("/goto/<id>",methods=['POST','GET'])
@ -193,7 +259,7 @@ def redirect(id ) :
return "0",403
pass
@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
"""
@ -203,7 +269,18 @@ def payme (id) :
args = {"product":mystore.product,"plans":mystore.get.plans(),"context":CONTEXT,"pricing":pricing}
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():
info = request.get_json()
@ -213,33 +290,61 @@ def get_dialog():
@app.route('/<id>/pay',methods=['POST','PUT'])
def _payme(id) :
info = request.get_json()
mystore = store.factory.instance(name=id,email=info['email'])
user = mystore.user
#
# ... Let us massage the data so it can go through properly
card = {"number":info['number'],"cvc":info['cvc']}
card['exp_month'] = info['exp'].split("/")[0]
card['exp_year'] = info['exp'].split("/")[1]
card['name'] = " ".join([info["fname"],info["lname"]])
_args = {"card":card,"email":info['email'],"amount":info['amount'],"currency":"usd"}
_args = {}
if 'has_card' not in info and not info['has_card'] :
card = {"number":info['number'],"cvc":info['cvc']}
card['exp_month'] = info['exp'].split("/")[0]
card['exp_year'] = info['exp'].split("/")[1]
card['name'] = " ".join([info["fname"],info["lname"]])
_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 '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
else:
#
# For now we consider this a donation
_args['metadata'] = {"payment":"donation","product":id}
_args['description'] = " ".join([id,_args['metadata']['payment']])
r = user.charge(**_args)
# For now we consider this a donation or other payment
_args['metadata'] = {"payment":"charge","product":id}
_args['statement_descriptor'] = " ".join([str(info['amount']),id])
r = user.charge(**_args)
if not r :
msg,status = "Problem enountered with card",403
msg,status = "Payment error enountered",403
else :
msg,status = "Payment accepted, Thank you", 200
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'])
def init(product):
@ -337,7 +442,8 @@ def get_plans(product) :
else:
return []
@app.route('/features/<product>')
# @app.route('/features/<product>')
@app.route('/<product>/features')
def features(product):
"""
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
#
return json.dumps(plans)
"""
This function returns a user's plans/status for an application
@header uid user's email address
"""
@app.route('/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()
#
# 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)
return json.dumps(plans),{"content-type":"application/json"}
# """
# This function returns a user's plans/status for an application
# @header uid user's email address
# """
# @app.route('/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>')
# @app.route('/<product>/signup')
# def _signup(product) :
# apikey = CONFIG['stripe']['pub'].strip()
#
# @TODO: Mark the plans the current user is signed up for
#
if 'themes' in CONFIG:
theme = CONFIG['themes'][product] if product in CONFIG['themes'] else CONFIG['theme']['default']
else:
theme = ''
platform='web' if 'platform' not in request.args else request.args['platform']
alias = plans[0]['product_alias']
if 'user-plan' in session :
user_plans = [item['id'] for item in session['user-plan']]
else :
user_plans = []
# active_plan = session['active-plans'] if 'active-plans' in session else []
args = {"context":CONTEXT,"theme":theme,"uid":uid,"alias":alias,"platform":platform,"app_name":product,"apikey":apikey,"plans":plans}
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) :
pass
"""
This function defines if a given user is a customer or not
We should be able to tell by how we create customers
"""
@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
# #
# # @TODO: Mark the plans the current user is signed up for
# #
# if 'themes' in CONFIG:
# theme = CONFIG['themes'][product] if product in CONFIG['themes'] else CONFIG['theme']['default']
# else:
# theme = ''
# platform='web' if 'platform' not in request.args else request.args['platform']
# alias = plans[0]['product_alias']
# if 'user-plan' in session :
# user_plans = [item['id'] for item in session['user-plan']]
# else :
# user_plans = []
# # active_plan = session['active-plans'] if 'active-plans' in session else []
# args = {"context":CONTEXT,"theme":theme,"uid":uid,"alias":alias,"platform":platform,"app_name":product,"apikey":apikey,"plans":plans}
# 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)
# @app.route('/subscribe/<name>',methods=['DELETE'])
# def cancel_subscribe(name) :
# pass
# """
# This function defines if a given user is a customer or not
# We should be able to tell by how we create customers
# """
# @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']
amount = sum([item['amount'] for item in plans])
apikey = CONFIG['stripe']['pub'].strip()
# session['plans']
# amount = sum([item['amount'] for item in plans])
# apikey = CONFIG['stripe']['pub'].strip()
amount = amount / 100
return render_template('bill.html',context=CONTEXT,apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount)
# amount = amount / 100
# return render_template('bill.html',context=CONTEXT,apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount)
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"}
var payment = {}
payment.setup = function(context,pricing){
payment.setup = function(context,pricing,product_name){
sessionStorage.store_context = context
sessionStorage.pricing = pricing.constructor == String ? pricing: JSON.stringify(pricing)
if (pricing != null){
sessionStorage.pricing = pricing.constructor == String ? pricing: JSON.stringify(pricing)
}else{
sessionStorage.pricing = "[]"
}
sessionStorage.product_name = product_name
sessionStorage.customer = JSON.stringify({info:{}})
$('.payment-amount > input').on('focusin',function(){
@ -27,7 +32,10 @@ payment.setup = function(context,pricing){
}
payment.init = function(index){
var p = JSON.parse(sessionStorage.pricing)
console.log(p)
if(p.length == 0){
return ;
}
var _item = p[index]
var amount = (_item.unit_amount/100).toFixed(2)
$('.amount').val( amount)
@ -39,8 +47,11 @@ payment.init = function(index){
}
payment.validate = function(e){
var id = e.target
console.log([id,e])
customer = JSON.parse(sessionStorage.customer)
if($(id).attr('class').match(/number|cvc|exp/) && sessionStorage.has_card){
return 1 ;
}
$(id).removeClass('error')
var pattern = $(id).attr('data-pattern')
var field = $(id).attr('class')
@ -78,6 +89,7 @@ payment.validate = function(e){
sessionStorage.customer = JSON.stringify(customer)
return 1
}else{
// id.className = id.className + ' error'
$(id).addClass('error')
return 0
}
@ -86,7 +98,7 @@ payment.validate = function(e){
}
payment.dialog = function(title,message,type){
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 httpclient = HttpClient.instance()
@ -96,22 +108,24 @@ payment.dialog = function(title,message,type){
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
if ($(_input).attr('class') != 'amount'){
$(_input).keyup()
}else {
$(_input).change()
}
return payment.validate({target:_input})
// 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){
if (n != jx.math.sum(values) && ! sessionStorage.has_card){
//
// 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')
info.amount = info.amount * 100
$('.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()
http.setData(JSON.stringify(info))
http.setHeader('content-type','application/json')
if(sessionStorage.has_card == "1"){
info.has_card = true
}
console.log(info)
http.setData(info,"application/json")
http.post(uri,function(x){
if(x.status == 200){
@ -143,9 +160,38 @@ payment.proceed = function(){
TITLE='.::'
TYPE='WARN'
}
$('.jxmodal')[$('.jxmodal')].remove()
$('.jxmodal')[$('.jxmodal').length -1 ].remove()
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:
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'] :
if field in card :
self.me['customer'][field] = card[field]
del card[field]
#
# tok_1HPyukEUWsmgY81ALQuKGBmT
# address information should be added to the card
#
# save_card = _args['save'] if 'save' in _args else 0
@ -47,14 +56,32 @@ class User :
del card['currency']
else:
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.init(self.me["customer"]['email'])
return r
except Exception as error :
print (error)
print ([' ** ',error])
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) :
"""
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.add = self._add_card
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])
def init(self,email):
@ -154,7 +181,7 @@ class User :
if 'sources' in customer and 'data' in customer['sources'] :
if customer['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:
# free_plan = free_plan[0]
# self.subscribe(free_plan['id'],email)
@ -291,15 +318,18 @@ class Plans(Store) :
_item['amount'] = item['unit_amount']
if item.recurring == None :
self.checkout.append(_item)
elif 'features' in item.metadata :
_item['metadata']['features'] = json.loads(item.metadata['features'])
else:
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)
# self.plans[index] = item.to_dict_recursive()
# if 'features' in self.plans[index]['metadata'] :
# self.plans[index]['metadata']['features'] = json.loads(self.plans[index]['metadata']['features'])
# elif self.plans
self.plans.sort(key=lambda item: item['unit_amount'])
self.checkout.sort(key=lambda item:item['unit_amount'])
if 'email' in args :
@ -373,7 +403,7 @@ class Plans(Store) :
self.user.init(email)
#
# 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(
customer = self.user.info()['id'],
items = [plan]

@ -3,16 +3,24 @@
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<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/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>
<script src="{{context}}/static/js/jx/ext/math.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>
@ -75,7 +83,8 @@
height:48px;
border:2px solid transparent;
padding:6px;
font-weight:lighter;
font-weight:normal;
color:#000000;
background-color:#f3f3f3;
}
.fa-exclamation-triangle{
@ -195,9 +204,11 @@
</style>
<script >
$(document).ready(function(){
$('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.setup("{{context|safe}}",{{pricing|tojson}})
setTimeout(function(){
jx.modal.close()
@ -229,9 +240,15 @@
<div class="payment-pane">
<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="bold">{{product.name}}</div>
<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">
{% 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>
@ -243,21 +260,29 @@
</div>
<div class="description">
<div class="description" align="center">
<!--
@TODO: if a plan is being loaded it should display here
-->
{{product.metadata.about}}
</div>
</div>
<div class="card" style="vertical-align:middle;">
<div style="margin-top:2px">
<input type="text" placeholder="Email@domain.com" class="email" data-pattern="^[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)*@[-!#-'*+\/-9=?^-~]+(?:\.[-!#-'*+\/-9=?^-~]+)+$" onkeyup="payment.validate(event)"/>
{% 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)"/>
{% endif %}
</div>
<div style="display:grid; grid-template-columns:50% 50%; gap:2px; margin-top:2px;">
<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>
<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 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="payment-amount">
<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 style="margin:4px;">Choose an amount</div>
{%for item in pricing %}
<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>
{% if pricing %}
<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 style="margin:4px;">Choose an amount</div>
{%for item in pricing %}
<div class="item active" style="font-weight:lighter" onclick="payment.init({{loop.index -1 }})">
</div>
{% endfor %}
<div style="margin:4px"></div>
</div>
{{'%0.2f'| format(item.unit_amount/100|float) }} <span style="text-transform: uppercase">{{item.currency}}</span>
</div>
{% endfor %}
<div style="margin:4px"></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 class="currency bold" style="padding:4px; display:grid; align-items:center" align="left"></div>
@ -305,14 +335,28 @@
<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;"
onclick="payment.proceed()"
>
<div style="font-size:18px" class="bold">
Pay Now</div>
{% if not pricing %}
Signup
{% else %}
Make Payment
{% endif %}
</div>
</div>
</div>

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

@ -1,9 +1,15 @@
<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="{{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/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>
<!--
@ -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/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/math.js"></script>
<script type="text/javascript" src="{{ context }}/static/js/jx/ext/cloud-view.js"></script>
<title style="text-transform: capitalize">{{product.replace('-',' ')}}</title>
<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>
</style>
<script>
sessionStorage.context = "{{context|safe}}"
var url = {{ session['cloud-view'] |tojson}}
var url = "{{ session['cloud-view'] |safe}}"
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()
httpclient.get(url,function(x){
var html = x.responseText ;
@ -40,8 +56,14 @@
var price = parseFloat(data.unit_amount / 100 ).toFixed(2)
price = ([price, data.recurring.interval]).join(' / ')
}
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')
bind (data)
//bind( JSON.parse( $(caller).attr('data')) )
@ -52,13 +74,14 @@
var http = HttpClient.instance()
http.get('{{context}}/card.html')
}
console.log("{{ session['cloud-view']}}")
/*
* Binding buttons to oauth 2.0 interface
*/
var bind = function(data){
var buttons = $('.signup-button')
var buttons = $('.signup-button') ;
jx.utils.patterns.visitor(buttons,function(button){
$(button).attr('data',data)
$(button).click(function(event){
@ -68,7 +91,10 @@
var host = window.location.href.match(/^.+\/\/([a-z,-,.]+)\/.+$/)[1]
var key = ([protocol,'://',host,'/cloud-view/oauth']).join('')
//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.oauth.init(id,key,function(p){
@ -77,12 +103,19 @@
// At this point we need to provide a screen to provide credit card information
// And or redirect to application framework
//
if (p == null){
$('.dialog').slideUp(function(){
$('.auth').slideDown()
})
return
}
var info = {"auth":p,"plan":data}
var httpclient = HttpClient.instance()
httpclient.setHeader("Content-Type","application/json")
httpclient.setData(JSON.stringify(info) )
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
//
@ -90,47 +123,7 @@
}else{
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){
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()
http.get('{{context}}/signup',function(x){
x = JSON.parse(x.responseText)
@ -195,20 +206,22 @@ var redirect_now = function(auth,plan){
var http = HttpClient.instance()
http.setHeader('Content-Type','application/json')
http.setData(JSON.stringify(info))
http.post('{{context|safe}}/goto/{{product|safe}}',function(x){
var uri = x.getResponseHeader('location')
http.post('{{context|safe}}/goto/{{product.name|safe}}',function(x){
//var uri = x.getResponseHeader('location')
var uri = '{{context}}/me'
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{
var icon= '<i class="fas fa-times fa-3x"></i>'
var msg = "Error found, please try again"
}
jx.dom.set.value('icon',icon)
jx.dom.set.value('message',msg)
if(jx.dom.exists('message')){
jx.dom.set.value('message',msg)
}
$('.auth').slideUp(function(){
$('.dialog').slideDown()
@ -229,32 +242,36 @@ var redirect_now = function(auth,plan){
})
}
$(document).ready(function(){
jx.cloudview.init( "{{session['cloud-view']}}")
})
</script>
<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 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 %}
<p><div class="caption">{{label}}</div>
<div class="" style="text-transform:capitalize">{{description}}</div>
</p>
</div>
</div>
<div align="center" style="text-transform:capitalize;grid-row:2; grid-column:1 /span 2;">{{description}}</div>
</div>
<div class="pricing">
{% for item in plans%}
@ -273,6 +290,7 @@ var redirect_now = function(auth,plan){
{%else%}
<div class="default" >
<span class="bold">$ {{ item.unit_amount/100 }}</span>
/ <span class="small">{{item.recurring.interval[:]}}</span>
@ -298,10 +316,10 @@ var redirect_now = function(auth,plan){
<div align="right" class="border-top">
<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>
{% if item.unit_amount == 0%}
{% if item.unit_amount == 0 %}
<div class="small text">
<span class="bold">Free</span>
</div>
@ -329,7 +347,7 @@ var redirect_now = function(auth,plan){
<div id="form" class="border-top" style="display:none">
</div>
<div class="small footer">
<div class="small black footer">
<div>support@the-phi.com</div>
<div>
all rights reserved &copy; {{ now }}, The Phi Technologys

@ -2,7 +2,9 @@
<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">
<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/jx/rpc.js"></script>
@ -12,22 +14,17 @@
<title style="text-transform: capitalize">Signup {{product.replace('-',' ')}}</title>
<style>
.pane {
/*display:grid;
display:grid;
align-content: center;
grid-gap:1px;
grid-template-columns: 50% auto;
*/min-width:500px;
grid-template-columns: 25% auto;
min-width:500px;
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 {
display:flex;
align-items:center;
@ -64,6 +61,7 @@
grid-gap:2px;
grid-template-columns: 32px auto;
align-items: center;
}
.input-form input[type=text] {
@ -84,12 +82,19 @@
<body >
<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 align="center" class="active" onclick="jx.modal.close()"><i class="fa fa-times" style="font-size:18px;"></i></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 align="center">
<img id='plan-image' src='' style="width:96px; margin:4px">
</div>
</div>
<div class="auth">
<p>
<div align="center" style="display:grid; align-content:center; justify-content:center" >
@ -121,18 +126,20 @@
</p>
</div>
</div>
<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%">
<div id="icon">
<i class="fas fa-cog fa-3x fa-spin"></i>
</div>
<div id="message">
Redirecting
<div class="dialog">
<br>
<div style="display:grid; grid-template-columns:80px auto; gap:2px; grid-gap:2px; align-items:center; ">
<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 id="message">
Redirecting
</div>
</div>
</div>
</div>
<!--
<div class="border-round {{theme}}">
<div class="border-bottom" align="right" style="padding:4px;">

@ -11,7 +11,7 @@ install(){
}
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(){

Loading…
Cancel
Save