Creating a Subscription Service with PayPal (Part 2 of 2): The Billing Agreement

By

This is part 2 of creating a subscription service. This second step to creating a subscription for a user is to create and execute a billing agreement, based on an existing activated billing plan. This tutorial assumes that you have already gone through and activated a Billing Plan in part 1, and have an ID for that billing plan to reference in the example.

If you want to jump ahead and just get a complete example for parts 1 & 2, you can get it from the PayPal Developer Github repository.

When you are setting up a billing agreement to create a subscription for a user, you’ll follow 3 steps, which you may find reminiscent to processing a standard PayPal payment:

  1. You create a billing agreement, referencing an underlying billing plan via the ID.
  2. Once created, you redirect the user to PayPal (if paying via PayPal) to confirm the subscription. Once confirmed, PayPal redirects the user back to your site using the redirect provided in the underlying billing plan.
  3. You then execute the billing agreement using a token provided back via the PayPal redirect.

This example is setting up an Express based HTTP server to showcase the billing agreement process.

To start the example, we first need to set up our configuration. We add four requirements, the PayPal SDK, body-parser for handling JSON encoded bodies, http for our simple server integration, and express for the Express framework. We then define our client ID and secret from creating an application, configure the SDK for the sandbox, then configure bodyParser for handling JSON bodies.

var paypal = require('paypal-rest-sdk'),
    bodyParser = require('body-parser'),
    http = require('http'),
    app = require('express')();

var clientId = 'YOUR APPLICATION CLIENT ID';
var secret = 'YOUR APPLICATION SECRET';

paypal.configure({
  'mode': 'sandbox', //sandbox or live
  'client_id': clientId,
  'client_secret': secret
});

app.use(bodyParser.json());

Next up, we need to create a route to handle the creation of a billing agreement and redirect the user to PayPal to confirm that subscription. We are assuming that a billing plan ID is passed as a query string parameter, such as by loading the following URL with a plan ID from the previous example:

http://localhost:3000/createagreement?plan=P-3N543779E9831025ECYGDNVQ

We now need to use that information to create the billing agreement.

app.get('/createagreement', function(req, res){
    var billingPlan = req.query.plan;

    var isoDate = new Date();
    isoDate.setSeconds(isoDate.getSeconds() + 4);
    isoDate.toISOString().slice(0, 19) + 'Z';

    var billingAgreementAttributes = {
        "name": "Standard Membership",
        "description": "Food of the World Club Standard Membership",
        "start_date": isoDate,
        "plan": {
            "id": billingPlan
        },
        "payer": {
            "payment_method": "paypal"
        },
        "shipping_address": {
            "line1": "W 34th St",
            "city": "New York",
            "state": "NY",
            "postal_code": "10001",
            "country_code": "US"
        }
    };

    // Use activated billing plan to create agreement
    paypal.billingAgreement.create(billingAgreementAttributes, function (
        error, billingAgreement){
        if (error) {
            console.error(error);
            throw error;
        } else {
            //capture HATEOAS links
            var links = {};
            billingAgreement.links.forEach(function(linkObj){
                links[linkObj.rel] = {
                    'href': linkObj.href,
                    'method': linkObj.method
                };
            })

            //if redirect url present, redirect user
            if (links.hasOwnProperty('approval_url')){
                res.redirect(links['approval_url'].href);
            } else {
                console.error('no redirect URI present');
            }
        }
    });
});

We start by extracting the billing plan ID from the query string and create the date when the plan should start.

The next object definition, billingAgreementAttributes, consists of information for the subscription. It contains readable information on the plan, a reference to the billing plan ID, the payment method, and shipping details (if needed for the subscription).

Next, a call to billingAgreement.create(...) is made, passing in the billingAgreementAttributes object we just created. If all is successful, we should have a billing agreement object passed back to us containing details about our newly created subscription. That object also contains a number of HATEOAS links providing us next steps that can be taken on this newly created agreement. The one we care about here is labeled as approval_url.

We loop through all provided links to put them into an easily referenced object. If approval_url is one of those links, we redirect the user to that link, which is PayPal.

At this point the user confirms the subscription on PayPal, and is redirected back to the URL provided in the underlying billing plan. Along with that URL, PayPal will also pass a token along the query string. That token is what we’re going to use to execute (or start) the subscription.

Let’s set up that functionality in the following route.

app.get('/processagreement', function(req, res){
    var token = req.query.token;

    paypal.billingAgreement.execute(token, {}, function (error, 
        billingAgreement) {
        if (error) {
            console.error(error);
            throw error;
        } else {
            console.log(JSON.stringify(billingAgreement));
            res.send('Billing Agreement Created Successfully');
        }
    });
});

We extract the token from the query string, then make a call to billingAgreement.execute, passing along that token. If all is successful, we now have a valid subscription for the user. The return object contains information about the active billing agreement.

Lastly, we set up our HTTP server to listen for traffic to our routes.

//create server
http.createServer(app).listen(3000, function () {
   console.log('Server started: Listening on port 3000');
});

With our billing plan in place, we are able to create a master subscription model for our services. Using that plan, we can then subscribe multiple users via an agreement, which allows us to scale and adjust our plans (and multiple users attached to that plan) very easily.