Subscription handling, with user interface

@TODO: Refactor endpoint and flow/sessions and user/email handling
legacy
Steve L. Nyemba 8 years ago
parent c3ae16aef8
commit 73bb8b63d9

@ -12,6 +12,7 @@ class User:
self.stripe = stripe;
self.stripeToken= stripeToken
self.user = None
"""
This function will cast an object to JSON,
It is designed to address an inherent limitation of stripe object hierarchy
@ -79,8 +80,7 @@ class User:
# call stripe to retrieve subscriptions for this user ...
#
if self.user :
r = stripe.Customer.retrieve(self.user['id'])
r = stripe.Customer.retrieve(self.user['id'])
return r.subscriptions
else:
return []
@ -97,6 +97,10 @@ class User:
customer=id,
plan=plan['id']
) ;
#
# We need to create an invoiced item out of this plan
# We will attach it later to an invoice ...
#
r.append(x)
return r
"""
@ -118,6 +122,11 @@ class User:
self.db.save_doc(user) ;
self.user = user
def invoice(self,uid,plans,iid):
self.user = self.db.get(uid)
id = self.user['id']
r = [ stripe.InvoiceItem.create(customer=id,amount=item['amount'],currency=item['currency'],description=item['statement_descriptor'],subscription=item['subscription'],invoice=iid) for item in plans]
return r
"""
This function creates an invoice
@pre : an item with amount > 0 and status past_due
@ -125,26 +134,28 @@ class User:
def get_invoices(self,uid):
info = self.db.get(uid)
id = info['id']
return stripe.Invoice.list(customer=id)
#return stripe.Invoice.list(customer=id)
"""
This function will clear a particular invoice (hopefully). No partial payments should be accepted
@pre : card,invoice
"""
def charge(self,uid,amount):
def charge(self,uid,token,plans):
info = self.db.get(uid)
id = info['id']
uid = info['_id']
invoices= self.get_invoices(uid)
index = -1
for invoice in invoices :
if invoice.paid == False and invoice.amount_due == amount and amount > 0:
index = info['invoices'].index(ii)
invoice.pay()
del info['invoices'][index]
self.db.save_doc(info)
break
pass;
sid = plans[0]['subscription']
user = stripe.Customer.retrieve(id)
user.source = token
user.save()
for plan in plans:
sid = plan['subscription']
if plan['amount'] > 0 :
print [' *** ',plan['amount']]
_invoice= stripe.Invoice.create(customer=id,subscription=sid)
r = _invoice.pay()
print r
"""
This function is designed to determine if the user exists or not
We will check the couchdb and the stripe backend

@ -66,15 +66,28 @@ def subscribe(app_name):
"""
@app.route('/get/info/<app_name>',methods=['GET'])
def get_plan_info(app_name) :
uid = request.headers['uid']
pid = request.headers['pid'] if 'pid' in request.headers else None
couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
info = couchdb.read()
lsub = info['subscriptions']
plans = [ sub['plan'] for sub in lsub if sub['ended_at'] is None ]
if pid is not None :
plans = [item['metadata'] for item in plans if item['id'] == pid]
plans = []
print ' *** ','uid' in request.headers
if 'uid' in request.headers :
uid = request.headers['uid']
pid = request.headers['pid'] if 'pid' in request.headers else None
couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
info = couchdb.read()
lsub = info['subscriptions']
plans = [ sub['plan'] for sub in lsub if sub['ended_at'] is None ]
if pid is not None :
plans = [item['metadata'] for item in plans if item['id'] == pid]
else:
#
# This function returns the plans for a given application
# We assume the application name is the prefix of the plan identifier in stripe
#
plans = stripe.Plan.list(limit=10)
handler = Domain.User(None)
plans = handler.cast(plans.data)
plans = [item for item in plans if re.match(app_name,item['name'])]
return json.dumps(plans)
@app.route('/get/sub/<app_name>')
def get_sub_info(app_name):
@ -94,7 +107,21 @@ def get_sub_info(app_name):
else:
subs = lsub
return json.dumps(subs)
@app.route('/get/plans/<app_name>')
def get_plans(app_name) :
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
#
uid = session['uid'] if 'uid' in session else 'nyemba@gmail.com'
plans = stripe.Plan.list(limit=10)
handler = Domain.User(None)
plans = handler.cast(plans.data)
plans = [item for item in plans if re.match(app_name,item['name'])]
return render_template('subscribe.html',uid=uid,app_name=app_name,plans=plans,apikey=apikey)
@app.route('/subscribe/<name>',methods=['DELETE'])
def cancel_subscribe(name) :
pass
@ -130,7 +157,7 @@ def is_customer (app_name):
# bill['count'] = len(amount)
# bill['amount']= sum(amount)
html = """
<form action="/pay" method="POST">
<form action="/pay/:app_name" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key=":key"
@ -148,19 +175,20 @@ def is_customer (app_name):
session['plans']
amount = sum([item['amount'] for item in plans])
apikey = CONFIG['stripe']['pub'].strip()
html = html.replace(":email",uid).replace(":amount",str(amount)).replace(":key",apikey)
html = html.replace(":email",uid).replace(":amount",str(amount)).replace(":key",apikey).replace(":app_name",app_name)
amount = amount / 100
return render_template('bill.html',apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount,html=html)
"""
This function is intended to performa an actual payment
"""
@app.route('/pay',methods=['POST'])
def pay():
@app.route('/pay/<app_name>',methods=['POST'])
def pay(app_name):
token = request.form['stripeToken']
uid = request.form['stripeEmail']
tokenType = request.form['stripeTokenType']
couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
DB = couchdb.dbase #COUCHDB.get_db(CONFIG['couchdb']['db']) ;
handler = Domain.User(DB,stripe) ;
plans = session['plans']
@ -168,24 +196,27 @@ def pay():
# - create an invoice with the designated subscriptions
# - create a charge on the invoice
#
#items = handler.invoice(uid,plans)
handler.charge(uid,token,plans)
# Let's insure the preconditions are met i.e
# card,invoice
info = session['user-info']
uid = info['_id']
if 'sources' not in info or token not in info['sources']:
#info = session['user-info']
#uid = info['_id']
#if 'sources' not in info or token not in info['sources']:
#
# Let's create the card
handler.save_card(uid,token)
# handler.save_card(uid,token)
#
# Let's create a charge here ...
plans = session['user-plans']
#plans = session['user-plans']
amount=[item['price'] for item in plans if item['status'] == 'past_due' ]
if len(amount) == 0:
amount = 0
else:
amount = sum(amount)
handler.charge(uid,amount)
#amount=[item['price'] for item in plans if item['status'] == 'past_due' ]
#if len(amount) == 0:
# amount = 0
#else:
# amount = sum(amount)
#handler.charge(uid,amount)
session['user-info'] = handler.user
return ('',204)
#return render_template("bill.html",plans=plans, total_amount=amount)

@ -80,7 +80,7 @@
{% for item in plans %}
<tr class="" style="font-size:11px">
<td> {{item.statement_descriptor}} </td>
<td align="right"> {{item.amount/100}} </td>
<td align="right"> {{item.amount/100}} </td>
<td style="text-transform:capitalize"> {{item.status}} </td>
</tr>

@ -0,0 +1,134 @@
<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">
<style>
body{
font-family:sans-serif;
font-size:14px;
font-weight:lighter;
}
.caption {
font-size:28px; font-weight:lighter ;
font-family:sans-serif;
text-transform:capitalize;
}
.medium-caption {
font-size:18px;
font-weight:lighter;
font-family:sans-serif ;
text-transform:capitalize;
}
.border-bottom{ border-bottom:1px solid #CAD5E0}
.left { float:left}
.small { font-family:verdana; font-size:11px;}
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;
border:1px solid #CAD5E0
}
.action:hover { background-color:#4682b4; color:white}
tr {font-family:sans-serif; font-size:14px;}
td {padding:4px; font-weight:lighter; padding:4px;}
@media only screen and (min-device-width: 320px){
table {width:99%}
body {font-size:12px;}
tr {font-family:sans-serif; font-size:12px;}
td {padding:4px; font-weight:lighter}
}
@media only screen and (min-device-width: 768px){
body {font-size:12px}
table {width:60%; margin-left:20%}
}
</style>
<body>
<div>
</div>
<br>
<div id="grid">
<table align="center" style="border-color:transparent">
<tr>
<td style="width:50%">
<div align="left" class="default">
<img src="/static/img/logo-0.png" style="width:48px; margin:4px" class="left" style="margin:4px">
<div style="padding:8px">The Phi Technology
<div class="small" style="text-transform:capitalize">{{ app_name }}</div>
</div>
</div>
</td>
<td class="small" valign="middle">
<div>Call : 615-866-0454</div>
<div>Email: support@the-phi.com</div>
</td>
</tr>
</table>
<table align="center" class="border-top" style="background-image:url(https://az616578.vo.msecnd.net/files/responsive/cover/main/desktop/2016/05/09/635983505329496433385654456_concert-audience.jpg)">
<tr >
<td colspan="{{ plans|length}}" align="center" class="border-bottom">
<div class="caption">{{app_name.replace('-',' ') }}</div>
<div class="small">Available Plans, Charges will apply after trial period</div>
</td>
</tr>
<tr class="default">
{% for item in plans%}
{% if loop.index0 > 0 %}
<td align="center" style="border-left:1px solid #CAD5E0">
{% else %}
<td align="center">
{% endif %}
<div class="medium-caption">{{ item.name.replace('-',' ') }}</div>
<div>{{ item.metadata.info}}</div>
{% if item.amount > 0%}
<div class="caption">{{ item.amount/100 }}</div>
<div class="small"> {{item.currency.upper() }}/{{ item.interval }}
{% if item.trial_period_days %}
&amp; {{ item.trial_period_days }} days of trial
{% endif %}
</div>
<br>
<div>
<form action="/pay/{{app_name|safe}}" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{apikey|safe}}"
data-email="{{uid|safe}}"
data-amount={{item.amount|safe}}
data-name="The Phi Technology LLC"
data-description=""
data-image="https://s3.amazonaws.com/stripe-uploads/acct_15kiA1EUWsmgY81Amerchant-icon-1443577505909-the-phi-logo.png"
data-locale="auto">
</script>
</form>
</div>
{% else %}
<div class="caption">Free</div>
{% endif %}
</td>
{% endfor %}
</tr>
</table>
<br>
</div>
</body>
Loading…
Cancel
Save