Skip to main content

Integrating HAPI Elements

Including the HAPI Elements Script

Once you get your HAPI Elements Client Token, include the loader script in your <head> section of your frontend code and replace

  • clientToken
  • walletCurrency
note

walletCurrency was initially used to create user wallets in specified currency for End User Payments however it's functionality got changed to work "app-wide" to also work with non-end user payment methods in Version 1.3 and changes are documented in Version 1.3 Wallet Currency Parameter Changes.


walletCurrency defaults to USD when not specified.

Programmatic way of adding the script with POST request
info

This method is recommended because with this method, you'll be making a POST request to get the contents of the loader.js file where in the body of the POST request you'll specify which gets encrypted as part of HTTPS protocol:

  • clientToken
  • walletCurrency
note

walletCurrency was initially used to create user wallets in specified currency for End User Payments however it's functionality got changed to work "app-wide" to also work with non-end user payment methods in Version 1.3 and changes are documented in Version 1.3 Wallet Currency Parameter Changes.


walletCurrency defaults to USD when not specified.

Loading...

Programmatic way of adding the script with GET request (less secure than POST)
note

Both this method and the HTML way of adding the script via <script> tag is identically same from the security standpoint however with the programmatic way, you get more control on which pages you would include the script versus with HTML, most of the time, you end up putting it in the <head> section of the entire HTML.

Loading...

HTML way of adding the script (less secure than programmatic way of adding via POST request)

Loading...

If your authentication was successful, you should see the widget; otherwise, the widget will hide itself with a big red text explaining your token is invalid and post console errors in the developer tools of your browser.

Ejecting/Re-injecting HAPI Elements

There are some use cases for unloading HAPI Elements such as:

  • X user logs out to log in to Y user under a tenant in a multi-tenant application
  • There is an "impersonation" feature that you use to log into customer accounts for debugging/management purposes

In both cases:

  • a new JWT token should be generated
  • then passed to HAPI Elements so that the correct user authentication is ensured and no data leaks from user X to user Y and vice versa

To do that, you would need to do the following:

const token = "your-new-token-generated"
try {
await window.hapiInjector.reinjectForNewUser(token) //async function
console.log('[HAPI Elements]: Re-injected Elements with a new `clientToken`', token)
// because Elements ejects and injects
// you need to re-run the code that you should have when HAPI Elements loads
// setting locales, setting theming, prefilling campaign form etc
} catch (error) {
console.log('[HAPI Elements]: Re-inject failed', error)
}
caution

reinjectForNewUser does:

  • window.hapiInjector.setConfig("clientToken", "NEW TOKEN HERE")
  • await window.hapiInjector.eject()
  • await window.hapiInjector.inject()
  • reload all widgets present in DOM, so they are refreshed with the latest user's data

IMPORTANT:


If you have a callback that runs when HAPI Elements loads (which you should have, for prefilling campaign form and other things like theming), you need to rerun that function. Any listeners on services, state, etc will be lost and a fresh instance of HAPI Elements will be mounted thus you need to re-execute all the dependent areas as if you just loaded HAPI elements.

Ejecting without re-injecting HAPI Elements

You may need to eject HAPI Elements completely and clear your window object. To do that you can do:

try {
await window.hapiInjector.eject() //async function
// you may inject again, so it is up to you to remove these from DOM
window.hapiInjectorConfig = undefined
window.hapiInjector = undefined
// then remove the script for injector.js which is
// <script defer type="module" src="https://ELEMENTS-ENVIRONMENT/api/injector.js"></script>
// note that you should revise the script and add an ID, so you can remove it without doing complex Query Selectors
const theIDWeGaveToHAPIElementsInjectorScript = "vonq-he-injector"
const injectorScriptEl = document.getElementById(theIDWeGaveToHAPIElementsInjectorScript)
if (injectorScriptEl) {
injectorScriptEl.remove()
}
// it is also up to you to remove any widgets present in your DOM
console.log('[HAPI Elements]: Ejected')
} catch (error) {
console.log('[HAPI Elements]: Eject failed', error)
}
note

eject does:

  • Unload necessary classes attached to window such as window.hapi
  • Unload config object attached to window such as window.hapiConfig (does not remove window.hapiInjectorConfig)
  • Remove the UI components we add to your body such as he-ui-alertbar and he-ui-modals
  • Remove debug panel (which is only added in non-production HAPI Elements environments)

IMPORTANT: Please make sure to read the comments inside the code in the above example on things that should be removed on your end

A note about Content Security Policy (CSP)

In case you have CSP rules in your headers, you would need to add the following as part of the allowed list of hosts:

frame-src 'self' https://*.vonq-aws.com https://*.vonq.com
connect-src 'self' https://*.vonq-aws.com https://*.vonq.com

vonq-aws.com is used for development/testing environments of VONQ.


If you get the following CSP error;

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src (...). Either the 'unsafe-inline' keyword, a hash ('sha256-vsEPtD4ipuOFerJfbP6WWYepfR9645C8YZOl5YYqaXo='), or a nonce ('nonce-...') is required to enable inline execution.

You should do the following:

  1. First create a nonce and whitelist it in your meta tags. You can use a hardcoded nonce or generate it every time you want to load HAPI Elements scripts. Modify your meta tags by replacing NONCE-YOU-GENERATED with the hardcoded one or the one you actually generated. The generated string can be anything random.
<!--
Assume that the nonce we generated is 'hapi-elements'
then meta tag would have 'nonce-hapi-elements' (including the single quotes)
-->
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' YOUR-OTHER-POLICIES 'nonce-NONCE-YOU-GENERATED'" />
  1. Add nonce attribute to the script tag with injector.js
<script
defer
type="module"
nonce="NONCE-YOU-GENERATED"
src="https://elements-environment-url.vonq.com/api/injector.js"
></script>

By doing this, you are whitelisting only the injector.js and loader.js scripts. When you call window.hapiInjector.inject(), it injects loader.js script to your DOM and that will reference the same nonce as the script tag with loader.js

Installing Typescript definitions from NPM to your Typescript application (Optional)

If you have a Typescript application and want to utilize the type definitions, you can do so:


To get started using the types, install the package as a dev dependency:

Using NPM:

npm install @vonq/hapi-elements-types --save-dev

Using Yarn:

yarn add -D @vonq/hapi-elements-types

All the types have been exported in a single file so you can get started using the types as such:

import {CampaignCreateForm} from "@vonq/hapi-elements-types";

const campaignObject: CampaignCreateForm = {} //Typescript will now warn about missing properties
note

If you are using React with TypeScript, you will most likely need to add typings for web components. Create a global.d.ts in the root folder of your project then include this file in the include array of tsconfig.json. Then place these inside global.d.ts:

import * as React from "react" //do not remove this

declare global {
namespace JSX {
interface IntrinsicElements {
[key: string]: any
}
}
}
note

If you get this error:

Module parse failed: Unexpected token (20:7)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| import { ATSUserTokenResponse } from "../ats"

You may need to overwrite the exclude rule for node_modules from ts-loader in webpack.config.jsas such:

{
//...rest of the webpack config
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules\/(?!\@vonq\/hapi-elements-types).*/, //changed this so Elements types are not excluded
},
]
}

FAQs

Can we install HAPI Elements via a Javascript Package Manager instead of loading with loader.js?

We do not have an installable package and have no plans to release such a package. We architected HAPI Elements in a way that works for all frameworks, and we don't want to be in favour of ATSs using React versus any other framework; thus, the architecture with iframes inside web components and the SDK to manage the Elements application does an excellent job of providing the same developer experience.


The goal of HAPI Elements is to be as-maintenance free as possible. The autocomplete behaviour on the browser's developer tools, the optional Typescript definitions package and the Playground in the documentation should be enough to help you implement your business requirements.