You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
8.4 KiB
Python
281 lines
8.4 KiB
Python
"""
|
|
This file handles customer & plans associated with a given product/app
|
|
The subscription works as follows:
|
|
-
|
|
"""
|
|
from __future__ import division
|
|
from flask import Flask, request, session, render_template,Response
|
|
import Domain
|
|
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
|
|
PORT = 8100 if 'port' not in PARAMS else int(PARAMS['port']) ;
|
|
path = PARAMS['path'] #os.environ['CONFIG']
|
|
f = open(path)
|
|
CONFIG = json.loads(f.read())
|
|
stripe_keys = {
|
|
'secret_key': CONFIG['stripe']['secret'].strip(),
|
|
'publishable_key': CONFIG['stripe']['pub'].strip()
|
|
}
|
|
|
|
stripe.api_key = stripe_keys['secret_key'].strip()
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
"""
|
|
This function will attempt to create an account for a user if the user does NOT exist
|
|
Then Setup the stage for initialization of {signup for, user-account}
|
|
"""
|
|
COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ;
|
|
|
|
"""
|
|
This function subscribes a user to a given service for an application
|
|
This function guarantees not to duplicate subscriptions
|
|
@resource name name of the application {cloud-music}
|
|
@header key service/plan
|
|
"""
|
|
@app.route('/subscribe/<app_name>',methods=['POST'])
|
|
def subscribe(app_name):
|
|
#
|
|
# The name is the full name of the service
|
|
#
|
|
|
|
key = request.headers['key']
|
|
uid = request.headers['uid']
|
|
plans = stripe.Plan.list().data
|
|
plan = [item for item in plans if item.id == key]
|
|
resp = "0"
|
|
if plan :
|
|
couch_handler = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid)
|
|
DB = couch_handler.dbase
|
|
handler = Domain.User(DB,stripe) ;
|
|
handler.init(uid,plan)
|
|
resp = plan[0].id
|
|
return resp
|
|
"""
|
|
This function returns the meta data about a given plan or set of plans for a given application
|
|
@resource app_name application identifier
|
|
@header pid plan identifier (optional)
|
|
@header uid user identifier
|
|
"""
|
|
@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]
|
|
return json.dumps(plans)
|
|
@app.route('/get/sub/<app_name>')
|
|
def get_sub_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']
|
|
#
|
|
# @TODO: Return critical information only i.e:
|
|
# - subscription state (dates,status)
|
|
# - how much is owed
|
|
# - subscription id
|
|
#
|
|
if pid is not None:
|
|
subs = [ sub for sub in lsub if sub['plan']['id'] == pid ]
|
|
else:
|
|
subs = lsub
|
|
return json.dumps(subs)
|
|
|
|
@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)
|
|
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
|
|
|
|
|
|
# bill = {"amount":0,"count":0}
|
|
# key = ""
|
|
# if 'user-plans' in session :
|
|
# plans = session['user-plans'] ;
|
|
# amount = [plan['amount'] for plan in plans if plan['amount'] > 0]
|
|
# if len(amount) > 0:
|
|
# key = CONFIG['stripe']['pub'].strip()
|
|
# bill['count'] = len(amount)
|
|
# bill['amount']= sum(amount)
|
|
html = """
|
|
<form action="/pay" method="POST">
|
|
<script
|
|
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
|
|
data-key=":key"
|
|
data-email=":email"
|
|
data-amount=":amount"
|
|
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>
|
|
"""
|
|
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)
|
|
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():
|
|
|
|
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']
|
|
# Assuming all is fine, we must do the following at this point
|
|
# - create an invoice with the designated subscriptions
|
|
# - create a charge on the invoice
|
|
#
|
|
# 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']:
|
|
#
|
|
# Let's create the card
|
|
handler.save_card(uid,token)
|
|
#
|
|
# Let's create a charge here ...
|
|
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)
|
|
session['user-info'] = handler.user
|
|
return ('',204)
|
|
#return render_template("bill.html",plans=plans, total_amount=amount)
|
|
|
|
|
|
@app.route('/bill',methods=['GET'])
|
|
def bill():
|
|
return render_template('bill.html')
|
|
@app.route('/buy',methods=['POST'])
|
|
def buy():
|
|
id = request.form['id'] ;
|
|
|
|
if id in ['subscribe','now']:
|
|
email = request.form['stripeEmail']
|
|
token = request.form['stripeToken']
|
|
user = Domain.User(DB,stripe,token) ;
|
|
user.init(email) ;
|
|
if user.exists() :
|
|
print "Update with anything if need be"
|
|
pass
|
|
else:
|
|
#
|
|
# Create the user with the associated plan/or payment method
|
|
#
|
|
user.save() ;
|
|
user.publish()
|
|
else:
|
|
pass
|
|
return "0"
|
|
"""
|
|
This function provides general information about service plans
|
|
@TODO: provide filtering criteria
|
|
"""
|
|
@app.route('/plans',methods=['GET'])
|
|
def plans():
|
|
plans = stripe.Plan.list().data
|
|
if 'filter' in request.headers:
|
|
filter = request.headers['filter']
|
|
|
|
plans = [ item for item in plans if re.match(filter,item.name)]
|
|
else:
|
|
#
|
|
# Let's get a user's subscription information
|
|
#
|
|
uid = request.headers['uid']
|
|
|
|
if 'uid' in request.headers and request.headers['uid'] != '':
|
|
uid = request.headers['uid']
|
|
DB = COUCHDB.get_db(CONFIG['couchdb']['db']) ;
|
|
handler = Domain.User(DB)
|
|
handler.init(uid)
|
|
myplans = [{"id":item.plan.id,"price":item.plan.amount/100,"feature":item.plan.metadata['info'],"status":item.status} for item in handler.subscriptions()]
|
|
keys = [plan['id'] for plan in myplans]
|
|
plans = [plan for plan in plans if plan['price'] > 0 and plan['id'] not in keys]
|
|
plans = {'myplans':myplans,"plans":plans}
|
|
session['user-plans'] = myplans
|
|
plans = json.dumps(plans)
|
|
return plans
|
|
"""
|
|
This function subscribes a user to a given plan(s)
|
|
If the plan is new, then we do NOT need a credit card info
|
|
|
|
@header app application/requesting service
|
|
@body info {user:_id,plan:[]}
|
|
"""
|
|
@app.route('/_subscribe',methods=['POST'])
|
|
def _subscribe():
|
|
|
|
|
|
|
|
|
|
if 'user-info' not in session:
|
|
info = request.get_json(silent=True)
|
|
user = info['user']
|
|
plans = info['plans']
|
|
else:
|
|
plans = [{"id":id} for id in request.get_json(silent=True)]
|
|
user = session['user-info']
|
|
app = request.headers['app']
|
|
#
|
|
# @TODO:
|
|
# This should be handled by transport layer ...
|
|
#
|
|
DB = COUCHDB.get_db(CONFIG['couchdb']['db']) ;
|
|
handler = Domain.User(DB,stripe)
|
|
r = handler.subscribe(user['id'],plans)
|
|
return json.dumps(r)
|
|
|
|
if __name__ == '__main__' :
|
|
app.debug = True ;
|
|
app.secret_key = '360-8y-[0v@t10n]+kr81v17y'
|
|
app.run(port=PORT,threaded=True)
|