Paypal Recurring Payments
There is a time when every SaaS need to get his money, and maybe you choose to get them month by month.
This is a post about the steps that I took to integrate Paypal recurring payments, and the problems that I run into.
Part 1. Find the right library
The most common and known library for payment, and the one I was using for instant payment with Paypal is the ActiveMerchant gem, an huge container of all the payment gateway of the galaxy.
The gem is great, until you realize that you need recurring payments....
Some users provided forks with their own implementatios, but there wans't a clean solution, and some messy code inside a huge library for me was not the way to go.
gem "paypal-recurring", "~> 1.1.0"
Part 2. Understanding Paypal
Before the integration of the gem is important to understand what we are going to do and why.
I attach an image from the Paypal developer website, that explain all the steps needed for a recurring payment:
Image credit of Paypal, source: Paypal Developer Network
We ask Paypal for an address where we can send our customer, with a return and an error url
Paypal give us the address, and we redirect the customer there
Customer signup and/or login to Paypal, and confirm the amout that we want to bill montly.
If the customer agree is redirected to the return url we provided. Paypal return us a token and an id to identify the customer and the transaction
We show the customer a review of the subscription
If the customer agree we instantly bill the amount and we create a recurring profile for the future payments.
WARN: The files in this section are only examples, the code miss some basic security, that can be easly added when integrated in your project.
Using the examples provided by the gem, and a little help from a recent Railscasts I ended up with this integration.
Subscription model: here you can manage the relations with the customers. One customer has many subscriptions, and a subscription belongs to a plan.
Through the paypal method we can access the PaypalPayment model, that manage the code between the gem "paypal-recurring" and our code.
The PaypalPayment module is as follow:
class PaypalPayment def initialize(subscription) @subscription = subscription end def checkout_details process :checkout_details end def checkout_url(options) process(:checkout, options).checkout_url end def make_recurring payment = process(:request_payment) if payment.approved? && payment.completed? # Set the subscription as paid end recurring = process(:create_recurring_profile, {period: :monthly, frequency: 1, start_at: Time.zone.now + 30.days}) @subscription.update_attribute(:paypal_profile_id, recurring.profile_id) end def cancel_recurring ppr = PayPal::Recurring.new(:profile_id => @subscription.paypal_profile_id) ppr.cancel end private def process(action, options = {}) options = options.reverse_merge( token: @subscription.paypal_token, payer_id: @subscription.paypal_payer_id, description: @subscription.name, amount: @subscription.amount, currency: "USD", ipn_url: "http://www.example.com" + "/paypal/notifications/#{@subscription.id}" ) response = PayPal::Recurring.new(options).send(action) raise response.errors.inspect if response.errors.present? response end end
Checkout details: return some details about the customer that is subscribing to our website, like name and email address.
Checkout url: return the url of paypal where we need to redirect the customer to give us the permission to create a recurring payment.
Make recurring: Create an instant payment, and a recurring profile. Note: The first month is paid with the instant payment, so the recurring start from the next month.
Cancel recurring: Cancel the recurring profile
Everything is glued in the controller
class PaymentsController :notification before_filter :load_subscription def checkout redirect_to @subscription.paypal.checkout_url( return_url: "http://www.example.com" + "/paypal/review/#{@subscription.id}", cancel_url: "http://www.example.com" + "/paypal/cancel/#{@subscription.id}" ) end def review @subscription.update_attributes(paypal_token: params["token"], paypal_payer_id: params["PayerID"]) end def complete @subscription.paypal.make_recurring end def cancel end # Manage the paypal IPN # def notification notify = PaypalNotification.new(request.raw_post) if notify.acknowledge begin if notify.complete? && @subscription.amount == notify.amount # Mark as paid else # Mark as error and investigate end rescue => e # Mark as error and investigate raise end end render :nothing => true end private def load_subscription @subscription = Subscription.find(params[:id]) end end
This steps should be enough to integrate paypal into your app. Paypal also provide a sandbox in wich you can create fake account and test that everything is working as expected.
Payment is mostly done in Paypal and model code, the views can be styled to reflect your website aspect and don't require specific code.
On another post I will show how to manage the IPN with a custom library.
The code in this post can be found in this Gist: https://gist.github.com/4197985