PayPal Digital Goods Subscriptions with PHP

Recurring Payments with Digital Goods for Express Checkout is the 8 word name PayPal chose for their most convenient subscription product. The verbose name is a good indicator of the API’s complexity.

It’s actually quite easy to integrate Digital Goods subscriptions. We send and receive a few HTTP requests and voilà, PayPal creates a subscription.

Unfortunately, the PayPal documentation is a labyrinth that never succinctly outlines the content and order of the HTTP requests required.

Nate, a PayPal staff member, helped map the labyrinth with this blog post. He didn’t cover subscriptions though, so this post will. You should read Nate’s post before continuing with this post.

3 Steps to Subscriptions

In a nutshell, the process for creating a subscription is like so:

  1. Tell PayPal we want to create a subscription;
  2. Confirm the subscription;
  3. Activate the subscription.

This process is the same as that outlined in Nate’s blog post. However, the API request strings differ, so this post focuses on the request strings’ content.

You can use all the API request strings provided in this post with the PPHttpPost() method Nate provides in his example.

Step 1: Request a Token

Before we can have a user agree to pay for a subscription, we need a token from PayPal to represent the subscription.

To request a subscription token, we Nate’s PPHttpPost() function with the $my_api_str parameter set to:

$my_api_str = $cred_str
         . "&METHOD=SetExpressCheckout"
         . "&RETURNURL="  
         . "&CANCELURL="
         . "&BILLINGTYPE=RecurringPayments"
         . "&BILLINGAGREEMENTDESCRIPTION=" . urlencode("Hacker Monthly Subscription")
         . "&CURRENCYCODE=USD"
         . "&MAXAMT=100";

At first, I thought when requesting a token I’d need to tell PayPal the details of the subscription, like the price, duration or frequency – we don’t. We just tell PayPal we want to create a subscription. We give PayPal the details of our subscription only when we activate it.

The MAXAMT Parameter

This parameter indicates the average price per billing period for the subscription. By default, PayPal sets this to $25.

If your subscription is for more than $25 per period, you must include the MAXAMT parameter with a value equal to or greater than your price per period.

Step 2: Use the token to start the subscription.

Once we have a token and the user has agreed to subscribe, PayPal suggests we have them confirm the charges.

Referring again to Nate’s post, we can use return.php and get the subscription’s details from PayPal with the API string:

$my_api_str = $cred_str . "&METHOD=GetExpressCheckoutDetails&TOKEN=" . urldecode($token);

We use the GetExpressCheckoutDetails method regardless of whether the request is for a subscription or one-off purchase.

Although suggested by PayPal, in my opinion, this step is redundant. Display the subscription price, period & frequency before directing the user to PayPal, then they are already aware of the details when they return to the site. In such a case, we can skip this step and go to Step 3.

Step 3: Activate the Subscription

Once a subscriber has confirmed the subscription, we post a request to PayPal to give them the details of the subscription and ask them to activate the subscription.

To do this, we call the PPHttpPost() function with:

$my_api_str = $cred_str
        . "&METHOD=CreateRecurringPaymentsProfile"
        . "&TOKEN=" . $_GET['token'] // Subscription we are activating
        . "&AMT=" . urlencode( '10.00' )
        . "&PROFILESTARTDATE=" . urlencode( date( 'Y-m-d\TH:i:s', time() + ( 24 * 60 * 60 ) ) )
        . "&BILLINGPERIOD=Month"
        . "&DESC=" . urlencode( 'Time Magazine subscription' )
        . "&INITAMT=".urlencode("80.00")

This string creates a 3 month subscription for $10 per month, starting from tomorrow, with a $80 sign-up fee. You can customise the NVP parameters in this request to set the price, frequency, duration, sign-up fee and trial period of the subscription.

Never Mind the Bollocks, Here’s the PHP Library

If you want to bypass the PayPal labyrinth altogether, you’re in luck. I’ve released a PayPal Digital Goods PHP Class that is friendly to humans. It’s a work in progress, but it strives to make it simpler to interact with the PayPal API by:

  • Eliminating the drudgery: for example, requesting a token, printing JavaScript and printing the purchase button is all done with one function print_buy_button();
  • Human friendly variable names: To reduce request size, PayPal’s API uses shortened parameter names. As we create instances of the class server-side, it can afford to use longer, more human friendly names. For example, initial_amount refers to PayPal’s INITAMT parameter.
  • Abstracting PayPalisms: PayPal loves verbiage, I don’t. The class attempts to simplify some of PayPal terms to more colloquial terms. For example, the get_subscription_details() function performs PayPal’s GetRecurringPaymentsProfileDetails method.
  • Not repeating code: we only need to create one instance of the class for each subscription in our application. The credential & NVP API strings for every request are then automatically built using simple function calls.

Check out the class on GitHub for more information.

About Brent

Born to code.
This entry was posted in Blogdex, Hacker Tales and tagged , , , . Bookmark the permalink.

14 Responses to PayPal Digital Goods Subscriptions with PHP

  1. Thomas says:

    Thank you. Thank you. Thank you.

  2. Tevya says:

    So when does the WooCommerce extension come out? I know there’s a lot of people who would love to sell subscriptions, and implementing this as an easy-to-use WooCommerce plugin would probably be super popular. I’d buy it.

    • Brent says:

      As WooCommerce doesn’t have a membership/subscriptions product type, I didn’t add subscriptions into the Digital Goods PayPal payment gateway. I considered it, but it felt like putting a hat on a cat – makes for rhyme not reason.

      • Tevya says:

        So once some kind of subscription option is added (or somebody builds an extension), perhaps Digital Goods will just be upgraded to work with that feature? Or you assume they’ll build it in to the Extension so it won’t be needed later?

  3. I really appreciate this and your examples on github. I do have a question though. I am hoping to also find a way to deal with the cancellations of subscriptions. I could not find anything pertaining to this in your examples and was hoping you may have something I am missing that I could use.

    If not, could you suggest an ideal approach to dealing with IPN notifications on digital good subscription cancellations?

    Thanks again!

    • Brent says:

      Hi Josh, a very good question. There is nothing built into the library to handle cancellations.

      The ideal approach is to set an IPN URL as part of the application (rather than manually in the PayPal Admin dashboard).

      I believe this can be done by setting the value of PAYMENTREQUEST_n_NOTIFYURL parameter in the DoExpressCheckoutPayment operation. PayPal has some (slightly out-of-date) documentation on this process here (which refers to PAYMENTREQUEST_n_NOTIFYURL parameter by its deprecated handle NOTIFYURL).

      You can find the up-to-date documentation on the DoExpressCheckoutPayment operation here.

      If you do extend the library to add such functionality, please make a pull request on Github so I can merge it into the core library to share with others.

      • Sounds good. I will look into doing this but I imagine it will be considerably more mundane than your code, considering I am just beginning with php and paypal integration.

        I have another question as well. I notice you can do purchases, and subscriptions, can your classes be used in conjunction to have one button that provides a digital purchase as well as a digital subscription at once?

        Thanks again Brent.

        • Brent says:

          I’m not sure if PayPal support having a purchase and subscription in the one checkout process, but you can always test it to find out!

          Also, I do take on some freelance work. My rates are usually too high for bootstrapped projects, but if you’ve got a company backing this and don’t mind sharing the code I write in the public library shoot me an email.

        • Brent says:

          Joshua, just a heads up. I’ve since learned it is possible to have both a purchase and subscription in the one checkout process. I need to refactor my library to make this possible for a product I’m working on, so the public library should have this functionality (along with clear documentation) in a couple of weeks. 🙂

  4. Brent,

    I am glad to hear about the subscription and purchase in one. I am also struggling with the Custom field currently. When I set the field it does not show on any IPN reports. Doing it with a button manually includes it, however running it through your class leaves the field empty.

    Any thoughts?

Comments are closed.