This repository provides a native NodeJS SDK, which enables to integrate your solution with myPOS APIs such as Checkout, Transactions, Devices, Webhooks and more to come.
This is the Hack.bg maintained fork of developermypos/mypos-js, which has not been updated since 2020. See CHANGELOG.md for what changed.
Requires Node.js 18.17 or later.
npm i github:hackbg/mypos-js#v0.1.0
Install a release tag rather than the default branch. npm records the tag's
commit in package-lock.json, so installs stay reproducible. To accept
compatible fixes on npm update, use a range instead:
github:hackbg/mypos-js#semver:^0.1.0.
This package follows Semantic Versioning. Every release
is a vX.Y.Z git tag with a GitHub release, and is listed in
CHANGELOG.md.
While the version is 0.x, a minor bump (0.1 to 0.2) may break the API and
a patch bump (0.1.0 to 0.1.1) never does. npm's caret ranges already treat
0.x this way: ^0.1.0 accepts 0.1.x but not 0.2.0. From 1.0.0 on, breaking
changes need a major bump.
Releasing:
- Move the
Unreleasedentries in CHANGELOG.md under the new version and date. - Bump
versionin package.json (npm version <patch|minor|major> --no-git-tag-version). - Merge to
master, then tag the merge commit and publish the release:git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z, thengh release create vX.Y.Z --notes-from-tag(or paste the changelog section).
TypeScript declarations are included.
Before calling any of the Checkout API methods, you need to initialize the SDK.
const mypos = require('@hackbg/mypos-js')({
isSandbox: true,
logLevel: 'info', // debug, info, error or none. Debug logs full requests, customer data included.
checkout: { // Checkout API specific configuration
sid: '', // Stored ID
lang: 'EN', // Preferred language
currency: '', // Store currency
clientNumber: '', // Available in the myPOS Account
okUrl: '', // Redirect URL on successful operation
cancelUrl: '', // Redirect URL on cancelled operation
notifyUrl: '', // Callback URL to be notified on operation result (HTTPS, no port)
cardTokenRequest: 0, // View details at https://developers.mypos.eu/en/doc/online_payments/v1_4/21-purchase-with-payment-card-(api-call--ipcpurchase)
paymentMethod: 1, // View details at https://developers.mypos.eu/en/doc/online_payments/v1_4/21-purchase-with-payment-card-(api-call--ipcpurchase)
paymentParametersRequired: 3, // https://developers.mypos.eu/en/doc/online_payments/v1_4/21-purchase-with-payment-card-(api-call--ipcpurchase)
keyIndex: 1, // Key index for the particular store
privateKey: '-----BEGIN RSA PRIVATE KEY-----\n' + // The private key for the particular store in PEM format
'...\n' +
'...\n' +
'...\n' +
'-----END RSA PRIVATE KEY-----',
publicCertificate: '-----BEGIN CERTIFICATE-----\n...', // myPOS public certificate, for verifySignature
}
});These return data instead of HTML, and are the recommended way to use the Checkout API.
// The signed IPCPurchase fields and the URL to POST them to. Render them as a
// form in your own page and submit it, keeping the fields in the given order.
const { action, method, fields } = mypos.checkout.purchaseForm(purchaseParams);
// Server-to-server, resolving with myPOS's parsed JSON reply.
const status = await mypos.checkout.paymentStatus({ orderId }); // status.PaymentStatus: 1 paid, 2 pending, 3 failed, 4 reversed
const refund = await mypos.checkout.refundPayment({ trnRef, amount: 9.99 });
const reversal = await mypos.checkout.reversePayment({ trnRef });
// IPCPurchaseNotify / IPCPurchaseOK: check the signature against the myPOS
// public certificate. Pass the parsed form body; field order is preserved.
app.post('/notify', express.urlencoded({ extended: false }), (req, res) => {
if (!mypos.checkout.verifySignature(req.body) || req.body.IPCmethod !== 'IPCPurchaseNotify') {
return res.status(400).send('Invalid notification');
}
// Look the order up by OrderID and compare Amount and Currency with what
// you charged, then mark it paid. Do nothing if it is already paid.
res.type('text/plain').send('OK');
});Deciding whether an order was paid. Check that PaymentStatus is 1 and that OrderID, Amount and Currency match your own record of the order, and verify the reply's signature (paymentStatus({ orderId }, { verifySignature: true }), or verifySignature(reply)). Do not rely on StatusMsg === 'Success' or on Status. Observed in production (2026-10-30):
| Order | Status / StatusMsg |
PaymentStatus |
|---|---|---|
| paid | 0 / "Success" |
1, with a PaymentReference |
| handed to the hosted page, never paid | 0 / "Success" |
2, empty PaymentReference |
| unknown to myPOS | 3 / "No data found" |
absent |
So StatusMsg says "Success" for an order that was never paid. Production sends numbers (PaymentStatus: 1, Amount: 35) where the sandbox sends strings ("1", "35.00"): compare amounts in cents, not as strings.
The sandbox reports every order as paid. In the test environment IPCGetPaymentStatus answers PaymentStatus: "1", StatusMsg: "Success" with a valid myPOS signature and the requested OrderID echoed back, for any order: one myPOS has never seen, a checkout that was handed off and abandoned, and a payment the hosted page itself declared unsuccessful. The Amount is a random whole number (observed 1.00 to 19.00 EUR), different on every call. So in the sandbox:
- signature,
OrderID,CurrencyandPaymentStatuschecks all pass for an unpaid order; - only the amount check catches it, and only probabilistically: anyone who can make your server repeat the status query can retry until the random amount matches a small order;
- a sandbox status reply is therefore never evidence of payment. Do not let anything that settles orders trust it while
isSandboxis on, and do not use the sandbox to test your "not paid" path through this call.
(Observed 2026-10-30 with the public test store.) The signed IPCPurchaseNotify callback to URL_Notify is the stronger settlement signal: myPOS sends it for completed payments, and a payment fails if that URL does not answer OK.
What verifySignature proves. myPOS signs the field values only, in order, joined with -. Field names and the message type are not signed, so a valid signature proves that myPOS produced this sequence of values at some point, nothing more. In particular:
- the customer's browser receives a signed
IPCPurchaseOKfor their own order and can replay it, including to yourURL_Notifyhandler. CheckIPCmethodis the message you expect on that endpoint; - check
OrderID,AmountandCurrencyagainst your own record of the order, never trust them from the message alone; - treat settlement as idempotent: a second valid notification for an order that is already paid must change nothing.
Logging. logLevel: 'debug' logs full request fields (customer names, emails, addresses) and full myPOS replies. Use it in development only. Auth headers, OAuth tokens and API keys are never logged at any level.
Each server-to-server call rejects on a network error, a timeout (15 s by default, { timeoutMs } to change) or a reply that is not JSON. A reply with a non-zero Status resolves: check it.
The callback-style methods below still work. Note that purchase renders a page that submits itself from an inline onload handler, which a Content Security Policy without 'unsafe-inline' blocks, and that refund, reversal and getPaymentStatus only return a signed HTML form: they do not contact myPOS.
const purchaseParams = {
orderId: uuidv4(), // A unique reference
amount: 23.45,
cartItems: [
{
name: 'HP ProBook 6360b sticker',
quantity: 2,
price: 10.00
},
{
name: 'Delivery',
quantity: 1,
price: 3.45
}
],
customer: {
email: 'name@website.com',
firstNames: 'John',
familyName: 'Smith',
phone: '+23568956958',
country: 'DEU',
city: 'Hamburg',
zipCode: '20095',
address: 'Kleine Bahnstr. 41'
},
note: 'Some note'
};Note: The customer object can contain only the customer's email and names (first and family), however the rest of the information can be required during Checkout.
Now that you have collected the purchase data, call the "purchase" method from the SDK and provide the purchase parameters and the response object. MyPOS will handle the redirect to the Online Checkout page after validating the provided parameters.
app.post('/purchase', (req, res) => {
mypos.checkout.purchase(purchaseParams, res);
});const refundParams = {
orderId: uuidv4(), // A unique reference
amount: 9.99,
trnRef: "MyTransactionReference"
};Now that you have collected the refund data, call the "refund" method from the SDK and provide the refund parameters.
app.post('/refund', (req, res) => {
mypos.checkout.refund(refundParams, (result) => {
res.send(result);
});
});const reversalParams = {
trnRef: "MyTransactionReference"
};Now that you have collected the reversal data, call the "reversal" method from the SDK and provide the reversal parameters.
app.post('/reversal', (req, res) => {
mypos.checkout.reversal(reversalParams, (result) => {
res.send(result);
});
});In order to check the status of a payment, you need to provide myPOS with the order ID.
const paymentParams = {
orderId: "MyOrderID"
};Now that you have collected the request data, call the "getPaymentStatus" method from the SDK and provide the required parameters.
app.post('/getPaymentStatus', (req, res) => {
mypos.checkout.getPaymentStatus(paymentParams, (result) => {
res.send(result);
});
});The below APIs use a different type of authentication and require a much simpler way of initializing the SDK.
const mypos = require('@hackbg/mypos-js')({
isSandbox: true, // Whether to use the Sandbox environment
apiKey: '', // Generated via the myPOS Account
apiSecret: '', // Generated via the myPOS Account
logLevel: 'info', // Logging level
});app.get('/transactions', (req, res) => {
mypos.transactions.list(req.query, (result) => {
res.send(result);
});
});app.get('/transactionDetails', (req, res) => {
mypos.transactions.get('POSA01720049UZNE', (result) => {
res.send(result);
});
});app.get('/devices', (req, res) => {
mypos.devices.list({
page: 1,
count: 5
}, (result) => {
res.send(result);
});
});app.get('/deviceDetails', (req, res) => {
mypos.devices.get('90005195', (result) => {
res.send(result);
});
});app.get('/deviceTransactions', (req, res) => {
mypos.devices.transactions.get('90004284', {},(result) => {
res.send(result);
});
});app.get('/deviceTransactionsAll', (req, res) => {
mypos.devices.transactions.list({}, (result) => {
res.send(result);
});
});app.get('/webhooks', (req, res) => {
mypos.webhooks.list(req.query, (result) => {
res.send(result);
});
});app.get('/webhooks/details', (req, res) => {
mypos.webhooks.details('d3fca256-ebbc-42d8-9195-d7f0784d546a' /* Webhook ID */, (result) => {
res.send(result);
});
});app.get('/webhooks/create', (req, res) => {
mypos.webhooks.create(
'https://example.com', // Payload URL
'my-secret', // Secret
(result) => {
res.send(result);
});
});app.get('/webhooks/update', (req, res) => {
mypos.webhooks.update(
'd3fca256-ebbc-42d8-9195-d7f0784d546a', // Webhook ID
'https://example.com', // Payload URL
'my-secret', // Secret
0, // Active flag
(result) => {
res.send(result);
});
});app.get('/webhooks/delete', (req, res) => {
mypos.webhooks.delete(
'd3fca256-ebbc-42d8-9195-d7f0784d546a', // Webhook ID
(result) => {
res.send(result);
});
});app.get('/webhook-events', (req, res) => {
mypos.webhooks.events.list(req.query, (result) => {
res.send(result);
});
});app.get('/webhooks/subscribe', (req, res) => {
mypos.webhooks.subscriptions.create(
'd3fca256-ebbc-42d8-9195-d7f0784d546a', // Webhook ID
'0c2ac3dd-c62b-404b-ac7f-ac805ea268ff', // Event ID
(result) => {
res.send(result);
});
});app.get('/webhook-subscriptions', (req, res) => {
mypos.webhooks.subscriptions.list(
req.query,
(result) => {
res.send(result);
});
});app.get('/webhook-subscriptions/details', (req, res) => {
mypos.webhooks.subscriptions.details(
'2b5b37ae-c88a-4563-936d-36d6e8280912', // Subscription ID
(result) => {
res.send(result);
});
});app.get('/webhooks/unsubscribe', (req, res) => {
mypos.webhooks.subscriptions.delete(
'18912b44-5c37-45ea-b933-103ff813cb71', // Subscription ID
(result) => {
res.send(result);
});
});