DEVELOPER

Back to Developer Blog

technicalseries

Embedding Payments into a Website with the iFrame JavaScript SDK Using Vue

By James Olaogun and Laura Olson | March 5th, 2025

North's iFrame JavaScript SDK is a type of hosted payment that lets you create a customized payment form and checkout experience on your website while keeping sensitive data out of your server environment.

The SDK generates an iframe that displays a payment gateway to customers on your site. It ensures secure payment processing by handling sensitive payment data within the iframe, which helps protect customer information and handles the scope of PCI DSS (Payment Card Industry Data Security Standard) compliance requirements on your behalf.

The iFrame JavaScript SDK provides a range of features, including multiple payment options (credit/debit card) as well as invoices, refunds, and voids. You can also customize the payment form fields based on your processing environment and business requirements.

The iFrame JavaScript SDK is designed to be easily integrated into applications built with any JavaScript framework. In this tutorial, you'll learn how to use the iFrame JS SDK to embed payments in your Vue application.

You can access the complete code for the Vue.js application and the Node.js server-side code on GitHub.

Vue Payment Form

Build this App

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

Prerequisites

To follow along with this tutorial, first review the Integration Guide, then make sure you have the following:

  • npm, Yarn, pnpm, or any of your preferred package managers installed
  • A globally accessible domain name as the SDK does not work on localhost

Create a new Vue project.

To start, you first need to create a new Vue project. This tutorial uses Vercel app to set up and deploy the Vue application you'll be using.

Follow this guide to set up and deploy a new Vue application.

Once you have completed all the steps, you should have access to your codebase via any of the Git platforms you use, and you should be able to access the Vue application via the URL that was assigned to it.

Register on North Developer and get credentials.

Next, you need to register on the North Developer if you have not done so before.

You can register here, then contact Sales Engineering for Sandbox credentials that can be used to try out this product in a test environment. We will ask that you provide us with the Domain Name (DNS) for the website which will be used to develop and test the integration.

After you perform all the steps required, your domain will be whitelisted. You will also get a test card for performing test transactions and an email notification regarding your request. For the purpose of this tutorial, take note of your Mid, DeveloperKey, Password, X-Nabwss-Appsource, and Gateway_public_key keys. You can find these keys in the Integrations page of your North Developer dashboard.

All testing and development should be conducted using the online testing domain that you created in the previous step.

Get in Touch

Talk to us about adding payments to your JavaScript application.

Create a component that lists products.

Clone the Vue application from your repo, and ensure you install all the necessary dependencies to make it work.

Once the Vue application is up and running on your local computer and you can see the landing page, open your terminal, install the vue-router package with the npm install vue-router, and change the directory to the components folder:

cd src/components

Next, create a component called Products.vue:

touch Products.vue

The code snippet below represents the Vue component to display all products. Copy and paste it into the Products.vue file:

HTML
<template>
    <h1>Products List</h1>
    <div class="product-list">
        <div v-for="product in products" :key="product.id" class="product-card">
           <RouterLink to="/make-payment" @click="saveToLocalStorage(product)">
                <img :src="product.image" alt="Product Image" class="product-image" />
                <h3 class="product-title">{{ product.name }}</h3>
                <p class="product-price">${{ product.price }}</p>
            </RouterLink>
        </div>
    </div>
</template>

<script>

export default {
  data() {
    return {
       products: [
        {
          id: 1,
          name: "Product 1",
          image: "img/logo.82b9c7a5.png",
          price: "1.99",
          description: 'This is my favorite product 1',
        },
        {
          id: 2,
          name: "Product 2",
          image: "img/logo.82b9c7a5.png",
          price: "1.59",
          description: 'This is my favorite product 2',
        },
        {
          id: 3,
          name: "Product 3",
          image: "img/logo.82b9c7a5.png",
          price: "1.97",
          description: 'This is my favorite product 3',
        },
        {
          id: 4,
          name: "Product 4",
          image: "img/logo.82b9c7a5.png",
          price: "1.19",
          description: 'This is my favorite product 4',
        },
        {
          id: 5,
          name: "Product 5",
          image: "img/logo.82b9c7a5.png",
          price: "1.39",
          description: 'This is my favorite product 5',
        },
        {
          id: 6,
          name: "Product 6",
          image: "img/logo.82b9c7a5.png",
          price: "1.22",
          description: 'This is my favorite product 6',
        },
        {
          id: 7,
          name: "Product 7",
          image: "img/logo.82b9c7a5.png",
          price: "1.44",
          description: 'This is my favorite product 7',
        },
        {
          id: 8,
          name: "Product 8",
          image: "img/logo.82b9c7a5.png",
          price: "18.99",
          description: 'This is my favorite product 8',
        },
        {
          id: 9,
          name: "Product 9",
          image: "img/logo.82b9c7a5.png",
          price: "1.76",
          description: 'This is my favorite product 9',
        },
        {
          id: 10,
          name: "Product 10",
          image: "img/logo.82b9c7a5.png",
          price: "1.32",
          description: 'This is my favorite product 10',
        },
        {
          id: 11,
          name: "Product 11",
          image: "img/logo.82b9c7a5.png",
          price: "1.71",
          description: 'This is my favorite product 11',
        },
        {
          id: 12,
          name: "Product 12",
          image: "img/logo.82b9c7a5.png",
          price: "1.09",
          description: 'This is my favorite product 12',
        }
      ]
    }
  },
  methods: {
    saveToLocalStorage(product){
      localStorage.setItem("productData", JSON.stringify(product));
    }
  },
  mounted() {
   //
  },
}
</script>

<style scoped>
.product-list {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin: auto 300px;
  justify-content: center;
}

.product-card {
  width: 200px;
  padding: 10px;
  margin-bottom: 20px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.product-image {
  width: 30%;
  height: auto;
  margin-bottom: 10px;
}

.product-title {
  margin: 0;
  font-size: 16px;
  font-weight: bold;
}

.product-price {
  margin: 0;
  font-size: 14px;
  color: #888;
}

a{
    text-decoration: none;
}
</style>

Create a component that prompts payment.

Open your terminal and change the directory to the components folder:

cd src/components

Next, create a component called MakePayment.vue:

touch MakePayment.vue

The code snippet below represents a Vue component created to display product details and incorporate the payment form for accepting payments for the product. Copy and paste it into the MakePayment.vue file:

HTML
<template>
  <img alt="Vue logo" :src="product.image">
  <div class="product">
    <h2 class="product_name">{{ product.name }}</h2>
    <p class="product_description">{{ product.description }}</p>
    <p class="product_price">Price: {{ product.price }} USD</p>
  </div>
</template>

<script>

export default {
  data() {
    return {
      product: {
	product: {
   id: 0,
      	  name: '',
              description: '',
              price: 0,
              image: ''
           }
           
      }
    }
  },
  methods: {
    },
  mounted() {
	const productData = localStorage.getItem("productData");
if (productData) {
      this.product = JSON.parse(productData)
    	}
    },
}
</script>

<style scoped>
.product {
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
  margin: 10px auto;
  max-width: 300px;
}

.product_name {
  font-size: 24px;
  margin-bottom: 10px;
}

.product_description {
  font-size: 16px;
  margin-bottom: 10px;
}

.product_price {
  font-size: 18px;
  margin-bottom: 10px;
}

.product_button {
  background-color: #4caf50;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 5px;
  cursor: pointer;
}

.product_button:hover {
  background-color: #3e8e41;
}
</style>

Next, go to src/App.vue and import the MakePayment component by replacing the src/App.vue code with the code snippet below:

HTML
<template>
  <RouterView />
</template>

<script>
import { RouterView } from 'vue-router'

export default {
  name: 'App',
  components: {
    RouterView
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Lastly, set up the router for the application. First, open your terminal and create a new directory called router in the src folder. Change the directory to the router folder with the cd src && mkdir router && cd src/router command.

Create a new file called index.js in the router folder, then copy and paste this code snippet that handles the routing system of the application into the file:

import { createRouter, createWebHistory } from 'vue-router'
import Products from '../components/Products.vue'
import MakePayment from '../components/MakePayment.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'products',
      component: Products
    },
    {
      path: '/make-payment',
      name: '/make-payment',
      component: MakePayment
    },
  ]
})

export default router

Open the src/main.js file and register the route by importing the index.js file you just created and replacing the createApp(App).mount('#app') line with the code snippet below:

import router from './router'

const app = createApp(App)
app.use(router)
app.mount('#app')

Integrate the iFrame JavaScript SDK.

Now that you have created the payment component, you will integrate the iFrame JavaScript SDK.

To get started, you need to create an empty JavaScript file and copy North's JavaScript script into it. For better organization, you'll create a public/js directory and place the newly created JavaScript file inside it.

First, change the directory to /public:

 cd public

Create a new folder named js:

mkdir js

Create a file named ph.js:

touch ph.js

Copy North's script below and paste it to the file ph.js while paying attention to the comments:

// this file contains all the JavaScript needed to initialize the iFrame JavaScript SDK and tokenize a card.
// you can customize the styling of the iFrame fields, adjust the error handling logic, and more by editing this file.

/**
 * Also pay attention to the following keyword in the script
 * {{link-to-your-server-side-application}} : The link to your server-side application. 
 * 
 */

const PayNowSdk = window.PayNow;

// call this function on page load
function initialize_sdk(mid, gateway_public_key)
{
  const options = {
    cardFieldId: 'card-number',
    cvvFieldId: 'card-cvv',
    //comment out the next three lines if you do not want the customer to enter their billing street and zip for AVS
    addressFieldId: 'address',
    zipFieldId: 'zipFirst5',
    zipPlusFourFieldId: 'zipPlus4'
  };

  // triggered when credentials are validated and iframes loaded:
  PayNowSdk().on('ready', () => {
    // set styling for the iframe fields
    const numberStyling = 'border: 1px solid black; width: 90%; height: 20px;';
    const cvvStyling = 'border: 1px solid black; width: 90%; height: 20px;';
    //comment out the next three lines if you do not want the customer to enter their billing street and zip for AVS
    const streetStyling = 'border: 1px solid black; width: 90%; height: 20px;';
    const zipStyling = 'border: 1px solid black; width: 90%; height: 20px;';
    const zip4Styling = 'border: 1px solid black; width: 90%; height: 20px;';

    PayNowSdk().setStyle('number', numberStyling);
    PayNowSdk().setStyle('cvv', cvvStyling);
    //comment out the next three lines if you do not want the customer to enter their billing street and zip for AVS
    PayNowSdk().setStyle('address', streetStyling);
    PayNowSdk().setStyle('zip', zipStyling);
    PayNowSdk().setStyle('zipPlusFour', zip4Styling);

    // set number format for card (optional)
    // can be 'plainFormat', 'prettyFormat', or 'maskedFormat'
    PayNowSdk().setNumberFormat('maskedFormat');

    console.log('ready!');
  });

  // triggered after addCard, checks valid field entries only
  PayNowSdk().on('validation', (inputProperties) => {
    console.log('validation', inputProperties);
  });

  // triggered after getCardToken
  // can be type: "validation" for missing fields or type: "server"
  PayNowSdk().on('errors', (errors) => {
    console.log('error', errors);
    // add your preferred error handling here
  });

  PayNowSdk().init(gateway_public_key, mid, options);
}

// call this function before submitting the payment form
const getToken = async () => {
  document.getElementById('card-token').value = "";

  //document.getElementById('alert_message').innerHTML = "Verifying...";

  //in addition to the fields listed here, the addCard method will also validate the credit card number, CVV, and AVS fields if present
  await PayNowSdk().addCard({
      month: document.getElementById('card-month').value,
      year: document.getElementById('card-year').value,    
  });

  const cardToken = PayNowSdk().getCardToken();

  if (cardToken == null)
  {
    console.log('unable to tokenize the card');
    //document.getElementById('alert_message').innerHTML = "Please verify the card information or use a different card.";
  }
  else
  {
    document.getElementById('card-token').value = cardToken;
    console.log('cardToken', cardToken);

    //document.getElementById('alert_message').innerHTML = "Processing...";
    
    //optional return of AVS fields
    //comment out the next three lines if you do not want the customer to enter their billing street and zip for AVS
    const avsFields = PayNowSdk().getAVSFields();
    const json_string = JSON.stringify(avsFields);
    document.getElementById('avs-fields-data').value = json_string;


    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");
    
    var raw = JSON.stringify({
      "amount": document.getElementById('product_price').value,
      "token": document.getElementById('card-token').value
    });
    
    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow'
    };
    
    fetch("{{link-to-your-server-side-application}}", requestOptions)
      .then(response => response.json())
      .then((result) => {

                console.log(result);

            } 
        )
      .catch(error => console.log('error', error));

  }
}

module.exports = { initialize_sdk, getToken }

The next step is to add the payment form to the product page you created in the MakePayment component. To do that, return to src/components/MakePayment.vue and add the code snippet below after the paragraph element with the product_price class:

HTML
<form id="pay">
      <label>Card Number:</label>
      <div id="card-number" class="form-field"></div>
      <div class="custom-inputs">
        <label>Card Month:</label>
        <input class="custom-input" id="card-month" type="text" autocomplete="off" size="4" maxlength="2">
      </div>
      <div class="custom-inputs">
        <label>Card Year:</label>
        <input class="custom-input" id="card-year" type="text" autocomplete="off" size="4" maxlength="4">
      </div>
      <label>CVV:</label>
      <div id="card-cvv" class="form-field"></div>
      <!-- comment out or remove the below section if you do not want the customer to enter their billing street and zip for AVS -->
      <label>Street:</label>
      <div id="address" class="form-field"></div>
      <label>Zip:</label>
      <div id="zipFirst5" class="form-field"></div>
      <label>Zip+4:</label>
      <div id="zipPlus4" class="form-field"></div>
      <input type="hidden" id="avs-fields-data" />
      <!-- end of AVS section -->
      <input type="hidden" id="card-token" />
    </form>
    <button class="product_button" name="tokenize_and_pay" value="Pay Now" @click="handlePayment">Pay Now</button>

Still in the MakePayment.vue file, add the code snippet below to the methods lifecycle:

async handlePayment() {
      // The handlePayment function is triggered when the payment form is submitted and then the getToken() function is then called in the ph.js script to tokenize the card.
      const ph = await require('../../public/js/ph.js');
      ph.getToken();
    }

To ensure that the JS SDK is initialized and the form iFrame shows the form inputs, add the code snippet below to the mounted() lifecycle:

const ph = require('../../public/js/ph.js');
ph.initialize_sdk('Your MID',' Your_Gateway_Public_Key');

In this snippet, replace Your MID with your North Merchant ID (MID) and Your_Gateway_Public_Key with your Gateway public key provided by North.

Finally, in the MakePayment.vue file, add the styles below to further style the payment form to the style tag:

.form-field{
  max-height: 70px;
}

.custom-input{
  border: 1px solid black;
  width: 90%;
  height: 20px;
  background: transparent;
  border-radius: 3px;
  padding: 0.65em 0.5em;
  font-size: 91%;
}

.custom-inputs{
  max-height: 100px;
  height: 90px;
}

Lastly, proceed to public/index.html and add the code snippet below after line 7:

HTML
    <script src="https://sdk.paymentshub.dev/pay-now.min.js"></script>
    <script>
      window.PayNow = PayNow.default;
    </script>

Save all the changes and run the npm run serve command to compile your application. Once it is done compiling, open your browser and type in the URL you have in the terminal.

Set up the server-side script.

Once you are done setting up the payment form, you need to set up a server that will handle the form input and send the API requests to North. This tutorial uses Node.js for handling all the server-side logic.

Start by initializing a new node application and installing the following packages: express, axios, cors, and body-parser.

Next, add the script below to your index.js file:

// index.js

/**
 * Pay attention to the following variables
 * {{your-frontend-application-url}} : The URL to access your view application
 * {{your-mid}} : Your Merchant ID
 * {{your-password}} : Your North Password in the Integration Keys
 * {{your-developerKey}} : Your Developers keys
 * {{your-x-nabwss-appsource}} :  Your x-nabwss-appsource key
 * {{your_gateway_public_key}} :  Your gateway public key
 */

const express = require('express')
var axios = require('axios');
const cors = require('cors');
const bodyParser = require('body-parser');


const app = express()
const PORT = 4000

app.use(cors({origin: ["{{your-frontend-application-url}}"], credentials: true}))
app.use(bodyParser.json());

app.listen(PORT, () => {
    console.log(`API listening on PORT ${PORT} `)
})

app.post('/send', (req, res) => {

    console.log("start")
    var data = JSON.stringify({
        "mid": "{{your-mid}}",
        "developerKey": "{{your-developerKey}}",
        "password": "{{your-password}}"
    });

    var config = {
    method: 'POST',
    url: 'https://proxy.payanywhere.com/auth',
    headers: { 
        'Content-Type' : 'application/json',
        'Accept': 'application/json',
        'x-nabwss-appsource' : '{{your-x-nabwss-appsource}}' 
    },
    data : data
    };

    axios(config)
    .then(function (response) {

        var data = JSON.stringify({
            "token": req.body.token,
            "amount": req.body.amount,
            "gateway_public_key": "{{your_gateway_public_key}}",
            "transaction_source": "PA-JS-SDK"
        });

        var config = {
        method: 'POST',
        url: 'https://proxy.payanywhere.com/mids/{{your-mid}}/gateways/payment',
        headers: { 
            'Content-Type': 'application/json', 
            'Accept': 'application/json',  
            'Authorization': 'Bearer ' + response.data.token,
            'x-nabwss-appsource': '{{your-x-nabwss-appsource}}',
        },
        data : data
        };

        axios(config)
        .then(function (response) {
            res.send({status: "payment successful"});
        })
        .catch(function (error) {
            res.send({status: error});
        });
    
    })
    .catch(function (error) {
        res.send(error);
    });


  })

// Export the Express API
module.exports = app

The script handles the payment form input and makes all the necessary requests to North. These requests include a POST request to the auth URL to get the authentication token and a payment URL to initiate the payment. Pay attention to the comments in the script for more details.

Finally, follow this guide to deploy your Node.js application to Vercel.

Test your application.

Congratulations! You've set up the Vue.js application and the backend to handle the server-side logic, so all that's left now is to check whether it works as it should.

Open your browser and visit the whitelisted URL that leads to your application. Click on any of the listed products to visit the payment page.

To test your app, use the details on the test card you received when you were setting up your North account and any address of your choice. Click Pay Now to make a test payment in the North Sandbox.

Conclusion

This article showed you how simple it is to use North's iFrame JS SDK for embedding payments into a Vue.js application.

You can access the complete code for the Vue.js application and the Node.js server-side code on GitHub.

How To Get Started

North’s Sales Engineering team provides support to developers and business decision-makers to help select the best possible payment system. 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.