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

@alufie/payuni

v0.1.0

Published

Headless, typed PAYUNi payment, refund, query, recurring, logistics, coupon, and webhook framework.

Readme

@alufie/payuni

Headless, typed PAYUNi framework for Node.js server applications. It provides encryption, request and response validation, payments, queries, refunds, recurring billing, logistics, coupons, settlement queries, verified webhooks, UI-neutral settings metadata, and structured log hooks. It has no runtime dependencies and no UI or Svelte dependency.

The package follows the live PAYUNi documentation as reviewed on 2026-07-24. The old raw-card /api/credit flow was removed by PAYUNi in July 2026; this package exposes /api/credit only as the current CreditHash token charge.

Install

pnpm add @alufie/payuni

Node.js 22 or newer is required. Keep every client, secret provider, recurring direct-card call, and webhook verifier in server-only code.

Five-minute integration

import {
  EnvSecretProvider,
  PayuniClient,
  resolveEnvironment
} from '@alufie/payuni';
import { payments } from '@alufie/payuni/payments';

const client = new PayuniClient({
  environment: resolveEnvironment(process.env.PAYUNI_ENVIRONMENT),
  secretProvider: new EnvSecretProvider(process.env)
});

const pay = payments(client);

const checkout = await pay.hosted({
  MerTradeNo: 'ORDER-20260724-1',
  TradeAmt: 1200,
  Timestamp: Math.floor(Date.now() / 1000),
  ProdDesc: 'Airway product',
  Credit: 1,
  ATM: 1,
  ReturnURL: 'https://example.com/payment/return',
  NotifyURL: 'https://example.com/api/payuni/notify'
});

checkout is UI-neutral:

{
  action: 'https://sandbox-api.payuni.com.tw/api/upp',
  method: 'POST',
  enctype: 'application/x-www-form-urlencoded',
  fields: {
    MerID: '...',
    Version: '2.0',
    EncryptInfo: '...',
    HashInfo: '...'
  }
}

Render those four fields as hidden inputs in your own form and submit it to action. The package never emits HTML.

Secrets from a database

The client resolves a provider for each request, so a site can rotate credentials without restarting:

import { PayuniClient, createSecretProvider } from '@alufie/payuni';

const client = new PayuniClient({
  environment: 'production',
  secretProvider: createSecretProvider(async () => {
    const row = await database.getPrivatePaymentSettings();
    return {
      merchantId: row.payuniMerchantId,
      hashKey: row.payuniHashKey,
      hashIv: row.payuniHashIv
    };
  })
});

cacheSecretProvider(provider, { ttlMs }) is available when a deliberate, short-lived credential cache is appropriate. Caching is off by default.

Trusted npm publishing

The repository includes .github/workflows/publish.yml for npm trusted publishing. It runs the full package checks and publishes on a v* tag or a manual workflow dispatch using GitHub Actions OIDC; no long-lived npm publish token is stored in GitHub. Configure npm package settings → Trusted Publisher with GitHub Actions, owner jmyt8, repository alufie-payuni, workflow filename publish.yml, and allow npm publish. Because this repository is private, npm will not attach a provenance attestation; trusted OIDC authentication still applies.

For direct API calls, result.envelope.Status and result.envelope.Message contain PAYUNi's outer response metadata, while result.data contains the hash-verified, decrypted transaction fields. The client throws PayuniProviderError when PAYUNi returns an error envelope without encrypted result data.

Feature imports

Every feature is an explicit subpath, so sites include only what they use:

import { payments } from '@alufie/payuni/payments';
import { asynchronous } from '@alufie/payuni/async';
import { queries } from '@alufie/payuni/queries';
import { refunds } from '@alufie/payuni/refunds';
import { recurring } from '@alufie/payuni/recurring';
import { logistics } from '@alufie/payuni/logistics';
import { coupons } from '@alufie/payuni/coupons';
import { settlements } from '@alufie/payuni/settlements';
import { verifyWebhook } from '@alufie/payuni/webhooks';

@alufie/payuni/operations exports the complete operation registry and defineOperation() for a documented PAYUNi extension or an upstream ambiguity. Custom operation paths must be root-relative and always resolve against the selected official PAYUNi origin. Built-in contracts are deeply immutable. The client validates unknown fields instead of silently submitting misspellings.

Settings form metadata

Settings metadata contains definitions only, never credential values:

import { Settings } from '@alufie/payuni/settings';

const fullForm = Settings.all;
const paymentForm = Settings.forFeatures(['payments']);
const credentialsOnly = Settings.pick(Settings.core, [
  'environment',
  'merchantId',
  'hashKey',
  'hashIv'
]);

for (const field of paymentForm.fields) {
  // Map valueType, title, constraints, and sensitive to your own components.
}

const issues = Settings.validate(credentialsOnly, submittedValues);

This is intentionally not tied to shadcn, Svelte, React, or any database.

Logs

import { createLogger } from '@alufie/payuni';

const logger = createLogger(async (event) => {
  await database.insertPayuniLog(event);
});

Hooks receive lifecycle metadata only. Decrypted payloads, card data, credentials, tokens, customer contact details, and raw callbacks are never passed to the logger. Storage, retention, and UI rendering remain site-owned because PAYUNi does not document a provider log-retention contract.

Webhooks

import {
  createWebhookResponse,
  webhookSchemas,
  verifyWebhook
} from '@alufie/payuni/webhooks';

const verified = await verifyWebhook(urlSearchParams, {
  credentials,
  schema: webhookSchemas.payment
});

await database.transaction(async (tx) => {
  await tx.insertWebhookOnce(verified.deduplicationKey, verified.data);
  await tx.updateOrderFromVerifiedPayuniEvent(verified.data);
});

return createWebhookResponse();

The verifier checks the hash before AES-GCM decryption, compares the resolved, inner, and outer merchant IDs, rejects duplicate envelope fields, and can validate the decrypted data with built-in payment, recurring, logistics, or coupon schemas. verified.status and verified.message are outer PAYUNi metadata; apply business transitions only from verified.data reconciled with your stored order. Its deduplication key is a digest of the verified signed envelope, so distinct callback payloads cannot collapse merely because their transaction status matches. PAYUNi does not document an acknowledgement body, automatic retry count, or retry backoff. createWebhookResponse() therefore defaults to an empty HTTP 204; change it if PAYUNi gives your merchant a different contract. Make processing idempotent and reconcile UNKNOWN through the query API.

Successful direct API responses are checked against the operation's runtime response schema. Raw response bodies are omitted by default. Only enable exposeRawResponseBodies: true for short-lived diagnostics in a protected environment; raw signed envelopes and upstream error bodies must not be logged.

Guides

Development

pnpm check
pnpm lint
pnpm test
pnpm build
npm pack --dry-run

No live PAYUNi request runs in the test suite. Live testing requires a merchant account, enabled payment tools, and (for several direct APIs) an approved source IP.