{"_id":"@aveplus_dev/uemoa-qrcode-sdk","_rev":"2-5e8f91e40bf1c5d3fb5c9e6665863ce4","name":"@aveplus_dev/uemoa-qrcode-sdk","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@aveplus_dev/uemoa-qrcode-sdk","version":"1.0.0","keywords":["bceao","uemoa","qrcode","emvco","payment"],"license":"Apache-2.0","_id":"@aveplus_dev/uemoa-qrcode-sdk@1.0.0","maintainers":[{"name":"aveplus-dev","email":"lfranck@aveplus.net"},{"name":"kjonathan","email":"jonathankabore82@gmail.com"}],"homepage":"https://gitlab.com/aveplus/uemoa-qrcode-sdk-js#readme","bugs":{"url":"https://gitlab.com/aveplus/uemoa-qrcode-sdk-js/issues"},"dist":{"shasum":"bc415fde2917c28eecb7ef4465f6b2fb6d1b4a22","tarball":"https://registry.npmjs.org/@aveplus_dev/uemoa-qrcode-sdk/-/uemoa-qrcode-sdk-1.0.0.tgz","fileCount":7,"integrity":"sha512-JugnT5y3hCNIHqsqNBjESC8AHfzTfvImPOn5MIv3gaCRCMIE6MF6Lm0jyeJiuAFqOUpmLakkD+5frFnr3qwILw==","signatures":[{"sig":"MEUCIQDQIMZijnR0IIzrsjqkIbp/W6PAVe27IgM2XdSbjc322gIgbW9jSVWL0saq/H4qwiM5Vamet4HlXW25OmttK2Kc0As=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":105257},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean","test:watch":"vitest"},"_npmUser":{"name":"kjonathan","email":"jonathankabore82@gmail.com"},"repository":{"url":"git+https://gitlab.com/aveplus/uemoa-qrcode-sdk-js.git","type":"git"},"_npmVersion":"10.9.2","description":"SDK JavaScript/TypeScript pour la generation et lecture de QR codes de paiement conformes a la norme BCEAO/UEMOA (portage du SDK Java uemoa-qrcode-sdk-core)","directories":{},"_nodeVersion":"22.17.1","dependencies":{"qrcode":"^1.5.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.2","vitest":"^1.6.0","typescript":"^5.4.5","@types/node":"^20.12.7","@types/qrcode":"^1.5.6"},"_npmOperationalInternal":{"tmp":"tmp/uemoa-qrcode-sdk_1.0.0_1785330777601_0.5105628792645225","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@aveplus_dev/uemoa-qrcode-sdk","version":"1.0.1","description":"SDK JavaScript/TypeScript pour la generation et lecture de QR codes de paiement conformes a la norme BCEAO/UEMOA (portage du SDK Java uemoa-qrcode-sdk-core)","license":"Apache-2.0","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"repository":{"type":"git","url":"git+https://gitlab.com/aveplus/uemoa-qrcode-sdk-js.git"},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean","test":"vitest run","test:watch":"vitest"},"keywords":["bceao","uemoa","qrcode","emvco","payment"],"devDependencies":{"@types/node":"^20.12.7","@types/qrcode":"^1.5.6","tsup":"^8.0.2","typescript":"^5.4.5","vitest":"^1.6.0"},"dependencies":{"qrcode":"^1.5.3"},"_id":"@aveplus_dev/uemoa-qrcode-sdk@1.0.1","gitHead":"3fbb9147fe7a68b10e5f525e2fa4be65d7a0ed98","bugs":{"url":"https://gitlab.com/aveplus/uemoa-qrcode-sdk-js/issues"},"homepage":"https://gitlab.com/aveplus/uemoa-qrcode-sdk-js#readme","_nodeVersion":"22.17.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-2ijWVc/r+va2OLGMg2ezxBbrf0cCaetTqU2hRlZwW6gfF0DvFZ6NPwxWU03auPYcRryqdzinLlMjqdhCUomr0Q==","shasum":"676e468da00b41df2920ae729419f4f01287d367","tarball":"https://registry.npmjs.org/@aveplus_dev/uemoa-qrcode-sdk/-/uemoa-qrcode-sdk-1.0.1.tgz","fileCount":8,"unpackedSize":112620,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCgI33nxdAy2NGFsFr7+jZZwIR+OJf2yG0Z3D2ffccmLAIgFHNkVuS2m/fVON+dBaJJ0EnhmkXOgrjusOnAxNneavU="}]},"_npmUser":{"name":"kjonathan","email":"jonathankabore82@gmail.com"},"directories":{},"maintainers":[{"name":"aveplus-dev","email":"lfranck@aveplus.net"},{"name":"kjonathan","email":"jonathankabore82@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/uemoa-qrcode-sdk_1.0.1_1785951439913_0.6552674706471311"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-29T13:12:57.435Z","modified":"2026-08-05T17:37:20.286Z","1.0.0":"2026-07-29T13:12:57.761Z","1.0.1":"2026-08-05T17:37:20.058Z"},"bugs":{"url":"https://gitlab.com/aveplus/uemoa-qrcode-sdk-js/issues"},"license":"Apache-2.0","homepage":"https://gitlab.com/aveplus/uemoa-qrcode-sdk-js#readme","keywords":["bceao","uemoa","qrcode","emvco","payment"],"repository":{"type":"git","url":"git+https://gitlab.com/aveplus/uemoa-qrcode-sdk-js.git"},"description":"SDK JavaScript/TypeScript pour la generation et lecture de QR codes de paiement conformes a la norme BCEAO/UEMOA (portage du SDK Java uemoa-qrcode-sdk-core)","maintainers":[{"name":"aveplus-dev","email":"lfranck@aveplus.net"},{"name":"kjonathan","email":"jonathankabore82@gmail.com"}],"readme":"# @aveplus_dev/uemoa-qrcode-sdk\r\n\r\nSDK JavaScript/TypeScript pour la génération et la lecture de QR codes de\r\npaiement conformes à la norme **BCEAO/UEMOA** (standard EMVCo).\r\n\r\nPortage du SDK Java de référence `uemoa-qrcode-sdk-core`, avec tests\r\nd'interopérabilité croisés garantissant qu'un QR généré par un SDK est\r\nlisible par les autres (Java, JavaScript, Python).\r\n\r\n- **Package npm** : [`@aveplus_dev/uemoa-qrcode-sdk`](https://www.npmjs.com/package/@aveplus_dev/uemoa-qrcode-sdk)\r\n- **Dépôt** : `gitlab.avepay.net/qrcode/qrcode-module-backend-js`\r\n- **Licence** : Apache-2.0\r\n\r\n---\r\n\r\n## Sommaire\r\n\r\n1. [Installation](#1-installation)\r\n2. [Démarrage rapide](#2-démarrage-rapide)\r\n3. [Types de QR supportés](#3-types-de-qr-supportés)\r\n4. [Référence de l'API](#4-référence-de-lapi)\r\n5. [Règles de validation](#5-règles-de-validation)\r\n6. [Interopérabilité entre SDK](#6-interopérabilité-entre-sdk)\r\n7. [Développement local](#7-développement-local)\r\n8. [Publication du SDK sur npm](#8-publication-du-sdk-sur-npm)\r\n9. [Structure du projet](#9-structure-du-projet)\r\n\r\n---\r\n\r\n## 1. Installation\r\n\r\n```bash\r\nnpm install @aveplus_dev/uemoa-qrcode-sdk\r\n```\r\n\r\nLe package est publié en **dual build** : il fonctionne aussi bien en\r\nCommonJS (`require`) qu'en ESM (`import`), et embarque ses propres types\r\nTypeScript.\r\n\r\n---\r\n\r\n## 2. Démarrage rapide\r\n\r\n```ts\r\nimport { UemoaQRService, MerchantChannel } from \"@aveplus_dev/uemoa-qrcode-sdk\";\r\n\r\nconst service = new UemoaQRService();\r\n\r\n// --- Génération ---\r\nconst qrData = service.generateStaticQR({\r\n  type: \"STATIC\",\r\n  merchantChannel: MerchantChannel.STATIC_WITH_AMOUNT,\r\n  merchantInfo: {\r\n    alias: \"shop-001\",\r\n    name: \"MA BOUTIQUE\",\r\n    city: \"Ouagadougou\",\r\n    countryCode: \"BF\",\r\n  },\r\n  amount: 2500,\r\n});\r\n// \"00020101021136280012int.bceao.pi0108shop-001...\"\r\n\r\n// --- Image PNG (base64, sans le préfixe data:) ---\r\nconst imageBase64 = await service.generateQRImageFromString(qrData);\r\n\r\n// --- Lecture d'un QR scanné ---\r\nconst details = service.getQRCodeDetails(qrData);\r\n// { valid: true, type: \"STATIC\", amount: \"2500\",\r\n//   channel: { code: 110, ... },\r\n//   merchant: { alias, name, city, country }, transactionId }\r\n\r\n// --- Validation (CRC + cohérence structurelle) ---\r\nconst isValid = service.validateQRCode(qrData); // true\r\n```\r\n\r\n---\r\n\r\n## 3. Types de QR supportés\r\n\r\n| Type | Usage | Méthode |\r\n|---|---|---|\r\n| **Statique** | QR fixe affiché en boutique, réutilisable par plusieurs clients | `generateStaticQR()` |\r\n| **Dynamique** | QR généré pour une transaction précise (montant, e-commerce) | `generateDynamicQR()` |\r\n| **P2P** | Transfert entre particuliers | `generateP2PQR()` |\r\n\r\n`generateQRData()` route automatiquement selon le champ `type`.\r\n\r\n### Canaux marchands (`MerchantChannel`)\r\n\r\n| Constante | Code | Description |\r\n|---|---|---|\r\n| `STATIC_ONSITE` | 100 | QR statique sur site |\r\n| `STATIC_WITH_AMOUNT` | 110 | QR statique avec montant |\r\n| `STATIC_WITH_TXID` | 120 | QR statique avec ID transaction |\r\n| `STATIC_INVOICE` | 131 | QR statique sur facture |\r\n| `DYNAMIC_ONSITE` | 500 | QR dynamique sur site |\r\n| `DYNAMIC_ECOMMERCE_WEB` | 521 | QR dynamique e-commerce Web |\r\n| `DYNAMIC_ECOMMERCE_APP` | 522 | QR dynamique e-commerce App |\r\n| `P2P_STATIC` | 731 | QR statique pour particulier |\r\n\r\n---\r\n\r\n## 4. Référence de l'API\r\n\r\n### `UemoaQRService`\r\n\r\nFaçade principale, couvre la majorité des besoins d'intégration.\r\n\r\n| Méthode | Retour | Description |\r\n|---|---|---|\r\n| `generateQRData(data)` | `string` | Génère selon `data.type` |\r\n| `generateStaticQR(data)` | `string` | Génère un QR statique |\r\n| `generateDynamicQR(data)` | `string` | Génère un QR dynamique |\r\n| `generateP2PQR(data)` | `string` | Génère un QR P2P |\r\n| `generateQRImageFromString(qr, size?, margin?)` | `Promise<string>` | Image PNG en base64 |\r\n| `parseQRCode(qr)` | `QRPaymentData` | Parse, **lève** si invalide |\r\n| `getQRCodeDetails(qr)` | `object` | Parse, **ne lève jamais** (`{valid:false, error}` si KO) |\r\n| `validateQRCode(qr)` | `boolean` | Vérifie CRC + cohérence structurelle |\r\n\r\n### Structure des données d'entrée\r\n\r\n```ts\r\ninterface QRPaymentData {\r\n  type: \"STATIC\" | \"DYNAMIC\" | \"P2P\";\r\n  merchantInfo?: {\r\n    alias: string;        // identifiant du compte (obligatoire)\r\n    name: string;         // max 25 caractères\r\n    city: string;         // max 15 caractères\r\n    countryCode: string;  // BF, CI, TG, SN, ML, BJ, GW, NE\r\n    categoryCode?: string;\r\n  };\r\n  amount?: string | number;   // > 0, sans décimales (XOF)\r\n  transactionId?: string;     // ^[A-Za-z0-9-]{1,25}$\r\n  billReference?: string;     // ^[A-Za-z0-9-]{1,25}$\r\n  subscriptionId?: string;    // ^[A-Za-z0-9-]{1,25}$\r\n  merchantChannel?: MerchantChannelInfo;\r\n  dynamicUrl?: string;\r\n  additionalData?: Record<string, string>;\r\n}\r\n```\r\n\r\n### Classes bas niveau\r\n\r\nExportées pour les cas avancés : `QRParser`, `CRCCalculator`,\r\n`EMVFormatter`, `StaticQRGenerator`, `DynamicQRGenerator`, `P2PQRGenerator`.\r\n\r\n### Exceptions typées\r\n\r\n| Classe | Levée quand |\r\n|---|---|\r\n| `QRValidationError` | Donnée d'entrée invalide (montant, pays, charset…) |\r\n| `QRParsingError` | QR illisible ou CRC invalide |\r\n| `QRImageGenerationError` | Échec de génération de l'image PNG |\r\n\r\n---\r\n\r\n## 5. Règles de validation\r\n\r\n| Champ | Règle |\r\n|---|---|\r\n| `amount` | Strictement > 0, **sans décimales** (le XOF n'a pas de sous-unité). `\"2500.00\"` est toléré → `2500` ; `50.5` est rejeté |\r\n| `countryCode` | Doit appartenir à la zone UEMOA (8 pays) |\r\n| `name` | Obligatoire, max 25 caractères |\r\n| `city` | Obligatoire, max 15 caractères |\r\n| `transactionId`, `billReference`, `subscriptionId` | `^[A-Za-z0-9-]{1,25}$` |\r\n| Tous les champs texte | Doivent être représentables en **ISO-8859-1** |\r\n\r\n### Contrainte de charset\r\n\r\nLes caractères hors ISO-8859-1 (`œ`, `€`, ou les orthographes de langues\r\nlocales comme `ɛ`, `ɔ`, `ŋ`) sont **rejetés** avec une `QRValidationError`.\r\n\r\nRaison : l'encodage ISO-8859-1 se comporte différemment selon le langage\r\nface à un caractère hors plage (remplacement silencieux en Java, exception\r\nen Python, corruption silencieuse en Node). Rejeter en amont garantit un\r\ncomportement identique dans les trois SDK. Détails dans\r\n[`docs/DECISIONS.md`](docs/DECISIONS.md).\r\n\r\n---\r\n\r\n## 6. Interopérabilité entre SDK\r\n\r\nUn QR généré par ce SDK est lisible par les SDK Java et Python, et\r\ninversement. C'est garanti par deux mécanismes :\r\n\r\n**Vecteurs de test partagés** — le fichier\r\n[`test-vectors/uemoa-qr-test-vectors.json`](test-vectors/uemoa-qr-test-vectors.json)\r\nest la source de vérité commune aux trois SDK. Il contient la spécification\r\nécrite et trois familles de vecteurs :\r\n\r\n- `interopVectors` — entrées à parser vers les mêmes données\r\n- `canonicalVectors` — sorties exactes attendues (générées par la référence Java)\r\n- `validationVectors` — entrées invalides à rejeter partout pareil\r\n\r\n**Tests croisés** — la suite de tests relit des QR réellement générés par\r\nle SDK Java et vérifie que les données décodées sont identiques.\r\n\r\n### Ordre des sous-champs\r\n\r\n```ts\r\nimport { setFieldOrderStrategy } from \"@aveplus_dev/uemoa-qrcode-sdk\";\r\n\r\nsetFieldOrderStrategy(\"java-hashmap\"); // défaut : reproduit le SDK Java actuel\r\nsetFieldOrderStrategy(\"sorted\");       // ordre canonique trié\r\n```\r\n\r\nCe réglage n'affecte **jamais** la lecture : le parsing se fait par tag et\r\nnon par position, et le CRC est recalculé sur les octets reçus. Les deux\r\nmodes sont donc pleinement interopérables ; seule la comparaison\r\noctet-à-octet des chaînes diffère.\r\n\r\n---\r\n\r\n## 7. Développement local\r\n\r\n```bash\r\ngit clone https://gitlab.avepay.net/qrcode/qrcode-module-backend-js.git\r\ncd qrcode-module-backend-js\r\n\r\nnpm install     # installe les dépendances\r\nnpm test        # lance les 100 tests (Vitest)\r\nnpm run build   # génère dist/ (CJS + ESM + types)\r\n```\r\n\r\n### Prérequis\r\n\r\n- Node.js 18+\r\n- npm 9+\r\n\r\n### Organisation des tests\r\n\r\n| Fichier | Contenu |\r\n|---|---|\r\n| `javaPortedTests.test.ts` | 18 tests repris un-à-un des tests JUnit du SDK Java |\r\n| `sharedVectors.test.ts` | Tests pilotés par les vecteurs partagés |\r\n| `crossValidation.test.ts` | Tests croisés avec des QR réellement générés par Java |\r\n| `charset.test.ts` | Règle ISO-8859-1 et cohérence longueur TLV / octets |\r\n| `crc.test.ts`, `emvFormatter.test.ts` | Tests unitaires bas niveau |\r\n| `orderStrategy.test.ts`, `reviewFixes.test.ts` | Stratégies d'ordre et régressions |\r\n\r\n---\r\n\r\n## 8. Publication du SDK sur npm\r\n\r\n### Prérequis\r\n\r\n- Être membre de l'organisation npm **`aveplus_dev`**\r\n- Être authentifié : `npm login` puis vérifier avec `npm whoami`\r\n\r\n### Procédure\r\n\r\n```bash\r\n# 1. Vérifier que tout passe\r\nnpm test\r\nnpm run build\r\n\r\n# 2. Incrémenter la version (semver)\r\nnpm version patch   # correction / documentation  (1.0.0 -> 1.0.1)\r\nnpm version minor   # nouvelle fonctionnalité     (1.0.1 -> 1.1.0)\r\nnpm version major   # changement incompatible     (1.1.0 -> 2.0.0)\r\n\r\n# 3. Vérifier le contenu du package avant envoi\r\nnpm publish --dry-run\r\n\r\n# 4. Publier\r\nnpm publish --access public\r\n```\r\n\r\n### Contenu publié\r\n\r\nSeuls `dist/`, `test-vectors/` et `docs/` sont inclus (champ `files` du\r\n`package.json`). Le code source TypeScript et les tests ne sont pas\r\ndistribués.\r\n\r\n### Authentification à double facteur\r\n\r\nL'organisation `aveplus_dev` impose la 2FA pour publier. Deux options :\r\n\r\n- **2FA activée sur le compte** : ajouter `--otp=XXXXXX` à la commande\r\n- **Token granulaire** : créer un token avec l'option *bypass 2FA*, limité\r\n  au scope `@aveplus_dev` et avec une expiration courte, puis :\r\n\r\n  ```bash\r\n  npm config set //registry.npmjs.org/:_authToken=<TOKEN>\r\n  npm publish --access public\r\n  npm config delete //registry.npmjs.org/:_authToken   # nettoyer après\r\n  ```\r\n\r\n  > Ce token contourne la 2FA sur un module de paiement : ne jamais le\r\n  > committer, le révoquer immédiatement après usage.\r\n\r\n### Notes\r\n\r\n- Le package est publié en **accès public**. Les packages privés npm\r\n  nécessitent un plan payant (erreur `402 Payment Required` sinon).\r\n- Une version publiée ne peut pas être remplacée : toute correction passe\r\n  par une nouvelle version.\r\n\r\n---\r\n\r\n## 9. Structure du projet\r\n\r\n```\r\nsrc/\r\n  index.ts               → UemoaQRService (façade publique)\r\n  models.ts              → MerchantInfo, QRPaymentData, MerchantChannel\r\n  errors.ts              → exceptions typées\r\n  crc.ts                 → CRC16-CCITT (ISO-8859-1, poly 0x1021, init 0xFFFF)\r\n  emvFormatter.ts        → formatage / parsing TLV EMVCo\r\n  validator.ts           → règles de validation\r\n  parser.ts              → lecture d'un QR\r\n  qrImage.ts             → génération d'image PNG (lib qrcode)\r\n  generators/\r\n    base.ts              → logique commune + stratégies d'ordre\r\n    staticGenerator.ts\r\n    dynamicGenerator.ts\r\n    p2pGenerator.ts\r\n\r\ntests/                   → 100 tests (Vitest)\r\ntest-vectors/            → vecteurs partagés entre les 3 SDK\r\ndocs/DECISIONS.md        → décisions de conception détaillées\r\n```\r\n","readmeFilename":"README.md"}