Bug fix and sanitized ui @TODO: Add context handling

legacy
Steve L. Nyemba 8 years ago
parent 546f3afdb9
commit b72f76fdc6

@ -17,7 +17,11 @@ class User:
self.stripe = stripe; self.stripe = stripe;
self.stripeToken= stripeToken self.stripeToken= stripeToken
self.user = None self.user = None
def initialize(self,uid) :
id = self.getId(uid)
if self.db.doc_exist(id) :
self.user = self.db.get(id)
""" """
This function will cast an object to JSON, This function will cast an object to JSON,
It is designed to address an inherent limitation of stripe object hierarchy It is designed to address an inherent limitation of stripe object hierarchy
@ -32,14 +36,17 @@ class User:
@param plans list of plans the user is subscribing @param plans list of plans the user is subscribing
""" """
def getId(self,uid): def getId(self,uid):
r = self.db.view('federation/ids',key=uid) r = self.db.view('federation/uid_map',key=uid)
if r.count() > 0 : if r.count() > 0 :
r = r.first() r = r.first()
return r['value'] return r['value']
else: else:
None None
def init (self,uid,plans): def init (self,uid,plans):
customer = {} customer = {}
id = self.getId(uid) id = self.getId(uid)
if id is not None: if id is not None:
self.user = self.db.get(id) self.user = self.db.get(id)
@ -50,9 +57,11 @@ class User:
if 'sources' not in self.user or self.user['sources'] is None: if 'sources' not in self.user or self.user['sources'] is None:
#@TODO: get the appropriate id #@TODO: get the appropriate id
customer = stripe.Customer.retrieve(self.user['id']) # customer = stripe.Customer.retrieve(self.user['id'])
customer.source=self.stripeToken #customer = stripe.Customer.retrieve(id)
customer.save() #customer.source=self.stripeToken
#customer.save()
pass
# self.hasPlan(uid,plan) # self.hasPlan(uid,plan)
has_plan = True has_plan = True
if self.user is None and plans and stripe : if self.user is None and plans and stripe :
@ -65,18 +74,23 @@ class User:
email=uid email=uid
) ; ) ;
#
# The customer is being persisted on our end
# We do this considering user federations i.e several emails attached to an account
#
has_plan = False has_plan = False
id = customer['id'] id = customer['id']
self.user = {"_id":uid,"id":id,"source":self.stripeToken} #self.user = {"_id":uid,"id":id,"source":self.stripeToken}
self.user = {"_id":id,"emails":[uid],"source":self.stripeToken}
else: else:
# #
# The user exists but let's see if the user is subscribed to this plan # The user exists but let's see if the user is subscribed to this plan
# If she is and the plan is still live then there is nothing to do # If she is and the plan is still live then there is nothing to do
# #
self.user = self.db.get(uid) ; #self.user = self.db.get(uid) ;
id = self.user['id'] self.user = self.db.get(id)
# id = self.user['id']
# #
# If this user's isn't already subscribed we should subscribe her # If this user's isn't already subscribed we should subscribe her
# We perform a set operation to determine if she is alread susbscribed # We perform a set operation to determine if she is alread susbscribed
@ -109,12 +123,17 @@ class User:
# #
# call stripe to retrieve subscriptions for this user ... # call stripe to retrieve subscriptions for this user ...
# #
if self.user :
r = stripe.Customer.retrieve(self.user['id']) if self.user is not None :
id = self.user['_id']
r = stripe.Customer.retrieve(id)
return r.subscriptions return r.subscriptions
else: else:
return [] return []
def plans(self):
lsub = self.subscriptions()
plans = [ sub['plan'] for sub in lsub if sub['ended_at'] is None ]
return plans
""" """
This function subscribes a customer to n-plans This function subscribes a customer to n-plans
@ -129,9 +148,11 @@ class User:
plan=plan['id'] plan=plan['id']
) ; ) ;
else: else:
#
# The provided token has been updated on the customer's profile (init function)
# Because of this we can just subscribe her and the card will be used accordingly
#
customer = stripe.Customer.retrieve(id) customer = stripe.Customer.retrieve(id)
self.user['sources'] = self.cast(customer.sources) self.user['sources'] = self.cast(customer.sources)
sub = self.stripe.Subscription.create( sub = self.stripe.Subscription.create(
customer=id, customer=id,
@ -146,30 +167,10 @@ class User:
# #
#r.append(x) #r.append(x)
return r return r
"""
This function will save card information
"""
def save_card(self,uid,token):
user = self.db.get(uid)
if 'sources' not in user or (token not in user['sources']):
#
# In this case we don't have a card
#
id = user['id']
customer = stripe.Customer.retrieve(id)
card = customer.sources.create(source = token)
if 'sources' not in user:
user['sources'] = []
user['sources'].append(card['id'])
self.db.save_doc(user) ;
self.user = user
#
# Creating an invoice for this user
def invoice(self,uid,sid): def invoice(self,uid,sid):
self.user = self.db.get(uid) #self.user = self.db.get(uid)
id = self.user['id'] #id = self.user['id']
id = self.getId(uid) ;
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] 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 return r
""" """
@ -177,9 +178,10 @@ class User:
@pre : an item with amount > 0 and status past_due @pre : an item with amount > 0 and status past_due
""" """
def get_invoices(self,uid): def get_invoices(self,uid):
info = self.db.get(uid) #info = self.db.get(uid)
id = info['id'] #id = info['id']
#return stripe.Invoice.list(customer=id) id = self.getId(uid)
return stripe.Invoice.list(customer=id)
""" """
This function will clear a particular invoice (hopefully). No partial payments should be accepted This function will clear a particular invoice (hopefully). No partial payments should be accepted
@ -187,8 +189,9 @@ class User:
""" """
def charge(self,uid,token,plans): def charge(self,uid,token,plans):
info = self.db.get(uid) #info = self.db.get(uid)
id = info['id'] #id = info['id']
id = self.getId(uid)
sid = plans[0]['subscription'] sid = plans[0]['subscription']
user = stripe.Customer.retrieve(id) user = stripe.Customer.retrieve(id)
user.source = token user.source = token
@ -221,17 +224,13 @@ class User:
else: else:
#perform an update #perform an update
pass pass
""" def update_user(self,**args) :
This function updates/creates a user remotely id = args['id']
@pre: uid= args['uid']
""" self.user = self.db.get(id)
def publish(self,info={}): self.user['emails'].append(uid)
# We need to figure out whether to create or update; self.user['emails'] = list(set(self.user['emails']))
# self.db.save_doc(self.user)
if self.user is not None and info is not None:
customer = self.stripe.Customer.retrieve(self.user['id']) ;
customer.metadata = info ;
customer.save() ;
#f = open('config.json') #f = open('config.json')
#conf = json.loads(f.read()) #conf = json.loads(f.read())
#f.close() #f.close()

@ -28,11 +28,39 @@ stripe.api_key = stripe_keys['secret_key'].strip()
app = Flask(__name__) app = Flask(__name__)
COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ;
""" """
This function will attempt to create an account for a user if the user does NOT exist This function will set the user information to the session and update the information
Then Setup the stage for initialization of {signup for, user-account} @header uid user email address
""" """
COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ; @app.route("/init/<app_name>",methods=['POST'])
def init(app_name):
if 'uid' in request.headers:
uid = request.headers['uid']
couchdb = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=None)
DB = couchdb.dbase
handler = Domain.User(DB,stripe) ;
handler.init(uid,None)
id=handler.getId(uid)
session['customer.id'] = id
return ('',204)
"""
This function will update the user's email
"""
@app.route('/init/<app_name>',methods=['PUT'])
def init_update(app_name):
uid = request.headers['uid']
id = session['customer.id']
couchdb = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=None)
DB = couchdb.dbase
handler = Domain.User(DB,stripe)
handler.update_user(uid=uid,id=id)
return ('',204)
""" """
This function subscribes a user to a given service for an application This function subscribes a user to a given service for an application
@ -51,76 +79,68 @@ def subscribe(app_name):
uid = request.form['stripeEmail'] uid = request.form['stripeEmail']
tokenType = request.form['stripeTokenType'] tokenType = request.form['stripeTokenType']
amount = request.form['amount'] amount = request.form['amount']
key = request.form['plan'] pid = request.form['plan']
else: else:
key = request.headers['key'] pid = request.headers['pid']
uid = request.headers['uid'] uid = request.headers['uid']
stripeToken = None stripeToken = None
plans = stripe.Plan.list().data plans = stripe.Plan.list().data
plan = [item for item in plans if item.id == key] plan = [item for item in plans if item.id == pid]
resp = "0" resp = "0"
couch_handler = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid) couch_handler = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid)
DB = couch_handler.dbase DB = couch_handler.dbase
handler = Domain.User(DB,stripe,stripeToken) ; handler = Domain.User(DB,stripe,stripeToken) ;
if plan : if plan :
handler.init(uid,plan) handler.init(uid,plan)
resp = plan[0].id resp = plan[0].id
return ('done',204) return ('',204)
""" """
This function returns the meta data about a given plan or set of plans for a given application This function returns the features of a user for a given application
@resource app_name application identifier The features are returned provided the plan is still valid i.e (end_date,status={active|trailing})
@header pid plan identifier (optional) @header uid user's email
@header uid user identifier
""" """
@app.route('/get/info/<app_name>',methods=['GET']) @app.route('/features/<app_name>')
def get_plan_info(app_name) : def features(app_name):
plans = [] plans = []
print ' *** ','uid' in request.headers
if 'uid' in request.headers : if 'uid' in request.headers :
uid = request.headers['uid'] 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) couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
info = couchdb.read() handler = Domain.User(couchdb.dbase,stripe)
lsub = info['subscriptions'] handler.initialize(uid)
lsub = handler.subscriptions()
plans = [ sub['plan'] for sub in lsub if sub['ended_at'] is None ] 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']]
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) return json.dumps(plans)
@app.route('/get/sub/<app_name>') """
def get_sub_info(app_name): 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'] 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) couchdb = CouchdbReader(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
info = couchdb.read() handler = Domain.User(couchdb.dbase,stripe)
lsub = info['subscriptions'] handler.initialize(uid)
# #lsub = handler.subscriptions()
# @TODO: Return critical information only i.e: plans = handler.plans()
# - subscription state (dates,status) return json.dumps(plans)
# - how much is owed """
# - subscription id This endpoint is the signup form for a given application,
# It will allow user's to be able to signup for various plans
if pid is not None: @pre 'uid' in request.headers
subs = [ sub for sub in lsub if sub['plan']['id'] == pid ] """
else: @app.route('/signup/<app_name>')
subs = lsub
return json.dumps(subs)
@app.route('/get/plans/<app_name>')
def get_plans(app_name) : def get_plans(app_name) :
apikey = CONFIG['stripe']['pub'].strip() apikey = CONFIG['stripe']['pub'].strip()
# #
@ -129,10 +149,22 @@ def get_plans(app_name) :
# #
uid = session['uid'] if 'uid' in session else 'nyemba@gmail.com' uid = session['uid'] if 'uid' in session else 'nyemba@gmail.com'
plans = stripe.Plan.list(limit=10) plans = stripe.Plan.list(limit=10)
handler = Domain.User(None)
plans = handler.cast(plans.data) # plans = handler.cast(plans.data)
plans = json.loads(json.dumps(plans.data, sort_keys=True, indent=2))
plans = [item for item in plans if re.match(app_name,item['name'])] plans = [item for item in plans if re.match(app_name,item['name'])]
if uid is not None:
couchdb = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid,create=False)
handler = Domain.User(couchdb.dbase,stripe)
handler.init(uid,[])
subs = handler.subscriptions().data
ids = [item.id for item in subs]
#
# @TODO: Mark the plans the current user is signed up for
#
return render_template('subscribe.html',uid=uid,app_name=app_name,plans=plans,apikey=apikey) return render_template('subscribe.html',uid=uid,app_name=app_name,plans=plans,apikey=apikey)
@app.route('/subscribe/<name>',methods=['DELETE']) @app.route('/subscribe/<name>',methods=['DELETE'])
@ -191,132 +223,6 @@ def is_customer (app_name):
html = html.replace(":email",uid).replace(":amount",str(amount)).replace(":key",apikey).replace(":app_name",app_name) html = html.replace(":email",uid).replace(":amount",str(amount)).replace(":key",apikey).replace(":app_name",app_name)
amount = amount / 100 amount = amount / 100
return render_template('bill.html',apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount,html=html) 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/<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']
# 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
#
#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']:
#
# 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__' : if __name__ == '__main__' :
app.debug = True ; app.debug = True ;

Loading…
Cancel
Save