Add bot subscriptions

To enable a user to subscribe to a recurring service with the bot, complete these steps:

  • Create a billing plan to be used as the subscription details.
  • Create a listener to hear the subscribe command from a user.
  • Create an interactive callback to start the payment processing.
  • Create web server endpoints to allow for finalizing the subscription once the user returns from PayPal, and for handling cancellations during the payment.

Creating a one time billing plan

Before you can begin processing subscriptions (billing agreements) for users, you have to set up a one-time billing plan with the details of what the users will be subscribing to.

For the sake of complete integration of functionality into the bot, create a /createplan endpoint that, when hit, will create a plan with your details and provide the billing plan ID that is needed to subscribe users.

/****************************************************************
* Create a billing plan to assign to users via billing agreement
****************************************************************/
controller.webserver.get('/createplan', function(req, res) {
  // Billing plan configuration
  var billingPlanAttribs = {
    name: 'Membership: Standard',
    description: 'Monthly plan for my bot service',
    type: 'fixed',
    payment_definitions: [{
      name: 'Standard Plan',
      type: 'REGULAR',
      frequency_interval: '1',
      frequency: 'MONTH',
      cycles: '11',
      amount: {
        currency: 'USD',
        value: '19.99'
      }
    }],
    merchant_preferences: {
      setup_fee: {
        currency: 'USD',
        value: '1'
      },
      cancel_url: redirect + '/cancel',
      return_url: redirect + '/process',
      max_fail_attempts: '0',
      auto_bill_amount: 'YES',
      initial_fail_amount_action: 'CONTINUE'
    }
  };
  
  // Attributes for PATCH call to activate billing plan
  var billingPlanUpdateAttributes = [{
    op: 'replace',
    path: '/',
    value: {
      state: 'ACTIVE'
    }
  }];

  // Create new billing plan
  paypal.billingPlan.create(billingPlanAttribs, function (error, billingPlan) {
    if (error) {
      console.log(JSON.stringify(error));
      throw error;
    } else {
      // Activate plan by changing status to active
      paypal.billingPlan.update(billingPlan.id, billingPlanUpdateAttributes, function(error, response) {
        if (error) {
          console.log(JSON.stringify(error));
          throw error;
        } else {
          res.send('Billing plan created under ID: ' + billingPlan.id);
        }
      });
    }
  });
});

When the endpoint is triggered:

  • Create the billing plan configuration details (payment information, processing and cancel endpoints, etc).
  • Create a billing plans update attribute variable, which will be used to change a new billing plan from inactive to active.
  • Call billingPlan.create(...) with with billing plan information to create the plan.
  • Call billingPlan.update(...) once the plan is created, passing the update variable, to change the billing plan to an active state.

When the plan endpoint is called, a billing plan ID will be provided. Take note of that value as it will be needed further in the code.

Listen for the subscribe command

In order to handle when a user sends a direct message to your bot with a subscribe command, set up the app to listen for those messages.

/****************************************************************
* If user has requested to subscribe to a service, provide a
* confirmation before pushing to callback for processing
****************************************************************/
controller.hears('subscribe', 'direct_message', function(bot, message) {
  bot.reply(message, {
    attachments: [{
      title: 'Would you like to subscribe to the service?',
      callback_id: '333',
      attachment_type: 'default',
      actions: [{
        name: 'yes',
        text: 'Yes',
        value: 'yes',
        type: 'button',
      },{
        name: 'no',
        text: 'No',
        value: 'no',
        type: 'button',
      }]
    }]
  });
});

The controller hears method specifies that when a user types subscribe in a direct message to the bot, you should provide an interactive button to confirm that a subscription should be started. The bot will reply with yes/no buttons asking if the user would like to subscribe to the bot service.

Create the interaction callback to start subscrption payment

Once the user clicks on the yes or no buttons, handle that activity through the interactive message callback. If no was clicked, simply cancel out. If yes was clicked, begin creating a link to redirect to PayPal to begin the subscription.

/****************************************************************
* Handle all payment requests in callback, once the user has 
* selected that they want to process a payment / subscription
****************************************************************/
controller.on('interactive_message_callback', function(bot, message) {
  // If user clicks no for payment, cancel, otherwise process payment
  if (message.actions[0].value === 'no') {
    bot.replyInteractive(message, 'Payment process cancelled');
  } else {
    bot.replyInteractive(message, 'Generating your payment link. Hold on a moment...');
    
    // Prepare base payment message for bot to respond with
    var reply = {
      attachments: [{
        fallback: 'Payment initiation information failed to load',
        color: '#36a64f',
        pretext: 'Click the link below to initiate payment',
        title: 'Make payment to COMPANY',
        footer: 'PayPal Payment Bot',
        footer_icon: 'https://s3-us-west-2.amazonaws.com/slack-files2/avatars/2016-08-17/70252203425_a7e48673014756aad9a5_96.jpg',
        ts: message.ts
      }]
    };
  
    var billingPlan = 'YOUR ACTIVATED BILLING PLAN ID';
        
    // Build isodate for billing agreement
    var isoDate = new Date();
    isoDate.setSeconds(isoDate.getSeconds() + 4);
    isoDate.toISOString().slice(0, 19) + 'Z';
        
    // Create billing agreement configuration
    var billingAgreementAttributes = {
      name: 'Standard Membership',
      description: 'Membership Description',
      start_date: isoDate,
      plan: {
        id: billingPlan
      },
      payer: {
        payment_method: 'paypal'
      }
    };

    // Use activated billing plan to create agreement
    paypal.billingAgreement.create(billingAgreementAttributes, function (error, billingAgreement) {
      if (error) {
        console.error(JSON.stringify(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, insert link into bot message and display
        if (links.hasOwnProperty('approval_url')) {
          reply.attachments[0].title_link = links['approval_url'].href;
          bot.replyInteractive(message, reply);
        } else {
          console.error('no redirect URI present');
        }
      }
    });
  }
});

Once the user clicks the yes button:

  • Create an interactive reply immediately to let the user know that the processing has started, so that they don't think that something is going wrong.
  • Create the interactive reply that will house the link to PayPal, since you can't automatically redirect the user to a web address.
  • The billingPlan variable is then populated with the plan ID that was receieved when creating the billing plan.
  • Next, create the billing agreement attributes.
  • Call billingAgreement.create(...), passing in the agreement attributes, to obtain the PayPal redirect.
  • If the billing agreement is created successfully, extract the approval URL (where you need to redirect the user to), add it to your bot reply, then display the reply to the user.

Once the user clicks that link, they will be redirected to PayPal to confirm the subscription.

Create an endpoint to finalize the subscription

Once the user confirms the subscription on PayPal, they will be redirected back to your application, to the /processagreement endpoint. To handle this redirect, add a /processagreement HTTP GET endpoint.

/****************************************************************
* Process a billing agreement once user is redirected back
****************************************************************/
controller.webserver.get('/processagreement', function(req, res) {
  // Extract validation token needed to process agreement
  var token = req.query.token;
  
  // Attempt to complete the billing agreement for the user
  paypal.billingAgreement.execute(token, {}, function (error, billingAgreement) {
    if (error) {
      console.error(JSON.stringify(error));
      throw error;
    } else {
      res.send('Billing agreement created successfully');
    }
  });
});

Once that endpoint is hit, extract the subscription token that is sent from PayPal. Once extracted, call billingAgreement.execute(...), passing in the token, to complete payment.

Create an endpoint to handle payment cancellations

If the user cancels during the PayPal payment confirmation step, they will be redirected back to the /cancel endpoint in your application. To handle this redirect add a /cancel HTTP GET endpoint.

/****************************************************************
* Payment incomplete: User cancelled the transaction on PayPal
****************************************************************/
controller.webserver.get('/cancel', function(req, res) {
  res.send('Payment cancelled');
});

This example simply displays a payment cancelled message, but your app should provide a properly handled experience.