mypos-online-checkout
v0.1.1
Published
TypeScript SDK for the myPOS Online Checkout API — generate signed payment forms and validate webhooks, with optional React helpers.
Maintainers
Readme
myPOS Online Checkout
A TypeScript SDK for the myPOS Online Checkout API, designed for server-side use in Node.js applications.
It handles RSA-SHA256 request signing, webhook signature verification, and provides typed wrappers for all IPC methods — so you can integrate myPOS payments without touching raw HTTP or cryptography directly.
Features
- Purchase — generate signed form fields for the myPOS hosted checkout page
- Webhook validation — verify the RSA-SHA256 signature on incoming
IPCPurchaseNotifycallbacks - Store a Card — tokenize a card via the hosted checkout page for future charges
- Charge Stored Card — charge a stored card token directly, no customer redirect required (requires myPOS enablement)
- Payment Status — query the outcome of any order by its Order ID
- Refund — issue full or partial refunds by transaction reference
- Reversal — void a transaction before it settles
- Pre-Authorization — hold funds on a card without charging, then capture or cancel later
- Authorization (stored card) — reserve funds using a card token, capture or reverse on demand
- Transfer of Funds — mandate management, send money, and request money between myPOS accounts
- Optional React helpers — a
<PaymentForm>component andusePaymentFormhook for rendering signed checkout forms - Dual ESM/CJS build — works with
importandrequire, ships full TypeScript types
Requirements
- Node.js ≥ 18
- A myPOS merchant account with Online Checkout enabled
- The configuration pack (base64 string) or individual credentials from the myPOS merchant portal
Server-only. All methods that sign requests or call the myPOS API use your private key and must only run in server-side code. Never expose credentials to the browser.
Installation
npm install mypos-online-checkoutSetup
1. Get your credentials
Log in to the myPOS merchant portal and download your configuration pack — a single base64-encoded string that contains your Store ID, Wallet Number, RSA private key, public certificate, and Key Index. Store it as a server-side environment variable.
MYPOS_CONFIG_PACK=eyJzaWQiOiI... # base64 config pack from the portal
MYPOS_TEST_MODE=true # set false in production2. Initialize the client
import { MyPOSClient } from 'mypos-online-checkout';
const client = new MyPOSClient({
configPack: process.env.MYPOS_CONFIG_PACK!,
testMode: process.env.MYPOS_TEST_MODE === 'true',
});Alternatively, supply credentials individually if you prefer to store them as separate environment variables:
const client = new MyPOSClient({
storeId: process.env.MYPOS_STORE_ID!,
walletNumber: process.env.MYPOS_WALLET_NUMBER!,
privateKey: process.env.MYPOS_PRIVATE_KEY!,
publicCertificate: process.env.MYPOS_PUBLIC_CERT!,
keyIndex: 1,
testMode: true,
});Instantiate the client once and reuse it across requests (e.g. as a module-level singleton or via a singleton pattern in your framework).
Methods
Purchase
The myPOS purchase flow works differently from a typical API call — the payment is initiated by submitting an HTML form directly to the myPOS hosted checkout page from the browser. Your server generates and signs the form fields; the browser submits them.
1. Generate signed form fields (server-side)
const fields = client.generatePurchaseFields(
{
items: [
{
article: 'Blue Widget',
quantity: 2,
price: 14.99,
amount: 29.98,
},
],
},
`ORDER-${Date.now()}`, // unique Order ID
{
currency: 'EUR',
urlOk: 'https://mystore.com/checkout/success',
urlCancel: 'https://mystore.com/checkout/cancel',
urlNotify: 'https://mystore.com/api/mypos/webhook',
}
);
// fields is a plain Record<string, string> — safe to serialise across the server/client boundary2. Render the form (client-side, with React helper)
import { PaymentForm } from 'mypos-online-checkout/react';
// Pass fields and checkoutUrl as props from the server component
<PaymentForm
fields={fields}
checkoutUrl={mypos.checkoutUrl}
submitLabel="Pay Now"
/>Or without the React helper:
<form method="POST" action="https://www.mypos.com/vmp/checkout-test/">
<!-- render one hidden input per field -->
<button type="submit">Pay Now</button>
</form>3. Validate the webhook (server-side)
myPOS POSTs a signed IPCPurchaseNotify callback to urlNotify after the customer pays.
// Next.js App Router route handler
import { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
const formData = await req.formData();
const result = await mypos.validateWebhook(formData);
if (!result.valid) {
return new Response('Invalid signature', { status: 400 });
}
const { orderId, ipcTrnref, amount, currency, paymentStatus } = result;
// paymentStatus === 1 means successful — fulfill the order
await fulfillOrder(orderId, ipcTrnref);
return new Response('OK');
}Key points:
OrderIDmust be unique per paid order. myPOS rejects duplicate Order IDs after a successful payment.urlNotifymust be an HTTPS URL with no port number.- Always validate the webhook signature before trusting the payload.
Store a Card
Tokenize a customer's card for future charges by running a standard IPCPurchase with cardTokenRequest: 2. The customer completes a small verification charge on the myPOS hosted checkout page; the reusable card token is returned in the IPCPurchaseNotify webhook.
// Server-side: generate signed fields
const fields = client.generatePurchaseFields(
{ items: [{ article: 'Card storage', quantity: 1, price: 0.50, amount: 0.50 }] },
`STORE-${Date.now()}`,
{
currency: 'EUR',
cardTokenRequest: 2, // store a reusable card token
urlOk: 'https://mystore.com/store-card/success',
urlCancel: 'https://mystore.com/store-card/cancel',
urlNotify: 'https://mystore.com/api/mypos/webhook',
}
);
// In your webhook handler — result.cardToken is the reusable token
const result = client.validateWebhook(formData);
if (result.isValid && result.cardToken) {
await db.saveCardToken(userId, result.cardToken, result.pan);
}The card token is sensitive — store it server-side and never expose it to the browser. A minimum charge of €0.50 is required to verify the card.
Charge Stored Card
Charge a stored card token directly without redirecting the customer to the hosted checkout page (IPCIAPurchase). Uses the same cart structure as generatePurchaseFields().
This method must be explicitly enabled by myPOS for your merchant account. Contact myPOS support to request access before using it in production.
const result = await client.iaPurchase({
cardToken: 'TOKEN_FROM_STORE_CARD_WEBHOOK',
orderId: `IA-${Date.now()}`,
cart: {
items: [{ article: 'Monthly subscription', quantity: 1, price: 29.99, amount: 29.99 }],
},
currency: 'EUR',
});
if (result.success) {
console.log('Charged:', result.ipcTrnref, result.amount, result.currency);
} else {
console.error(`[${result.status}] ${result.statusMsg}`);
}Use a unique
orderIdper charge — different from the order that originally stored the card. The currency must match the currency of the original purchase that produced the token.
Payment Status
Query the current status of an order by its Order ID.
const result = await client.getPaymentStatus({
orderId: 'ORDER-1234567890',
includeDeclinedPayments: false, // set true to get individual decline reason codes
});
if (result.success && result.paymentStatus === 1) {
// paymentStatus: 1=Successful, 2=Pending, 3=Unsuccessful, 4=Reversed
console.log('Trnref:', result.ipcTrnref);
console.log('Amount:', result.amount, result.currency);
}Refund
Issue a full or partial refund using the transaction reference from the purchase webhook.
const result = await client.refund({
ipcTrnref: '12345678923', // from IPCPurchaseNotify webhook
amount: 9.99,
currency: 'EUR',
orderId: 'REF-ORDER-001', // optional — use a distinct ID from the original
});
if (result.success) {
console.log('Refund confirmed:', result.ipcTrnref);
}Partial refunds are supported — pass any amount up to the original transaction total. The currency must match the original transaction currency.
Reversal
Void a transaction in full before it settles at the network level.
const result = await client.reversal({
ipcTrnref: '12345678923', // from IPCPurchaseNotify webhook
});
if (result.success) {
console.log('Reversed:', result.ipcTrnref);
}A reversal voids the transaction entirely. If the transaction has already settled, use Refund instead.
Pre-Authorization
Pre-authorization is a two-stage flow: first hold the funds on the customer's card (via the hosted page), then either capture or void the hold later.
Pre-Authorize (hold funds via hosted page)
Works like Purchase — generate signed fields server-side, submit the form from the browser.
const fields = client.generatePreAuthFields(
{
items: [{ article: 'Hotel Stay', quantity: 1, price: 200, amount: 200 }],
},
`PREAUTH-${Date.now()}`,
{
currency: 'EUR',
urlOk: 'https://mystore.com/pre-auth/success',
urlCancel: 'https://mystore.com/pre-auth/cancel',
urlNotify: 'https://mystore.com/api/mypos/webhook',
}
);Store the orderId from the urlNotify webhook — it is required for Complete and Cancel.
Complete (capture held funds)
const result = await client.completePreAuth({
orderId: 'PREAUTH-1234567890',
amount: 150.00, // can be less than or equal to the pre-authorized amount
currency: 'EUR',
});Cancel (void the hold)
const result = await client.cancelPreAuth({
orderId: 'PREAUTH-1234567890',
});Status
const result = await client.getPreAuthStatus({
orderId: 'PREAUTH-1234567890',
});
// result.status: 1=Pending, 3=Captured, 4=Declined, 5=Reversed, 6=Cancelled
// result.expiryDate: ISO date string — act before the hold expiresAuthorization (stored card)
Use these methods to reserve and settle funds against a card token obtained from a prior purchase.
To receive a card token, include cardTokenRequest: 1 (one-time token) or cardTokenRequest: 2 (reusable token) in generatePurchaseFields(). The token is delivered in the IPCPurchaseNotify webhook.
Authorize
const result = await client.authorize({
orderId: `AUTH-${Date.now()}`,
amount: 49.99,
currency: 'EUR',
cardToken: 'YOUR_CARD_TOKEN',
itemName: 'Subscription renewal',
});
// Store result.orderId — needed for Capture or ReverseCapture
const result = await client.captureAuthorization({
orderId: 'AUTH-1234567890',
amount: 49.99,
currency: 'EUR',
});Reverse
const result = await client.reverseAuthorization({
orderId: 'AUTH-1234567890',
});List pending authorizations
const result = await client.listAuthorizations();
if (result.success && result.authorizations) {
for (const auth of result.authorizations) {
console.log(auth.orderId, auth.amount, auth.currency);
}
}Transfer of Funds
Note: These methods require additional enablement or a signed contract with myPOS. Contact [email protected] for access.
Mandate Management
Register or cancel a direct debit mandate against a customer's myPOS account. A registered mandate is required before calling Request Money.
// Register
await client.mandateManagement({
mandateReference: 'MANDATE-001',
customerWalletNumber: '12345678901',
action: 1, // 1=Register, 2=Cancel
mandateText: 'Monthly subscription', // optional
});
// Cancel
await client.mandateManagement({
mandateReference: 'MANDATE-001',
customerWalletNumber: '12345678901',
action: 2,
});Send Money
Transfer funds from your myPOS merchant account to another myPOS account.
const result = await client.sendMoney({
customerWalletNumber: '12345678901',
amount: 50.00,
currency: 'EUR',
transactionReference: `TRF-${Date.now()}`,
reason: 'Invoice #12345',
});Request Money
Pull funds from a customer's myPOS account using a pre-registered mandate.
const result = await client.requestMoney({
mandateReference: 'MANDATE-001',
customerWalletNumber: '12345678901',
orderId: `REQ-${Date.now()}`,
amount: 99.99,
currency: 'EUR',
reason: 'Invoice #12345',
});Configuration Reference
MyPOSConfig
Two forms are accepted — provide either configPack or individual credential fields. The two forms are mutually exclusive.
| Option | Type | Description |
| --------------------- | ----------- | ------------------------------------------------------------------------ |
| configPack | string | Base64-encoded config pack from the myPOS merchant portal (recommended) |
| storeId | string | Store ID (sid) — alternative to configPack |
| walletNumber | string | Wallet number (cn) — alternative to configPack |
| privateKey | string | PEM-encoded RSA private key — alternative to configPack |
| publicCertificate | string | PEM-encoded public certificate — alternative to configPack |
| keyIndex | number | Key index (idx) — alternative to configPack |
| testMode | boolean | Use the sandbox environment. Default:false |
| source | string | Sent as the Source field on every request. Default: 'NodeJS SDK' |
| partnerId | string | myPOS Partners Program partner ID — enables IPC version 1.4.1 |
| applicationId | string | myPOS Partners Program application ID |
Supported languages
The language field in PurchaseConfig and PreAuthConfig accepts: EN, FR, IT, DE, SV, PT, NL, EL, BG, ES.
Error handling
The SDK throws typed errors for configuration and signing failures:
import { MyPOSConfigError, MyPOSSignatureError, MyPOSCartError } from 'mypos-online-checkout';
try {
const fields = mypos.generatePurchaseFields(cart, orderId, config);
} catch (err) {
if (err instanceof MyPOSCartError) { /* empty cart or invalid item */ }
if (err instanceof MyPOSSignatureError) { /* private key is malformed */ }
if (err instanceof MyPOSConfigError) { /* config pack could not be decoded */ }
}For API calls (refund, getPaymentStatus, etc.), errors from the myPOS gateway are returned as result.success === false with a status code and statusMsg string rather than thrown exceptions.
React helper
The optional mypos-online-checkout/react sub-package exports a ready-made form component and hook. React 18+ is required as a peer dependency.
import { PaymentForm } from 'mypos-online-checkout/react';
// Render a signed payment form — fields come from generatePurchaseFields() on the server
<PaymentForm
fields={fields}
checkoutUrl={mypos.checkoutUrl}
submitLabel="Pay Now"
/>Next.js usage guide
Project structure
app/
checkout/
page.tsx ← Server Component: generates signed fields
CheckoutForm.tsx ← Client Component ('use client'): renders <PaymentForm>
api/
mypos/
webhook/
route.ts ← Route Handler: validates IPCPurchaseNotifyServer Component (app/checkout/page.tsx)
import { mypos } from '@/lib/mypos';
export default async function CheckoutPage() {
const fields = mypos.generatePurchaseFields(
{ items: [{ article: 'Product', quantity: 1, price: 9.99, amount: 9.99 }] },
`ORDER-${Date.now()}`,
{
currency: 'EUR',
urlOk: `${process.env.NEXT_PUBLIC_BASE_URL}/checkout/success`,
urlCancel: `${process.env.NEXT_PUBLIC_BASE_URL}/checkout/cancel`,
urlNotify: `${process.env.NEXT_PUBLIC_BASE_URL}/api/mypos/webhook`,
}
);
return <CheckoutForm fields={fields} checkoutUrl={mypos.checkoutUrl} />;
}Client Component (app/checkout/CheckoutForm.tsx)
'use client';
import { PaymentForm } from 'mypos-online-checkout/react';
import type { PaymentFormFields } from 'mypos-online-checkout';
export function CheckoutForm({
fields,
checkoutUrl,
}: {
fields: PaymentFormFields;
checkoutUrl: string;
}) {
return <PaymentForm fields={fields} checkoutUrl={checkoutUrl} submitLabel="Pay Now" />;
}Webhook route (app/api/mypos/webhook/route.ts)
import { NextRequest } from 'next/server';
import { mypos } from '@/lib/mypos';
export async function POST(req: NextRequest) {
const formData = await req.formData();
const result = await mypos.validateWebhook(formData);
if (!result.valid || result.paymentStatus !== 1) {
return new Response('Ignored', { status: 200 });
}
await fulfillOrder(result.orderId, result.ipcTrnref);
return new Response('OK');
}Shared client singleton (lib/mypos.ts)
import { MyPOSClient } from 'mypos-online-checkout';
export const mypos = new MyPOSClient({
configPack: process.env.MYPOS_CONFIG_PACK!,
testMode: process.env.MYPOS_TEST_MODE === 'true',
});API reference
Full IPC method documentation is available on the myPOS developer portal:
- Purchase / Store a Card
- Purchase Notify (webhook)
- Charge Stored Card (IPCIAPurchase)
- Payment Status
- Refund
- Reversal
- Pre-Authorization
- Authorization
- Transfer of Funds
License
MIT
