Online EFTPOS Integration Guide

Introduction

Hello from Online EFTPOS! This document describes what Online EFTPOS is, its features and how to integrate to the REST APIs to enable your customers to pay.

Online EFTPOS is NZ’s first Open Banking integrated product. Open Banking enables us to integrate directly with NZ banks and offer secure, trusted and innovative payment solutions. Your customers can pay from their NZ bank account and safely approve payment or consent requests in their banking app. No need to use physical cards or remember the card number, customers just need their mobile phone and bank app.

Integration Modes

Use Hosted Page for a quick and secure setup with minimal development effort, or Custom Page to embed the payment experience directly into your site for greater control and a seamless customer experience.

Choose the integration mode that best suits your needs.

Hosted Page is a secure Worldline-hosted payment page where customers are redirected during checkout to complete their payment.

After the payment is processed, you can choose how to handle the customer experience:

  • Redirect your customer back to your website via the redirectUrl supplied in the create payment intent request, or

  • Allow Worldline to display a confirmation message on your behalf.

Custom Page integration allows you to embed the payment plugin directly within your website using an <iframe>, without needing to redirect your customer away from your website. You simply render the pre-built form on your checkout page using the paymentId returned from the create payment intent API response. Your customer can then complete the payment seamlessly within your site.

Trusted

Let your customers checkout quicker with Trusted by Online EFTPOS. If your registered customers enable Trusted, they can skip the payment approval in their bank app. To find out more, refer to our section on Trusted.

Getting Started

To integrate with Online EFTPOS, follow these key steps:

  1. Generate API Key and Secret
    Generate your API key and secret from the Merchant Portal.

  2. Authenticate API Requests
    Use your API credentials to authenticate requests via HTTP Bearer Authentication.

  3. Create a Payment Intent
    Initiate a payment by calling the create payment intent API endpoint.

  4. Integrate using either:

  5. Receive Payment Status
    Confirm payment completion using webhook notifications or by querying the payment status.

  6. How to Make a Refund
    Refund a payment through the API or directly in the merchant portal.

  7. Get Ready to Go Live
    Complete the pre-Go Live checklist to ensure your integration is production-ready.

Note:
Refer to Environment URLs for sandbox and production URLs.

API Overview

Our API provides a set of endpoints to enable secure and reliable payment processing. Below is a high-level summary of the key endpoints required for integration.

Open API Specification

Please refer to the Open API Sepcification file for a complete API definition including request schema definition.

Download Open API Specification (Swagger file)

Note:

  • Ensure your integration is configured to include the correct API version in the base URL.

  • Example v2 in /oe/transactions/v2/payments/create-intent.

Authentication

Endpoint Method Description
/bearer POST Obtain a Bearer Token using your API Key and Secret via Basic Auth (Client Credentials Grant)

Generate the Key and Secret in the Merchant Portal

Payments

Endpoint Method Description
/oe/transactions/v2/payments/create-intent POST Create a payment intent to register customer’s intent to pay
/oe/transactions/v2/payments/{paymentId} GET Retrieve payment by paymentId

Refunds

Endpoint Method Description
/oe/transactions/v2/refunds POST Create a refund against a payment
/oe/transactions/v2/refunds/{refundId} GET Retrieve refund by refundId

Generate API Key and Secret

Go to the Integration page in the portal and generate your API key and secret. You will use these credentials to obtain a Bearer Token for making server-to-server calls from your website’s backend to our APIs.

Authentication

All requests to our API must be authenticated using a Bearer Token provided in the Authorization HTTP header. This token-based mechanism allows you to access and use our APIs. Our server will validate this token before continuing to process your request.

To obtain a Bearer Token you must exchange your API Key and Secret with our authorisation server. These credentials can be generated from the Integration page in the Merchant Portal.

Step-by-Step Process

  1. Generate a Basic Authorization Token
    Concatenate your API Key and API Secret with a colon (:) separator:
APIKey:APISecret

Then, base64 encode this string. This becomes your Basic Authorization Token.

  1. Request a Bearer Token
    Make a POST request to the token endpoint using the Basic Authorization Token in the Authorization header, and include grant_type=client_credentials in the request body.

Obtain Bearer Token

POST https://apitest.paymark.co.nz/bearer
Requestsexample 1
Headers
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64-encoded APIKey:APISecret>
Body
grant_type=client_credentials
Responses200
Headers
Content-Type: application/json
Body
access_token: `eyJhbGci...`
token_type: `Bearer`
expires_in: 3600

POST/bearer

Obtain a Bearer Token using your API Key and Secret.


Using the Bearer Token

Once you’ve obtained a Bearer Token, you can use it to authenticate all API requests by including it in the Authorization header:

Authorization: Bearer <access_token>

Token Usage Guidelines

  • Reuse the same token for all requests until it expires.

  • Do not request a new token for every API call. This can lead to rate limiting or unnecessary load on the authorisation server.

  • The token’s lifetime is indicated in the expires_in field (seconds).

  • Once expired, simply repeat the token request process to obtain a new token using the same API Key and Secret.

  • A token can be used for all merchant accounts under the account in which the credentials were generated. See Merchant Account Hierarchy for information on organisaction structures. E.g. credentials generated at a Root Parent can be used for all Parent and Child accounts under it.

Token Expiry

If you use an expired token, the API will return a 401 Unauthorized response. In that case, generate a new token and retry the request.

Create Payment Intent

First step in the payment flow is to create a payment intent. A payment intent captures the details for the payment and indicates to us that you have a customer who is wanting to pay for some goods or service.

The create payment intent call must be made by your back-end server, supplying the Bearer Token you obtained in the previous step. This is to ensure that the payment intent details are created in a secure manner and free from tampering through the front-end.

The key information to send in the create payment intent request will be the amount you are wanting to charge your customer, your merchantId, and also the reference and description (optional) for the payment.

Create Payment Intent

POST https://apitest.paymark.co.nz/oe/transactions/v2/payments/create-intent
Requestsexample 1
Headers
Content-Type: application/json
Authorization: Bearer <access_token>
Body
{
  "merchantTransactionId": "98102214-20f5-4fb9-a699-6574ca6771cb",
  "integrationMode": "CUSTOM",
  "merchant": {
    "url": "https://icecreamshop.demo.paymark.co.nz/open-checkout",
    "redirectUrl": "https://icecreamshop.demo.paymark.co.nz/open-checkout/redirect",
    "notificationUrl": "https://icecreamshop.demo.paymark.co.nz/open-checkout/notification"
  },
  "oepayment": {
    "amount": 1500,
    "currency": "NZD",
    "reference": "icecream0131"
  },
  "risk": {
    "userAgentInfo": {
      "userAgent": "Mozilla/5.0",
      "userIpAddress": "192.168.1.1",
      "userAgentType": "DESKTOP",
      "latitude": -36.8485,
      "longitude": 174.7633
    },
    "customerType": "CONSUMER",
    "serviceType": "ECOMMERCE_GOODS",
    "deliveryAddress": {
      "addressType": "DELIVERY_TO",
      "addressLine": [
        "88 Shortland Street",
        "CBD"
      ],
      "streetName": "Shortland St.",
      "buildingNumber": "18",
      "postCode": "1010",
      "townName": "Auckland",
      "countrySubDivision": "Auckland",
      "country": "NZ"
    }
  }
}
Responses201
Headers
Content-Type: application/json
Body
{
  "paymentId": "54f501aa-b130-4ec1-9f07-cb5d8dc8438a",
  "merchantTransactionId": "98102214-20f5-4fb9-a699-6574ca6771cb"
}

POST/oe/transactions/v2/payments/create-intent

Create a payment intent to register a customer’s intent to pay.


Sample Responses

Integration Mode – Custom

{
  "paymentId": "54f501aa-b130-4ec1-9f07-cb5d8dc8438a",
  "merchantTransactionId": "98102214-20f5-4fb9-a699-6574ca6771cb"
}

Integration Mode – Hosted

{
  "paymentId": "54f501aa-b130-4ec1-9f07-cb5d8dc8438a",
  "merchantTransactionId": "98102214-20f5-4fb9-a699-6574ca6771cb"
  "paymentUrl": "https://hosted.demo.worldline.co.nz/?paymentId=54f501aa-b130-4ec1-9f07-cb5d8dc8438a"
}

Field Definition

Field Type Required Validation Description
merchantTransactionId String Y UUID Merchant supplied ID for idempotency
integrationMode Enum Y Values: CUSTOM, HOSTED Field indicating how payment request will be presented to the end customer
merchant.url String Y Valid URL Merchant’s base URL. Used to validate where the plugin will be embedded for Custom Page integration
merchant.redirectUrl String N Valid URL Only for Hosted Page integration. Merchant’s URL to return customer to upon payment completion
merchant.notificationUrl String Y Valid URL Merchant’s URL where notification callback will be sent to
merchant.merchantId String Y Regex: ^\d{9}$ Unique 9 digit number assigned to merchant during onboarding
oepayment.amount Integer Y amount must be greater than 0 Payment amount in cents
oepayment.currency Enum Y Values:NZD Currency of the payment amount
oepayment.reference String Y Regex: ^[A-Za-z0-9 -]{0,100}$ Reference of transaction on customer’s statement

Only first 12 characters will be displayed.
oepayment.description String N Regex: ^[A-Za-z0-9 -.,]{0,100}$ Optional internal merchant description of the transaction
risk.userAgentInfo.userAgent String Y Regex: ^[\S ]{1,8192}$ The raw user agent string of the client making the request. Typically identifies the browser, operating system, and device type
risk.userAgentInfo.userIpAddress String Y Regex: ^([0-1]?\d?\d 2[0-4]\d
risk.userAgentInfo.userAgentType Enum N Values: DESKTOP, MOBILE, TABLET, POS_TERMINAL, EMBEDDED_DEVICE, BOT, SMART_TV, GAME_CONSOLE, WEARABLE, UNKNOWN A high-level classification of the device type inferred from the user agent
risk.userAgentInfo.latitude Decimal N The approximate geographic latitude of the user/device at the time of the request, if available
risk.userAgentInfo.longitude Decimal N UUID The approximate geographic longitude of the user/device at the time of the request, if available
risk.customerType Enum N Values: NEW, REGULAR, ANONYMOUS, BUSINESS Indicates the general classification of the customer
risk.serviceType Enum N Values: ECOMMERCE_GOODS, ECOMMERCE_SERVICE, OTHER Describes the category of service or product associated with the payment
risk.deliveryAddress.addressType Enum N Values: DELIVERY_TO Identifies the nature of the postal address
risk.deliveryAddress.addressLine List N Regex: ^[A-Za-z0-9 ,.-]{1,50}$ The full, free-form address line provided for delivery. May contain street, building, and unit details
risk.deliveryAddress.streetName String N Regex: ^[A-Za-z0-9 -.]{1,500}$" Name of a street or thoroughfare component of the delivery address
risk.deliveryAddress.buildingNumber String N Regex: ^[A-Za-z0-9 #]{1,20}$ The building number or unit number in the delivery address
risk.deliveryAddress.postCode String N Regex: ^[0-9]{4}$ The postal or ZIP code of the delivery address
risk.deliveryAddress.townName String N Regex: ^[A-Za-z0-9 ,.-]{1,50}$ The town or city name for the delivery address
risk.deliveryAddress.countrySubDivision String N Regex: ^[A-Za-z0-9 .-]{1,50}$ The state, province, or region within the country for the delivery address
risk.deliveryAddress.country String N Regex: ^[A-Z]{2}$ The 2-letter ISO country code (e.g., NZ) for the delivery address

Note:

  • Risk fields should always be populated in the request if you have these details for the customer’s payment.

  • These are used by the banks for fraud mitigation.

Idempotency

Create payment intent request is idempotent through the merchantTransactionId supplied in the request. This allows you to replay the request in the event of an unexpected error from our server. A successful response will return a 201 with a paymentId.

Processed Payment Statuses

Status Description
AUTHORISED Approved successfully
DECLINED Customer declined in bank app or rejected by bank
EXPIRED No action from customer within the timeframe allowed
ERROR Bank or Worldline unable to process

Hosted Page Integration

When using the Hosted Page integration, you will only need to redirect your customer to the paymentUrl received in the response of the create payment intent request.

There, they will be able to complete the payment. Upon completing the payment the customer will be redirected back to your website through the redirectUrl supplied in the create payment intent request.

Please refer to Receive Payment Status for how to retrieve the processed payment’s details.

Custom Page Integration

For Custom Page integration you will need to embed the payment plugin in your checkout page. To render the plugin, you’ll need to use the paymentId returned from the create payment intent API response. The plugin is loaded into your page via a script tag and mounted to a container element of your choosing.

Embed the Plugin

This section outlines how to embed the plugin, initialise it, and handle key lifecycle events to guide your customer through the payment process.

1. Update Content Security Policy (CSP)
Ensure seamless integration of the embedded plugin by including the following mandatory Content Security Policy directive in your payment page:

Content-Security-Policy: frame-src *;

2. Add Target Div and Add JavaScript
Place the following HTML where you want the plugin to render (e.g., checkout form, modal, or sidebar):

<!-- Primary Embed Container (Required) -->
<div id="plugin-container" class="plugin-embed">
  <!-- Plugin will load here dynamically -->
</div>

3. Include the Plugin Script

<script src="https://open.demo.worldline.co.nz/plugin.js"></script>

4. Add Custom Buttons

For the Custom Page integration, you have the option to use your own buttons for triggering different functions in the plugin. This is so that you can use your own custom styling. Please refer to Custom Buttons for more information and how to use them.

Initialise Plugin

Initialize the plugin supplying the necessary values for the parameters below:

Supported init(config) Parameters:

Parameter Type Required Default Description
elementId String Y The ID of the element where the plugin will be embedded.
paymentId String Y The paymentId recevied in the response of the create payment intent request.
merchantUrl String Y The URL of the your website where the plugin is embedded — used for verification.
renderTiles Boolean N true If true, bank selection will be rendered as tiles. If false, bank selection will be rendered as a drop-down list.
hasCustomButton Boolean N false If true, the plugin will not render its own Next button and you must add your own button and use the submit(event) function

Additional Parameters for Trusted:
See Trusted for more information.

Parameter Type Required Default Description
hasCustomTrustedButton Boolean N false If true, the plugin will not render its own Pay button and you must add your own button and use the submit(event) function.
hasCustomTrustedNavigationButton Boolean N false If true, the plugin will not render its own Navigation button and you must add your own button and use the navigateTrusted(event) function.

Example:

<script>
    window.addEventListener("load", () => {
        const nextBtn = document.getElementById("nextBtn");
        const divContainer = document.getElementById("divId");
        const urlParams = new URLSearchParams(window.location.search);
        const paymentId = urlParams.get("paymentId");
        const merchantUrl = urlParams.get("merchantUrl");
        // ✅ Initialize plugin
        window.plugin.init({
            elementId: "divId",
            paymentId: paymentId,
            merchantUrl: merchantUrl,
            hasCustomButton: true,
        });
        // ✅ Submit handler
        nextBtn.addEventListener("click", (e) => {
            window.plugin.submit(e);
        });
    });
</script>

Plugin API Reference

Below is a detailed breakdown of the plugin methods and supported configuration parameters. Always ensure window.plugin is available before calling any methods.

Method Description
init(config) Initializes and embeds the plugin. Accepts config object with parameters listed below.
submit(event) Triggers payment submission from parent window to plugin. Used when supplying own Next button
updatePaymentId(paymentId) Updates the plugin by injecting a new paymentId. Use to restart or change transaction in the case of an update to customer cart.
getPayment() Returns the most recent payment data received from the iframe.
getPaymentStatus() Returns the latest known status of the payment. Useful for real-time UI updates.
navigateTrusted(event) Used to switch the payment view from saved trusts to the bank selection screen and viceversa
getCustomPageSettings() Return the settings to help give context for rendering page elements.

Custom Buttons

The Custom integration lets you use your own buttons to trigger plugin functions, so you can style them to match your website’s design. There are two main sets of buttons you can supply — the Next/Pay button and the Navigate button. The Pay button and the Navigate button are only relevant for Trusted.

Next/Pay Button

These buttons are used to call the plugin.submit(event) method:

  1. Next Button on the Bank Selection page
<button id="nextBtn">Next</button>
  1. Pay Button on the Trust Selection page
<button id="payBtn">Pay ${amount.toFixed(2)}</button>

Navigate Button

These buttons are used to call the plugin.navigateTrusted(event) method:

  1. Pay with a saved account button on the Bank Selection page.
  2. Use another account button on the Trust Selection page.
<button id="navBtn">{label}</button>

Button Visibility

To control which buttons are shown or hidden in the plugin, use visibility flags when calling plugin.init(). These flags allow you to hide the default plugin buttons and replace them with your own custom buttons.

Flag Description
hasCustomButton When true, hides the plugin’s default Next button (on the Bank Selection page)
hasCustomTrustedButton When true, hides the plugin’s default Pay button (on the Trust Selection page)
hasCustomTrustedNavigationButton When true, hides the plugin’s default Navigation buttons (on the Bank Selection and Trust Selection pages)

Example: Hide All Default Plugin Buttons

window.plugin.init({
  elementId: "plugin-container",
  paymentId: "c5192016-8cb4-4542-9eca-b65280ddfe1c",
  merchantUrl: window.location.origin,
  hasCustomButton: true,
  hasCustomTrustedButton: true,
  hasCustomTrustedNavigationButton: true
});

Example: Detect Which Custom Buttons to Show

Use plugin.getCustomPageSettings() and plugin.getPaymentStatus() to determine what buttons to render:

window.addEventListener("message", checkButtonVisibility);

function checkButtonVisibility() {
  const nextBtn = document.getElementById("nextBtn");
  const payBtn = document.getElementById("payBtn");
  const trustNavigateBtn = document.getElementById("trustNavigateBtn");
  
  const settings = window.plugin?.getCustomPageSettings?.();
  const status = window.plugin?.getPaymentStatus?.();
  
  if (!settings || !status) return;
  
  // Show Next only when showCustomButton is true
  nextBtn.style.display = settings.showCustomButton ? "inline-block" : "none";
  
  // Show Pay only when showCustomTrustedButton is true
  payBtn.style.display = settings.showCustomTrustedButton ? "inline-block" : "none";
  
  // Show Navigation if allowed
  trustNavigateBtn.style.display =
    settings.showCustomTrustedNavigationButton
      ? "inline-block"
      : "none";
  
  // Change Navigation text, showCustomTrustedButton can only be true in Trust Selection Page
  trustNavigateBtn.textContent = settings.showCustomTrustedButton
      ? "Use another account"
      : "Pay with a saved account";
}

Example: Trigger Plugin Actions from Custom Buttons

// Submit action (Next or Pay)
nextBtn.addEventListener("click", (e) => {
  window.plugin.submit(e);
});

payBtn.addEventListener("click", (e) => {
  window.plugin.submit(e);
});

// Navigate Trusted action
trustNavigateBtn.addEventListener("click", (e) => {
  window.plugin.navigateTrusted(e);
}

Events

If you’re using the Custom Page integration, your system can listen for payment lifecycle events emitted directly by the plugin. These events notify your application in real time when the payment reaches key states, including when a payment is processed. These notifications allow you to dynamically update the UI to create an optimal customer experience.

Plugin event listening is only available for a Custom Page integration. If you are using a Hosted Page integration, you must use webhook notifications or poll the API.

Considerations

  • Events are useful for internal logic and feedback loops but are not a substitute for verifying the processed payment outcome via webhook notification or polling

  • Ensure your event handlers are resilient to retries, deduplicated, and error-tolerant.

Event Types

The plugin emits the following events:

  1. payment_bank_selector – Triggered when the user interacts with the bank selector.
  2. payment_input – Triggered when the user enteres mobile number, will return if number is valid or not.
  3. payment_status – Provides the current status of the payment. The data.status field will contain one of the following:
    • SUBMITTED - The payment has been submitted to the customer’s bank app for authorisation
    • AUTHORISED - The payment has been AUTHORISED
    • ERROR - An error has occured in the payment process
    • NEW - Plugin awaiting customer’s payment details
    • DECLINED - The payment was declined
    • EXPIRED - The payment expired
  4. custom_page_settings – Return the settings to give context for rendering page elements.
    • showCustomButton – A flag that returns true if the Next button should be displayed for the Bank Selection page, or false if it should be hidden.
    • showCustomTrustedButton – A flag that returns true if the Pay button should be displayed for the Trust Selection page, or false if it should be hidden.
    • showCustomTrustedNavigationButton – A flag that returns true if the custom navigation button should be dispayed, false if it should be hidden.

Implementation Example (React)

import { useEffect } from "react";
import { useRouter } from "next/router";
import {
  PaymentEvent,
  PaymentEventType,
  PaymentStatus,
} from "./types"; // adjust the import path as needed

const PaymentListener = ({
  setPaymentSessionStarted,
  setPaymentSubmitted,
  setShowPaymentButton,
  setShowRestartButton,
  setPaymentFailed,
}) => {
  const router = useRouter();

  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      const message = event.data as PaymentEvent;

      if (!message?.event) return;

      switch (message.event) {
        case PaymentEventType.paymentStatus:
          switch (message.data.status) {
            case "SUBMITTED":
              setPaymentSessionStarted(true);
              setPaymentSubmitted(true);
              break;
            case "AUTHORISED":
              if (message.data?.type === "REDIRECT" && message.data.redirectUrl) {
                router.push(message.data.redirectUrl);
              }
              break;
            case "EXPIRED":
              setShowPaymentButton(false);
              setShowRestartButton(true);
              break;
            case "DECLINED":
            case "ERROR":
              setPaymentFailed(true);
              break;
          }
          break;

        case PaymentEventType.paymentBankSelector:
          // Handle bank selector logic here if needed
          break;

        case PaymentEventType.paymentInput:
          // Handle input interaction if needed
          break;

        default:
          break;
      }
    };

    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, []);

  return null;
};

Receive Payment Status

When a payment reaches a processed state (e.g. AUTHORISED, DECLINED, ERROR, EXPIRED), your system can be notified or you can retrieve the processed status using the following methods:

  1. Webhook Notification (Recommended):
    We send a callback to your notificationUrl with a signed JWT containing the payment details.

  2. GET oe/transactions/v2/payment/{paymentId}:
    You can poll our API to check the current status of the payment by its ID.

We recommend webhook notifications as the primary and most secure way to receive real-time status updates, with the other two methods serving as fallbacks or for specific platform needs.

Webhook Notification

This is the main mechanism to rely on for receiving updates about the status of a payment. Once a payment is processed, we will send an HTTP callback (webhook) to the notificationUrl you provided when creating the payment intent.

This callback contains a JSON Web Token (JWT) in the body, which includes a claim with the details of the payment (e.g., paymentId, status, transactionTime, etc).

The JWT is digitally signed using our private key to ensure the integrity and authenticity of the callback. You must verify the signature of the JWT using our published JSON Web Key Set (JWKS).

This helps ensure:

  • The message is authentic (sent by Worldline).

  • The contents have not been tampered with.

  • The JWT is valid (not expired, and matches the expected claims).

Download Notification JWT Sample

How to Verify the JWT

  1. Fetch the JWKS
    We publish our public keys at the following endpoint:
https://apitest.paymark.nz/worldlinejwks/OETransaction

Sample response:

{
  "keys": [
    {
      "crv": "P-521",
      "kid": "5322fb2d-0580-4122-b23b-5e3055bac347",
      "kty": "EC",
      "x": "AXbhzIBbyax0GicdVl_HAaJi-b9nkkMDhGmtObXXwx48D_Evnt3G7BVgeU-98L8f1o3u4Olxc-kN-PlrwzH-T5ee",
      "y": "AIj6gTTj0Qo9i5f2CA583hjwfjyf6YeZXyWXCY8M_T_hGsQiwGYybB_nC30QN1fgYaePpWY3-iTsgCjQ4B71K6YI"
    }
  ]
}
  1. Decode the JWT
    Extract the algorithm (alg) and key ID (kid)

  2. Select the Right Public Key
    Use the kid (Key ID) field in the JWT header to find the matching key in the JWKS. You’ll use that key to verify the signature.

  3. Verify the Signature
    Use a JWT library that supports the algorithm. Supply:

  • The raw JWT.

  • The public key from the JWKS.

  • Optionally: expected claims (iss, aud, etc.).

Polling

Polling should only be performed:

  • As a fallback if your notification server is down or delayed as part of your clean up process.

  • For manual status checks, retries, or debugging.

If you decide to poll the API for processed payment status, we recommend the following best practices:

  • Start polling only after the payment has been created and you are awaiting a processed payment.

  • Poll using our recommended strategy else, with exponential backoff or fixed intervals — e.g. every 10 seconds initially, gradually increasing to 20-30 seconds.

  • Stop polling once a final status is received:

    • AUTHORISED, DECLINED, EXPIRED, or ERROR
  • Timeout gracefully after a max polling duration (e.g., 2-5 minutes) to avoid indefinite loops.

  • Respect API rate limits — high-frequency polling could lead to throttling or errors.

Polling should be used sparingly. When possible, rely on webhook notifications as your primary integration path.

Recommended Polling Strategy
To efficiently check for a processed payment status without overloading the system, we recommend the following polling intervals:

  • Start polling 60 seconds after the payment is created.

  • Poll every 10 seconds for the next 60 seconds.

  • Then, poll every 20 seconds for the next 60 seconds.

  • Then, poll every 30 seconds for the next 120 seconds.

  • After that, poll every 45 seconds until a processed payment status is received (AUTHORISED, DECLINED, ERROR, or EXPIRED).

Polling via GET /oe/transactions/v2/payments/{paymentId}
You can actively retrieve the latest status of a payment by making a GET request to the payment endpoint using its paymentId.

Retrieve Payment by paymentId

GET https://apitest.paymark.co.nz/oe/transactions/v2/payments/2e1e8b06-e892-4474-9ada-4511bf37f4d0
Requestsexample 1
Headers
Content-Type: application/json
Authorization: Bearer <access_token>
Responses200
Headers
Content-Type: application/json
Body
{
  "merchantTransactionId": "7949fa62-1f2d-40bf-891e-69235491660f",
  "paymentId": "2e1e8b06-e892-4474-9ada-4511bf37f4d0",
  "integrationMode": "CUSTOM",
  "transactionType": "REGULAR",
  "status": "AUTHORISED",
  "transactionTime": "2025-04-17T02:34:13.833Z",
  "lastModifiedTime": "2025-04-17T02:34:20.34Z",
  "createdBy": "OEMERCHANT",
  "merchant": {
    "url": "https://www.bagelandcapers.co.nz/",
    "redirectUrl": "https://www.bagelandcapers.co.nz/redirect",
    "notificationUrl": "https://bagelandcapers.co.nz/notification",
    "merchantId": "300700390"
  },
  "oepayment": {
    "amount": 1500,
    "currency": "NZD",
    "reference": "PO003288",
    "description": "Cat treats"
  },
  "risk": {
    "userAgentInfo": {
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
      "userIpAddress": "114.36.198.162"
    }
  }
}

GET/oe/transactions/v2/payments/{paymentId}

Retrieve the latest status for a specific payment intent.

URI Parameters
HideShow
paymentId
string (required) Example: 2e1e8b06-e892-4474-9ada-4511bf37f4d0

Make a Refund

You can refund a payment using one of two methods:

  1. Merchant Portal – For manual or customer-service driven refunds.
  2. Refund API – For programmatic refunds as part of your integration flow.

When issuing a refund, note the following:

  • Refunds are only allowed on payments that have an AUTHORISED or REFUNDED (if payment has been partially refunded) status.

  • You can perform full or partial refunds by specifying the amount.

  • The refund request is processed immediately, and the response includes the final result.

  • Refunds will settle overnight into your customers account.

Merchant Portal

The merchant portal provides an easy-to-use interface for initiating refunds without requiring code.

How to Refund

  1. Log in to the Merchant Portal
  2. Navigate to the Transactions page.
  3. Locate and select the payment you want to refund.
  4. Click Refund and confirm the amount and an optional description.
  5. Submit the request.

Features:

  • Refund status is tracked in the Merchant Portal.

  • Ideal for manual workflows, customer support, or when you don’t want to handle refunds programmatically.

Refund API

Create Refund

To automate refunds, you can create a refund via the API. This process mirrors how payments are initiated.

POST https://apitest.paymark.co.nz/oe/transactions/v2/refunds
Requestsexample 1
Headers
Content-Type: application/json
Authorization: Bearer <access_token>
Body
{
  "merchantTransactionId": "bd31893d-bc1a-4a08-9162-b04db9acd709",
  "merchant": {
    "merchantId": "300700390"
  },
  "oerefund": {
    "refundAmount": 1000,
    "description": "",
    "originalPaymentId": "bf591254-223b-4c20-9f4a-85eedba4c52d"
  },
  "risk": {
    "userAgentInfo": {
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
      "userIpAddress": "114.36.198.162"
    }
  }
}
Responses201
Headers
Content-Type: application/json
Body
{
  "merchantTransactionId": "bd31893d-bc1a-4a08-9162-b04db9acd709",
  "refundId": "ec456e84-991f-4843-be4f-6989a4c762e6",
  "transactionTime": "2025-04-17T02:34:13.833Z",
  "lastModifiedTime": "2025-04-17T02:34:20.34Z",
  "createdBy": "capers@fluffy.com",
  "status": "REFUNDED",
  "merchant": {
    "merchantId": "300700390"
  },
  "oerefund": {
    "refundAmount": 1000,
    "description": "Expired treats",
    "originalPaymentId": "bf591254-223b-4c20-9f4a-85eedba4c52d"
  },
  "risk": {
    "userAgentInfo": {
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
      "userIpAddress": "114.36.198.162"
    }
  }
}

POST/oe/transactions/v2/refunds


Field Definition

Field Type Required Validation Description
merchantTransactionId String Y UUID Merchant supplied ID for idempotency
merchant.merchantId String Y Regex: ^\d{9}$ Unique 9 digit number assigned to merchant during onboarding
oerefund.refundAmount Integer Y amount must be greater than 0 The amount to be refunded in cents. Must not exceed the original payment amount, taking into consideration any previous partial refunds made against the same payment
oerefund.description String N Regex: ^[A-Za-z0-9 -.,]{0,100}$ Optional internal merchant description of the transaction
oerefund.originalPaymentId String Y UUID The paymentId of the payment being refunded
risk.userAgentInfo.userAgent String Y Regex: ^[\S ]{1,8192}$ The raw user agent string of the client making the request. Typically identifies the browser, operating system, and device type
risk.userAgentInfo.userIpAddress String Y Regex: ^([0-1]?\d?\d|2[0-4]\d|25[0-5])(\.([0-1]?\d?\d|2[0-4]\d|25[0-5])){3}$ The public IP address of the end user or device initiating the request. Used for geolocation and fraud analysis

Idempotency

The create refund request is idempotent through the use of the merchantTransactionId supplied in the request. This allows you to replay the create refund request in the event of an unexpected error from our server. A successful response will return a 201 with the refund response as per the example above.

Processed Refunds Statuses

Status Description
REFUNDED Approved successfully
DECLINED Rejected by bank
ERROR Bank or Worldline unable to process

Note:
Always configure the correct API version and environment in your integration.

Test Scenarios

Payments

To simulate the payment flow during testing:

  1. Select any bank.
  2. Enter a valid New Zealand mobile number.
  3. Click the Next button.

Use the table below to test different processed payment statuses by submitting the corresponding payment amounts.

Payment Status ANZ ASB BNZ WESTPAC COOP
AUTHORISED $3.00 or more $2.00 or more $2.30 or more $1.20 or more $1.20 or more
DECLINED $2.00 $1.17 $1.17 $1.02
EXPIRED $1.20 $2.00 $1.18
ERROR $2.20 $1.40 $2.10 $1.08 $1.04

Note:
In Production, your customers will have up to 7 minutes (varies by bank) to action the payment request in their bank app after they click on Next button.

Refunds

To test successful refunds for any bank, please specify a refund amount that is less than $1.00 or more than $2.00.

Trusted

Trusted by Online EFTPOS enables a quicker way to pay with Online EFTPOS.

Let your registered customers save you as a Trusted merchant so the next time they pay, they do not need to approve the request in their bank app!

Your registered customer must be logged in before beginning the payment process. For banks that support Trusted, a checkbox will appear in the plugin below the bank tiles, allowing customers to enable the Trusted feature. Once selected, the customer proceeds with the payment and approves the request in their banking app. After the payment is processed you will be saved as a Trusted merchant against their selected bank and account.

When your customer returns to make their next purchase, is logged in and selects Online EFTPOS as the payment method they will then see their saved Trust. If they pay with the saved Trust, it will automatically process the payment from their nominated bank account. There is no need to approve the request in their bank app.

Requirements

In order to use this feature:

  • Your merchant account with us must have this feature enabled.

  • Your website or app must support a secure login for your customer before they checkout.

  • Your customer must be logged in and you must supply your customer’s unique identifier in the trusted.merchantCustomerId field in create payment intent request.

  • If using Custom Page integration, you must define your own buttons (Custom Buttons) or use the plugin’s default buttons.

Field Type Validation Description
trusted.merchantCustomerId String Regex: ^[a-zA-Z0-9-_]{1,50}$ Merchant’s unique customer Id

Sample Request

{
  "merchantTransactionId": "98102214-20f5-4fb9-a699-6574ca6771cb",
  "integrationMode": "CUSTOM",
  "merchant": {
    "url": "https://icecreamshop.demo.paymark.co.nz/open-checkout",
    "redirectUrl": "https://icecreamshop.demo.paymark.co.nz/open-checkout/redirect",
    "notificationUrl": "https://icecreamshop.demo.paymark.co.nz/open-checkout/notification"
  },
  "trusted": {
    "merchantCustomerId": "4b578882-6689-4261-98b9-01816e02918c"
  },
  "oepayment": {
    "amount": 1500,
    "currency": "NZD",
    "reference": "icecream0131"
  },
  "risk": {
    "userAgentInfo": {
      "userAgent": "Mozilla/5.0",
      "userIpAddress": "192.168.1.1",
      "userAgentType": "DESKTOP",
      "latitude": -36.8485,
      "longitude": 174.7633
    },
    "customerType": "CONSUMER",
    "serviceType": "ECOMMERCE_GOODS",
    "deliveryAddress": {
      "addressType": "DELIVERY_TO",
      "addressLine": [
        "88 Shortland Street",
        "CBD"
      ],
      "streetName": "Shortland St.",
      "buildingNumber": "18",
      "postCode": "1010",
      "townName": "Auckland",
      "countrySubDivision": "Auckland",
      "country": "NZ"
    }
  }
}

Note:
Response body is identical to Create Payment Intent

Environment URLs

Refer to the table below for configuring your systems to use the desired environments.

URL Sandbox Production
Merchant Portal https://portal.demo.worldline.co.nz/ https://portal.worldline.co.nz/
API https://apitest.paymark.co.nz/ https://api.paymark.nz/
Plugin https://open.demo.worldline.co.nz/plugin.js https://open.worldline.co.nz/plugin.js
JWKS Endpoint https://apitest.paymark.nz/worldlinejwks/OETransaction https://api.paymark.nz/worldlinejwks/OETransaction
Hosted Page https://hosted.demo.worldline.co.nz/ https://hosted.worldline.co.nz/

Merchant Portal Overview

The Merchant Portal is your central dashboard for managing day-to-day payment operations, team access, and account settings. It’s designed to give you visibility and control over transactions, user permissions, and business reporting — all in one place.

Whether you’re a single-store merchant or part of a larger, multi-level organisation, the portal supports flexible account structures and access levels to match your business needs.

User Roles and Access Control

Access to data in the portal is controlled through role-based access control (RBAC). Give your team members the right role so they have the access they need.

Choose the level of access required for individual members based on the hierarchy table below. You may need a more privileged role with less restrictions to change your current role.

Admin Roles

Role Description Restrictions
Merchant Org Admin Full access to all portal pages and features.

This role is automatically assigned to the portal user created during account registration
No restrictions - full access
Merchant Admin Full access to the Transactions, Users, My Account, and Reporting pages.

Can manage most portal functions except those on the Integration page
Cannot access the Integration page or manage API keys

View Only Role

Role Description Restrictions
Merchant Viewer Read-only access to the Transactions and Reporting pages.

Limited access with ability to perform selected non-administrative tasks
Cannot access the Integration page, make refunds, or manage other users’ information

Merchant Account Hierarchy

Connect and manage your accounts with support for up to three levels of hierarchy.

If your organisation requires multiple levels of account management — for example, a head office overseeing regional offices, which in turn manage individual stores — a three-level structure may be suitable.

To set up a multi-level account structure, please consult with our sales team.

Hierarchy Levels

Type Description Restrictions
Root Parent (Top-Level) Acts as the primary account for the organisation.

Can access and manage all linked accounts below it (including Parent and Child accounts).

Used for grouping and oversight across the entire account structure.

Transactions should generally not be processed directly through this account
Created during onboarding.

Only one Root Parent Account per organisation
Parent (Mid-Level) Used for managing a subset of Child accounts, such as regional groups.

Can access and manage its linked Child accounts.

Not required in all account structures.

Transactions should not be processed through this account in a three-level setup
Must be linked to a Root Parent Account
Child (Child-Level) Represents an individual merchant store.

This is where transactions are processed.

Can operate under either a Root Parent Account (in 2-tier setups) or a Parent Account (in 3-tier setups)
Must be linked to a Parent or Root Parent Account, depending on the structure.

Note:

  • A 2-level hierarchy involves a Root Parent (Top-Level) Account and one or more Child-Level Accounts directly beneath it.

  • A 3-level hierarchy introduces Parent (Mid-Level) Accounts to group and manage multiple Child (Child-Level) Accounts.

Creating a New Account
To create a new merchant account, please contact our Sales or Support team. They’ll guide you through the setup process and ensure your account is configured correctly.

Updating an Existing Account
To update an existing account, reach out to our Support team with the field name(s) and the new information you’d like applied. They’ll assist you with making the necessary changes.

Portal Pages and Functions

Use the navigation menu to access key sections of the merchant portal. Each page provides specific capabilities based on user roles.

Navigation Menu

Page Description Role Restrictions
Integration Manage API credentials for system integration Not accessible to Merchant Admin and Merchant Viewer
Transactions View or search transactions, or make a refund Merchant Viewer can view but cannot refund
Users Create, view, or manage portal user accounts Not accessible to Merchant Viewer
My Account View your Online EFTPOS account details Not accessible to Merchant Viewer
My Profile View or update your portal user settings, including password reset No restrictions
Reporting Generate and download transaction or settlement history reports No restrictions

Go Live

Go-Live Checklist

Below checklist is designed to assist in preparing your production integration to be ready for use by your customers. Perform these checks as your Go Live readiness check

  • Production Account Configured

  • Integration Completed

    • reference field (max 12 characters) is unique to serve as an additional identifier to distinguish individual payments within your system.
  • Payment Status Handling

  • Refund Support

  • Testing Complete

    • AUTHORISED, DECLINED, EXPIRED, ERROR).
  • Ready for Production

Generated by aglio on 25 Jun 2025