Online EFTPOS Integration Guide
API Endpoint
https://apitest.paymark.co.nzIntroduction
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:
-
Generate API Key and Secret
Generate your API key and secret from the Merchant Portal. -
Authenticate API Requests
Use your API credentials to authenticate requests via HTTP Bearer Authentication. -
Create a Payment Intent
Initiate a payment by calling the create payment intent API endpoint. -
Integrate using either:
-
Receive Payment Status
Confirm payment completion using webhook notifications or by querying the payment status. -
How to Make a Refund
Refund a payment through the API or directly in the merchant portal. -
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
- 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.
- Request a Bearer Token
Make aPOST
request to the token endpoint using the Basic Authorization Token in theAuthorization
header, and includegrant_type=client_credentials
in the request body.
Obtain Bearer Token ¶
Headers
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64-encoded APIKey:APISecret>
Body
grant_type=client_credentials
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 ¶
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"
}
}
}
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:
- Next Button on the Bank Selection page
<button id="nextBtn">Next</button>
- 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:
- Pay with a saved account button on the Bank Selection page.
- 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:
- payment_bank_selector – Triggered when the user interacts with the bank selector.
- payment_input – Triggered when the user enteres mobile number, will return if number is valid or not.
- 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 authorisationAUTHORISED
- The payment has been AUTHORISEDERROR
- An error has occured in the payment processNEW
- Plugin awaiting customer’s payment detailsDECLINED
- The payment was declinedEXPIRED
- The payment expired
- custom_page_settings – Return the settings to give context for rendering page elements.
showCustomButton
– A flag that returnstrue
if the Next button should be displayed for the Bank Selection page, orfalse
if it should be hidden.showCustomTrustedButton
– A flag that returnstrue
if the Pay button should be displayed for the Trust Selection page, or false if it should be hidden.showCustomTrustedNavigationButton
– A flag that returnstrue
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:
-
Webhook Notification (Recommended):
We send a callback to your notificationUrl with a signed JWT containing the payment details. -
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
- 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"
}
]
}
-
Decode the JWT
Extract the algorithm (alg
) and key ID (kid
) -
Select the Right Public Key
Use thekid
(Key ID) field in the JWT header to find the matching key in the JWKS. You’ll use that key to verify the signature. -
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
, orERROR
-
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
, orEXPIRED
).
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 ¶
Headers
Content-Type: application/json
Authorization: Bearer <access_token>
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.
- paymentId
string
(required) Example: 2e1e8b06-e892-4474-9ada-4511bf37f4d0
Make a Refund ¶
You can refund a payment using one of two methods:
- Merchant Portal – For manual or customer-service driven refunds.
- 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
orREFUNDED
(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
- Log in to the Merchant Portal
- Navigate to the Transactions page.
- Locate and select the payment you want to refund.
- Click Refund and confirm the amount and an optional description.
- 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.
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"
}
}
}
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:
- Select any bank.
- Enter a valid New Zealand mobile number.
- 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