Skip to content

PIX Cash-In -- Générer un QR Code

Génère une facturation PIX avec QR Code pour la réception de valeurs sur le compte associé à votre API Key.

Endpoint

POST /api/external/pix/cash-in

Headers

HeaderTypeObligatoireDescription
AuthorizationStringOuiApiKey {client_id}:{client_secret}
Content-TypeStringOuiapplication/json
hmacStringOuiSignature HMAC-SHA512 du body (en savoir plus)
Idempotency-KeyStringNonClé unique pour éviter le traitement en double (max 256 caractères)
X-Key-CaseStringNonDéfinissez à camelCase pour recevoir les champs de la réponse en camelCase (par défaut snake_case)

Request Body

ChampTypeObligatoireDescriptionExemple
amountIntegerOuiValeur en centavos (R$ 30,00 = 3000)3000
descriptionStringNonDescription de la facturation. Si omis, la valeur par défaut est "Cobranca PIX"."Commande #1234"
external_idStringNonIdentifiant de votre système pour le suivi. Max 128 caractères. Uniquement a-zA-Z0-9._:- (les valeurs hors pattern sont silencieusement écartées et le champ revient en null -- voir avertissement ci-dessous). Retourné dans les réponses et webhooks."order-9876"
pix_keyStringNonClé PIX spécifique pour générer le QR Code. Si omise, utilise la clé active la plus récente du compte (ordre par inserted_at DESC)."12345678901"
cityStringNonVille du bénéficiaire dans le QR Code. Par défaut : SAO PAULO. Tronquée automatiquement à 15 caractères."RIO DE JANEIRO"

Envoyez amount TOUJOURS en entier (en centavos)

Le champ amount DOIT être un entier en centavos. N'envoyez JAMAIS des valeurs float/decimal :

  • "amount": 3000 → R$ 30,00
  • "amount": 30.00 → interprété comme 30 centavos = R$ 0,30 (facturation 100× plus petite que voulue)
  • "amount": 30 → R$ 0,30 (également incorrect)

En JavaScript, convertissez toujours avec Math.round avant d'envoyer :

js
const valeurEnReais = 30.0;
const amount = Math.round(valeurEnReais * 100); // 3000

En Python : amount = round(valeur_en_reais * 100). En Go : amount := int(math.Round(valeurEnReais * 100)).

Valeurs monétaires (entrée × réponse)

Les valeurs d'entrée sont en centavos (R$ 1,00 = 100). Les valeurs de réponse sont en unités de base (R$ 1,00 = 10000). Pour convertir la réponse en reais, divisez par 10 000. N'utilisez jamais de virgule flottante dans aucun sens.

external_id invalide = rejet silencieux

Si le external_id contient des caractères hors de a-zA-Z0-9._:-, dépasse 128 octets, ou ne contient que des espaces blancs, le serveur rejette silencieusement la valeur et la réponse revient avec "external_id": null (aucune erreur n'est retournée). Vérifiez toujours le external_id présent dans la réponse avant de l'utiliser pour la conciliation -- s'il est null, votre identifiant a été rejeté par la validation.

Exemple

bash
BODY='{"amount":3000,"description":"Commande #1234","external_id":"order-9876"}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')

curl -X POST https://api.owem.com.br/api/external/pix/cash-in \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "hmac: $HMAC" \
  -d "$BODY"

Réponse de succès (200)

json
{
  "worked": true,
  "transaction_id": "7popu57v6us7p6pcicgq12345",
  "qr_code": "00020126580014br.gov.bcb.pix...",
  "qr_code_image": "data:image/png;base64,iVBORw0KGgo...",
  "external_id": "order-9876",
  "amount": 300000,
  "status": "active",
  "type": "dynamic",
  "location_url": "https://qrcode.owem.com.br/pix/v2/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "expires_at": "2026-03-07T16:30:00Z"
}
ChampTypeDescription
workedBooleantrue indique le succès de l'opération
transaction_idString | nullIdentifiant unique de la facturation (tx_id du QR Code, alphanumérique max 35 caractères selon spec BACEN). null uniquement en cas de défaillances internes rares de persistance
qr_codeString | nullCode EMV copier-coller pour paiement. null (valeur JSON littérale, pas la chaîne "null") uniquement en cas de défaillances internes rares de persistance
qr_code_imageStringImage du QR Code encodée en base64 (PNG, préfixe data:image/png;base64,). Chaîne vide ("") en cas de défaillances internes rares
external_idString | nullVotre identifiant, retourné tel qu'envoyé. null s'il n'a pas été renseigné, s'il est invalide (voir avertissement ci-dessus), ou s'il a été rejeté par la sanitisation
amountIntegerValeur de la facturation en unités de base (÷ 10 000 pour reais). 300000 = R$ 30,00
statusStringStatus initial toujours active (QR Code actif pour paiement). L'évolution ultérieure (paid/expired/cancelled) est visible dans les consultations et webhooks (voir cycle de vie du QR)
typeString | nullType du QR Code : dynamic (par défaut depuis avril/2026) ou static. Dynamique exige que la banque du payeur consulte location_url et valide le JWS
location_urlString | nullURL de consultation du payload dynamique du BACEN (ex. : https://qrcode.owem.com.br/pix/v2/{uuid}). Ce n'est pas le JWS lui-même -- la banque du payeur fait un GET sur cet endpoint pour recevoir le payload ISO 20022 signé (JWS PS256). Présent uniquement en QR dynamique ; null en statique
expires_atString | nullDate/heure d'expiration du QR Code en ISO 8601 UTC avec suffixe Z (ex. : 2026-03-07T16:30:00Z). null si QR sans expiration

QR dynamique est le défaut

Tous les comptes en production utilisent QR dynamique par défaut depuis avril/2026. Le QR dynamique inclut une URL (location_url) que la banque du payeur consulte pour obtenir le payload signé via JWS -- compatibilité maximisée avec les banques BACEN-strict.

Réponse d'erreur (400)

json
{
  "worked": false,
  "detail": "O campo amount é obrigatório"
}

Réponse d'erreur (401)

json
{
  "error": {
    "status": 401,
    "message": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
  }
}

Réponse d'erreur (422)

json
{
  "worked": false,
  "detail": "Invalid HMAC signature"
}

Flux recommandé

  1. Générez la facturation avec cet endpoint
  2. Affichez le QR Code (qr_code_image) ou le code copier-coller (qr_code) au payeur
  3. Recevez la confirmation via Webhook quand le paiement est effectué
  4. Ou consultez le status : par ID, par E2E, par Tag

Validité du QR Code

Le QR Code généré a une validité de 1 heure par défaut. La validité est configurable par compte (champ qrcode_expiration_seconds) ; vérifiez toujours le expires_at retourné dans la réponse pour la date/heure exacte.

Après expiration, le status passe à expired automatiquement (via un worker interne, routine toutes les 5 minutes). Pour les facturations annulées manuellement par le merchant, le status est cancelled. Voir la liste complète des status dans Consulter Cash-In par ID.

Cycle de vie du QR Code

Le QR Code passe par ces états après la création :

ÉtatOrigineDescription
activeInitialQR généré avec succès, prêt à être scanné/payé
paidPaiement reçuPaiement BACEN confirmé et lié au QR (via worker interne après ACCC)
expiredWorker QrExpirationCheckerTTL atteint expires_at. Le worker tourne toutes les 5 min marquant les QR active expirés et déclenchant le webhook pix.charge.expired
cancelledManuel ou en masseAnnulé par le merchant (via portail) ou lors d'opérations administratives (ex. : migration statique→dynamique en avril/2026 a annulé les QRs statiques actifs avec valeur)
usedLegacyÉtat intermédiaire transitoire du pipeline ancien. Les nouveaux clients doivent le traiter comme équivalent à paid

Dans les consultations, paid peut apparaître comme settled

Les endpoints GET /transactions/:id et équivalents retournent le champ status du point de vue de la transaction (pas du QR). Un QR payé apparaît comme status: "settled" dans la consultation, avec type: "pix" (ou type: "pix_qrcode" dans une courte fenêtre post-settlement tant que la ligne finale dans transactions est encore en persistance). Voir Consulter Cash-In par ID pour le tableau complet.

Owem Pay Instituição de Pagamento — ISPB 37839059