store front signup with account @TODO: add simple payments like donation

legacy
Steve L. Nyemba 5 years ago
parent 3e30fd73f7
commit d5705e1eb6

@ -7,20 +7,25 @@
- Having a free product insures there is no excuse not to signup users
- If a product doesn't fall under this model we will find a way to fix it.
-
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_cors import CORS
import Domain
from User import User
from couchdbkit import Server, Document
# import Domain
# from User import User
# from couchdbkit import Server, Document
import stripe
import json
from StringIO import StringIO
import re
import os
from utils.params import PARAMS
from utils.transport import Couchdb, CouchdbReader
from transport import factory
# from store import Store
import store
from datetime import datetime
# from utils.transport import Couchdb, CouchdbReader
PORT = 8100 if 'port' not in PARAMS else int(PARAMS['port']) ;
path = PARAMS['path'] #os.environ['CONFIG']
f = open(path)
@ -36,23 +41,71 @@ stripe.api_key = stripe_keys['secret_key'].strip()
app = Flask(__name__)
CORS(app)
COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ;
SYS_STORE = CONFIG['couchdb']
# CORS(app)
# COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ;
# SYS_STORE = CONFIG['couchdb']
SYS_STORE = CONFIG['store']
@app.route("/")
def index():
mystore = store.factory.instance(name='music')
products = mystore.get.products()
args = {"context":CONTEXT,"products":products}
return json.dumps(products),"content-type:application/json"
# return render_template("index.html",**args)
@app.route("/json/<id>")
def get_plans_json(id):
"""
This function will set the user information to the session and update the information
@header uid user email address
This function returns the plans for a given product
"""
HEADER="Content-type:application/json"
try:
mystore = store.factory.instance(name=id)
return json.dumps(mystore.get.plans()),HEADER
except Exception as e:
#
# Log this shit or not
pass
return "[]",HEADER
@app.route("/ui/signup/<id>")
def signup(id):
"""
"""
mystore = store.factory.instance(name=id)
plans = mystore.get.plans()
index = int(request.args['index'])
plan = plans[index]
args = {"product":id,"label":mystore.product['statement_descriptor'],"plan":plan,"context":CONTEXT,"now":datetime.now().year}
args['theme'] = 'theme-clouds'
return render_template("signup.html",**args)
@app.route("/ui/<id>")
def get_plans_ui(id):
mystore = store.factory.instance(name=id)
#
# sort plans by
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['theme'] = 'theme-clouds'
print (mystore.product.keys())
return render_template('plans.html',**args)
@app.route("/init/<product>",methods=['POST'])
def init(product):
"""
This function initializes a product to a given user,
This function initializes/logs a product to a given user, i.e it will create a session for users/product/plans
if the user has provided a user identifier it will be used as her primary email. The understanding is that a product may have multiple plans under it but always a free one
@param uid user's email (primary)
@param pid plan identifier
"""
#
# get product and plans
#
uid = request.headers['uid']
plan_id = request.headers['pid'] if 'pid' in request.headers else None
#
# We should just pull the factory method and get a storage handler to handle the logs
#
store = dict(CONFIG['couchdb'])
store['dbname'] = product
user = User(stripe=stripe,store=store,product=product)
@ -193,7 +246,7 @@ def status(app_name):
@pre 'uid' in request.headers
"""
@app.route('/signup/<product>')
def signup(product) :
def _signup(product) :
apikey = CONFIG['stripe']['pub'].strip()
#
@ -263,7 +316,10 @@ def is_customer (app_name):
return render_template('bill.html',context=CONTEXT,apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount)
if __name__ == '__main__' :
#
# setup mongodb session management (not sure why)
app.debug = True ;
app.secret_key = '360-8y-[0v@t10n]+kr81v17y'
app.config['MAX_CONTENT_LENGTH'] = 1600 * 1024 * 1024
app.run(port=PORT,threaded=True,host='0.0.0.0')

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

@ -0,0 +1,218 @@
.theme-raspberry{
background: #00B4DB; /* fallback for old browsers */
background: -webkit-linear-gradient(to bottom, #0083B0, #00B4DB); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to bottom, #0083B0, #00B4DB); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
color:#ffffff;
font-family:verdana;
font-weight:lighter;
text-transform:capitalize;
}
.theme-dark-ocean {
background: #373B44; /* fallback for old browsers */
background: -webkit-linear-gradient(to bottom, #4682B4, #d3d3d3); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to bottom, #4682B4, #d3d3d3); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
color:#f3f3f3;
}
.theme-gray {
background: #bdc3c7; /* fallback for old browsers */
background: -webkit-linear-gradient(to bottom, #2c3e50, #bdc3c7); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to bottom, #2c3e50, #bdc3c7); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
color:#f3f3f3f3;
}
.theme-clouds {
background: #ECE9E6; /* fallback for old browsers */
background: -webkit-linear-gradient(to bottom, #FFFFFF, #D3D3D3); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to bottom, #FFFFFF, #D3D3D3); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
img {width:48px}
body{
font-family:sans-serif;
font-size:14px;
width:80%;
margin-left:10%;
}
.default {font-size:16px;
font-family: sans-serif;
}
.button {
width:auto;
padding:6px;
font-weight:bold;
border-radius:8px;
border:2px solid #4682b4;
background-color:#4682b4;
color:white;
cursor:pointer;
text-align:center;
}
.button:hover {
background-color:#ffffff;
color:#4682b4;
}
.active {
font-weight:bold;
font-family:verdana;
cursor:pointer ;
border:2px solid transparent;
padding:4px;
}
.active:hover { border-bottom:2px solid #4682B4}
.current-plan {
border-color: #20B2AA #20B2AA transparent transparent;
border-style: solid;
border-width: 15px 15px 15px 15px;
height: 0px;
width: 0px;
}
.bold { font-weight:bold}
.caption {
font-size:28px; font-weight:bold ;
font-family:sans-serif;
text-transform:capitalize;
}
.medium-caption {
font-size:18px;
font-weight:lighter;
font-family:sans-serif ;
text-transform:capitalize;
}
.plan {
padding:4px;
margin:4px;
border-radius: 4px;
border:1px solid #CAD5E0
}
.pricing {
display:grid;
grid-template-columns: repeat(auto-fit,minmax(250px, 1fr) );
/* align-content:center; */
}
.pricing-frame {
display:grid;
height:70%;
align-content:center;
}
.footer {
bottom:10px;
position:fixed;
width: 80%;
padding:8spx;
text-align:center;
display:grid;
grid-template-columns: 10% auto 10%;
grid-gap: 2px;
}
.plan .title {
display:grid;
grid-template-columns: auto 32px;
grid-gap:2px;
font-size:24px; font-family:verdana; text-transform: capitalize; border-bottom:1px solid #CAD5E0; }
.plan .feature {display:grid; grid-template-columns: 60% 40%; grid-gap:2px; margin:2px;}
.plan .feature .label {text-transform: uppercase; grid-column:1; font-size:14px;}
.plan .feature .status {font-size:20px; text-align: right; font-size:14px;}
.border{border:1px solid #CAD5E0;}
.border-bottom{ border-bottom:1px solid #CAD5E0}
.border-right{ border-right:1px solid #CAD5E0}
.border-left{ border-left:1px solid #CAD5E0}
.border-top{ border-top:1px solid #CAD5E0}
.left { float:left}
.small { font-family:verdana; font-size:11px; font-weight:lighter; color:#000000;}
.hidden {display:none}
.width-50{width:50%; padding:4px;}
.width-25{width:25%; padding:4px}
.height-48{height:48px;}
.fa-times {color:maroon; font-size:18px;}
.theme-dark-ocean .fa-check {color:green; font-size:18px}
.fa-check {color:#20B2AA; font-size:18px;}
table { border:1px solid #CAD5E0;
border-radius:8px;
-webkit-border-radius:8px;
padding:4px;
margin:4px;
}
.action {
cursor:pointer;
padding:4px;
padding-left:10px;
padding-right:10px;
border-radius:4px;
-webkit-border-radius:4px;
-moz-border-radius:4px;
font-weight:bold;
color:#4682b4;
}
.action:hover { background-color:#4682b4; color:white}
tr {font-family:sans-serif; font-size:14px;}
td {padding:4px; font-weight:lighter; padding:4px;}
.gradient{
background-image: -ms-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%);
background-image: -moz-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%);
background-image: -o-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(-120, #008080), color-stop(50, #FFFFFF), color-stop(120, #005757));
background-image: -webkit-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%);
background-image: linear-gradient(to bottom, #008080 -120%, #FFFFFF 50%, #005757 120%);
}
@media only screen and (min-device-width: 320px){
table {width:99%}
img {width:28px}
.medium-caption {font-size:14px}
body {font-size:12px;}
.default {font-size:12px}
.height-48{height:40px}
.width-50{width:47%; padding:4px;}
.width-25{width:24%; padding:4px}
.pricing {display:block;}
.plan .title { font-size:18px; font-family:verdana; text-transform: capitalize; border-bottom:1px solid #CAD5E0; }
.plan .feature {display:grid; grid-template-columns: 60% 40%; grid-gap:4px; margin:4px}
.plan .feature .label {font-size:12px; text-transform: uppercase; grid-column:1}
.plan .feature .status {font-size:16px; text-align: right; font-size:14px;}
.header{display:none}
.caption {font-size:20px}
}
@media only screen and (min-device-width: 768px){
body {font-size:12px}
.default {font-size:12px}
.header .logo{
display:grid;
margin:4px;
grid-template-columns: 48px auto ;
grid-gap:2px;
align-items: center;
}
.header .logo img {width:32px}
.header {display:block;}
table {width:70%; margin-left:20%}
.medium-caption {font-size:18px}
.width-50{width:50%; padding:4px;}
.width-25{width:25%; padding:4px}
.height-48{height:48px;}
.pricing {
display:grid;
grid-template-columns: repeat(auto-fit,minmax(250px, 1fr) )
}
.plan .title { padding:4px; font-size:22px; font-family:verdana; text-transform: capitalize; border-bottom:1px solid #CAD5E0; }
.plan .feature {display:grid; grid-template-columns: 60% 40%; grid-gap:4px; margin:4px; padding:4px;}
.plan .feature .label {text-transform: uppercase; grid-column:1}
.plan .feature .status {font-size:20px; text-align: right; font-size:14px;}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,123 @@
<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">
<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 type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.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/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>
<title style="text-transform: capitalize">{{product.replace('-',' ')}}</title>
<script>
var signup_form = function(index){
var url = '{{context}}/ui/signup/{{product}}?index=:index'.replace(/:index/,index)
var httpclient = HttpClient.instance()
httpclient.get(url,function(x){
var html = x.responseText ;
jx.modal.show({html:html})
})
}
</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">
<p><div class="caption">{{label}}</div>
<div class="" style="text-transform:capitalize">{{description}}</div>
</p>
</div>
<div class="pricing">
{% for item in plans%}
<div class="plan {{theme}}">
<div class="title" align="center">
<div>{{ item.nickname }}</div>
{% if item.id in active_plans %}
<div class="current-plan"></div>
{% else %}
<div></div>
{% endif %}
{% if item.amount == 0%}
<div class="default">
<span class="bold">Free</span>
</div>
{%else%}
<div class="default" >
<span class="bold">$ {{ item.amount/100 }}</span>
/ <span class="small">{{item.interval[:]}}</span>
</div>
{% endif %}
</div>
<div class="">
{% for key,value in item.metadata['features'].iteritems() %}
<div class="feature">
<div class="label">{{ key }}</div>
{% if value == True %}
<div class="status"><i class="fa fa-check"></i></div>
{% elif value == False %}
<div class="status"><i class="fa fa-times"></i></div>
{%else %}
<div class="status">{{value}}</div>
{% endif %}
</div>
{% endfor %}
</div>
<div align="right" class="border-top">
<p>
<div class="button " onclick="signup_form({{loop.index-1}})">
Signup Now
</div>
</p>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="small footer">
<div>support@the-phi.com</div>
<div>
all rights reserved &copy; {{ now }}, The Phi Technology
</div>
<div>
Privacy & Terms
</div>
</div>
</body>

@ -0,0 +1,141 @@
<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">
<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 type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.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/utils.js"></script>
<script type="text/javascript" src="{{ context }}/static/js/jx/dom.js"></script>
<title style="text-transform: capitalize">Signup {{product.replace('-',' ')}}</title>
<style>
.pane {
display:grid;
align-content: center;
grid-gap:1px;
grid-template-columns: 50% auto;
width:auto;
}
.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;
padding:4px;
border:2px solid #CAD5E0;
border-radius:4px;
margin:2px;
height:30px;
font-size:14px;
width:185px;
cursor:pointer;
background-color:#FFFFFF;
color:#000000;
}
.signup-button img {
margin:4px;
width:30px;
background:#ffffff;
margin-right:15px;
}
.signup-button span {font-weight:bold}
.signup-button:hover {
color:#4682b4;
background-color:#ffffff;
border-color:#4682b4
}
.signup-button:hover i{
color:#000000;
}
.title {
display:grid;
grid-gap:2px;
grid-template-columns: 32px auto;
align-items: center;
}
</style>
<body>
<p></p>
<div class="pane border-round {{theme}}" >
<div class=" border-right">
<div class="title">
<div>
<i class="fa fa-check fa-2x"></i>
</div>
<div>
<div class="caption">{{label}}</div>
<div class="default bold" style="text-transform:capitalize">
<span class="">{{plan.nickname}} Plan, </span>
<span class="bold ">{{plan.amount / 100}} </span> / <span style="text-transform: lowercase; font-weight:lighter">{{plan.interval}}</span>
</div>
</div>
</div>
<p>
<div class="plan border-none " >
{% for key,value in plan.metadata.features.iteritems() %}
<div class="feature">
<div class="label">
{{key}}
</div>
<div class="status">
{% if value == 1%}
<i class="fa fa-check"></i>
{% elif value == 0%}
<i class="fa fa-times"></i>
{% else %}
{{ value}}
{% endif %}
</div>
</div>
{% endfor%}
</div>
</p>
</div>
<div class="" >
<div class="medium-caption" align="center">Signup using a cloud account</div>
<p>
<div align="center" style="display:grid; align-content:center; justify-content:center" >
<input type="image" src="{{context}}/static/assets/google/normal.png" style="height:48px;" data-value="google-drive"/>
<div class="signup-button" data-value="dropbox">
<img src="{{context}}/static/img/accounts/dropbox.png">
<span>Dropbox</span>
</div>
<div class="border-left border-bottom signup-button" data-value="one-drive">
<img src="{{context}}/static/img/accounts/microsoft.png"> <span>One Drive</span>
</div>
<div class="border-left border-bottom signup-button" data-value="box">
<img src="{{context}}/static/img/accounts/box.png"> <span>Box</span>
</div>
</div>
</p>
</div>
</div>
</body>
Loading…
Cancel
Save