PayPal is an easy-to-use tool for making payments and ActiveMerchant is a great Ruby library for payment processing. A lot of us already have PayPal accounts for eBay and it works great in auctions, where you pay just once. However, ActiveMerchant currently does not support recurring billing through PayPal.
One requirement of a recent project was to charge for a monthly or yearly subscription plan, via PayPal. Therefore, I did some Googling and found that Jon Baker has already extended ActiveMerchant to add this functionality using PayPal’s Name-Value Pair (NVP) API. However, as Cody Fauser pointed out, the NVP API was taken out from ActiveMerchant, so I had to implement that with PayPal’s SOAP API.
First, download this file and put it in vendor/plugins/active_merchant/lib/active_merchant/billing/gateways/. Use the GitHub from now because maintaining a separate file download is too troublesome. Alternatively, there’s a fork on GitHub at http://github.com/rayvinly/active_merchant/.
In your controller, after a user selects one of your subscription plan, the form goes to the checkout action:
def checkout response = gateway.setup_agreement(:description => description, :return_url => return_url, :cancel_return_url => cancel_return_url) redirect_to gateway.redirect_url_for(response.token) end
This redirects the user to PayPal so he can login and read the description you provided. After he confirms, he is redirected back to your application’s return_url, which I set it to be the complete action below. If he cancels, he is redirected back to the cancel_return_url. You can set cancel_return_url to be the plan selection page where he can choose a different plan.
If he confirms, here’s the complete action:
def complete token = params[:token] response = gateway.create_profile(token, :description => description, :start_date => start_date, :frequency => frequency_in_months, :amount => amount_in_dollars, :auto_bill_outstanding => true) end # Save this profile_id in your transactions table. This is used to cancel/modify the plan in the future. profile_id = response.params["profile_id"] if response.success? flash[:notice] = "Your PayPal account was successfully set up for the <strong>#{description}</strong> payment plan." redirect_to login_path else flash.now[:notice] = "There was a problem setting up your PayPal account for the <strong>#{description}</strong> payment plan" render cancel_url end end
I default the frequency to be in months and turn on auto_bill_outstanding because that is what I need, but you can look at the file you downloaded and the PayPal documentation to see what other options are available.
If the user wants to cancel the payment plan, a cancel action would look like:
def cancel response = gateway.cancel_profile(paypal_profile_id, :note => 'Payment plan was canceled by user') flash[:notice] = 'You have successfully canceled your membership' end
paypal_profile_id is the profile_id you saved in the complete action from above. Keeping this profile_id is very handy.
That’s it. Enjoy making money!