Pay JS SDK

To establish a full integration with NHP, you will need Pay JS SDK. It contains classes you utilize to open management and checkout features of NHP. Both of them requrire an internal authentication endpoint to be defined, which the SDK calls to sign the JWT token it uses when communicating with NHP Backend.

The JS SDK may be downloaded and bundled along with the JS libraries of the PMS, or the PMS may link directly to the SDKs located on the NHP CDN.

See Integration example for reference.

Auth

The PMS must implement an eTrust tenant-token endpoint that is able to sign JWT access tokens compatible with the Payment Gateway APIs. See Authentication.

  • The endpoint must respond to AJAX/Fetch requests from the authenticated PMS users

  • Cross-domain requests must NOT be allowed

  • The endpoint is called on behalf of the logged in user (for the specific tenant).

  • The endpoint must be able to assign scopes for the user.

  • The scopes limit the user’s permissions inside the PMS (e.g. admin user -> open mangement-ui (pay:manageIntegration))

  • The endpoint returns the actual token and the validity of the token in seconds.

Once the endpoint for the SDK is in place, you create an authentication object like this:

const auth = new eTrust.Auth({
// required
tokenEndPoint: 'myapp.com/my-token-endpoint',
// optional, can be used if e.g. a csrfmiddlewaretoken needs to be passed
tokenRequestExtraParams: {
    csrfmiddlewaretoken: '...',
},
})

The SDK will request access tokens from the tenant-token endpoint of the PMS and use them to access the tenant’s resources in the NH Pay Payment Gateway.

Management UI

The pay-js-sdk provides JS class eTrust.Pay.Management that can be used to open the Payment Gateway Management UI for the tenant (e.g. a vet clinic chain).

In the Management UI the tenant superuser can:

  • Sing up to Payment Service Providers (KYC)

  • View transactions, payouts and other reports

  • Manage & order payment terminals

  • See available payment methods

The PMS settings section should have a button that the tenant superuser can click to open the Management UI. The UI will open in a new window pointing to https://pay.nordhealth.com/management

Following data will be passed from the PMS to Payment Gateway when the UI opens:

  • Tenant access token for API authorization (scope pay:manageIntegration required)

  • Organization structure Organization → Companies → Departments

About organization structure:

  • Each company will have to sign up to Payment Service Providers (Adyen) and pass the KYC (know your customer) verification checks.

  • Department is the actual “store”. Transactions belong under one department.

  • All companies in an Organization share the shoppers and their tokenized cards (shopper id’s should be unique in organization level)

const auth = new eTrust.Auth({ tokenEndPoint: 'myapp.com/my-token-endpoint' })

const managementOptions = {
    auth: auth,
    apiUrl: 'example.com/v1/', // api url to be used in checkout-ui
    baseUrl: 'example.com', // base url that will be opened in modal iframe
    locale: 'fi-FI',
    organizationStructure: [
        // Each company should have its own organization structure object
        {
        name: 'John Doe Therapists',
        reference: '123',
        ern: '//companies/123',
        departments: [
            {
            name: 'John Doe Clinic',
            reference: '123',
            ern: '//departments/123',
            active: true, // optional, only 1 department should be active (used as a default e.g. when initial onboarding)
            phone: '+123456789', // optional, used to prefill the onboarding form
            streetNumber: '10', // optional, used to prefill the onboarding form
            streetName: 'Downing Street', // optional, used to prefill the onboarding form
            postalCode: 'SW1A 2AA', // optional, used to prefill the onboarding form
            state: '', // optional, used to prefill the onboarding form
            city: 'London', // optional, used to prefill the onboarding form
            country: 'GB', // optional, used to prefill the onboarding form
            },
        ],
        country: 'FI',
        email: 'example@example.com', // optional, used to prefill the onboarding form
        mcc: '0742', // optional, used to prefill the onboarding form,
        webAddress: 'https://www.example.com', // optional, used to prefill the onboarding form
        phoneNumber: '+123456789', // optional, used to prefill the onboarding form
        stakeholderFirstName: 'John', // optional, used to prefill the onboarding form
        stakeholderLastName: 'Doe', // optional, used to prefill the onboarding form
        stakeholderType: 'Controller', // optional, used to prefill the onboarding form. Allowed values are 'Owner' or 'Controller'
        stakeholderJobTitle: 'Owner', // optional, used to prefill the onboarding form
        },
    ],
    timezone: 'UTC', // optional, defaults to UTC if omitted
    page: 'reports', // optional, automatically opens the management UI to a specific page,
    scope: 'pay:manageReports', // optional, by default the largest scope is set to access all pages
    skipSynchronization: false, // optional, skip synchronisation of the organization structure when opening the Management UI
}

new eTrust.Pay.Management(managementOptions).openWindow()

Checkout UI

The pay-js-sdk provides a eTrust.Pay.Checkout JS class that can be used to open the Payment Gateway Checkout UI to take a payment from a Shopper. The Checkout UI works in a similar fashion as the Management UI, but instead of new browser window, it will be rendered in an iframe on top of the PMS.

Following data will be passed from the PMS to the Checkout UI when the UI opens:

  • Payment amount and currency

  • Store ERN of the active department to display available payment terminals and other payment methods

  • Shopper ERN (optional) to retrieve tokenized payment methods or to tokenize a new payment method

  • Shopper email and phone for online payments

  • Optional metadata for the payment )Metadata will be relayed back to the PMS with webhook notifications)

  • Optional metadata for the tokenization

  • Optional paymentDetails.lineItems, containing invoice items row by row, for some specific online payment methods (e.g. Klarna)

  • Locale (to display the pay by link page in correct language)

The PMS can also pass a paymentStatusCallback JS function that will be called when the payment result is known and the Checkout UI is closing. For successful payments, the payment result will arrive in a webhook notification as well. Because of e.g. network errors, the paymentStatusCallback might not always be called. However, usually you receive the status much faster with the JS callback, so ideally you should be able to handle both cases (even though they relay the same data).

Open checkout-ui:

// Create an auth instance
const auth = new eTrust.Auth({ tokenEndPoint: 'myapp.com/my-token-endpoint' })

const checkoutOptions = {
    auth: auth,
    apiUrl: 'example.com/v1/', // api url to be used in checkout-ui
    baseUrl: 'example.com', // base url that will be opened in modal iframe
    shopperErn: '//exampleshopper/1/',
    storeErn: '//examplestore/1/',
    locale: 'fi-FI', // language that will be used in checkout ui
    shopperEmail: 'hello@example.com'
    shopperPhone: '+358123456789'
    shopperCountry: 'FI',
    metaData: {
        // this will be sent to payment requests metadata
        invoice_id: 1,
    },
    paymentStatusCallback: (notification) => {
        // Handle notification that occurred during payment.
        // This might be called before the payment is finished, so avoid refreshing the page here.
    },
    paymentDetails: { // These details are needed for some payment methods (e.g. Klarna)
        lineItems: [
        {
            quantity: 1,
            amountExcludingTax: 1.23,
            taxPercentage: 1.23,
            description: 'lorem ipsum',
            id: '123',
            taxAmount: 1.23,
            amountIncludingTax: 1.23,
        }],
    },
    allowedOnlinePaymentMethods: [], // Optional, specify which online payment methods should be available (only for payment links). All others will be blocked, should not be passed if blockedOnlinePaymentMethods is already passed.
    blockedOnlinePaymentMethods: ['directdebit_GB', 'sepadirectdebit'], // Optional, specify which online payment methods should be blocked for the customer (only for payment links). All others will be allowed, should not be passed if allowedOnlinePaymentMethods is already passed.
    }
    const tokenization = {
        meta: {
            example: 1,
        },
        required: true, // Force tokenization even if payment method doesn't allow it
        allowRemoval: true // Display option to remove the tokenized card from the UI (defaults to false)
    }
const modalOptions = {
    uiType: 'payment',
    currency: 'EUR',
    amount: 12.30,
    maximumAmount: 12.30 // Optional, set a maximum amount that can be paid for this transaction (if not set, the maximum amount is the total of the invoice or refund)
    allowedPaymentMethods: ['card-on-file', 'pos', 'payment-link'] // optional, uses backend settings if omitted
    disabledPaymentMethods: {'payment-link': 'Some explanation as to why this method is disabled', 'pos'} // optional, will be displayed in the UI but disabled. If a value is provided for each key, a tooltip will be displayed next to the payment method to explain why.
    tokenization: tokenization, // optional
    // This will be called when the user closes the checkout UI modal, or when the payment is successful
    onClose: (data) => {
        if (data.wasCharged) {
            // Handle successful payment
            return
        }
        console.log('Modal closed by the user.')
    },
    paymentLink: { // optional
        expires: '2019-08-24T14:15:22Z' // optional
        duration: 1440 // optional, in minutes. Uses backend settings if omitted.
        editDuration: false // optional, controls whether the link duration can be editted from the UI. Defaults to true.
    },
    recurringModel: 'one-off', // optional
    description: 'lorem ipsum' // optional, displayed on the online payment UI
}

new eTrust.Pay.Checkout(checkoutOptions).openModal(modalOptions)

Open checkout-ui in refund mode:

const auth = new eTrust.Auth({ tokenEndPoint: 'myapp.com/my-token-endpoint' })

const checkoutOptions = {
    // same as in payment
}

const modalOptions = {
    uiType: 'refund',
    extReference: 'original payment id',
    currency: 'EUR',
    refundReason: 'Why refund was made'
    onClose: (data) => {},
}

new eTrust.Pay.Checkout(checkoutOptions).openModal(modalOptions)