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.

244 lines
6.6 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
"""
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/ids',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.source=self.stripeToken
customer.save()
# self.hasPlan(uid,plan)
has_plan = True
if self.user is None and plans and stripe :
#
# 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
) ;
has_plan = False
id = customer['id']
self.user = {"_id":uid,"id":id,"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']
#
# 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()
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 :
r = stripe.Customer.retrieve(self.user['id'])
return r.subscriptions
else:
return []
"""
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:
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
"""
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']
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']
#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']
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
"""
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() ;
#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) ;