Metadata-Version: 2.4
Name: bceao-pispi-qrcode
Version: 1.0.1
Summary: Pyhton SDK pour générer et décoder des QR Codes PI-SPI conformes EMV.
Home-page: https://github.com/pi-spi/qrcode-python.git
Author: BCEAO PI-SPI
Author-email: BCEAO PI-SPI <pisfn-sandbox@bceao.int>
License: MIT
Keywords: bceao,pispi,pi-spi,pi,spi,qr,emv,uemoa,payment
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: segno>=1.5.0
Requires-Dist: qrcode[pil]>=7.4.0
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# BCEAO PI-SPI QR Code Python Module

Le package Phyton `bceao_pispi_qrcode` fournit une interface robuste, sécurisée et conforme aux standards EMV pour intégrer les QR Codes PI-SPI, permettant aux applications python d'interagir avec l'écosystème PI-SPI de la BCEAO.

---

## Fonctionnalités principales

- Génération de QR Codes **statiques** et **dynamiques**.
- Construction de payloads **conformes EMV**.
- Décodage et vérification des payloads QR.
- Calcul automatique du **CRC16** pour l'intégrité des données.
- Validation des **alias (UUID v4)** pour la sécurité des comptes.
- Gestion complète des **exceptions** avec codes d'erreur structurés.
- Génération de QR Codes en **SVG** pour export ou impression.

Ce package est conçu pour les systèmes de paiement dans les pays de l'UEMOA.

---

## Intégration

### 1️⃣ Installation

Dans le dossier racine :

```sh
pip install build
python -m build
pip install bceao-pispi-qrcode
```


2️⃣ Importer la bibliothèque
```py
from bceao_pispi_qrcode import *
```

3️⃣ Générer un payload QR
```py
    input_data = PispiQrPayloadInput(
        qr_type= PispiQrType.STATIC,              # QR Code dynamique
        qr_user= PispiQrUser.BUSINESS_ENTITY,       # Personne morale / entreprise
        alias= '111c3e1b-4312-49ec-b75e-4c8c74c10fd7', # Alias du compte (UUID v4)
        country= PispiQrCountry.CI,               # Code pays
        amount= 5000,                             # Montant de la transaction (optionnel)
        merchant_channel= '000',                   # Canal marchand
        reference_label= 'TX000000001',            # Label de référence (optionnel pour statique, obligatoire pour dynamique)
    )
    payload = PispiQrPayload.encode(input_data)
```
### PispiQrPayloadInput
| Champ               | Type               | Valeurs possibles                                                                                                                            | Contrainte    | Description                          |
| ------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------ |
| **qr_user**          | `PispiQrType`      | • `STATIC`<br>• `DYNAMIC`                                                                                                                    | ✅ Obligatoire | Type de QR Code à générer            |
| **qr_user**          | `PispiQrUser`      | • `INDIVIDUAL_CUSTOMER` — Personne physique<br>• `INDIVIDUAL_MERCHANT` — Personne physique commerçante<br>• `BUSINESS_ENTITY` — Personne morale | ✅ Obligatoire | Catégorie d’utilisateur PI-SPI       |
| **alias**           | `str` (UUID v4) | Format UUID v4                                                                                                                               | ✅ Obligatoire | Alias du compte PI-SPI               |
| **country**         | `PispiQrCountry`   | • `BJ` • `BF` • `CI` • `GW`<br>• `ML` • `NE` • `SN` • `TG`                                                                                   | ✅ Obligatoire | Code pays ISO 3166-1 alpha-2         |
| **merchant_channel** | `str`           | • `731` — Personne physique<br>• `000` — Commerçant / Personne morale<br>• `400` — Personne morale                                           | ✅ Obligatoire | Code canal marchand BCEAO            |
| **amount**          | `float`           | Valeur numérique                                                                                                                             | ⚪ Optionnel   | Montant de la transaction            |
| **reference_label**  | `str`           | Max. 24 caractères                                                                                                                           | ⚪ Optionnel   | Référence unique de transaction (ID) |



4️⃣ Générer un QR Code en SVG
```py
    svg_qr = PispiQrGenerator.svg(
        payload,
        size= 200,                  # Taille du QR
        pi_icon_size= 40,           # Taille du logo PI-SPI
        background_color= 'white',
        data_color= 'black',
        eye_color= 'black',
        margin= 10,
    )
```
| Paramètre           | Type     | Défaut  | Description                 |
| ------------------- | -------- | ------- | --------------------------- |
| **payload**         | `str` | —       | Payload EMV à encoder       |
| **size**            | `float` | `200`   | Taille totale du QR         |
| **pi_icon_size**      | `float` | `40`    | Taille du logo central      |
| **background_color** | `Optional[str]`  | `white` | Couleur de fond             |
| **data_color**       | `str`  | `black` | Couleur des modules         |
| **eye_color**        | `str`  | `black` | Couleur des finder patterns |
| **margin**          | `float` | `10`    | Marge externe (quiet zone)  |

Vous pouvez ensuite l'afficher dans un widget SvgPicture

5️⃣ Décoder un payload QR
```py
result = PispiQrPayload.decode(payload)
print(result.to_dict())
print(result.merchant_account_information.account_proxy)
print(result.transaction_mount)
```
### PispiQrPayloadDecodeResult
| Champ                          | Type                         | Tag EMV | Obligatoire | Description                                     |
| ------------------------------ | ---------------------------- | ------- | ----------- | ----------------------------------------------- |
| **payload_format_indicator**     | `str`                     | `00`    | ✅ Oui       | Indicateur de format (toujours `"01"`)         |
| **merchant_account_information** | `MerchantAccountInformation` | `36`    | ✅ Oui       | Informations du compte marchand                 |
| **merchant_category_code**       | `str`                     | `52`    | ✅ Oui       | Code catégoriel marchand (MCC)                  |
| **transaction_currency**        | `str`                     | `53`    | ✅ Oui       | Devise de transaction (952 = XOF)               |
| **transaction_amount**          | `float`                     | `54`    | ⚪ Optionnel | Montant de la transaction (null si QR statique) |
| **country_code**                | `str`                     | `58`    | ✅ Oui       | Code pays ISO 3166-1 alpha-2                    |
| **merchant_name**               | `str`                     | `59`    | ✅ Oui       | Nom du marchand (toujours `"X"`)                |
| **merchant_city**               | `str`                     | `60`    | ✅ Oui       | Ville du marchand (toujours `"X"`)              |
| **additional_data**             | `AdditionalData`             | `62`    | ✅ Oui       | Données additionnelles                          |
| **crc**                        | `str`                     | `63`    | ✅ Oui       | Code CRC16 de validation                        |

#### MerchantAccountInformation
| Champ            | Type     | Sous-Tag | Obligatoire | Description                                |
| ---------------- | -------- | -------- | ----------- | ------------------------------------------ |
| **gui**          | `str` | `36.00`  | ✅ Oui       | Global Unique Identifier du système PI-SPI (toujours `"int.bceao.pi"`) |
| **account_proxy** | `str` | `36.01`  | ✅ Oui       | Alias du compte (UUID v4)                  |

#### AdditionalData
| Champ               | Type     | Sous-Tag | Obligatoire | Description                     |
| ------------------- | -------- | -------- | ----------- | ------------------------------- |
| **merchant_channel** | `str` | `62.11`  | ✅ Oui       | Canal marchand            |
| **reference_label**  | `str` | `62.05`  | ⚪ Optionnel | Référence unique de transaction |



6️⃣ Valider un alias
```py
    is_valid = PispiQrPayload.isValidAlias(
        '111c3e1b-4312-49ec-b75e-4c8c74c10fd7'
    )
```

---

## Exemple

---

```py
from bceao_pispi_qrcode.pispi_qr_payload import PispiQrPayload
from bceao_pispi_qrcode.pispi_qr_generator import PispiQrGenerator

from bceao_pispi_qrcode.models.enums import PispiQrCountry, PispiQrType, PispiQrUser
from bceao_pispi_qrcode.models.models import PispiQrPayloadInput


input = PispiQrPayloadInput(
    PispiQrType.DYNAMIC,
    PispiQrUser.BUSINESS_ENTITY,
    "550e8400-e29b-41d4-a716-446655440000",
    PispiQrCountry.CI,
    "400",
    amount=2000,
    reference_label= "Tx-caise1"
)

payload = PispiQrPayload.encode(input)

print("================ PAYLOAD ENCODÉ ==============")
print(payload)

decode = PispiQrPayload.decode(payload)
print("================ PAYLOAD DECODÉ ==============")
print(decode.to_dict())

svg = PispiQrGenerator.svg(payload,background_color='white')
print("================ PAYLOAD SVG STRING ==============")
print(svg)
```


Sécurité & Conformité

Payloads conformes EMV.
Validation CRC16 pour l'intégrité des données.
Validation d’alias pour correspondance correcte des comptes.
Gestion des exceptions structurées.
Seuls les pays et types d'utilisateurs supportés sont autorisés.

Types d'utilisateurs QR

individualCustomer – Personne physique (non marchand)
individualMerchant – Personne physique marchande
businessEntity – Personne morale / entreprise

Types de QR Code

static – QR Code fixe avec payload statique
dynamic – QR Code à usage unique par transaction

⚠️ Gestion des exceptions

Toutes les exceptions sont typées et fournissent des codes d'erreur explicites :

PispiQrPayloadInputException – Levée lors de la création d’un payload.
PispiQrPayloadDecodeException – Levée lors du décodage d’un payload.

Les codes d’erreur détaillés se trouvent dans PispiQrPayloadDecodeError.

Licence

MIT License – libre d’utilisation et de modification, même dans des applications commerciales.

Support

Pour toute question, problème ou contribution :

Email : pisfn-sandbox@bceao.int

GitHub : [https://github.com/pi-spi/qrcode-python.git]
