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.
252 lines
7.1 KiB
Python
252 lines
7.1 KiB
Python
"""
|
|
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 id is not None and 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:
|
|
return 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.
|
|
#
|
|
|
|
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
|
|
#
|
|
|
|
plans = [{"id":plans}]
|
|
lsub = self.subscriptions()
|
|
if len(lsub.data) > 0 and len(plans[0]["id"]) > 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]
|
|
print lplans,'\n',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 = [ dict({"status":sub['status']},**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:
|
|
|
|
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 :
|
|
_invoice= stripe.Invoice.create(customer=id,subscription=sid)
|
|
r = _invoice.pay()
|
|
|
|
"""
|
|
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']
|
|
print ['update ...',id,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) ;
|