{"_id":"@alexhalfborg/sgpaynowqr","_rev":"2-a86bb273fe7a5c68aec43392950dc744","name":"@alexhalfborg/sgpaynowqr","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@alexhalfborg/sgpaynowqr","version":"0.1.0","keywords":["paynow","sgqr","singapore","qr","qr-code","payments","emvco","fintech","sdk","uen"],"author":{"name":"alexhalfborg"},"license":"MIT","_id":"@alexhalfborg/sgpaynowqr@0.1.0","maintainers":[{"name":"alexhalfborg","email":"alex@halfborg.com"}],"homepage":"https://developers.sgpaynowqr.com/docs","bugs":{"url":"https://github.com/alexhalfborg/sgpaynowqr-sdk/issues"},"dist":{"shasum":"eea3c2f8144e9b85ac29a3eed70495074dfe341a","tarball":"https://registry.npmjs.org/@alexhalfborg/sgpaynowqr/-/sgpaynowqr-0.1.0.tgz","fileCount":9,"integrity":"sha512-WHgCJEeJhfUSqv9q6eMPZFYfHMU7nNrMgTE3C8yz20hWp6C7TjDZYVcX7/BEvaaaphzwtD40P2jcCA9ZiJTzEA==","signatures":[{"sig":"MEUCIA3X9enoH0j8n3EOG3tOVWpldsWaMWFBbW4/3AbMIB9jAiEA+a/8jDmlr+0Y1AZUif3TMwxHbUteEpGLRcAZI0WtlLk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32209},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"c0fbc893895b7de5462b9e9cd44af97291ec2da3","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"alexhalfborg","email":"alex@halfborg.com"},"repository":{"url":"git+https://github.com/alexhalfborg/sgpaynowqr-sdk.git","type":"git"},"_npmVersion":"11.9.0","description":"TypeScript SDK for the SGPayNowQR API — generate Singapore PayNow QR codes","directories":{},"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^3.0.0","typescript":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sgpaynowqr_0.1.0_1774922980809_0.33731642030235665","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"_id":"@alexhalfborg/sgpaynowqr@0.2.0","bugs":{"url":"https://github.com/alexhalfborg/sgpaynowqr-sdk/issues"},"dist":{"shasum":"ad8bd7055cbeeaff39081daf17fb5ed27d27b4e8","tarball":"https://registry.npmjs.org/@alexhalfborg/sgpaynowqr/-/sgpaynowqr-0.2.0.tgz","fileCount":9,"integrity":"sha512-OoC+7EXGpO4PrKNKu/XZ9jqUcVZAkL9uzvg8qwW62EAnooKT3QYfRRQoUKULUQKWwT9Sd40Xn0NVqpy3JbklcA==","signatures":[{"sig":"MEUCIGhj//1S5E9+z5KMwwb2yzrUW5X2eqanPcsuZOFH1t/qAiEAx1Q1V4GrC82yz1gdi34atbIly9B/2iL15fMSw7jpYMs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHKq+RLcWabBCI/WQLm8wNgQmP7kpKNtTg8WoV/10uXcAiEAroC2J6aiNgOt/LRskaSxEund61xz7ECgEgmTQSedlLs="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexhalfborg%2fsgpaynowqr@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":32573},"main":"./dist/index.cjs","name":"@alexhalfborg/sgpaynowqr","type":"module","types":"./dist/index.d.ts","author":{"name":"alexhalfborg"},"module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"29c64433359bfc5f0cf6d9d35e49f294e590fb91","license":"MIT","scripts":{"test":"vitest run","build":"tsup","release":"node scripts/release.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"version":"0.2.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9ed0a0c1-b9b7-4385-a64f-3321f37f0b4f"}},"homepage":"https://developers.sgpaynowqr.com/docs","keywords":["paynow","sgqr","singapore","qr","qr-code","payments","emvco","fintech","sdk","uen"],"repository":{"url":"git+https://github.com/alexhalfborg/sgpaynowqr-sdk.git","type":"git"},"_npmVersion":"11.19.0","description":"TypeScript SDK for the SGPayNowQR API — generate Singapore PayNow QR codes","directories":{},"maintainers":[{"name":"alexhalfborg","email":"alex@halfborg.com"}],"_nodeVersion":"24.21.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^3.0.0","typescript":"^5.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sgpaynowqr_0.2.0_1790229615033_0.5616401883987663"}}},"time":{"created":"2026-03-31T02:09:40.741Z","modified":"2026-09-24T06:00:16.190Z","0.1.0":"2026-03-31T02:09:40.979Z","0.2.0":"2026-09-24T06:00:15.160Z"},"bugs":{"url":"https://github.com/alexhalfborg/sgpaynowqr-sdk/issues"},"author":{"name":"alexhalfborg"},"license":"MIT","homepage":"https://developers.sgpaynowqr.com/docs","keywords":["paynow","sgqr","singapore","qr","qr-code","payments","emvco","fintech","sdk","uen"],"repository":{"url":"git+https://github.com/alexhalfborg/sgpaynowqr-sdk.git","type":"git"},"description":"TypeScript SDK for the SGPayNowQR API — generate Singapore PayNow QR codes","maintainers":[{"name":"alexhalfborg","email":"alex@halfborg.com"}],"readme":"# sgpaynowqr\n\nTypeScript SDK for the [SGPayNowQR API](https://developers.sgpaynowqr.com/docs) — generate EMVCo-compliant Singapore PayNow QR codes.\n\n- Zero dependencies (uses native `fetch`)\n- Full TypeScript types with discriminated unions\n- Dual CJS/ESM output\n- Node.js 18+\n\n## Install\n\n```bash\nnpm install @alexhalfborg/sgpaynowqr\n```\n\n## Quick Start\n\nGet your API key at [developers.sgpaynowqr.com](https://developers.sgpaynowqr.com/register).\n\n### UEN Payment\n\n```typescript\nimport { createClient } from \"@alexhalfborg/sgpaynowqr\";\n\nconst client = createClient(\"sgpn_your_api_key\");\n\nconst { data } = await client.generate({\n  payment_type: \"uen\",\n  uen: \"201234567A\",\n  merchant_name: \"My Company\",\n  amount: 25.0,\n  reference: \"INV001\",\n});\n\nconsole.log(data.qr_string); // EMVCo QR string\n```\n\n### Mobile Payment\n\n```typescript\nconst { data } = await client.generate({\n  payment_type: \"mobile\",\n  mobile_number: \"+6591234567\",\n  amount: 10.5,\n});\n```\n\n### VPA Payment\n\n```typescript\nconst { data } = await client.generate({\n  payment_type: \"vpa\",\n  vpa: \"+6591234567#OCBC\",\n  amount: 5.0,\n  reference: \"DONATION\",\n});\n```\n\n## Response\n\nEvery `generate()` call returns `data`, `meta`, and `rateLimit`:\n\n```typescript\nconst result = await client.generate({\n  payment_type: \"uen\",\n  uen: \"201234567A\",\n  merchant_name: \"My Company\",\n  amount: 25.0,\n});\n\n// QR code data\nresult.data.qr_string;        // \"000201010212...\"\nresult.data.qr_image_base64;  // base64-encoded PNG (when include_image=true)\nresult.data.image_url;        // signed link to the PNG (always returned)\nresult.data.payment_type;     // \"uen\"\nresult.data.amount;           // \"25.00\" (string)\nresult.data.currency;         // \"SGD\"\nresult.data.reference;        // string | null\nresult.data.expiry;           // \"24h\"\n\n// API metadata\nresult.meta.request_id;       // \"req_abc123def456\"\nresult.meta.usage?.used;      // 42\nresult.meta.usage?.limit;     // 200\n\n// Rate limit info (from response headers)\nresult.rateLimit.limit;       // 200\nresult.rateLimit.remaining;   // 158\nresult.rateLimit.reset;       // \"2026-04-01T00:00:00+08:00\"\n```\n\n## Save QR Image to File\n\n```typescript\nimport { writeFileSync } from \"node:fs\";\n\nconst { data } = await client.generate({\n  payment_type: \"uen\",\n  uen: \"201234567A\",\n  merchant_name: \"My Company\",\n  amount: 25.0,\n});\n\nif (data.qr_image_base64) {\n  writeFileSync(\"qr.png\", Buffer.from(data.qr_image_base64, \"base64\"));\n}\n```\n\n## Optional Parameters\n\n```typescript\nconst { data } = await client.generate({\n  payment_type: \"uen\",\n  uen: \"201234567A\",\n  merchant_name: \"My Company\",\n  amount: 25.0,\n  reference: \"INV001\",        // alphanumeric, max 25 chars\n  expiry: \"6h\",               // \"1h\" | \"2h\" | \"6h\" | \"12h\" | \"24h\" | \"none\"\n  qr_color: \"ff0000\",         // hex color without #\n  qr_size: 400,               // 200 | 300 | 400 pixels\n  include_image: false,        // set false to skip base64 PNG\n});\n```\n\n## Error Handling\n\nAll API and network errors throw `SGPayNowQRError`:\n\n```typescript\nimport { createClient, SGPayNowQRError } from \"@alexhalfborg/sgpaynowqr\";\n\nconst client = createClient(\"sgpn_your_api_key\");\n\ntry {\n  await client.generate({\n    payment_type: \"uen\",\n    uen: \"201234567A\",\n    merchant_name: \"My Company\",\n    amount: 25.0,\n  });\n} catch (err) {\n  if (err instanceof SGPayNowQRError) {\n    console.error(err.code);       // \"VALIDATION_ERROR\" | \"UNAUTHORIZED\" | \"RATE_LIMIT_EXCEEDED\" | ...\n    console.error(err.message);    // \"Missing required fields\"\n    console.error(err.status);     // 400 (HTTP status, 0 for network errors)\n    console.error(err.requestId);  // \"req_abc123def456\"\n    console.error(err.rateLimit);  // { limit, remaining, reset }\n\n    if (err.code === \"RATE_LIMIT_EXCEEDED\") {\n      console.log(`Resets at: ${err.rateLimit?.reset}`);\n    }\n  }\n}\n```\n\n### Error Codes\n\n| HTTP | Code | Description |\n|------|------|-------------|\n| 400 | `VALIDATION_ERROR` | Invalid parameters or missing required fields |\n| 400 | `INVALID_JSON` | Request body is not valid JSON |\n| 400 | `INVALID_CONTENT_TYPE` | Content-Type must be application/json |\n| 401 | `UNAUTHORIZED` | Missing or invalid API key |\n| 429 | `RATE_LIMIT_EXCEEDED` | Monthly usage limit exceeded |\n| 500 | `INTERNAL_ERROR` | Server error |\n| 0 | `NETWORK_ERROR` | Network failure (DNS, timeout, etc.) |\n\n## TypeScript\n\n`GenerateParams` is a discriminated union — TypeScript enforces the correct fields for each payment type:\n\n```typescript\n// TypeScript will error: Property 'uen' is missing\nclient.generate({\n  payment_type: \"uen\",\n  amount: 10,\n  // uen and merchant_name are required when payment_type is \"uen\"\n});\n\n// TypeScript will error: 'uen' does not exist on mobile type\nclient.generate({\n  payment_type: \"mobile\",\n  uen: \"201234567A\", // wrong field for mobile\n  amount: 10,\n});\n```\n\nImport types directly:\n\n```typescript\nimport type { GenerateParams, GenerateResponse, RateLimitInfo } from \"@alexhalfborg/sgpaynowqr\";\n```\n\n## Health Check\n\n```typescript\nconst { data } = await client.health();\nconsole.log(data.status); // \"ok\" | \"degraded\"\n```\n\n## Configuration\n\n```typescript\nconst client = createClient(\"sgpn_your_api_key\", {\n  baseUrl: \"https://staging.sgpaynowqr.com/api/v1\", // custom base URL\n  fetch: customFetchFn,                              // custom fetch implementation\n});\n```\n\n## Rate Limits\n\nLimits are per account, shared across all your API keys, per calendar month (Singapore timezone). Only successful requests count.\n\n| Tier | Monthly Limit |\n|------|---------------|\n| Free | 50 |\n| Starter | 200 |\n| Pro | 2,000 |\n| Enterprise | 10,000 |\n\nRate limit info is available on every response via `result.rateLimit`.\n\n## Requirements\n\n- Node.js 18+ (uses native `fetch`)\n- API key from [developers.sgpaynowqr.com](https://developers.sgpaynowqr.com/register)\n\n## License\n\nMIT\n","readmeFilename":"README.md"}