DEVELOPER

Back to Developer Blog

technicalseries

ISVs: Automate Merchant Onboarding for Payment Acceptance in your Node.js App

By Amr Abdou and Laura Olson | October 25th, 2024

Merchant onboarding is the process by which Independent Software Vendors (ISVs) and technology platforms enable new merchants to accept electronic payments in their systems. It applies to a wide range of merchants, from small businesses looking to accept digital payments to large corporations seeking to integrate payment processing into their existing systems, as well as businesses of all sizes wanting to expand into new markets, both domestically and internationally.

Traditionally, merchant onboarding is a lengthy manual process that often takes weeks. It involves back-and-forth communication, filing an application, signing paperwork, and a verification process followed by configuring payment processing services. It's time-consuming and error-prone, which can increase costs and cause delays. It can also be frustrating for merchants, impeding the start of an ISV's business partnership with them.

Much of this hassle can be removed by automating the merchant onboarding process.

In this article, you will learn more about the steps involved in merchant onboarding, the benefits of automating this process, and how to implement an automated merchant onboarding workflow using North's Merchant Boarding API in Node.js.

Click through the interactive visualizer below to view details about the API calls that are used in the Simplified Enrollment flow.

Build this App

Clone this code repository to quickly create your own app today!

Get Authentication Token
Create New Application
Send Link to Merchant
Validate Application
Submit Application
Step 5
Submit Application
enroll/application/submit/{key}

Submit a completed application to Underwriting for review.

When performed manually, the merchant onboarding process starts by collecting business information such as corporate registration documents, sales volume, bank account information, business address, business contact information, and business owner's information from the merchant. The payment processing company then verifies, analyzes, and reviews this information to be legally compliant and ensure the business is legitimate. If the initial review process shows signs of high-risk activities, extra documents such as financial statements may be needed for further review.

Manually collecting and sharing this type of information and documentation not only extends the process but also makes it prone to human errors. Using a payment partner with a well-established automated merchant onboarding system can simplify and shorten this process considerably to reduce human errors and delays, giving your merchants a much smoother onboarding experience.

Implementing an automated onboarding workflow can be done using an API that allows merchants to apply and provide their supporting documents online and to receive real-time updates about the verification process. Boarding APIs can also help verify the merchant's information using digital methods to complete the process quickly.

If you have your own merchant portal, you can benefit from integrating with North's Custom Enrollment Flow. If you don't have your own portal, you can use North's Simplified Enrollment Flow. As shown in the diagram below, North's Simplified Enrollment process allows merchants to complete their application and upload their supporting documents on the existing North portal:

In this tutorial, you'll learn how to implement merchant onboarding if you don't have a merchant portal.

Automating Merchant Onboarding Using North's Merchant Boarding API

To integrate with North's Merchant Boarding API, you will complete the following steps:

  • Get the API access credentials
  • Set up the project
  • Create a server
  • Create an interface
  • Create routes
  • Create event listeners
  • Create an authentication helper
  • Create a controller
  • Test the application

You can find the code for this project in this GitHub repo .

Get in Touch

Talk to us about automating merchant onboarding for Merchant Processing Accounts.

Prerequisites

To implement the steps in this tutorial, you need to have Node and npm installed on your machine. You also need to sign up for a free North Developer account.

Getting the API Access Credentials

To be able to call the API endpoints, you need to obtain North sandbox credentials that you will use for development and testing. Log in to North Developer and click Contact in the top menu. Submit your request using the contact form. A North representative will be in touch to provide API credentials.

Creating a New Project

In this demo project, you will use Node to authenticate and send requests to North's Merchant Boarding API. To get started, create a new folder, navigate to this folder in your terminal window, and run the following command to create a package.json file:

npm init -y

Next, run the following command to install the required packages and their dependencies:

npm install express axios body-parser ejs uuid

These packages are as follows:

  • express to create a server
  • axios to authenticate and send HTTP requests
  • body-parser to parse request bodies
  • ejs to set as a rendering engine
  • uuid to generate random application keys

Then, create the following files and folders:

  • views folder containing a newMerchantApplication.ejs file
  • public folder containing app.css and app.js files
  • index.js
  • createApplication.ejs
  • applicationController.js
  • routes.js
  • helper.js

Creating a Server

The next step is to create a server. Open your index.js file and add the following code to it:

const express = require('express');
const path = require('path');
const router = require('./routes');


const app = express();

app.use('/public', express.static('public'));
app.set('view engine', 'ejs');

app.use('/', router);
app.listen(3000);

This file creates a server that listens to port 3000 and sets ejs as a rendering engine, the public folder as the public resources directory, and the routes that you will create in the routes.js file.

Creating an Interface

Next, you'll create an interface that contains the form where the user will enter the initial info to create a new merchant application, then send it to the merchant to complete and finally submit it to North for underwriting.

Open the views/newMerchantApplication.ejs file and paste this code into it:

HTML
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Merchant Onboarding Using North's Merchant Boarding API</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
  <link rel="stylesheet" href="public/app.css">
</head>
<body>
  <div class="row">
    <div class="col-md-3">
    </div>
    <div class="col-md-6 bg-light page-container">
      <form id="checkoutForm">
        <h1>New Merchant Application</h1>
        
        <!-- The initially required fields to create a new merchant application -->
        <fieldset>
          
          <p>
            <label for="application_name">Application Name</label>
            <input type="text" required id="application-name" name="application_name" class="form-control" placeholder="Application Name" value="Sample Application">
          </p>

          <p>
            <label for="agent_id" class="form-label">Agent ID</label>
            <input type="text" required id="agent-id" name="agent_id" class="form-control" placeholder="Sales Agent ID" value="12345">
          </p>
          
          <p>
            <label for="plan_id">Plan ID</label>
            <input type="text" required id="plan-id" name="plan_id" class="form-control" placeholder="Plain ID" value="123">
          </p>
          
          <p>
            <label for="first-name">First Name</label>
            <input type="text" required id="first-name" name="first_name" class="form-control" placeholder="First Name" value="John">
          </p>
          
          <p>
            <label for="last-name">Last Name</label>
            <input type="text" required id="last-name" name="last_name" class="form-control" placeholder="Last Name" value="Doe">
          </p>
          
          <p>
            <label for="street">Street</label>
            <input type="text" required id="street" name="street" class="form-control" placeholder="street" value="55 street address">
          </p>
          
          <p>
            <label for="city">City</label>
            <input type="text" required id="city" name="city" class="form-control" placeholder="city" value="NYC">
          </p>
          
          <p>
            <label for="state">State</label>
            <input type="text" required id="state" name="state" class="form-control" placeholder="state" value="NY">
          </p>
          
          <p>
            <label for="zipcode">Zip Code</label>
            <input type="text" required id="zipcode" name="zipcode" class="form-control" placeholder="zipcode" value="12345">
          </p>

          <p>
            <label for="ssn">social Security Number</label>
            <input type="text" required id="ssn" name="ssn" class="form-control" placeholder="social Security Number" value="123456789">
          </p>
          
          <p>
            <label for="driver-license-number">driver License Number</label>
            <input type="text" required id="driver-license-number" name="driver_license_number" class="form-control" placeholder="driver License Number" value="ABC1234567890" required>
          </p>
          
          <p>
            <label for="driver-license-state">driver License Issuing State</label>
            <input type="text" required id="driver-license-state" name="driver_license_state" class="form-control" placeholder="First Name" value="GA">
          </p>
          
          <p>
            <label for="plan_id">Date Of Birth</label>
            <input type="text" required id="date-of-birth" name="date_of_birth" class="form-control" placeholder="1955-12-25" value="1955-12-23">
          </p>
          
          <p>
            <label for="phone_number">Phone Number</label>
            <input type="text" required id="phone-number" name="phone_number" class="form-control" placeholder="Phone Number" value="1234567890">
          </p>
          
          <p>
            <label for="email">Email</label>
            <input type="text" required id="email" name="email" class="form-control" placeholder="Email" value="user@example.com">
          </p>
          
          <p>
            <label for="equity-ownership-percentage">Equity Ownership Percentage</label>
            <input type="text" required id="equity-ownership-percentage" name="equity_ownership_percentage" class="form-control" placeholder="Ownership Percentage" value="100">
          </p>
          
          <p>
            <label for="title">Title</label>
            <select required id="title" name="title" class="form-control">
              <option value="ceo">ceo</option>
              <option value="manager">manager</option>
              <option value="owner">owner</option>
              <option value="partner">partner</option>
              <option value="president">president</option>
              <option value="vice president">vice president</option>
            </select>
          </p>
          
          <p>
            <label for="is-personal-guarantor">Is Personal Guarantor?</label>
            <select required id="is-personal-guarantor" name="is_personal_guarantor" class="form-control">
              <option value="true">Yes</option>
              <option value="false">No</option>
            </select>
          </p>
          
        </fieldset>

        <!-- The Button to submit a new application -->
        <p>
          <button type="button" id="create-app-button" class="btn btn-primary form-control">Create Application</button>
        </p>

        <!-- The Button to send the application to the merchant -->
        <p>
          <button type="button" id="send-app-button" class="btn form-control btn-success">Send Application</button>
        </p>

        <!-- The Button to submit the application -->
        <p>
          <button type="button" id="submit-app-button" class="btn form-control btn-success">Submit Application</button>
        </p>

        <!-- The Button to update the application -->
        <p>
          <button type="button" id="update-app-button" class="btn form-control btn-primary">Update Application</button>
        </p>

        <!-- Loading Notification -->
        <div class="alert alert-success request-status-bar" id="request-status-success" role="alert">
          Success
        </div>
        <!-- Success Notification -->
        <div class="alert alert-warning request-status-bar" id="request-status-loading" role="alert">
          Processing...
        </div>
        <!-- Error Notification -->
        <div class="alert alert-danger request-status-bar" id="request-status-error" role="alert">
          Error: 
        </div>

      </form>

      <br>
      <!-- JSON response will show here -->
      <h2>JSON Response here:</h2>
      <code id="boarding-api-response">
        -- Empty --
      </code>

    </div>
    <div class="col-md-3">
    </div>
  </div>

</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="public/app.js"></script>
</html>

The file imports two external resources, bootstrap for styling and jQuery to handle AJAX requests, in addition to the two resource files that you created, app.css and app.js. The form contains the required fields to create a new application, four buttons to run the API requests, three hidden status bars to show the status of the request, and a code block at the bottom of the page where the response of the API requests will appear.

Add CSS Styles

To enhance the view of this HTML page, open the public/app.css file and add the following CSS code:

.page-container {
	padding: 20px;
}

label {
	text-transform: capitalize;
}

#send-app-button,
#update-app-button, 
#submit-app-button{
	display: none;
}

.request-status-bar {
	text-align: center;
	display: none;
}

Creating Routes

To create the routes for your application, add this code to the routes.js file:

const express = require('express');
const path = require('path');
const router = express.Router();
const bodyParser = require('body-parser');
const applicationController = require('./applicationController')

const app = express();

// Set Render Engine to EJS
app.use('/public', express.static('public'));
app.set('view engine', 'ejs');

// Use Body-parser to handle JSON responses
router.use(bodyParser.urlencoded({extended: true}));
router.use(bodyParser.json());

// GET: Home Route
router.get('/', async (req, res) => {
    res.render('newMerchantApplication');
});

// POST: Create Application
router.post('/create-application', async (req, res, next) => {
    applicationController.createApplication(req, res, next);
});

// POST: Update Application
router.post('/update-application', async (req, res, next) => {
    applicationController.updateApplication(req, res, next);
});

// POST: Send Application to Merchant
router.post('/send-application', async (req, res, next) => {
    applicationController.sendApplication(req, res, next);
});

// POST: Submit Application
router.post("/submit-application", (req, res, next) => {
  applicationController.submitApplication(req, res, next);
});

module.exports = router;

This code creates five routes:

  • GET:/: to render the homepage interface
  • POST:/create-application: to handle the application creation API request
  • POST:/update-application: to handle the application update API request
  • POST:/send-application: to handle the application sending API request
  • POST:/submit-application: to handle the application submission API request

Creating Event Listeners

To handle the button clicks on the interface page, you need to create event listeners. Open the public/app.js file and paste the following code into it:

var applicationKey = "";

$(function(){ 

// Create Application Button Event Listener
  $("#create-app-button").click(function(event){
    event.preventDefault();
    $("#request-status-loading").slideDown();

    // Create Application Button Event Listener
    $.ajax({
      type: 'POST',
      url: '/create-application',
      data: {
        'appName': $("#application-name").val(),
        'agentId': $("#agent-id").val(),
        'planId': $("#plan-id").val(),
        "principals":
          {
            "istreet": $("#street").val(),
            "city": $("#city").val(),
            "state": $("#state").val(),
            "zipCode": $("#zipCode").val(),
            "firstName": $("#first-name").val(),
            "lastName": $("#last-name").val(),
            "socialSecurityNumber": $("#ssn").val(),
            "driverLicenseNumber": $("#driver-license-number").val(),
            "driverLicenseIssuedState": $("#driver-license-state").val(),
            "dateOfBirth": $("#date-of-birth").val(),
            "phoneNumber": $("#phone-number").val(),
            "email": $("#email").val(),
            "equityOwnershipPercentage": $("#equity-ownership-percentage").val(),
            "title": $("#title").val(),
            "isPersonalGuarantor": $("#is-personal-guarantor").val()
          }
      }, 
      success: function (data, status, xhr) {

        // Show data
        $("#boarding-api-response").html(JSON.stringify(data));
        applicationKey = data.application.externalKey;

        // Show Status
        $("#request-status-error").slideUp();
        $("#request-status-loading").slideUp();

        // Show Send application button
        $("#create-app-button").slideUp();
        $("#update-app-button").slideDown();
        $("#send-app-button").slideDown();
      },
      error: function (jqXhr, textStatus, errorMessage) {
        // Show Error
        $("#request-status-loading").slideUp();
        $("#request-status-error").slideDown();
        $("#request-status-error").html(errorMessage);
        console.log('Error' + errorMessage);
      }
    });

  });


// Update Application Button Event Listener
  $("#update-app-button").click(function(event){
    event.preventDefault();
    $("#request-status-loading").slideDown();

    // Update Application Button Event Listener
    $.ajax({
      type: 'POST',
      url: '/update-application',
      data: {
        'appName': $("#application-name").val(),
        'agentId': $("#agent-id").val(),
        'planId': $("#plan-id").val(),
        'externalKey': applicationKey,
        "principals":
          {
            "istreet": $("#street").val(),
            "city": $("#city").val(),
            "state": $("#state").val(),
            "zipCode": $("#zipCode").val(),
            "firstName": $("#first-name").val(),
            "lastName": $("#last-name").val(),
            "socialSecurityNumber": $("#ssn").val(),
            "driverLicenseNumber": $("#driver-license-number").val(),
            "driverLicenseIssuedState": $("#driver-license-state").val(),
            "dateOfBirth": $("#date-of-birth").val(),
            "phoneNumber": $("#phone-number").val(),
            "email": $("#email").val(),
            "equityOwnershipPercentage": $("#equity-ownership-percentage").val(),
            "title": $("#title").val(),
            "isPersonalGuarantor": $("#is-personal-guarantor").val()
          }
      }, 
      success: function (data, status, xhr) {

        // Show data
        $("#boarding-api-response").html(JSON.stringify(data));

        // Show Status
        $("#request-status-error").slideUp();
        $("#request-status-loading").slideUp();

      },
      error: function (jqXhr, textStatus, errorMessage) {
        // Show Error
        $("#request-status-loading").slideUp();
        $("#request-status-error").slideDown();
        $("#request-status-error").html(errorMessage);
        console.log('Error' + errorMessage);
      }
    });

  });


// Send Application Button Event Listener
  $("#send-app-button").click(function(event){
    event.preventDefault();
    $("#request-status-loading").slideDown();

    // AJAX Call
    $.ajax({
      type: 'POST',
      url: '/send-application',
      data: {
        'externalKey': applicationKey
      }, 
      success: function (data, status, xhr) {

        // Show data
        $("#boarding-api-response").html(JSON.stringify(data));

        // Show Status
        $("#request-status-error").slideUp();
        $("#request-status-loading").slideUp();

        // Show submit application button
        $("#send-app-button").slideUp();
        $("#submit-app-button").slideDown();
      },
      error: function (jqXhr, textStatus, errorMessage) {
        // Show Error
        $("#request-status-loading").slideUp();
        $("#request-status-error").slideDown();
        $("#request-status-error").html(errorMessage);
        console.log('Error' + errorMessage);
      }
    });

  });

// Submit Application Button Event Listener
  $("#submit-app-button").click(function(event){
    event.preventDefault();
    $("#request-status-loading").slideDown();

  // AJAX Call
    $.ajax({
      type: 'POST',
      url: '/submit-application',
      data: {
        'externalKey': applicationKey
      }, 
      success: function (data, status, xhr) {

        // Show data
        $("#boarding-api-response").html(JSON.stringify(data));

        // Show Status
        $("#request-status-error").slideUp();
        $("#request-status-loading").slideUp();

        // Show update application button
        $("#send-app-button").slideUp();
        $("#submit-app-button").slideUp();
      },
      error: function (jqXhr, textStatus, errorMessage) {
        // Show Error
        $("#request-status-loading").slideUp();
        $("#request-status-error").slideDown();
        $("#request-status-error").html(errorMessage);
        console.log('Error' + errorMessage);
      }
    });
  });
});

This code starts by declaring an empty string variable called applicationKey. This variable stores the application's externalKey value during the browser session.

Then, four event listeners are defined for the four buttons that you previously created in the views/newMerchantApplication.ejs interface file. The first listener handles the click event on the Create Application button. When the Create Application button is clicked, a POST request is sent to the /create-application route. The request body includes the values obtained from the input fields of the form. After this, the event listeners for the Send Application, Submit Application and Update Application buttons send POST requests to the /send-application, /submit-application and /update-application routes, respectively. The applicationKey value is included in the request bodies of the four buttons so it can be handled in the applicationController.js file.

When each of the POST requests executes successfully, the JSON response appears in the code block at the end of the page.

Creating an Authentication Helper

Next, you will create an authentication helper that you'll use to generate an authentication token.

Open the helper.js file and paste the following code into it:

const axios = require('axios');

const CLIENT_ID = 'YOUR_CLIENT_ID_HERE';
const CLIENT_SECRET = 'YOUR_CLIENT_SECRET_HERE';
const API_AUTH_URL = "https://api-auth.paymentshub.dev"; // The Sandbox Authentication URL
const API_SANDBOX_URL = "https://boarding-api.paymentshub.dev"; // The Sandbox Merchant Boarding API URL


class authHelper{
    
    constructor(){
    }
    /*
    * Get Auth Token
    */
    static async getAuthToken(){

        try{
            return await axios.post(API_AUTH_URL + '/oauth/token', {
                grant_type: "client_credentials",
                scope: "all",
                client_id: CLIENT_ID,
                client_secret: CLIENT_SECRET
            });
            
        } 
        catch (error) {
            console.error("Error");
            throw error;
        }
    }
}

exports.authHelper = authHelper;

The helper.js file contains the getAuthToken function that sends a post request using axios.post method to generate the access token. This access token is a JWT bearer token that must be generated to call the application endpoints.

Remember to replace YOUR_CLIENT_ID_HERE with your client ID and YOUR_CLIENT_SECRET_HERE with your client secret key, which you obtained in the first step of this tutorial by contacting North.

Creating a Controller

Next, you'll create the functions to handle the communication with North's Merchant Boarding API. Open your applicationController.js file and paste the following code into it:

const axios = require('axios');
const {authHelper} = require('./helper');
const { v4: uuidv4 } = require('uuid');

const API_SANDBOX_URL = "https://boarding-api.paymentshub.dev"; // The Sandbox Enrollment API URL

class applicationController{

    constructor(){
    }
    /*
    * Create a new application
    */
    static async createApplication(request, response, next) {
            const accessToken = await authHelper.getAuthToken(); // Get Access Token
            const applicationKey = uuidv4(); // Generate Application Key

            let requestBody = {
                "agent": parseInt(request.body.agentId),
                "applicationName": request.body.appName,
                "externalKey": applicationKey,
                "plan": {
                  "planId": parseInt(request.body.planId)
                },
                "principals": [
                    {
                        "istreet": request.body.principals.istreet,
                        "city": request.body.principals.city,
                        "state": request.body.principals.state,
                        "zipCode": request.body.principals.zipCode,
                        "firstName": request.body.principals.firstName,
                        "lastName": request.body.principals.lastName,
                        "socialSecurityNumber": request.body.principals.socialSecurityNumber,
                        "driverLicenseNumber": request.body.principals.driverLicenseNumber,
                        "driverLicenseIssuedState": request.body.principals.driverLicenseIssuedState,
                        "dateOfBirth": request.body.principals.dateOfBirth,
                        "phoneNumber": request.body.principals.phoneNumber,
                        "email": request.body.principals.email,
                        "equityOwnershipPercentage": parseInt(request.body.principals.equityOwnershipPercentage),
                        "title": request.body.principals.title,
                        "isPersonalGuarantor": Boolean(request.body.principals.isPersonalGuarantor)
                    }
                ]
            };

            // Authorization Header
            let config = {
                headers: { Authorization: `Bearer ${accessToken.data.access_token}` }
            };

            // HTTP Request
            axios.post(API_SANDBOX_URL + '/enroll/application', requestBody, config)
            .then((result) => {
                response.json(result.data.data);
            }).catch((error) => {
                response.json(error.response.data);
            });
    }

    /*
    * Send application to merchant
    */
    static async sendApplication(request, response, next) {
            const accessToken = await authHelper.getAuthToken(); // Get Access Token
            
            // Authorization Header
            let config = {
                headers: { Authorization: `Bearer ${accessToken.data.access_token}` }
            };
            
            // HTTP Request
            axios.put(API_SANDBOX_URL + '/enroll/application/merchant/send/key/' + request.body.externalKey, {}, config)
            .then((result) => {
                response.json(result.data.data);
            })
            .catch((error) => {
                response.json(error.response.data);
            });
    }

    /*
    * Submit application
    */
    static async submitApplication(request, response, next) {
            const accessToken = await authHelper.getAuthToken(); // Get Access Token
            
            // Authorization Header
            let config = {
                headers: { Authorization: `Bearer ${accessToken.data.access_token}` }
            };
            
            // HTTP Request
            axios.put(API_SANDBOX_URL + '/enroll/application/submit/' + request.body.externalKey, {}, config)
            .then((result) => {
                response.json(result.data.data);
            })
            .catch((error) => {
                response.json(error.response.data);
            });
    }

    /*
    * Update an existing application
    */
    static async updateApplication(request, response, next) {
            const accessToken = await authHelper.getAuthToken(); // Get Access Token

            let requestBody = {
                "agent": parseInt(request.body.agentId),
                "applicationName": request.body.appName,
                "plan": {
                  "planId": parseInt(request.body.planId)
                },
                "principals": [
                    {
                        "istreet": request.body.principals.istreet,
                        "city": request.body.principals.city,
                        "state": request.body.principals.state,
                        "zipCode": request.body.principals.zipCode,
                        "firstName": request.body.principals.firstName,
                        "lastName": request.body.principals.lastName,
                        "socialSecurityNumber": request.body.principals.socialSecurityNumber,
                        "driverLicenseNumber": request.body.principals.driverLicenseNumber,
                        "driverLicenseIssuedState": request.body.principals.driverLicenseIssuedState,
                        "dateOfBirth": request.body.principals.dateOfBirth,
                        "phoneNumber": request.body.principals.phoneNumber,
                        "email": request.body.principals.email,
                        "equityOwnershipPercentage": parseInt(request.body.principals.equityOwnershipPercentage),
                        "title": request.body.principals.title,
                        "isPersonalGuarantor": Boolean(request.body.principals.isPersonalGuarantor)
                    }
                ]
            };

            // Authorization Header
            let config = {
                headers: { Authorization: `Bearer ${accessToken.data.access_token}` }
            };

            // HTTP Request
            axios.patch(API_SANDBOX_URL + '/enroll/application/key/' + request.body.externalKey, requestBody, config)
            .then((result) => {
                response.json(result.data.data);
            }).catch((error) => {
                response.json(error.response.data);
            });
    }
} 

module.exports = applicationController;

This code starts by importing the axios library, the helper.js file, and the uuid module and then declaring the Sandbox Authentication URL. Then, the applicationController class is created. The class contains four functions that do the following:

  • The createApplication function uses the authHelper.getAuthToken function to get an access token then includes this token in the config variable, which will be used as request header. Next, the requestBody variable is created from the AJAX request values. To send the application creation request, the axios.post function is used with the /enroll/application request URI. The third argument of this function includes the required application information object. For more information about this object, check out the Merchant Boarding API documentation.
  • The sendApplication function uses the axios.put method to send a PUT request with enroll/application/merchant/send/key/'key' as the request URI. This PUT request doesn't require a body, but the request URI includes the externalKey attribute that was created using the createApplication.js function.
  • The submitApplication function submits a completed application to underwriting for review. This function also uses the axios.put function to send a PUT request with the request URI as /enroll/application/submit/'externalKey'.
  • The updateApplication function updates the application after it has been created. Its code is similar to the createApplication function, with its main difference being that it uses the externalKey attribute to include it in the /enroll/application/'key' request URI, and it uses the axios.patch method to send a PATCH request.

Testing the Application

To test your integration, you will create an application using the interface that you created, send it to a client, and submit it for underwriting.

Navigate to the project folder in your terminal window and run the following command to start the node server:

node index.js

Then open your browser and go to http://localhost:3000/. Click Create Application. You won't need to complete the form since the form fields contain pre-assigned values. After the execution of the API request, the JSON response will appear at the bottom of the page.

To update some of the information of this application, change the value in the Equity Ownership Percentage field to 99 and click Update Application.

The JSON response for this request will include the updated application object. After the application is created, the Send Application button will appear. Click it to send the application to the merchant.

A successful response will display the applicationStatus as waiting for merchant and the underwritingStatus as Pending Submission.

To finally submit the application for underwriting, click the Submit Application button that should appear after sending the application. The JSON response will include a validation error because the full application hasn't been completed.

To simulate a successful submission of an application that passes validation, open the applicationController.js file and replace the requestBody object in the create Application function with the following object. You will need to replace some of the test information with valid sandbox data, such as your sandbox Agent ID and plan ID. For more information about how to get this data, refer to the Merchant Boarding API Integration Guide.

let requestBody = {
   agent: 12345,
   applicationName: "Sample Application Name",
   externalKey: `${applicationKey}`,
   plan: {
     planId: 123
    },
    principals: [{
        street: "790 Selah Drive",
        street2: "Suite 125",
        city: "South Burlington",
        state: "VT",
        zipCode: "12345",
        firstName: "Joseph Jr.",
        lastName: "Jameson",
        socialSecurityNumber: "123456789",
        driverLicenseNumber: "ABC1234567890",
        driverLicenseIssuedState: "GA",
        dateOfBirth: "1955-12-23",
        phoneNumber: "1234567890",
        email: "user@example.com",
        equityOwnershipPercentage: 100,
        title: "owner",
        isPersonalGuarantor: true
    }],
    business: {
        dbaName: "Sample Business",
        corporateName: "Sample Business Inc.",
        businessType: "C",
        federalTaxIdNumber: "123456789",
        federalTaxIdType: "SSN",
        mcc: "5812",
        phone: "1234567890",
        email: "example@business.com",
        averageTicketAmount: 1001,
        averageMonthlyVolume: 1001,
        highTicketAmount: 1001,
        merchandiseServicesSold: "Sample Services",
        percentOfBusinessTransactions: {
            cardSwiped: 65,
            keyedCardPresentNotImprinted: 35,
            mailOrPhoneOrder: 0,
            internet: 0
        },
        businessContact: {
            firstName: "Roy",
            lastName: "Martin",
            socialSecurityNumber: "123456789",
            dateOfBirth: "1947-11-05",
            street: "828 Late Avenue",
            street2: "",
            zipCode: "12345",
            city: "South Burlington",
            state: "VT",
            phoneNumber: "1234567890",
            email: "user@example.com"
        },
        businessAddress: {
            dba: {
                street: "1072 Clinton St",
                city: "South Burlington",
                state: "VT",
                zipCode: "12345"
            },
            corporate: {
                street: "1447 Sun Valley Rd",
                city: "South Burlington",
                state: "VT",
                zipCode: "12345"
            },
            shipTo: {
                street: "4735 Saint James Drive",
                city: "South Burlington",
                state: "VT",
                zipCode: "12345"
            }
        },
    },
    bankAccount: {
        abaRouting: "000000001",
        demandDepositAccount: "1234567890",
        accountType: "checking"
    }
};

If you repeat the testing steps, the application should be submitted successfully.

In this article, you learned more about merchant onboarding and the benefits of automating it. You also learned how to integrate with North's Merchant Boarding API to automate accepting merchant applications and submitting them for underwriting, which allows you to speed up this process, reduce errors, and improve your merchant onboarding experience.

Merchant onboarding application made with North's Merchant Boarding API

How To Get Started

North’s Sales Engineering team provides support to developers and business decision-makers to help select the best possible payment solution. Contact us to learn more about how to connect your system to the North ecosystem.


Start your free Developer account and try it now.


©2025 North is a registered DBA of NorthAB, LLC. All rights reserved. North is a registered ISO of BMO Harris Bank N.A., Chicago, IL, Citizens Bank N.A., Providence, RI, The Bancorp Bank, Philadelphia, PA, FFB Bank, Fresno, CA, Wells Fargo Bank, N.A., Concord, CA, and PNC Bank, N.A.