npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uqpay/sdk

v0.2.0

Published

Official Node.js SDK for the UQPay API

Readme

UQPay Node.js SDK

Official Node.js SDK for the UQPay API.

Requirements

  • Node.js 20+
  • A UQPay account with API credentials

Installation

npm install @uqpay/sdk

Quick Start

import { UQPayClient } from '@uqpay/sdk'

const client = new UQPayClient({
  clientId: 'your-client-id',
  apiKey: 'your-api-key',
  environment: 'sandbox', // or 'production'
})

// Retrieve an account
const account = await client.account.accounts.retrieve('acc-123')
console.log(account.status) // 'ACTIVE'

Configuration

const client = new UQPayClient({
  clientId: 'your-client-id',   // required
  apiKey: 'your-api-key',        // required
  environment: 'sandbox',        // 'sandbox' | 'production' — default: 'sandbox'
  webhookSecret: 'whsec_...',    // required for webhook verification
  timeout: 30_000,               // request timeout in ms — default: 30000
  maxRetries: 2,                 // automatic retry count — default: 2
  logLevel: 'info',              // 'none' | 'info' | 'debug' — default: 'none'
})

Authentication

The SDK handles authentication automatically. It exchanges your clientId and apiKey for a token, caches it, and refreshes it before expiry. You do not need to manage tokens manually.

Resources

Account

// Retrieve a single account
const account = await client.account.accounts.retrieve('acc-123')

// List accounts (paginated)
const page = await client.account.accounts.list({ page_number: 1, page_size: 20 })

// Create a sub-account
const subAccount = await client.account.subAccounts.create({
  business_type: 'BANKING',
  entity_type: 'COMPANY',
  nickname: 'My Sub-account',
  // ...
})

// Get required additional documents for a country
const docs = await client.account.additionalDocs.get({
  country: 'SG',
  business_code: 'BANKING',
})

Banking

// Balances
const balances = await client.banking.balances.list({ page_number: 1, page_size: 20 })
const transactions = await client.banking.balances.listTransactions({ page_number: 1, page_size: 20 })

// Deposits
const deposits = await client.banking.deposits.list({ page_number: 1, page_size: 20 })

// Transfers
const transfer = await client.banking.transfers.create({
  source_account_id: 'acc-123',
  destination_account_id: 'acc-456',
  currency: 'SGD',
  amount: 100,
})
const transfers = await client.banking.transfers.list({ page_number: 1, page_size: 20 })

// Payouts
const payout = await client.banking.payouts.create({
  beneficiary_id: 'ben-123',
  currency: 'SGD',
  amount: 50,
  purpose_code: 'PERSONAL',
  // ...
})

// Beneficiaries
const beneficiary = await client.banking.beneficiaries.create({ /* ... */ })
const beneficiaries = await client.banking.beneficiaries.list({ page_number: 1, page_size: 20 })

// Conversions
const quote = await client.banking.conversions.createQuote({
  sell_currency: 'SGD',
  buy_currency: 'USD',
  amount: 100,
  fixed_side: 'SELL',
})
const conversion = await client.banking.conversions.create({
  quote_id: quote.quote_id,
})

// Virtual Accounts
const va = await client.banking.virtualAccounts.create({
  currency: 'SGD',
  // ...
})

// Exchange Rates
const rates = await client.banking.conversions.listCurrentRates({
  currency_pairs: ['SGD/USD', 'USD/SGD'],
})

// Payment Methods
const methods = await client.banking.paymentMethods.list({ page_number: 1, page_size: 20 })

Issuing

// ── Cardholders ────────────────────────────────────────────────────────────────
const cardholder = await client.issuing.cardholders.create({
  email: '[email protected]',
  first_name: 'Jane',
  last_name: 'Smith',
  country_code: 'SG',
  phone_number: '+6512345678',
})

// ── Cards ──────────────────────────────────────────────────────────────────────
const card = await client.issuing.cards.create({
  card_currency: 'SGD',
  cardholder_id: cardholder.cardholder_id,
  card_product_id: 'prod-123',
})

const cards = await client.issuing.cards.list({ page_number: 1, page_size: 20 })
const retrieved = await client.issuing.cards.retrieve(card.card_id)

// Freeze / unfreeze
await client.issuing.cards.updateStatus(card.card_id, { card_status: 'FROZEN' })
await client.issuing.cards.updateStatus(card.card_id, { card_status: 'ACTIVE' })

// Recharge and withdraw
await client.issuing.cards.recharge(card.card_id, { amount: 100 })
await client.issuing.cards.withdraw(card.card_id, { amount: 50 })

// ── Secure card display (iFrame) ───────────────────────────────────────────────
// Returns a short-lived URL (60s) that renders card number, CVV, and expiry in a
// PCI-compliant embedded iFrame hosted by UQPay. Open in a browser or embed in
// your frontend. MCP/CLI tools can surface the URL directly to the user.
const { iframeUrl, expires_at } = await client.issuing.cards.getSecureIframeUrl(card.card_id)
console.log(iframeUrl)   // https://embedded-sandbox.uqpaytech.com/iframe/card?token=...
console.log(expires_at)  // 2026-04-02T18:50:23+08:00

// Optional: set display language and custom styles
const { iframeUrl: styled } = await client.issuing.cards.getSecureIframeUrl(card.card_id, {
  lang: 'zh',
  styles: { '.card-number': { color: '#333', 'font-size': '18px' } },
})

// ── Balances ───────────────────────────────────────────────────────────────────
const balances = await client.issuing.balances.list({ page_number: 1, page_size: 20 })
const balance = await client.issuing.balances.retrieve('SGD')
const txns = await client.issuing.balances.listTransactions({ page_number: 1, page_size: 20 })

// ── Transactions ───────────────────────────────────────────────────────────────
const transactions = await client.issuing.transactions.list({ page_number: 1, page_size: 20 })

// ── Transfers ──────────────────────────────────────────────────────────────────
const transfer = await client.issuing.transfers.create({
  source_account_id: 'acc-123',
  destination_account_id: 'acc-456',
  currency: 'SGD',
  amount: 100,
})

// ── Products ───────────────────────────────────────────────────────────────────
const products = await client.issuing.products.list({ page_number: 1, page_size: 20 })

// ── Reports ────────────────────────────────────────────────────────────────────
const report = await client.issuing.reports.create({
  report_type: 'SETTLEMENT',
  start_time: '2026-01-01T00:00:00Z',
  end_time: '2026-01-31T23:59:59Z',
})

Payment

// ── Payment Intents ────────────────────────────────────────────────────────────
const intent = await client.payment.paymentIntents.create({
  amount: '100.00',
  currency: 'SGD',
  // ...
})
await client.payment.paymentIntents.confirm(intent.payment_intent_id, { /* ... */ })
await client.payment.paymentIntents.capture(intent.payment_intent_id, { /* ... */ })
await client.payment.paymentIntents.cancel(intent.payment_intent_id)

const intents = await client.payment.paymentIntents.list({ page_number: 1, page_size: 20 })

// ── Refunds ────────────────────────────────────────────────────────────────────
const refund = await client.payment.refunds.create({
  payment_intent_id: intent.payment_intent_id,
  amount: '50.00',
})
const refunds = await client.payment.refunds.list({ page_number: 1, page_size: 20 })

// ── Bank Accounts ──────────────────────────────────────────────────────────────
const bankAccount = await client.payment.bankAccounts.create({
  currency: 'USD',
  bank_account_number: '1234567890',
  bank_routing_number: '021000021',
  bank_address: '12 Marina Blvd, Singapore',
  // ...
})

// ── Payouts ────────────────────────────────────────────────────────────────────
const payout = await client.payment.payouts.create({
  bank_account_id: bankAccount.bank_account_id,
  amount: '100.00',
  currency: 'USD',
})

// ── Balances ───────────────────────────────────────────────────────────────────
const balances = await client.payment.balances.list({ page_number: 1, page_size: 20 })

// ── Payment Attempts ───────────────────────────────────────────────────────────
const attempts = await client.payment.paymentAttempts.list({ page_number: 1, page_size: 20 })

// ── Settlements ────────────────────────────────────────────────────────────────
const settlements = await client.payment.settlements.list({ page_number: 1, page_size: 20 })

Supporting (File Upload/Download)

import { readFileSync } from 'fs'

// Upload a file
const uploaded = await client.supporting.files.upload(
  readFileSync('./kyc-document.pdf'),
  { filename: 'kyc-document.pdf', mimeType: 'application/pdf' }
)
console.log(uploaded.file_id)

// Get download links
const links = await client.supporting.files.downloadLinks([uploaded.file_id])
console.log(links)

Simulator (sandbox only)

The simulator is only available in the sandbox environment. Calling any simulator method in production throws a SimulatorNotAvailableError.

// Simulate a card authorization
const auth = await client.simulator.issuing.authorize({
  card_id: 'card-123',
  amount: 25,
  currency: 'SGD',
  merchant_name: 'Coffee Shop',
  // ...
})

// Simulate a reversal
await client.simulator.issuing.reverse({
  transaction_id: auth.transaction_id,
})

// Simulate a deposit
const deposit = await client.simulator.deposits.simulate({
  currency: 'SGD',
  amount: 500,
})

Pagination

All list methods return a PaginatedResponse<T> with data, total_pages, and total_items.

const page = await client.issuing.cards.list({ page_number: 1, page_size: 50 })
console.log(page.data)        // Card[]
console.log(page.total_pages) // total number of pages
console.log(page.total_items) // total number of items

To paginate manually, increment page_number until page_number >= total_pages.

Webhooks

Verify incoming webhook signatures from UQPay:

import express from 'express'

const app = express()

// IMPORTANT: Use raw body parser — do NOT use express.json() for webhook routes
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  let event

  try {
    event = client.webhooks.constructEvent(req.body, req.headers)
  } catch (err) {
    return res.status(400).send(`Webhook error: ${err.message}`)
  }

  switch (event.event_name) {
    case 'card.create.succeeded':
      // handle event
      break
  }

  res.json({ received: true })
})

The rawBody passed to constructEvent must be the original request body string or Buffer — not a parsed JSON object.

Authorization Decision (PGP)

Handle real-time card transaction authorization decisions. UQPAY sends PGP-encrypted transactions to your endpoint; you decrypt, decide, and return an encrypted response.

Generate PGP Key Pair

import { generateAuthDecisionKeyPair } from '@uqpay/sdk'

const { publicKey, privateKey } = await generateAuthDecisionKeyPair({
  name: 'Acme Corp',
  email: '[email protected]',
})
// Send publicKey to UQPAY, save privateKey securely

Configure and Create Handler

// Configure PGP keys (once after client initialization)
// Accepts file paths (.asc/.pgp/.gpg) or armored key strings
await client.issuing.authDecision.configure({
  privateKey: './keys/my-private.asc',
  uqpayPublicKey: './keys/uqpay-public.asc',
  passphrase: process.env.PGP_PASSPHRASE, // optional
})

// Create handler — only write your business logic
const handler = client.issuing.authDecision.createHandler({
  decide: async (transaction) => {
    // transaction contains: billing_amount, merchant_name, card_id, etc.
    if (transaction.billing_amount > 10000) {
      return { response_code: '51' } // Insufficient Funds
    }
    return { response_code: '00', partner_reference_id: 'ref-001' }
  },
  onError: (err) => console.error('Auth decision error:', err),
})

// Mount to Express
app.post('/auth-decision', handler)

The SDK handles all PGP encryption/decryption and automatically injects transaction_id into the response.

Error Handling

All API errors throw typed error classes:

import {
  UQPayClient,
  AuthenticationError,
  ValidationError,
  NotFoundError,
  RateLimitError,
  ServerError,
  NetworkError,
  SimulatorNotAvailableError,
} from '@uqpay/sdk'

try {
  const account = await client.account.accounts.retrieve('acc-123')
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log('Account not found')
  } else if (err instanceof ValidationError) {
    console.log('Bad request:', err.message)
  } else if (err instanceof RateLimitError) {
    console.log('Rate limited, will retry automatically')
  } else if (err instanceof NetworkError) {
    console.log('Network error:', err.message)
  } else if (err instanceof SimulatorNotAvailableError) {
    console.log('Simulator is only available in sandbox')
  }
}

All UQPayError subclasses expose:

| Field | Type | Description | |-------|------|-------------| | message | string | Human-readable error message | | httpStatus | number | HTTP status code | | type | string | API error type string | | code | string | API error code |

Idempotency

All requests automatically include a UUID idempotency key. To provide your own:

await client.account.subAccounts.create(params, {
  headers: { 'x-idempotency-key': 'your-uuid-v4' },
})

Acting on Behalf of a Sub-account

await client.account.accounts.retrieve('acc-123', {
  headers: { 'x-on-behalf-of': 'sub-account-id' },
})

Retries

The SDK automatically retries failed requests (server errors, rate limits, network errors) with exponential backoff. Configure globally or per-request:

// Global
const client = new UQPayClient({ ..., maxRetries: 3 })

// Per-request
await client.account.accounts.retrieve('acc-123', { maxRetries: 0 })

Escape Hatch

For endpoints not yet covered by the SDK:

const result = await client.request('POST', '/v1/some/endpoint', {
  body: { key: 'value' },
})

Partner / Platform Info

Append your platform info to the User-Agent header:

client.setAppInfo('MyPlatform', '2.0.0', 'https://myplatform.com')
// User-Agent: uqpay-node/0.1.0 node/20.0.0 MyPlatform/2.0.0 (https://myplatform.com)

Logging

const client = new UQPayClient({
  ...,
  logLevel: 'debug', // logs all requests and responses
})

Sensitive fields (apiKey, card_number, cvc, iban, etc.) are automatically redacted from logs. Add custom fields:

const client = new UQPayClient({
  ...,
  logLevel: 'debug',
  redactFields: ['my_secret_field'],
})

Documentation

Full API reference: https://docs.uqpay.com/

License

MIT