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.stripeToken= stripeToken
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,
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
"""
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 :
r = r.first()
return r['value']
else:
None
def init (self,uid,plans):
customer = {}
id = self.getId(uid)
if id is not None:
self.user = self.db.get(id)
@ -50,9 +57,11 @@ class User:
if 'sources' not in self.user or self.user['sources'] is None:
#@TODO: get the appropriate id
customer = stripe.Customer.retrieve(self.user['id'])
customer.source=self.stripeToken
customer.save()
# customer = stripe.Customer.retrieve(self.user['id'])
#customer = stripe.Customer.retrieve(id)
#customer.source=self.stripeToken
#customer.save()
pass
# self.hasPlan(uid,plan)
has_plan = True
if self.user is None and plans and stripe :
@ -65,18 +74,23 @@ class User:
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
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:
#
# 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
#
self.user = self.db.get(uid) ;
id = self.user['id']
#self.user = self.db.get(uid) ;
self.user = self.db.get(id)
# id = self.user['id']
#
# If this user's isn't already subscribed we should subscribe her
# 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 ...
#
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
else:
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
@ -129,9 +148,11 @@ class User:
plan=plan['id']
) ;
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)
self.user['sources'] = self.cast(customer.sources)
sub = self.stripe.Subscription.create(
customer=id,
@ -146,30 +167,10 @@ class User:
#
#r.append(x)
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):
self.user = self.db.get(uid)
id = self.user['id']
#self.user = self.db.get(uid)
#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]
return r
"""
@ -177,9 +178,10 @@ class User:
@pre : an item with amount > 0 and status past_due
"""
def get_invoices(self,uid):
info = self.db.get(uid)
id = info['id']
#return stripe.Invoice.list(customer=id)
#info = self.db.get(uid)
#id = info['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
@ -187,8 +189,9 @@ class User:
"""
def charge(self,uid,token,plans):
info = self.db.get(uid)
id = info['id']
#info = self.db.get(uid)
#id = info['id']
id = self.getId(uid)
sid = plans[0]['subscription']
user = stripe.Customer.retrieve(id)
user.source = token
@ -221,17 +224,13 @@ class User:
else:
#perform an update
pass
"""
This function updates/creates a user remotely
@pre:
"""
def publish(self,info={}):
# We need to figure out whether to create or update;
#
if self.user is not None and info is not None:
customer = self.stripe.Customer.retrieve(self.user['id']) ;
customer.metadata = info ;
customer.save() ;
def update_user(self,**args) :
id = args['id']
uid= args['uid']
self.user = self.db.get(id)
self.user['emails'].append(uid)
self.user['emails'] = list(set(self.user['emails']))
self.db.save_doc(self.user)
#f = open('config.json')
#conf = json.loads(f.read())
#f.close()

@ -28,11 +28,39 @@ stripe.api_key = stripe_keys['secret_key'].strip()
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
Then Setup the stage for initialization of {signup for, user-account}
This function will set the user information to the session and update the information
@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
@ -51,76 +79,68 @@ def subscribe(app_name):
uid = request.form['stripeEmail']
tokenType = request.form['stripeTokenType']
amount = request.form['amount']
key = request.form['plan']
pid = request.form['plan']
else:
key = request.headers['key']
pid = request.headers['pid']
uid = request.headers['uid']
stripeToken = None
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"
couch_handler = Couchdb(uri=CONFIG['couchdb']['uri'],dbname=app_name,uid=uid)
DB = couch_handler.dbase
handler = Domain.User(DB,stripe,stripeToken) ;
if plan :
handler.init(uid,plan)
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
@resource app_name application identifier
@header pid plan identifier (optional)
@header uid user identifier
This function returns the features of a user for a given application
The features are returned provided the plan is still valid i.e (end_date,status={active|trailing})
@header uid user's email
"""
@app.route('/get/info/<app_name>',methods=['GET'])
def get_plan_info(app_name) :
@app.route('/features/<app_name>')
def features(app_name):
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']
handler = Domain.User(couchdb.dbase,stripe)
handler.initialize(uid)
lsub = handler.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['metadata'] for item in plans if item['id']]
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):
"""
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']
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('/get/plans/<app_name>')
handler = Domain.User(couchdb.dbase,stripe)
handler.initialize(uid)
#lsub = handler.subscriptions()
plans = handler.plans()
return json.dumps(plans)
"""
This endpoint is the signup form for a given application,
It will allow user's to be able to signup for various plans
@pre 'uid' in request.headers
"""
@app.route('/signup/<app_name>')
def get_plans(app_name) :
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'
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'])]
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)
@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)
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/<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__' :
app.debug = True ;

Loading…
Cancel
Save