Setting Up Recurring Charges with the Rapyd Collect API
Tags: node, api, subscriptions
Published: 2023-05-10 19:16:59

By Anshuman Bhardwaj*

Recurring charges are a common feature in any digital service, or e-commerce applications, as they allow businesses to set up subscriptions for their customers. Recurring charges are beneficial for subscription-based services, such as streaming video or music, software-as-a-service products, such as Slack, or consumable products that need to be replenished regularly, such as monthly deliveries of coffee or other consumables. By setting up recurring charges, businesses can automate their billing process and ensure that they receive regular payments from their customers. The customers don't have to keep a tab on the bill due dates—they can sit back, and the payment providers will notify them when a payment is due.

[Rapyd](https://rapyd.net) is a global payment acceptance platform that supports various payment methods, such as e-wallets and international cards, bank transfers, and cash. The Rapyd Collect API has built-in support for recurring charges or subscriptions. It provides a range of features and endpoints that allow developers to quickly set up and manage recurring charges, including creating customers, linking payment methods to customers, and generating regular invoices. With the Rapyd Collect API, businesses can quickly implement recurring charges in their e-commerce applications and streamline their billing process.

In this article, you'll create a simple application to collect recurring payments for a monthly parking subscription. To implement the application, you'll create customer profiles, link payment methods with customers, and set up recurring charges using a Node.js server.

## Implementing Recurring Charges Using the Rapyd Collect API

Before getting started, you’ll need Node version 14 or above and npm version 6 or above installed on your computer. You can also clone and set up this [GitHub repository](https://github.com/Anshuman71/rapyd-recurring-charges-example) locally to follow along with the tutorial.

### Setting Up the Node.js Project

Create a new project folder called `rapyd-recurring-charges-example` and navigate to it in your terminal:

```bash
  cd rapyd-recurring-charges-example
```

Then, run the following command to create a `package.json` file:

```bash
  npm init -y
```

Once the `package.json` file is created, you can start installing the project dependencies. First, you need [Express](https://expressjs.com/), a web application framework for Node.js that provides a range of features and functions for building web applications and APIs. Install Express by running the following command:

```bash
  npm install express
```

With Express installed, you can now create a file named `index.js` in your project folder and add the following code:

```js
const express = require("express");
const app = express();
const port = 3000;

app.get("/", (request, response) => {
  response.send("Rapyd example!");
});

app.listen(port, () => {
  console.log(`Rapyd app listening on port ${port}`);
});
```

The above code creates an Express app that listens for HTTP requests on port 3000 and responds with a message when the root URL `http://localhost:3000` is accessed.

To start the app, open your terminal and navigate to your project folder. Then, run the following command:

```bash
  node index.js
```

To test the app, open your web browser and navigate to `http://localhost:3000`. You should see the message "Rapyd example!" displayed in the browser.

### Setting Up Request Signing

You’ll need to configure the authentication mechanism before requesting Rapyd Collect API. Rapyd verifies each request to ensure it's coming from you and not from a malicious party.

To get started, complete the following steps:

1. Open the [Rapyd Client Portal](https://dashboard.rapyd.net) in a web browser.
   ![Save access key and secret key](https://i.imgur.com/hj2e3MY.png)
2. Click **Developers** on the left-navigation to open the Developers page.
3. Select **Credentials Details** on the Developers page.
4. Copy the access key and secret key using the copy button.
5. Create a new `.env` file in the project and paste both the values:

```env
ACCESS_KEY=<copied-value>
SECRET_KEY=<copied-value>
```

> You’ll need to restart the server using `node index.js` whenever you change any project file. You can also use [Nodemon](https://www.npmjs.com/package/nodemon) to automate this.

Now, create an utility function, `makeRequest`, in the `index.js` file to make signed requests to the Rapyd Collect API.

To implement this function, you'll need to install the following npm packages:

- [axios](https://axios-http.com/docs/intro): to make HTTP requests
- [crypto-js](https://www.npmjs.com/package/crypto-js): to encrypt the request payload
- [dotenv](https://www.npmjs.com/package/dotenv): to properly load the environment variables

Run the below command to install the above packages:

```bash
npm i axios crypto-js dotenv

```

Now update the `index.js` file with following code to implement the `makeRequest` function, as recommended by the [Rapyd authentication documentation](https://docs.rapyd.net/build-with-rapyd/reference/authentication):

```js
require("dotenv").config();
const axios = require("axios");
const CryptoJS = require("crypto-js");

const accessKey = process.env.ACCESS_KEY;
const secretKey = process.env.SECRET_KEY;

function makeRequest(path, data, method) {
  const salt = CryptoJS.lib.WordArray.random(12);
  const timestamp = (Math.floor(new Date().getTime() / 1000) - 10).toString();
  let toSign = method + path + salt + timestamp + accessKey + secretKey;
  toSign = data ? toSign + JSON.stringify(data) : toSign;
  let signature = CryptoJS.enc.Hex.stringify(
    CryptoJS.HmacSHA256(toSign, secretKey)
  );

  signature = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(signature));
  const headers = {
    access_key: accessKey,
    signature,
    salt,
    timestamp,
    "Content-Type": `application/json`,
  };

  const request = {
    url: `https://sandboxapi.rapyd.net${path}`,
    headers,
    method,
  };
  if (data) {
    request["data"] = data;
  }
  return axios(request);
}
```

The above code uses CryptoJS to sign the outgoing requests payload and returns the response of the `axios` network request.

### Solution Overview

Before starting the server implementation, it's essential to understand the solution and go through the architecture. The below diagram explains how the user, your Node.js application, and Rapyd Collect API communicate together.

![Architecture diagram](https://i.imgur.com/Lm1sbNx.png)

The completed process is divided into four parts, each of which is described in detail below.

### Collecting Customer Information

As shown in the diagram above, the first step of the process is to collect customer information. To do this, create a new `public` directory inside the project and add an HTML file, `index.html`, inside it. Add the following code to create an HTML form to collect customer's email and name:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Rapyd subscription</title>
  </head>
  <body>
    <h1>Subscribe to monthly parking</h1>
    <form action="/add-customer" method="POST">
      <input required placeholder="Joe" type="text" name="name" />
      <input required placeholder="joe@example.com" type="email" name="email" />
      <button type="submit">Subscribe</button>
    </form>
  </body>
</html>
```

The form has three input fields (for email, name, and a submit button) and specifies an action of `/add-customer` (implemented later in the next step) and a method of `POST`. This means that when the form is submitted, an HTTP POST request will be sent to the `/add-customer` URL with the form data included in the request body.

To serve the static HTML file from the Express app, you need to register the `express.static` middleware function using the `app.use` utility inside `index.js`.

```js
const express = require("express");
const app = express();
const port = 3000;

app.use(express.static("public"));

app.get("/", (request, response) => {
  response.send("Rapyd example!");
});

app.listen(port, () => {
  console.log(`Rapyd app listening on port ${port}`);
});
```

Now when a request is made for a `http://localhost:3000`, the `public/index.html` file will be served to the client without any further processing by the app, as shown in the image below:

![Application home page](https://i.imgur.com/0Mer3Ra.png)

### Selecting Preferred Payment Method

To accept and parse the form data from the incoming requests, you’ll need to configure the `express.json()` and `express.urlencoded()` middlewares in your `index.js` file, as shown in the code below:

```js
// ... other imports
app.use(express.static("public"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/", (request, response) => {
  response.send("Rapyd example!");
});
// ... rest of the code
```

For this first step, you need to collect the user's information and prompt them to select their preferred payment method. To do this, create a POST request handler for the `/add-customer` endpoint.

This endpoint accepts the form submission data from `index.html` and responds with the available payment methods for USD in India using the `/v1/payment_methods/countries` API from Rapyd. The response contains another HTML form with a `select` input element that submits to the `/add-payment-method` endpoint.

```js
app.post("/add-customer", async (request, response) => {
  const paymentMethodsEndpoint =
    "/v1/payment_methods/countries/IN?currency=USD";
  try {
    const { email, name } = request.body;

    const methods = await makeRequest(paymentMethodsEndpoint, null, "get");

    response.send(
      `
      <h1>Select payment method</h1>
      <form action="/add-payment-method?email=${email}&name=${name}" method="post">
      <select name="method_type">
    ${methods.data.data.map(
      (item) =>
        `<option value="${item.type}" name="${item.name}">${item.name}</option>`
    )} 
  </select>
  <button type="submit">Proceed</button>
  </form>`
    );
  } catch (e) {
    response.send("Something went wrong.");
  }
});
```

![Select preferred payment method type](https://i.imgur.com/GwPYlZH.png)

### Collecting Preferred Payment Method Information

As the second step, you need to collect the selected payment method information. For this purpose, implement a POST request handler for the `/add-payment-method` endpoint to handle the form submission from the previous step.

```js
app.post("/add-payment-method", async (request, response) => {
  const { method_type } = request.body;
  const { email, name } = request.query;
  const methodRequiredFields = await makeRequest(
    `/v1/payment_methods/required_fields/${method_type}`,
    null,
    "get"
  );

  response.send(
    `
    <h1>Payment method information</h1>
    <form action="/subscribe?email=${email}&name=${name}&method_type=${method_type}" method="post">
  ${methodRequiredFields.data.data.fields
    .map(
      (item) =>
        `<input placeholder="${item.instructions}" name="${item.name}"/>`
    )
    .join("<br />")} 
    <br />
<button type="submit">Proceed</button>
</form>`
  );
});
```

The above handler accepts the user's selected payment method type and responds with another form to request the required information fields for the selected payment method. This form will submit a POST request to the `/subscribe` endpoint with the payment method required fields. The resulting form is shown below:

![Collect required payment method fields](https://i.imgur.com/vsVA3I7.png)

### Creating the Customer Profile 

To handle the form submission request from the previous step, create a new `/subscribe` endpoint. This handler receives the `email`, `name`, and `method_type` as the incoming request's query params and the required payment fields as the incoming request's body.

Now that you have collected all the required information to create a new customer profile, you can finally make a request to Rapyd's `/v1/customers` API endpoint to create a customer profile. You'll receive the newly created customer object as the response.

```js
app.post("/subscribe", async (request, response) => {
  const { email, name, method_type } = request.query;
  const customerData = {
    email,
    name,
    payment_method: {
      fields: request.body,
      type: method_type,
      complete_payment_url: "https://complete.rapyd.net",
      error_payment_url: "https://error.rapyd.net",
    },
  };
  const createCustomerPath = "/v1/customers";
  const answer = await makeRequest(createCustomerPath, customerData, "post");
  const customerProfile = answer.data.data;
  const customerId = customerProfile.id;
  response.send(customerId);
});
```

### Creating Subscription and Collecting Payment

As the final step, you need to create a monthly payment subscription to receive recurring payment from the newly created customer.

To create a subscription, you'll need the `subscription_items` information, such as the `plan` ID. If you don't already have a subscription plan, you can create a new product and plan by following this [documentation](https://docs.rapyd.net/build-with-rapyd/docs/subscriptions#create-product).

You also need to provide the `payment_method` in the subscription request so that the checkout page preloads with the customer's preferred payment method. You must specify the customer's country while creating the subscription to ensure that their default payment method is selected on the checkout page.

Use the `complete_payment_url` and `error_payment_url` fields to specify where to redirect the user after payment is successfully completed or failed.

Once the subscription is created, you must redirect the user to the `redirect_url` to take them to the hosted subscription checkout page, where they can complete the payment.

```js
app.post("/subscribe", async (request, response) => {
  const { email, name, method_type } = request.query;
  const customerData = {
    email,
    name,
    payment_method: {
      fields: request.body,
      type: method_type,
      complete_payment_url: "https://complete.rapyd.net",
      error_payment_url: "https://error.rapyd.net",
    },
  };
  const createCustomerPath = "/v1/customers";
  const answer = await makeRequest(createCustomerPath, customerData, "post");
  const customerProfile = answer.data.data;
  const customerId = customerProfile.id;
  const subscriptionPayload = {
    customer: customerId,
    country: "in",
    billing: "pay_automatically",
    payment_method: customerProfile.default_payment_method,
    subscription_items: [
      {
        plan: "plan_af8a418da8f6d5b08af5d68e7021105d",
        quantity: 1,
      },
    ],
  };
  const checkoutPath = "/v1/checkout/subscription";
  const subsAnswer = await makeRequest(
    checkoutPath,
    subscriptionPayload,
    "post"
  );
  const subscriptionData = subsAnswer.data.data;
  response.redirect(subscriptionData.redirect_url);
});
```

An invoice is generated automatically for the subscription, which you can view on the Rapyd Client Portal, as explained in [viewing subscription invoices](https://docs.rapyd.net/client-portal/docs/viewing-subscription-invoices).

Your monthly parking subscription application is ready.

![Complete payment flow](https://i.imgur.com/BOAAaN9.gif)

## Conclusion

You've successfully created an application to collect monthly parking subscription payments. While doing so, you learned about different Rapyd Collect API endpoints, such as the API to manage customer profiles, payment methods, and hosted checkout page for recurring payments. You also learned about authenticating and protecting your application server when working with payment platforms like Rapyd.

[Rapyd](https://rapyd.net) is a fintech platform that provides a range of tools and services, including payments, payouts, fraud protection, compliance support, and business support to help businesses succeed in the digital economy. Whether you're a small business just starting out or a large enterprise looking to expand your global reach, [Rapyd](https://rapyd.net) can help you thrive in today's digital economy.