payOS Node.js Library

The payOS Node library provides convenient access to the payOS Merchant API from applications written in JavaScript or Typescript.
To learn how to use payOS Merchant API, checkout our API Reference and Documentation. We also have some examples in Examples.
Requirements
Node 20 or higher.
Installation
npm install @payos/node
[!IMPORTANT]
If update from v1, check Migration guide for detail migration.
Usage
Basic usage
First you need initialize the client to interacting with payOS Merchant API.
import { PayOS } from '@payos/node';
const { PayOS } = require('@payos/node');
const payos = new PayOS({
clientId: process.env.PAYOS_CLIENT_ID,
apiKey: process.env.PAYOS_API_KEY,
checksumKey: process.env.PAYOS_CHECKSUM_KEY,
});
Then you can interact with payOS Merchant API, example create a payment link using paymentRequests.create().
const paymentLink = await payos.paymentRequests.create({
orderCode: 123,
amount: 2000,
description: 'payment',
returnUrl: 'https://your-url.com',
cancelUrl: 'https://your-url.com',
});
Webhook verification
You can register an endpoint to receive the payment webhook.
const confirmResult = await payos.webhooks.confirm('https://your-url.com/payos-webhook');
Then using webhooks.verify() to verify and receive webhook data.
const webhookData = await payos.webhooks.verify({
code: '00',
desc: 'success',
success: true,
data: {
orderCode: 123,
amount: 3000,
description: 'VQRIO123',
accountNumber: '12345678',
reference: 'TF230204212323',
transactionDateTime: '2023-02-04 18:25:00',
currency: 'VND',
paymentLinkId: '124c33293c43417ab7879e14c8d9eb18',
code: '00',
desc: 'Thành công',
counterAccountBankId: '',
counterAccountBankName: '',
counterAccountName: '',
counterAccountNumber: '',
virtualAccountName: '',
virtualAccountNumber: '',
},
signature: '8d8640d802576397a1ce45ebda7f835055768ac7ad2e0bfb77f9b8f12cca4c7f',
});
For more information about webhooks, see the API doc.
Handling errors
When the API return a non-success status code (i.e, 4xx or 5xx response) or non-success code data (any code except '00'), a class APIError or its subclass will be thrown:
payos
.get({
path: '/not-found',
})
.catch((err) => {
if (err instanceof APIError) {
console.log(err.name);
console.log(err.message);
console.log(err.status);
console.log(err.headers);
console.log(err.code);
console.log(err.desc);
} else {
throw err;
}
});
List method in the payOS Merchant API are paginated, You can use the for await ... of syntax to iterate though items across all pages:
const allPayouts = [];
const payoutPage = await payos.payouts.list({ limit: 3 });
for await (const payout of payoutPage) {
allPayouts.push(payout);
}
console.log(allPayouts);
const payouts = await payoutPage.toArray();
console.log(payouts);
Or you can request single page at a time:
let page = await payos.payouts.list({
limit: 3,
});
for (const payout of page.data) {
console.log(payout);
}
while (page.hasNextPage()) {
page = await page.getNextPage();
}
Advanced usage
Custom configuration
You can customize the PayOS client with various options:
const payos = new PayOS({
clientId: process.env.PAYOS_CLIENT_ID,
apiKey: process.env.PAYOS_API_KEY,
checksumKey: process.env.PAYOS_CHECKSUM_KEY,
partnerCode: process.env.PAYOS_PARTNER_CODE,
baseURL: 'https://api-merchant.payos.vn',
timeout: 30000,
maxRetries: 3,
logLevel: 'info',
logger: console,
fetchOptions: {
headers: {
'Custom-Header': 'value',
},
},
});
Custom fetch implementation
You can provide a custom fetch implementation:
import fetch from 'node-fetch';
const payos = new PayOS({
clientId: process.env.PAYOS_CLIENT_ID,
apiKey: process.env.PAYOS_API_KEY,
checksumKey: process.env.PAYOS_CHECKSUM_KEY,
fetch: fetch as any,
});
Request-level options
You can override client-level settings for individual requests:
const paymentLink = await payos.paymentRequests.create(
{
orderCode: 123,
amount: 2000,
description: 'payment',
returnUrl: 'https://your-url.com',
cancelUrl: 'https://your-url.com',
},
{
maxRetries: 5,
timeout: 10000,
signal: abortController.signal,
},
);
Logging and debugging
The log level can be configured in two ways:
- Via the
PAYOS_LOG environment variable.
- Using the
logLevel client option (override the environment if set).
By default, this library logs to globalThis.console. You can also provide a custom logger. If your logger doesn't work, please open an issue.
import { createLogger } from 'winston';
const payos = new PayOS({
clientId: process.env.PAYOS_CLIENT_ID,
apiKey: process.env.PAYOS_API_KEY,
checksumKey: process.env.PAYOS_CHECKSUM_KEY,
logLevel: 'debug',
logger: createLogger({
level: 'debug',
transports: [new transports.Console()],
}),
});
Direct API access
For advanced use cases, you can make direct API calls:
const response = await payos.get('/v2/payment-requests');
const response = await payos.post('/v2/payment-requests', {
body: {
orderCode: 123,
amount: 2000,
description: 'payment',
returnUrl: 'https://your-url.com',
cancelUrl: 'https://your-url.com',
},
});
const response = await payos.request({
method: 'POST',
path: '/v2/payment-requests',
body: requestData,
maxRetries: 3,
timeout: 15000,
});
Signature
The signature can be manually created by PayOS.crypto:
const signature = await payos.crypto.createSignatureOfPaymentRequest(data, payos.checksumKey);
const signature = await payos.crypto.createSignatureFromObj(
{ amount, cancelUrl, description, orderCode, returnUrl },
payos.checksumKey,
);
const signature = await payos.crypto.createSignatureFromObj(data, payos.checksumKey);
const signature = await payos.crypto.createSignature(payos.checksumKey, data);
Contributing
See the contributing documentation.