Skip to main content

Frontend SDK

Hokodo provides a frontend SDK that gives you two UI components out of the box:

Company Search element

The Company Search element finds the buyer's company based on a company name entered as free-text.

For sole traders, the buyer also the company's address and the owner's personal name, date-of-birth, and address.

The SDK handles the API interaction for Company Search for you.

Checkout element

The Checkout element is a complete payment form, including:

  • Embedded payment plan selection, to let the buyer choose between multiple payment terms
  • Embedded payment method selection, to let the buyer use their preferred method.

Setting up the SDK

To get these elements onto the page, you need to:

Initialising the SDK

Follow these steps to get the Hokodo SDK as a JavaScript object on your page.

Download the Hokodo SDK on your page

Include the Hokodo script on the pages where you wish to access the SDK. It should always be loaded directly from https://js.hokodo.co or js-sandbox.hokodo.co/hokodo-js/v1/, rather than included in a bundle or hosted yourself.

<!--Sandbox URL for use while developing-->
<script src="https://js-sandbox.hokodo.co/hokodo-js/v1" />

<!--Production URL-->
<script src="https://js.hokodo.co/hokodo-js/v1" />

When using async or defer tags, executing any SDK methods must be made only after the script execution has finished. You will need to account for this in your code if you use these attributes.

You will now have an object named Hokodo in the global scope:

window.Hokodo;
// or
Hokodo;

Instantiate the SDK object with your Public Key

Pass your SDK Public Token into the Hokodo object to instantiate it:

const hokodo = Hokodo("pk_test_xxxx");

The elements provided by the SDK are now available as hokodo.elements:

const elements = hokodo.elements();

Content security policy

If you deploy a content security policy, you will need to add the following in order to fully support Hokodo's frontend SDK:

For Sandbox

For Production

Creating the SDK Elements

Once you have the elements object in scope, there are two stages to getting either the Company Search or the Checkout onto the page. First, you instantiate the object in memory. Then, you mount it onto the page (i.e. into the DOM).

Use the elements object to instantiate a companySearch object:

// Instantiate a companySearch element
const companySearch = elements.create("companySearch", {
companyId: "co-Fxg554ndIQzI2v347DoOBm", // optional
country: "GB", // optional
countryOptions: ["GB", "ES", "FR", "DE", "NL", "BE"], // optional
});

Then use its mount() method, passing in a CSS selector for an element you wish to attach it to:

// In your HTML
<div id="hokodoCompanySearch"></div>;

// In your JS
companySearch.mount("#hokodoCompanySearch");

Checkout

Use the elements object to instantiate a checkout object:

// Create Checkout element with payment offer
const checkout = elements.create("checkout", {"paymentOffer": ..., "paymentPlanId": ...});

// OR

// Create Checkout element with payment intent
const checkout = elements.create("checkout", {"intent": ...});

If you're implmenting the standard API flow, the paymentOffer and paymentPlanId (optional) are obtained by fetching the Payment Offers for an Order, see Step 2a of the integration guide for details on how to obtain these.

Otherwise, for the new simplified integration, the intent object is obtained from the the POST /v1/payment/intents request.

In your implementation, you should store the paymentOffer or intent object as a variable when you make the relevant request.

KeyValue
intentThis is the object returned from the /payment/intents endpoint (see Simplified API workflow).

Note: This is not required if you are going to pass the paymentOffer directly.
paymentOfferThis is the object returned from the /payment/offers endpoint (see API Reference).

Note: Use this option if you are not using the payment intent API workflow.
paymentPlanId (optional)The ID [string] of the payment plan you wish to select for the buyer,
e.g. "ppln-EWjykhvDNWydKUfeJQyoM4". If used, the list of available payment plans will be replaced by a single pre-selected plan.

Note: This is a useful option if you have the ability to select a payment plan within your application. You can use the update method to inform the Checkout Element when the user selects a different plan.

Use this option if you are not using the payment intent API workflow.
headless (optional)The headless [boolean] option allows you to integrate the payment page in the headless mode that renders no UI. Note that this option is only available to select merchants and requires the paymentPlanId to also be passed. Moreover, Invoice is the only payment method in headless mode. More details about the headless mode can be found here.
buttonlessSubmission (optional)With the buttonlessSubmission [boolean] option, the checkout element will not display the button to submit the order. Instead, you will have to call the confirmOrder method as a final step for order submission. This is useful if you want to use your existing order submission button on your checkout page.
Important

When integrating the checkout element, you must choose between passing a paymentOffer for the standard API workflow or an intent for the single API call workflow. Do not pass both to ensure a seamless integration process.

Expand the blue boxes below to see a full examples:

Detailed Checkout Creation Example with Payment Offer

In your implementation, you should store the paymentOffer object as a variable when you make the POST /v1/payment/offers call. For the sake of this example, we have replaced it with a literal.

const checkout = elements.create("checkout", {
paymentPlanId: "ppln-EWjykhvDNWydKUfeJQyoM4",
paymentOffer: {
url: "https://api.hokodo.co/v1/payment/offers/offr-vyR4AFXGJWZz8XDgXoWVtZ",
id: "offr-vyR4AFXGJWZz8XDgXoWVtZ",
order: "order-YrG5y79iuxTTEpkzPbaqcX",
offered_payment_plans: [
{
id: "ppln-G4d57Dkmrgbh2HHbhhv89n",
name: "Pay in 3x",
template: "pptemp-tq28UahiUe49cDb4knmf4U",
currency: "GBP",
scheduled_payments: [
{
date: "2022-05-09",
amount: 102000,
allowed_payment_methods: [
{
type: "direct_debit",
},
{
type: "invoice",
},
{
type: "card",
},
],
},
{
date: "2022-06-08",
amount: 99000,
allowed_payment_methods: [
{
type: "direct_debit",
},
{
type: "invoice",
},
{
type: "card",
},
],
},
{
date: "2022-07-08",
amount: 99000,
allowed_payment_methods: [
{
type: "direct_debit",
},
{
type: "invoice",
},
{
type: "card",
},
],
},
],
merchant_fee: {
currency: "GBP",
amount: 9000,
},
customer_fee: {
currency: "GBP",
amount: 0,
},
valid_until: "2027-05-08T14:52:30.698Z",
payment_url:
"https://pay.hokodo.co/?order=order-YrG5y79iuxTTEpkzPbaqcX&plan=ppln-G4d57Dkmrgbh2HHbhhv89n&key=AnPbAFHxtmt6cAvMvATbPqxhfCGGqc3-kA84RWIb1vg&template=pptemp-tq28UahiUe49cDb4knmf4U",
status: "declined",
rejection_reason: {
code: "buyer-country",
detail:
"We're currently unable to provide payment plans to Buyers domiciled in {debtor_country}.",
params: {
debtor_country: "Japan",
},
},
},
{
id: "ppln-EWjykhvDNWydKUfeJQyoM4",
name: "Pay in 30 days",
template: "pptemp-28L6wQ66UCguEiGFqwfHjM",
currency: "GBP",
scheduled_payments: [
{
date: "2022-06-08",
amount: 300000,
allowed_payment_methods: [
{
type: "direct_debit",
},
{
type: "invoice",
},
{
type: "card",
},
],
},
],
merchant_fee: {
currency: "GBP",
amount: 9000,
},
customer_fee: {
currency: "GBP",
amount: 0,
},
valid_until: "2027-05-08T14:52:30.902Z",
payment_url:
"https://pay.hokodo.co/?order=order-YrG5y79iuxTTEpkzPbaqcX&plan=ppln-EWjykhvDNWydKUfeJQyoM4&key=AnPbAFHxtmt6cAvMvATbPqxhfCGGqc3-kA84RWIb1vg&template=pptemp-28L6wQ66UCguEiGFqwfHjM",
status: "declined",
rejection_reason: {
code: "buyer-country",
detail:
"We're currently unable to provide payment plans to Buyers domiciled in {debtor_country}.",
params: {
debtor_country: "Japan",
},
},
},
],
legals: {
type: "a",
terms_url: "https://static.hokodo.co/payments/a_v1.0/a_v1.0_gb.pdf",
},
urls: {
success: "",
failure: "",
cancel: "",
notification: "",
merchant_terms: "",
},
locale: "en-gb",
},
});

Detailed Checkout Creation Example with Payment Intent

In your implementation, you should store the intent object as a variable when you make the POST /v1/payment/intents call. For the sake of this example, we have replaced it with a literal.

const checkout = elements.create("checkout", {
intent: {
id: "intent-pn5vmdgxjs5WXQ98o6gyBH",
payment_url:
"https://pay-dev.hokodo.co/?intent=intent-pn5vmdgxjs5WXQ98o6gyBH&key=0mRR45rtXHY2cim1eBd3b5ve3Cx4p5HS6UdN6gDh2Qg",
order: "order-Q7HEz6f8CiLfZi8j7iUQZQ",
created: "2024-02-01T17:37:43.172271Z",
request: {
order: {
customer: {
user: {
email: "john.doe@email.com",
name: "John Smith",
phone: "07969746763",
registered: "2023-12-05",
},
invoice_address: {
name: "Invo Smith",
address_line1: "1 Invoice Street",
town: "London",
postcode: "TN12 9GL",
country: "GB",
},
delivery_address: {
name: "Deli Smith",
address_line1: "1 Delivery Street",
postcode: "TN12 9HU",
country: "GB",
},
},
unique_id: "223",
currency: "GBP",
total_amount: 19999,
items: [
{
item_id: "1",
type: "product",
description: "Super ergonomic chair",
metadata: {
color: "black",
name: "Karmus",
variant: "M",
},
reference: "702.611.50",
category: "Furniture > Chairs",
supplier_id: "7363",
quantity: 1,
unit_price: 19999,
tax_rate: "0",
total_amount: 19999,
tax_amount: 0,
},
],
},
merchant_urls: {
success: "https://merchant.com/payment/success",
failure: "https://merchant.com/checkout",
cancel: "https://merchant.com/checkout",
notification: "https://backend.merchant.com/payment/notifications",
},
locale: "en-gb",
},
},
});

Now you can mount the Checkout Element on the page:

// In your HTML
<div id="hokodoCheckout"></div>;

// In your JS
checkout.mount("#hokodoCheckout");
Please put event listeners for the checkout before the mount

Putting the event listeners after the mount may cause issues as the events will not be binded

Events

Both SDK elements have several .on() events that allow for communication between the element and your application. See below for tables of event names and descriptions of their behaviour for each element.

To register an event handler, pass the event name to the .on method, along with a function to call when the event is triggered.

companySearch.on(event, handler);

We recommended creating these events before running .mount to ensure they are triggered correctly.

// Register some basic event handlers
companySearch.on("ready", () => {
alert("ready!");
});
companySearch.on("countryInputChange", (country) => {
alert(country);
});
// Then mount the companySearch element
companySearch.mount("#hokodoCompanySearch");

Company Search

Here are the events supported by the Company Search Element.

The companySelection event may be of particular interest if you need to access the Hokodo Company ID (co-xxxx) in your code.

Event nameDescription
readyWhen the element has fully loaded after .mount has been called
failureWhen there has been a fatal error when loading the element
countryInputChangeOn change of the country input (this is a <select> input), returning the selected country value
companyTypeInputChangeOn change of the company type input, returning the selected company type value ("registered-company" or "sole-trader").
companySelectionWhen the user selects a company. The object returned will be a Company or Sole Trader object, or null if no company is selected. The object will have an extra field called type, giving the selected company type (either registered-company or sole-trader).

Checkout

Here are the events supported by the Checkout Element. These are used to plug the Hokodo Checkout Element into your overall checkout flow, so binding handlers to these events is required to complete the integration.

Event nameDescription
readyWhen the element has fully loaded post mount method execution.
successOn successful submission of Hokodo as the payment method.
failureWhen there has been a fatal error when loading the element.
rejectedWhen the company has not been approved for any payment offers.
formStateWhen Hokodo checkout submission status changes. The payload contains a boolean isReadyToSubmit flag that allows you to decide when to show and hide the order submission button on your page. This is only available with buttonlessSubmission option.

Methods

Company Search

The Company Search Element provides these methods:

MethodDescription
.mountThis method mounts the company search element to the DOM.
.unmountThis method unmounts the company search element from the DOM, while retaining the element in memory, along with all the associated .on events.

This allows the calling of the .mount method again in the future, remounting the existing company search element within the desired target, with all the existing .on events.
.destroySimilar to .unmount, the .destroy method removes the element from the DOM but in addition removes all the .on event handlers and the element from memory.

Once this method has been called the component will no longer exist in the DOM or in memory and you will need to recreate the element before events can be associated or it can be mounted again.
.updateThis provide the ability to pass any of the options (noted in the Creating the Company Search Element section) post .mount. The company search element will rerender to reflect any options that have been passed.

Checkout

The Checkout Element provides these methods:

MethodDescription
.mountThis method mounts the checkout element to the DOM.
.unmountThis method unmounts the checkout element from the DOM, while retaining the element in memory, along with all the associated .on events.

This allows the calling of the .mount method again in the future, remounting the existing company search element within the desired target, with all the existing .on events.
.destroySimilar to .unmount, the .destroy method removes the element from the DOM but in addition removes all the .on event handlers and the element from memory.

Once this method has been called the component will no longer exist in the DOM or in memory and you will need to recreate the element before events can be associated or it can be mounted again.
.updateThis provide the ability to pass any of the options (noted in the Displaying the Checkout Element section) post .mount. The Checkout element will rerender to reflect any options that have been passed.
.confirmOrderThis method provides the ability to trigger order confirmation programatically when using headless or buttonless submission mode. You can call this method from your existing confirm order button. More details about the headless mode can be found here.

Accessing elements from memory

If you can no longer access the companySearch or checkout variables due to function encapsulation or for other reasons, you can get it back using the elements.getElement method:

const companySearch = elements.getElement("companySearch");

Headless payment page

How to decide if you need headless checkout?

Most of our merchants use Hokodo’s prebuilt UI for Buyers to complete the checkout process. However for some merchants, the large volume of Deferred Payments that are created per day per Buyer, and the fact that the payment terms and method are all pre-set, it makes more sense to use our headless SDK. Your Solutions Engineer will be able to advise you on this decision.

Headless mode allows you to integrate the checkout element without rendering any UI. This option is only available to select merchants who meet the following criteria:

  • Hokodo is the only checkout option
  • Buyers will be making many orders in a day
  • Predetermined payment plan (credit terms)
  • Invoice is the only payment method

We would also require you to add the following Hokodo legal text above the confirm order button:

By proceeding, you agree to be bound by Hokodo's payment terms and privacy policy.