{"_id":"@alufie/payuni","name":"@alufie/payuni","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@alufie/payuni","version":"0.1.0","description":"Headless, typed PAYUNi payment, refund, query, recurring, logistics, coupon, and webhook framework.","repository":{"type":"git","url":"git+https://github.com/jmyt8/alufie-payuni.git"},"bugs":{"url":"https://github.com/jmyt8/alufie-payuni/issues"},"homepage":"https://github.com/jmyt8/alufie-payuni#readme","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","sideEffects":false,"engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./settings":{"types":"./dist/settings.d.ts","import":"./dist/settings.js"},"./statuses":{"types":"./dist/statuses.d.ts","import":"./dist/statuses.js"},"./operations":{"types":"./dist/operations.d.ts","import":"./dist/operations.js"},"./payments":{"types":"./dist/features/payments.d.ts","import":"./dist/features/payments.js"},"./async":{"types":"./dist/features/async.d.ts","import":"./dist/features/async.js"},"./queries":{"types":"./dist/features/queries.d.ts","import":"./dist/features/queries.js"},"./refunds":{"types":"./dist/features/refunds.d.ts","import":"./dist/features/refunds.js"},"./recurring":{"types":"./dist/features/recurring.d.ts","import":"./dist/features/recurring.js"},"./logistics":{"types":"./dist/features/logistics.d.ts","import":"./dist/features/logistics.js"},"./coupons":{"types":"./dist/features/coupons.d.ts","import":"./dist/features/coupons.js"},"./settlements":{"types":"./dist/features/settlements.d.ts","import":"./dist/features/settlements.js"},"./webhooks":{"types":"./dist/features/webhooks.d.ts","import":"./dist/features/webhooks.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.build.json","check":"tsc --noEmit","lint":"eslint src tests","test":"vitest run","test:coverage":"vitest run --coverage","prepack":"pnpm run check && pnpm run lint && pnpm run test && pnpm run build"},"keywords":["payuni","payment","taiwan","typescript","sveltekit","headless"],"license":"MIT","publishConfig":{"access":"public","provenance":false},"devDependencies":{"@eslint/js":"^10.0.1","@types/node":"^22.19.0","eslint":"^10.7.0","typescript":"^5.9.3","typescript-eslint":"^8.65.0","vitest":"^4.1.10"},"gitHead":"50b5d2d9eed6d7f0f2c926fd4a74bb7c1ea25b95","_id":"@alufie/payuni@0.1.0","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-ilWiS3FWmtxfvPSGRedJIviM8vP68IXEQMEe6nnUJ8Weh4fnlDMc92RDlzNvWNUFEgaK2AxH7RNni6BAUfsKBQ==","shasum":"3dd23773d9cead1db1e676a22d2a65f428f29925","tarball":"https://registry.npmjs.org/@alufie/payuni/-/payuni-0.1.0.tgz","fileCount":103,"unpackedSize":335341,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICSdeJC/0/FZGQBR7P7oITsj3fM3DviCD/G7cnRx6TsQAiAUjCukbWaSo99AMG3/6/+LwyTuZMwoj6uMnan4vTo05Q=="}]},"_npmUser":{"name":"alufie","email":"dev@alufie.com"},"directories":{},"maintainers":[{"name":"alufie","email":"dev@alufie.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/payuni_0.1.0_1785472523826_0.8157751757056491"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T04:35:23.715Z","0.1.0":"2026-07-31T04:35:23.986Z","modified":"2026-07-31T04:35:24.332Z"},"maintainers":[{"name":"alufie","email":"dev@alufie.com"}],"description":"Headless, typed PAYUNi payment, refund, query, recurring, logistics, coupon, and webhook framework.","homepage":"https://github.com/jmyt8/alufie-payuni#readme","keywords":["payuni","payment","taiwan","typescript","sveltekit","headless"],"repository":{"type":"git","url":"git+https://github.com/jmyt8/alufie-payuni.git"},"bugs":{"url":"https://github.com/jmyt8/alufie-payuni/issues"},"license":"MIT","readme":"# @alufie/payuni\n\nHeadless, typed PAYUNi framework for Node.js server applications. It provides\nencryption, request and response validation, payments, queries, refunds, recurring billing,\nlogistics, coupons, settlement queries, verified webhooks, UI-neutral settings\nmetadata, and structured log hooks. It has no runtime dependencies and no UI or\nSvelte dependency.\n\nThe package follows the live PAYUNi documentation as reviewed on 2026-07-24.\nThe old raw-card `/api/credit` flow was removed by PAYUNi in July 2026; this\npackage exposes `/api/credit` only as the current `CreditHash` token charge.\n\n## Install\n\n```bash\npnpm add @alufie/payuni\n```\n\nNode.js 22 or newer is required. Keep every client, secret provider, recurring\ndirect-card call, and webhook verifier in server-only code.\n\n## Five-minute integration\n\n```ts\nimport {\n  EnvSecretProvider,\n  PayuniClient,\n  resolveEnvironment\n} from '@alufie/payuni';\nimport { payments } from '@alufie/payuni/payments';\n\nconst client = new PayuniClient({\n  environment: resolveEnvironment(process.env.PAYUNI_ENVIRONMENT),\n  secretProvider: new EnvSecretProvider(process.env)\n});\n\nconst pay = payments(client);\n\nconst checkout = await pay.hosted({\n  MerTradeNo: 'ORDER-20260724-1',\n  TradeAmt: 1200,\n  Timestamp: Math.floor(Date.now() / 1000),\n  ProdDesc: 'Airway product',\n  Credit: 1,\n  ATM: 1,\n  ReturnURL: 'https://example.com/payment/return',\n  NotifyURL: 'https://example.com/api/payuni/notify'\n});\n```\n\n`checkout` is UI-neutral:\n\n```ts\n{\n  action: 'https://sandbox-api.payuni.com.tw/api/upp',\n  method: 'POST',\n  enctype: 'application/x-www-form-urlencoded',\n  fields: {\n    MerID: '...',\n    Version: '2.0',\n    EncryptInfo: '...',\n    HashInfo: '...'\n  }\n}\n```\n\nRender those four `fields` as hidden inputs in your own form and submit it to\n`action`. The package never emits HTML.\n\n## Secrets from a database\n\nThe client resolves a provider for each request, so a site can rotate credentials\nwithout restarting:\n\n```ts\nimport { PayuniClient, createSecretProvider } from '@alufie/payuni';\n\nconst client = new PayuniClient({\n  environment: 'production',\n  secretProvider: createSecretProvider(async () => {\n    const row = await database.getPrivatePaymentSettings();\n    return {\n      merchantId: row.payuniMerchantId,\n      hashKey: row.payuniHashKey,\n      hashIv: row.payuniHashIv\n    };\n  })\n});\n```\n\n`cacheSecretProvider(provider, { ttlMs })` is available when a deliberate,\nshort-lived credential cache is appropriate. Caching is off by default.\n\n## Trusted npm publishing\n\nThe repository includes `.github/workflows/publish.yml` for npm trusted\npublishing. It runs the full package checks and publishes on a `v*` tag or a\nmanual workflow dispatch using GitHub Actions OIDC; no long-lived npm publish\ntoken is stored in GitHub. Configure npm package settings → Trusted Publisher\nwith GitHub Actions, owner `jmyt8`, repository `alufie-payuni`, workflow\nfilename `publish.yml`, and allow `npm publish`. Because this repository is\nprivate, npm will not attach a provenance attestation; trusted OIDC\nauthentication still applies.\n\nFor direct API calls, `result.envelope.Status` and\n`result.envelope.Message` contain PAYUNi's outer response metadata, while\n`result.data` contains the hash-verified, decrypted transaction fields. The\nclient throws `PayuniProviderError` when PAYUNi returns an error envelope\nwithout encrypted result data.\n\n## Feature imports\n\nEvery feature is an explicit subpath, so sites include only what they use:\n\n```ts\nimport { payments } from '@alufie/payuni/payments';\nimport { asynchronous } from '@alufie/payuni/async';\nimport { queries } from '@alufie/payuni/queries';\nimport { refunds } from '@alufie/payuni/refunds';\nimport { recurring } from '@alufie/payuni/recurring';\nimport { logistics } from '@alufie/payuni/logistics';\nimport { coupons } from '@alufie/payuni/coupons';\nimport { settlements } from '@alufie/payuni/settlements';\nimport { verifyWebhook } from '@alufie/payuni/webhooks';\n```\n\n`@alufie/payuni/operations` exports the complete operation registry and\n`defineOperation()` for a documented PAYUNi extension or an upstream ambiguity.\nCustom operation paths must be root-relative and always resolve against the\nselected official PAYUNi origin. Built-in contracts are deeply immutable. The\nclient validates unknown fields instead of silently submitting misspellings.\n\n## Settings form metadata\n\nSettings metadata contains definitions only, never credential values:\n\n```ts\nimport { Settings } from '@alufie/payuni/settings';\n\nconst fullForm = Settings.all;\nconst paymentForm = Settings.forFeatures(['payments']);\nconst credentialsOnly = Settings.pick(Settings.core, [\n  'environment',\n  'merchantId',\n  'hashKey',\n  'hashIv'\n]);\n\nfor (const field of paymentForm.fields) {\n  // Map valueType, title, constraints, and sensitive to your own components.\n}\n\nconst issues = Settings.validate(credentialsOnly, submittedValues);\n```\n\nThis is intentionally not tied to shadcn, Svelte, React, or any database.\n\n## Logs\n\n```ts\nimport { createLogger } from '@alufie/payuni';\n\nconst logger = createLogger(async (event) => {\n  await database.insertPayuniLog(event);\n});\n```\n\nHooks receive lifecycle metadata only. Decrypted payloads, card data, credentials,\ntokens, customer contact details, and raw callbacks are never passed to the\nlogger. Storage, retention, and UI rendering remain site-owned because PAYUNi\ndoes not document a provider log-retention contract.\n\n## Webhooks\n\n```ts\nimport {\n  createWebhookResponse,\n  webhookSchemas,\n  verifyWebhook\n} from '@alufie/payuni/webhooks';\n\nconst verified = await verifyWebhook(urlSearchParams, {\n  credentials,\n  schema: webhookSchemas.payment\n});\n\nawait database.transaction(async (tx) => {\n  await tx.insertWebhookOnce(verified.deduplicationKey, verified.data);\n  await tx.updateOrderFromVerifiedPayuniEvent(verified.data);\n});\n\nreturn createWebhookResponse();\n```\n\nThe verifier checks the hash before AES-GCM decryption, compares the resolved,\ninner, and outer merchant IDs, rejects duplicate envelope fields, and can\nvalidate the decrypted data with built-in payment, recurring, logistics, or\ncoupon schemas. `verified.status` and `verified.message` are outer PAYUNi\nmetadata; apply business transitions only from `verified.data` reconciled with\nyour stored order. Its deduplication key is a digest of the verified signed\nenvelope, so distinct callback payloads cannot collapse merely because their\ntransaction status matches. PAYUNi does not document an acknowledgement body,\nautomatic retry count, or retry backoff. `createWebhookResponse()` therefore\ndefaults to an empty HTTP 204; change it if PAYUNi gives your merchant a\ndifferent contract. Make processing idempotent and reconcile `UNKNOWN` through\nthe query API.\n\nSuccessful direct API responses are checked against the operation's runtime\nresponse schema. Raw response bodies are omitted by default. Only enable\n`exposeRawResponseBodies: true` for short-lived diagnostics in a protected\nenvironment; raw signed envelopes and upstream error bodies must not be logged.\n\n## Guides\n\n- [SvelteKit integration](docs/SVELTEKIT.md)\n- [Transaction submission requirements](docs/TRANSACTION_REQUIREMENTS.md)\n- [Security and operational guidance](docs/SECURITY.md)\n\n## Development\n\n```bash\npnpm check\npnpm lint\npnpm test\npnpm build\nnpm pack --dry-run\n```\n\nNo live PAYUNi request runs in the test suite. Live testing requires a merchant\naccount, enabled payment tools, and (for several direct APIs) an approved source\nIP.\n","readmeFilename":"README.md","_rev":"1-674a0dfec0e3e732edb5d01a1908658b"}