- Introduction
- Contributing
- Minimum Requirements
- Dependency
- Quick Start
- Configuration
- Client Creation
- Request ID, Correlation ID and Idempotency Key
- Full Examples
- Individual API Call Examples
This SDK allows merchants with TypeScript- or JavaScript-based e.g. React e-commerce site to integrate with Blink PayNow and Blink AutoPay.
This SDK was written in TypeScript 5.
We welcome contributions from the community. Your pull request will be reviewed by our team.
This project is licensed under the MIT License.
- Axios 1.7
- Node.js 22.10 LTS
- Install via NPM
npm install blink-debit-api-client-node --save
Append the BlinkPay environment variables to your .env
file.
BLINKPAY_DEBIT_URL=https://sandbox.debit.blinkpay.co.nz
BLINKPAY_CLIENT_ID=...
BLINKPAY_CLIENT_SECRET=...
BLINKPAY_RETRY_ENABLED=true
BLINKPAY_TIMEOUT=10000
Create and use the client:
sample.js
import axios from 'axios';
import log from 'loglevel';
import { BlinkDebitClient, ConsentDetailTypeEnum, AuthFlowDetailTypeEnum, AmountCurrencyEnum } from 'blink-debit-api-client-node';
const client = new BlinkDebitClient(axios);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: "https://www.blinkpay.co.nz/sample-merchant-return-page"
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: '0.01'
},
pcr: {
particulars: 'particulars',
code: 'code',
reference: 'reference'
}
};
async function createQuickPayment() {
const qpCreateResponse = await client.createQuickPayment(request);
log.info("Redirect URL: {}", qpCreateResponse.redirectUri); // Redirect the consumer to this URL
const qpId = qpCreateResponse.quickPaymentId;
const qpResponse = await client.awaitSuccessfulQuickPaymentOrThrowException(qpId, 300); // Will throw an exception if the payment was not successful after 5min
}
await createQuickPayment();
sample.ts
import axios from 'axios';
import log from 'loglevel';
import { BlinkDebitClient, QuickPaymentRequest, ConsentDetailTypeEnum, AuthFlowDetailTypeEnum, GatewayFlow, AuthFlow, AmountCurrencyEnum, Amount, Pcr } from 'blink-debit-api-client-node';
const client = new BlinkDebitClient(axios);
const request: QuickPaymentRequest = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: "https://www.blinkpay.co.nz/sample-merchant-return-page"
} as GatewayFlow
} as AuthFlow,
amount: {
currency: AmountCurrencyEnum.NZD,
total: '0.01'
} as Amount,
pcr: {
particulars: 'particulars',
code: 'code',
reference: 'reference'
} as Pcr
};
async function createQuickPayment() {
const qpCreateResponse = await client.createQuickPayment(request);
log.info("Redirect URL: {}", qpCreateResponse.redirectUri); // Redirect the consumer to this URL
const qpId = qpCreateResponse.quickPaymentId;
const qpResponse = await client.awaitSuccessfulQuickPaymentOrThrowException(qpId, 300); // Will throw an exception if the payment was not successful after 5min
}
await createQuickPayment();
Append the BlinkPay environment variables to your .env
file. Notice the REACT_APP_
prefix.
REACT_APP_BLINKPAY_DEBIT_URL=https://sandbox.debit.blinkpay.co.nz
REACT_APP_BLINKPAY_CLIENT_ID=...
REACT_APP_BLINKPAY_CLIENT_SECRET=...
REACT_APP_BLINKPAY_RETRY_ENABLED=true
REACT_APP_BLINKPAY_TIMEOUT=10000
You may need to install react-app-rewired
and other dependencies to override the default webpack configuration and to disable path and fs which only work for Node.js environment
. Create a config-overrides.js
file:
module.exports = function override(config, env) {
config.resolve.fallback = {
...config.resolve.fallback,
"path": false,
"fs": false,
"os": require.resolve("os-browserify/browser"),
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"buffer": require.resolve("buffer/")
};
return config;
};
Create an Axios instance:
axiosInstance.js
import axios from 'axios';
const globalAxios = axios.create({
headers: {
'Accept': 'application/json'
}
});
export default globalAxios;
axiosInstance.ts
import axios, {AxiosInstance} from 'axios';
const globalAxios: AxiosInstance = axios.create({
headers: {
'Accept': 'application/json'
}
});
export default globalAxios;
Create the BlinkDebitClient instance:
blinkDebitClientInstance.js
import {BlinkDebitClient} from 'blink-debit-api-client-node';
import globalAxios from './axiosInstance';
const blinkPayConfig = {
blinkpay: {
debitUrl: process.env.REACT_APP_BLINKPAY_DEBIT_URL || '',
clientId: process.env.REACT_APP_BLINKPAY_CLIENT_ID || '',
clientSecret: process.env.REACT_APP_BLINKPAY_CLIENT_SECRET || '',
timeout: 10000,
retryEnabled: true
}
};
const client = new BlinkDebitClient(globalAxios, blinkPayConfig);
export default client;
blinkDebitClientInstance.ts
import {BlinkPayConfig, BlinkDebitClient} from 'blink-debit-api-client-node';
import globalAxios from './axiosInstance';
const blinkPayConfig: BlinkPayConfig = {
blinkpay: {
debitUrl: process.env.REACT_APP_BLINKPAY_DEBIT_URL || '',
clientId: process.env.REACT_APP_BLINKPAY_CLIENT_ID || '',
clientSecret: process.env.REACT_APP_BLINKPAY_CLIENT_SECRET || '',
timeout: 10000,
retryEnabled: true
}
};
export const client = new BlinkDebitClient(globalAxios, blinkPayConfig);
In your component, create a function for submitting a form:
cart.jsx
import React, {Component} from 'react';
import lollipop from '../lollipop.jpg';
import {
AmountCurrencyEnum,
AuthFlowDetailTypeEnum,
ConsentDetailTypeEnum
} from 'blink-debit-api-client-node';
import client from '../blinkDebitClientInstance';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {
errorResponse: {},
disabled: false
}
this.submitForm = this.submitForm.bind(this);
}
async submitForm(e) {
e.preventDefault();
this.setState({
errorResponse: {},
disabled: true
});
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: window.location.origin + "/redirect"
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: "0.40"
},
pcr: {
particulars: "lollipop",
code: "code",
reference: "reference"
}
};
const qpCreateResponse = await client.createQuickPayment(request);
// redirect to gateway
const redirectUri = qpCreateResponse.redirectUri;
if (redirectUri !== undefined) {
window.location.assign(redirectUri);
}
}
render() {
return (
<div className="container">
<h3>Shopping Cart</h3>
<form onSubmit={this.submitForm} className="form">
<table className="table">
<tbody>
<tr>
<td className="border">
<img src={lollipop} alt="lollipop" width="200"/>
</td>
<td className="border">
Red Heart Lollipop, unwrapped
</td>
<td className="border">
$0.40
</td>
</tr>
</tbody>
</table>
<br/>
<div className="form-group form-row align-items-center justify-content-center">
<button type="submit" className="btn btn-primary ml-1 mr-1"
disabled={this.state.disabled}>Checkout
</button>
</div>
</form>
</div>);
}
}
export default Cart;
cart.tsx
import React, {Component} from 'react';
import lollipop from '../lollipop.jpg';
import {
Amount,
AmountCurrencyEnum,
AuthFlow,
AuthFlowDetailTypeEnum,
ConsentDetailTypeEnum,
GatewayFlow,
Pcr,
QuickPaymentRequest
} from 'blink-debit-api-client-node';
import {client} from '../blinkDebitClientInstance';
interface State {
errorResponse: any,
disabled: boolean
}
class Cart extends Component<{}, State> {
constructor(props: {}) {
super(props);
this.state = {
errorResponse: {},
disabled: false
}
this.submitForm = this.submitForm.bind(this);
}
async submitForm(e: React.FormEvent) {
e.preventDefault();
this.setState({
errorResponse: {},
disabled: true
});
const request: QuickPaymentRequest = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: window.location.origin + "/redirect"
} as GatewayFlow
} as AuthFlow,
amount: {
currency: AmountCurrencyEnum.NZD,
total: "0.40"
} as Amount,
pcr: {
particulars: "lollipop",
code: "code",
reference: "reference"
} as Pcr
};
const qpCreateResponse = await client.createQuickPayment(request);
// redirect to gateway
const redirectUri = qpCreateResponse.redirectUri;
if (redirectUri !== undefined) {
window.location.assign(redirectUri);
}
}
render() {
return (
<div className="container">
<h3>Shopping Cart</h3>
<form onSubmit={this.submitForm} className="form">
<table className="table">
<tbody>
<tr>
<td className="border">
<img src={lollipop} alt="lollipop" width="200"/>
</td>
<td className="border">
Red Heart Lollipop, unwrapped
</td>
<td className="border">
$0.40
</td>
</tr>
</tbody>
</table>
<br/>
<div className="form-group form-row align-items-center justify-content-center">
<button type="submit" className="btn btn-primary ml-1 mr-1"
disabled={this.state.disabled}>Checkout
</button>
</div>
</form>
</div>);
}
}
export default Cart;
- Customise/supply the required properties in your
config.json
and.env
. This file should be available in your project folder. - The BlinkPay Sandbox debit URL is
https://sandbox.debit.blinkpay.co.nz
and the production debit URL ishttps://debit.blinkpay.co.nz
. - The client credentials will be provided to you by BlinkPay as part of your on-boarding process.
Warning Take care not to check in your client ID and secret to your source control.
Configuration will be detected and loaded according to the hierarchy -
.env
config.json
(Node.js environment
)- Default values
This file is NOT pushed to the repository.
BLINKPAY_DEBIT_URL=<BLINKPAY_DEBIT_URL>
BLINKPAY_CLIENT_ID=<BLINKPAY_CLIENT_ID>
BLINKPAY_CLIENT_SECRET=<BLINKPAY_CLIENT_SECRET>
BLINKPAY_TIMEOUT=10000
BLINKPAY_RETRY_ENABLED=true
Substitute the correct values in your config.json
file. Since this file can be pushed to your repository, make sure that the client secret is not included.
{
"blinkpay": {
"debitUrl": "${BLINKPAY_DEBIT_URL}",
"timeout": 10000,
"retryEnabled": true
}
}
In a Node.js environment
, if you've configured the .env
file locally or via CI/CD, you can just create the client with:
const client = new BlinkDebitClient(axios);
Another way is to pass the path to a JSON configuration file:
const directory = '/path/to/config/directory';
const fileName = 'my-config.json'
const client = new BlinkDebitClient(axios, directory, fileName);
In a browser environment
, the client can be created by passing the BlinkPayConfig:
JavaScript
const blinkPayConfig = {
blinkpay: {
debitUrl: process.env.REACT_APP_BLINKPAY_DEBIT_URL || '',
clientId: process.env.REACT_APP_BLINKPAY_CLIENT_ID || '',
clientSecret: process.env.REACT_APP_BLINKPAY_CLIENT_SECRET || '',
timeout: 10000,
retryEnabled: true
}
};
const client = new BlinkDebitClient(axios, blinkPayConfig);
TypeScript
const blinkPayConfig: BlinkPayConfig = {
blinkpay: {
debitUrl: process.env.REACT_APP_BLINKPAY_DEBIT_URL || '',
clientId: process.env.REACT_APP_BLINKPAY_CLIENT_ID || '',
clientSecret: process.env.REACT_APP_BLINKPAY_CLIENT_SECRET || '',
timeout: 10000,
retryEnabled: true
}
};
const client = new BlinkDebitClient(axios, blinkPayConfig);
or by providing the required parameters:
const client = new BlinkDebitClient(axios, process.env.REACT_APP_BLINKPAY_DEBIT_URL,
process.env.REACT_APP_BLINKPAY_CLIENT_ID, process.env.REACT_APP_BLINKPAY_CLIENT_SECRET);
An optional request ID, correlation ID and idempotency key can be added as arguments to API calls. They will be generated for you automatically if they are not provided.
A request can have one request ID and one idempotency key but multiple correlation IDs in case of retries.
Note: For error handling, a BlinkServiceException can be caught.
A quick payment is a one-off payment that combines the API calls needed for both the consent and the payment.
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: 'https://www.blinkpay.co.nz/sample-merchant-return-page'
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: '0.01'
},
pcr: {
particulars: 'particulars',
code: 'code',
reference: reference
}
};
const qpCreateResponse = await client.createQuickPayment(request);
_logger.LogInformation("Redirect URL: {}", qpCreateResponseredirectUri); // Redirect the consumer to this URL
const qpId = qpCreateResponse.quickPaymentId;
const qpResponse = await client.awaitSuccessfulQuickPaymentOrThrowException(qpId, 300); // Will throw an exception if the payment was not successful after 5min
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Redirect,
bank: Bank.BNZ,
redirectUri: 'https://www.blinkpay.co.nz/sample-merchant-return-page'
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: '0.01'
},
pcr: {
particulars: 'particulars'
}
};
const createConsentResponse = await client.createSingleConsent(request);
const redirectUri = createConsentResponse.redirectUri; // Redirect the consumer to this URL
const paymentRequest = new PaymentRequest
{
consentId = createConsentResponse.consentId
};
const paymentResponse = await client.createPayment(paymentRequest);
_logger.LogInformation("Payment Status: {}", await client.getPayment(paymentResponse.paymentId).status);
// TODO inspect the payment result status
Supplies the supported banks and supported flows on your account.
const bankMetadataList = await client.getMeta();
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createQuickPaymentResponse = await client.createQuickPayment(request);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri,
flowHint: {
type: FlowHintTypeEnum.Redirect,
bank: bank
}
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createQuickPaymentResponse = await client.createQuickPayment(request);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri,
flowHint: {
type: FlowHintTypeEnum.Decoupled,
bank: bank,
identifierType: identifierType,
identifierValue: identifierValue
}
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createQuickPaymentResponse = await client.createQuickPayment(request);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Redirect,
bank: bank,
redirectUri: redirectUri
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createQuickPaymentResponse = await client.createQuickPayment(request);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Decoupled,
bank: bank,
identifierType: identifierType,
identifierValue: identifierValue,
callbackUrl: callbackUrl
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createQuickPaymentResponse = await client.createQuickPayment(request);
const quickPaymentResponse = await client.getQuickPayment(quickPaymentId);
await client.revokeQuickPayment(quickPaymentId);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createConsentResponse = await client.createSingleConsent(request);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri,
flowHint: {
type: FlowHintTypeEnum.Redirect,
bank: Bank.PNZ
}
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createConsentResponse = await client.createSingleConsent(request);
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri,
flowHint: {
type: FlowHintTypeEnum.Decoupled,
bank: bank,
identifierType: identifierType,
identifierValue: identifierValue
}
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createConsentResponse = await client.createSingleConsent(request);
Suitable for most consents.
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Redirect,
bank: bank,
redirectUri: redirectUri
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createConsentResponse = await client.createSingleConsent(request);
This flow type allows better support for mobile by allowing the supply of a mobile number or previous consent ID to identify the customer with their bank.
The customer will receive the consent request directly to their online banking app. This flow does not send the user through a web redirect flow.
const request = {
type: ConsentDetailTypeEnum.Single,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Decoupled,
bank: bank,
identifierType: identifierType,
identifierValue: identifierValue,
callbackUrl: callbackUrl
}
},
amount: {
currency: AmountCurrencyEnum.NZD,
total: total
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
};
const createConsentResponse = await client.createSingleConsent(request);
Get the consent including its status
const consent = await client.getSingleConsent(consentId);
await client.revokeSingleConsent(consentId);
Request an ongoing authorisation from the customer to debit their account on a recurring basis.
Note that such an authorisation can be revoked by the customer in their mobile banking app.
const request = {
type: ConsentDetailTypeEnum.Enduring,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri
}
},
maximumAmountPeriod: {
currency: AmountCurrencyEnum.NZD,
total: total
},
fromTimestamp: startDate,
expiryTimestamp: endDate,
period: period
};
const createConsentResponse = await client.createEnduringConsent(request);
const request = {
type: ConsentDetailTypeEnum.Enduring,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri,
flowHint: {
type: FlowHintTypeEnum.Redirect,
bank: Bank.PNZ
}
}
},
maximumAmountPeriod: {
currency: AmountCurrencyEnum.NZD,
total: total
},
fromTimestamp: startDate,
expiryTimestamp: endDate,
period: period
};
const createConsentResponse = await client.createEnduringConsent(request);
const request = {
type: ConsentDetailTypeEnum.Enduring,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Gateway,
redirectUri: redirectUri,
flowHint: {
type: FlowHintTypeEnum.Decoupled,
bank: bank,
identifierType: identifierType,
identifierValue: identifierValue
}
}
},
maximumAmountPeriod: {
currency: AmountCurrencyEnum.NZD,
total: total
},
fromTimestamp: startDate,
expiryTimestamp: endDate,
period: period
};
const createConsentResponse = await client.createEnduringConsent(request);
const request = {
type: ConsentDetailTypeEnum.Enduring,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Redirect,
bank: bank,
redirectUri: redirectUri
}
},
maximumAmountPeriod: {
currency: AmountCurrencyEnum.NZD,
total: total
},
fromTimestamp: startDate,
expiryTimestamp: endDate,
period: period
};
const createConsentResponse = await client.createEnduringConsent(request);
const request = {
type: ConsentDetailTypeEnum.Enduring,
flow: {
detail: {
type: AuthFlowDetailTypeEnum.Decoupled,
bank: bank,
identifierType: identifierType,
identifierValue: identifierValue,
callbackUrl: callbackUrl
}
},
maximumAmountPeriod: {
currency: AmountCurrencyEnum.NZD,
total: total
},
fromTimestamp: startDate,
expiryTimestamp: endDate,
period: period
};
const createConsentResponse = await client.createEnduringConsent(request);
const consent = await client.getEnduringConsent(consentId);
await client.revokeEnduringConsent(consentId);
The completion of a payment requires a consent to be in the Authorised status.
const paymentRequest = {
consentId: consentId
};
const paymentResponse = await client.createPayment(request);
If you already have an approved consent, you can run a Payment against that consent at the frequency as authorised in the consent.
const paymentRequest = {
consentId: consentId,
enduringPayment: {
amount: {
total: total,
currency: AmountCurrencyEnum.NZD
},
pcr: {
particulars: particulars,
code: code,
reference: reference
}
}
};
const paymentResponse = await client.createPayment(request);
const payment = await client.getPayment(paymentId);
const refundRequest = {
type: RefundDetailTypeEnum.AccountNumber,
paymentId: paymentId
}
const refundResponse = await client.createRefund(request);
const refundRequest = {
type: RefundDetailTypeEnum.FullRefund,
paymentId: paymentId,
pcr: {
particulars: particulars,
code: code,
reference: reference
},
consentRedirect: redirectUri
}
const refundResponse = await client.createRefund(request);
const refundRequest = {
type: RefundDetailTypeEnum.PartialRefund,
paymentId: paymentId,
pcr: {
particulars: particulars,
code: code,
reference: reference
},
consentRedirect: redirectUri,
amount: {
total: total,
currency: AmountCurrencyEnum.NZD
}
}
const refundResponse = await client.createRefund(request);
const refund = await client.getRefund(refundId);