Recurly subscription integration in Django
In my last post I discussed setting up a Payment Form in Django for entering credit card information. Here is how I tied it in on the back-end to integrate with Recurly for the recurring payments on http://getpropeller.com.
I added the following to a file called subscriptions.py and added it to one of my Django Apps. It provides a light wrapper around the Recurly Python library.
class SubscriptionManager: """ A class that handles the subscriptions on Propeller """ def __init__(self, company): self.company = company recurly.API_KEY = settings.RECURLY_API_KEY recurly.DEFAULT_CURRENCY = settings.RECURLY_DEFAULT_CURRENCY # Get the coupon amount from their company information if self.company.coupon_id: coupon = CouponCode.objects.get(id=self.company.coupon_id) self.coupon_amount = coupon.amount else: self.coupon_amount = 1 def create(self, plan, payment_details): """ Created a new recurly subscription. """ subscription = recurly.Subscription() subscription.plan_code = self._get_plan_code(plan) trial_end = self.company.created + datetime.timedelta(days=(self.coupon_amount * 30)) if datetime.datetime.now() < trial_end: subscription.trial_ends_at = trial_end account = recurly.Account(account_code=self.company.id) account.email = self.company.user_id.email account.first_name = payment_details.cleaned_data.get('first_name') account.last_name = payment_details.cleaned_data.get('last_name') billing_info = recurly.BillingInfo() billing_info.number = payment_details.cleaned_data.get('number') billing_info.month = int(payment_details.cleaned_data.get('expire_month')) billing_info.year = int(payment_details.cleaned_data.get('expire_year')) account.billing_info = billing_info subscription.account = account subscription.save() # Update the company information and flush the company cache self.company.active_subscription = subscription.uuid self.company.save() def downgrade(self, plan): """ Downgrades a recurly subscrption. This can be used to downgrade from one paying account to another, in which case the subscription is updated, or to downgrade to a free account, in which case the subscription is cancelled """ # Determine if we are downgrading to another paying account, if so, update subscription if plan > 0: self._update_subscription(plan) else: # If we are downgrading to free, cancel subscription self._cancel_subscription() def upgrade(self, plan, payment_details=None): """ Upgrades a recurly subscription. This can be used to upgrade from a free account, in which case a new subscription is created, or from an existing account, in which case the subscription is updated """ # Check if this is a new subscription if not self.company.active_subscription or self.company.active_subscription == "": return self.create(plan, payment_details) else: #Its an update to an existing subscription return self._update_subscription(plan) def update(self, payment_details=None): """ Updates credit card information on Recurly """ account = self.account() billing_info = account.billing_info billing_info.first_name = payment_details.cleaned_data.get('first_name') billing_info.last_name = payment_details.cleaned_data.get('last_name') billing_info.verification_value = payment_details.cleaned_data.get('cvv_number') billing_info.number = payment_details.cleaned_data.get('number') billing_info.month = int(payment_details.cleaned_data.get('expire_month')) billing_info.year = int(payment_details.cleaned_data.get('expire_year')) try: billing_info.save() return True except recurly.ValidationError: return False def reactivate(self): if self.company.active_subscription: try: subscription = recurly.Subscription.get(self.company.active_subscription) subscription.reactivate() return True except: pass return False def payments(self): """ Gets a list of all payments (Recurly calls them invoices) that a user has for their account """ try: account = recurly.Account.get(self.company.id) return account.invoices() except recurly.NotFoundError: return False def subscription(self): """ Gets a users current subscription """ if self.company.active_subscription: try: return recurly.Subscription.get(self.company.active_subscription) except: pass return False def account(self): """ Gets a users current account information from Recurly """ try: return recurly.Account.get(self.company.id) except recurly.NotFoundError: return False def invoice(self, invoice_id): """ Tries to get an invoice and determines if it is available for the current company """ try: invoice = recurly.Invoice.get(invoice_id) return invoice if invoice.uuid == self.company.active_subscription: return invoice except: pass return False def _update_subscription(self, plan): """ Updates an existing recurly subscription """ if self.company.active_subscription: try: subscription = recurly.Subscription.get(self.company.active_subscription) except: pass else: subscription.plan_code = self._get_plan_code(plan) if self.company.plan > plan: #this is an upgrade so process now subscription.timeframe = 'now' # Update immediately. else: #this is a downgrade, do it at renewal time subscription.timeframe = 'renewal' # Update when the subscription renews. try: subscription.save() return True except: pass return False def _cancel_subscription(self): """ Cancels an existing recurly subscription """ if self.company.active_subscription: try: subscription = recurly.Subscription.get(self.company.active_subscription) subscription.cancel() return True except: pass return False def _get_plan_code(self, plan): """ Gets the coupon amount (free month count) """ plan_code = 'plan-one' if plan == 2: plan_code = 'plan-two' return plan_code
The class above can then be easily called to create, upgrade, downgrade, or cancel between different plan levels. There are still things I need to add to finish the process (such as processing the push notifications from Recurly when the cancellation actually goes through, but I will leave that for a future post). This is related to my post called Django dynamically retrieve and serve PDF file, so you will see some similar code.



Discussions
Post new comment