""" This class is designed to handle Customers in coordination with stripes. Identity Federation A user with several emails should not have to pay for a service more than once because she has several email/cloud accounts. We have therefore decided to tie several emails to a stripe customer identity """ import stripe from couchdbkit import Server, Document import json from sets import Set class User: def __init__(self,db,stripe=None,stripeToken=None) : self.db = db 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 """ def cast(self,object) : return json.loads(json.dumps(object, sort_keys=True, indent=2)) """ This function creates a stripe customer, and saves a copy with us for internal use We use couchdb as a cache of sorts @param uid customer's email @param plans list of plans the user is subscribing """ def getId(self,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) # if self.db.doc_exist(uid) : # self.user = self.db.get(uid) if self.stripeToken is not None: 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 = stripe.Customer.retrieve(id) #customer.source=self.stripeToken #customer.save() pass # self.hasPlan(uid,plan) has_plan = True if id is None : # # First time customer, register them and sign them up for the first plan # #customer['sources'] = str(self.stripeToken) customer = self.stripe.Customer.create( source=self.stripeToken, 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} # # At this point we have a new user and a plan identifier # We formt the plan as it is given to us as a string ... later will be subscribed. # plans = [{"id":plans}] 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) ; 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 # lsub = self.subscriptions() if len(lsub.data) > 0 : lplans = [str(item['plan']['id']) for item in lsub.data if item.ended_at in [None,""]] x_plans = [item['id'] for item in plans] if lplans and not set(x_plans) - set(lplans) : has_plans = False x = list(set(x_plans) - set(lplans)) plans = [ item for item in plans if item.id in x] else: # # In case the user doesn't have any plans with us # has_plan = False if has_plan == False : r = self.subscribe(id,plans) lsub.data.append(r[0]) # # Backing up the information to be processed later # - We assume any interaction with the payment classes will start by updating information associated operation lsub = self.cast(lsub.data) self.user['subscriptions'] = lsub self.db.save_doc(self.user) def subscriptions(self): # # call stripe to retrieve subscriptions for this user ... # 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 @pre isinstance(plans,list) """ def subscribe(self,id,plans): r = [] for plan in plans: print ' --- >> ' plan if self.stripeToken is None: sub = self.stripe.Subscription.create( customer=id, 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, plan=plan['id'], source=self.stripeToken ) ; r.append(sub) # # We need to create an invoiced item out of this plan # We will attach it later to an invoice ... # #r.append(x) return r def invoice(self,uid,sid): #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 """ This function creates an invoice @pre : an item with amount > 0 and status past_due """ def get_invoices(self,uid): #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 @pre : card,invoice """ def charge(self,uid,token,plans): #info = self.db.get(uid) #id = info['id'] id = self.getId(uid) 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 """ def exists(self,uid): return self.db.doc_exist(uid) # return self.user is not None ; """ This function will store the user within our system """ def attach(self,attachment,name,mime): self.db.put_attachment(self.user,attachment,name,mime) ; def save(self,uid=None): if self.exists() == False: self.init(uid) ; else: #perform an update pass 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() #server = Server(uri=conf['couchdb']['uri']) ; #db = server.get_db(conf['couchdb']['db']) ; #print db.doc_exist('steve@gmail.com') #doc = db.get('steve@gmail.com') #doc['type'] = 'business' #db.save_doc(doc) ;