Badminton on Rails
RSS icon Home icon
  • PayPal Recurring Billing with ActiveMerchant in Ruby on Rails

    Posted on February 20th, 2009 Raymond Law 42 comments

    ActiveMerchant is great and PayPal is easy to use. A lot of us already have a PayPal account for EBay. It works great in auctions where you pay once. However, ActiveMerchant currently does not support PayPal recurring billing. One requirement of a recent project is to be able to charge for a monthly or yearly subscription plan with 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)
     
        # 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. In particular, read these two PDFs:

    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!

    UPDATE 2009-02-04

    I updated the extension to include a credit_card hash. You will probably capture the credit card details submitted by the user in a form as in params[:credit_card], but I will just create a hash to show as an example. To make recurring payment with a credit card, just pass nil for the token (this is required, because PayPal would use the token in preference to the credit card). Pass the credit_card hash as follows:

        credit_card = Hash.new
        credit_card[:type] = 'Visa'
        credit_card[:number] = '1234567812345678'
        credit_card[:exp_month] = '07'
        credit_card[:exp_year] = '2016'
        credit_card[:cvv2] = '123'
        credit_card[:card_owner] = 'John Doe'
        response = gateway.create_profile(nil, :credit_card => credit_card, :description => description, :start_date => start_date, :frequency => frequency, :amount => amount, :auto_bill_outstanding => true)

    However, I got the famous DPRP is disabled for this merchant error message when I tested it. I did a little googling but I haven’t found the cause. There may just be something that you have to turn on in your PayPal developer account. I also read that it works for newer accounts only. But since I don’t have a need for this, I didn’t look further. If you guys know, please let me know. I will try to update the extension as soon as possible to make it work with a credit card.