Tiago Fortunato
ProjectsOdysPayment

PIX Flow

PIX EMV BR Code generation, CRC16, QR rendering

PIX Flow

The PIX payment system, a cornerstone of the Brazilian financial landscape, offers instant payments and transfers. This module is dedicated to generating static PIX QR codes, adhering to the EMV BR Code standard, and rendering them for users. It provides a robust mechanism for professionals to receive payments, integrating directly with their profile information stored in the application's database.

Overview

The PIX flow within the application is primarily managed by two distinct but interconnected components: src/lib/pix.ts and src/components/pix-qr.tsx. The src/lib/pix.ts file serves as the backend logic, responsible for constructing the raw EMV BR Code payload string, which is the standardized format for static PIX QR codes. This payload includes all necessary merchant and transaction details. Complementing this, src/components/pix-qr.tsx is a client-side React component that takes the generated payload and visually renders it as a QR code, alongside user-friendly information and a convenient copy mechanism.

The system leverages data from the professionals table, which contains 24 columns, including crucial PIX-related fields such as pixKeyType and pixKey. These fields are essential for identifying the recipient of the PIX payment and are directly used in the payload generation process.

PIX Payload Generation (src/lib/pix.ts)

The src/lib/pix.ts file is the heart of the PIX EMV BR Code generation. It encapsulates the logic required to assemble a compliant PIX payload string.

At its core, the field helper function simplifies the construction of EMV TLV (Tag-Length-Value) structures. It takes an id (tag) and a value, then formats them into a string where the length of the value is prepended, padded to two digits. This adherence to the TLV format is fundamental for EMV BR Code compliance, ensuring that each piece of data can be correctly parsed by PIX-enabled payment applications.

A critical component for data integrity in the EMV BR Code standard is the CRC16 checksum. The crc16 function implements the CRC-16-CCITT algorithm, using a polynomial of 0x1021 and an initial value of 0xffff. This function calculates a 4-character hexadecimal checksum for the entire payload string. This checksum is appended to the end of the payload, allowing scanning applications to verify that the data has not been corrupted during transmission or display. Without a correct CRC16, the PIX QR code would be invalid.

The buildPixPayload function orchestrates the entire payload construction. It accepts parameters such as the key (the PIX key itself), the name of the professional, an optional city (defaulting to "Brasil"), and an optional amount (specified in cents). Before incorporating the name and city into the payload, the function normalizes these strings by removing diacritics and converting them to uppercase, then truncates them to fit the maximum allowed lengths (25 characters for name, 15 for city) as per the PIX specification.

The function then meticulously constructs the various fields of the EMV BR Code:

  • 00: Payload Format Indicator, always "01" for static PIX.
  • 26: Merchant Account Information, which itself contains sub-fields like 00 for the GUID "br.gov.bcb.pix" and 01 for the actual PIX key.
  • 52: Merchant Category Code, set to "0000".
  • 53: Transaction Currency, set to "986" for Brazilian Real.
  • 54: Transaction Amount. This field is conditionally included only if an amount is provided and is greater than zero. The amount (in cents) is converted to a decimal string with two fixed decimal places (e.g., 8000 cents becomes "80.00").
  • 58: Country Code, "BR" for Brazil.
  • 59: Merchant Name, using the normalized professionalName.
  • 60: Merchant City, using the normalized city.
  • 62: Additional Data Field, with sub-field 05 set to "***" as a transaction ID placeholder.
  • 6304: The CRC placeholder, which is later replaced by the calculated CRC16 value.

Finally, the function concatenates all these fields and appends the calculated CRC16 checksum, producing the complete, spec-compliant EMV BR Code string.

PIX QR Code Component (src/components/pix-qr.tsx)

The src/components/pix-qr.tsx file defines a React client component responsible for displaying the PIX QR code and related payment information to the user. The "use client" directive at the top signifies that this component is intended for client-side rendering, allowing it to utilize React hooks and browser APIs.

The component receives several props: pixKey, pixKeyType, professionalName, an optional amount (in cents), and paymentType. These props provide the necessary data to generate and display the QR code.

It uses React's useState hook to manage a copied state, which provides visual feedback to the user when the PIX code has been copied to their clipboard.

A PIX_TYPE_LABELS object maps the pixKeyType (e.g., "phone", "email", "cpf") to user-friendly Portuguese labels, enhancing the user experience by clearly indicating the type of PIX key being displayed.

The includeAmount variable determines whether the transaction amount should be embedded in the PIX payload. This decision is based on the paymentType prop (e.g., "upfront" payments typically include an amount, while "after" payments might not) and whether a positive amount is provided. This conditional logic ensures flexibility in how payments are presented.

The buildPixPayload function from src/lib/pix.ts is then called with the relevant props to generate the actual PIX payload string. This payload is the data that will be encoded into the QR code.

The handleCopy asynchronous function is triggered when the user clicks the "Copiar código PIX" button. It uses the browser's navigator.clipboard.writeText API to copy the generated payload string to the user's clipboard. After a successful copy, it updates the copied state to display "Copiado!" temporarily, then reverts after 2 seconds.

The component's JSX renders a visually distinct payment section. It provides clear instructions based on the paymentType and prominently displays the QR code using the QRCodeSVG component, which takes the payload as its value. Below the QR code, the pixKey is shown, along with its human-readable type from PIX_TYPE_LABELS. Finally, a button allows users to copy the PIX code, and a summary of the amount and favored professional name is displayed, again conditionally based on whether an amount is included.

Design decisions

The design of the PIX flow reflects a clear separation of concerns and adherence to the PIX standard:

  • Separation of Logic and UI: The core logic for generating the EMV BR Code payload is isolated in src/lib/pix.ts. This makes the payload generation reusable, testable, and independent of any specific UI framework. The src/components/pix-qr.tsx component then focuses solely on presenting this information to the user, including rendering the QR code and handling user interactions like copying.
  • Standard Compliance: The explicit implementation of the field helper and the crc16 function directly addresses the requirements of the Banco Central do Brasil's Manual de Padrões para Iniciação do Pix. This ensures that the generated QR codes are universally scannable by any PIX-enabled application.
  • Client-Side QR Rendering: Using the QRCodeSVG library within a client component ("use client") allows the QR code to be generated dynamically in the user's browser. This avoids server-side rendering overhead for the image itself and ensures that the QR code is always up-to-date with the latest payload.
  • Conditional Amount Inclusion: The paymentType and amount props allow for flexible payment scenarios. By conditionally including the amount in the payload, the system supports both fixed-amount payments (e.g., for upfront bookings) and open-amount payments (e.g., for payments "after" a service, where the client might enter the amount manually).
  • User Experience for Copying: The handleCopy function and the copied state provide immediate, clear feedback to the user, improving usability by confirming that the PIX code has been successfully copied.
  • Data Normalization: The normalize("NFD").replace(/[\u0300-\u036f]/g, "").slice(0, X).toUpperCase() pattern for merchantName and merchantCity ensures that these fields conform to the character set and length restrictions of the EMV BR Code specification, preventing potential parsing errors by PIX applications.

Potential improvements

  1. Robust Input Validation in buildPixPayload: The buildPixPayload function in src/lib/pix.ts currently assumes valid inputs for key, name, city, and amount. Adding explicit validation for these parameters (e.g., checking key format based on pixKeyType, ensuring name and city are not empty after normalization) could prevent the generation of malformed PIX payloads and provide clearer error messages earlier in the process.
  2. Dynamic City Information: The city parameter in buildPixPayload in src/lib/pix.ts currently defaults to "Brasil". While functional, fetching the actual city from the professionals table (if available) or allowing it to be passed as a prop to PixQR would provide more accurate and personalized PIX codes, enhancing the user experience and compliance for professionals outside major metropolitan areas.
  3. Amount Display Precision: In src/components/pix-qr.tsx, the displayed amount uses (amount! / 100).toFixed(0). This rounds the amount to a whole number of Reais. While the PIX payload itself uses toFixed(2), the display might lose precision if the amount includes cents (e.g., R$80.50 would display as R$80). Changing this to toFixed(2) for display would ensure consistency and accuracy for the user.
  4. Internationalization of PIX_TYPE_LABELS: The PIX_TYPE_LABELS object in src/components/pix-qr.tsx contains hardcoded Portuguese strings. For an application that might expand to support multiple languages, these labels should be moved to an internationalization (i18n) system to allow for dynamic language switching.

References

  • src/lib/pix.ts
  • src/components/pix-qr.tsx

On this page