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

"""
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)