@@ -35,20 +35,20 @@ export class StaticPixService {
3535 */
3636 private calculateCRC16 ( payload : string ) : string {
3737 const polynomial = 0x1021 ;
38- let crc = 0xFFFF ;
39-
38+ let crc = 0xffff ;
39+
4040 for ( let i = 0 ; i < payload . length ; i ++ ) {
41- crc ^= ( payload . charCodeAt ( i ) << 8 ) ;
41+ crc ^= payload . charCodeAt ( i ) << 8 ;
4242 for ( let j = 0 ; j < 8 ; j ++ ) {
4343 if ( crc & 0x8000 ) {
4444 crc = ( crc << 1 ) ^ polynomial ;
4545 } else {
4646 crc = crc << 1 ;
4747 }
48- crc &= 0xFFFF ;
48+ crc &= 0xffff ;
4949 }
5050 }
51-
51+
5252 return crc . toString ( 16 ) . toUpperCase ( ) . padStart ( 4 , '0' ) ;
5353 }
5454
@@ -68,34 +68,52 @@ export class StaticPixService {
6868 if ( pixKey . includes ( '@' ) && pixKey . includes ( '.' ) ) {
6969 return / ^ [ ^ \s @ ] + @ [ ^ \s @ ] + \. [ ^ \s @ ] + $ / . test ( pixKey ) ;
7070 }
71-
71+
7272 // Phone format (+5511999999999)
7373 if ( pixKey . startsWith ( '+55' ) ) {
7474 return / ^ \+ 5 5 \d { 10 , 11 } $ / . test ( pixKey ) ;
7575 }
76-
76+
7777 // CPF format (11 digits)
7878 if ( / ^ \d { 11 } $ / . test ( pixKey ) ) {
7979 return true ;
8080 }
81-
81+
8282 // CNPJ format (14 digits)
8383 if ( / ^ \d { 14 } $ / . test ( pixKey ) ) {
8484 return true ;
8585 }
86-
86+
8787 // Random key (UUID format)
8888 if ( / ^ [ 0 - 9 a - f ] { 8 } - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 12 } $ / i. test ( pixKey ) ) {
8989 return true ;
9090 }
91-
91+
9292 return false ;
9393 }
9494
95+ /**
96+ * Removes accents and special characters from text
97+ */
98+ private removeAccents ( text : string ) : string {
99+ return text
100+ . normalize ( 'NFD' )
101+ . replace ( / [ \u0300 - \u036f ] / g, '' )
102+ . replace ( / [ ^ \w \s ] / gi, ' ' )
103+ . replace ( / \s + / g, ' ' )
104+ . trim ( ) ;
105+ }
106+
95107 /**
96108 * Generates static Pix payload according to BACEN EMV 4.0 standard
97109 */
98- private generatePixPayload ( { pixKey, amount, recipientName, recipientCity, description } : StaticPixRequest ) : string {
110+ private generatePixPayload ( {
111+ pixKey,
112+ amount,
113+ recipientName,
114+ recipientCity,
115+ description,
116+ } : StaticPixRequest ) : string {
99117 // Validate inputs
100118 if ( ! this . validatePixKey ( pixKey ) ) {
101119 throw new Error ( 'Invalid Pix key format' ) ;
@@ -105,9 +123,11 @@ export class StaticPixService {
105123 throw new Error ( 'Amount must be between 0.01 and 999,999.99' ) ;
106124 }
107125
108- // Truncate names if they exceed limits
109- const truncatedRecipientName = recipientName . substring ( 0 , 25 ) ;
110- const truncatedRecipientCity = recipientCity . substring ( 0 , 15 ) ;
126+ // Remove accents and truncate names if they exceed limits
127+ const cleanRecipientName = this . removeAccents ( recipientName ) ;
128+ const cleanRecipientCity = this . removeAccents ( recipientCity ) ;
129+ const truncatedRecipientName = cleanRecipientName . substring ( 0 , 25 ) ;
130+ const truncatedRecipientCity = cleanRecipientCity . substring ( 0 , 15 ) ;
111131
112132 // Build EMV payload
113133 let payload = '' ;
@@ -119,9 +139,8 @@ export class StaticPixService {
119139 payload += this . formatEMVField ( '01' , '12' ) ;
120140
121141 // Merchant Account Information (26)
122- const merchantInfo =
123- this . formatEMVField ( '00' , 'BR.GOV.BCB.PIX' ) +
124- this . formatEMVField ( '01' , pixKey ) ;
142+ const merchantInfo =
143+ this . formatEMVField ( '00' , 'BR.GOV.BCB.PIX' ) + this . formatEMVField ( '01' , pixKey ) ;
125144 payload += this . formatEMVField ( '26' , merchantInfo ) ;
126145
127146 // Merchant Category Code (52) - Generic
@@ -144,7 +163,8 @@ export class StaticPixService {
144163
145164 // Additional Data Field Template (62)
146165 if ( description ) {
147- const additionalData = this . formatEMVField ( '05' , description . substring ( 0 , 25 ) ) ;
166+ const cleanDescription = this . removeAccents ( description ) ;
167+ const additionalData = this . formatEMVField ( '05' , cleanDescription . substring ( 0 , 25 ) ) ;
148168 payload += this . formatEMVField ( '62' , additionalData ) ;
149169 }
150170
@@ -166,14 +186,16 @@ export class StaticPixService {
166186 margin : 1 ,
167187 color : {
168188 dark : '#000000' ,
169- light : '#FFFFFF'
189+ light : '#FFFFFF' ,
170190 } ,
171- width : 256
191+ width : 256 ,
172192 } ) ;
173-
193+
174194 return qrCodeDataUrl ;
175195 } catch ( error ) {
176- throw new Error ( `Failed to generate QR code: ${ error instanceof Error ? error . message : 'Unknown error' } ` ) ;
196+ throw new Error (
197+ `Failed to generate QR code: ${ error instanceof Error ? error . message : 'Unknown error' } `
198+ ) ;
177199 }
178200 }
179201
@@ -184,14 +206,16 @@ export class StaticPixService {
184206 try {
185207 // Generate Pix payload
186208 const pixCode = this . generatePixPayload ( request ) ;
187-
209+
188210 // Generate QR Code
189211 const qrCodeDataUrl = await this . generateQRCode ( pixCode ) ;
190212
191- // Apply same truncation as in payload generation
192- const truncatedRecipientName = request . recipientName . substring ( 0 , 25 ) ;
193- const truncatedRecipientCity = request . recipientCity . substring ( 0 , 15 ) ;
194-
213+ // Apply same cleaning and truncation as in payload generation
214+ const cleanRecipientName = this . removeAccents ( request . recipientName ) ;
215+ const cleanRecipientCity = this . removeAccents ( request . recipientCity ) ;
216+ const truncatedRecipientName = cleanRecipientName . substring ( 0 , 25 ) ;
217+ const truncatedRecipientCity = cleanRecipientCity . substring ( 0 , 15 ) ;
218+
195219 return {
196220 success : true ,
197221 message : 'Static Pix QR code generated successfully!' ,
@@ -201,13 +225,15 @@ export class StaticPixService {
201225 amountFormatted : `R$ ${ request . amount . toFixed ( 2 ) } ` ,
202226 recipient : truncatedRecipientName ,
203227 city : truncatedRecipientCity ,
204- description : request . description || ''
228+ description : request . description ? this . removeAccents ( request . description ) : '' ,
205229 } ,
206230 pixCode,
207- qrCodeDataUrl
231+ qrCodeDataUrl,
208232 } ;
209233 } catch ( error ) {
210- throw new Error ( `Failed to create static Pix: ${ error instanceof Error ? error . message : 'Unknown error' } ` ) ;
234+ throw new Error (
235+ `Failed to create static Pix: ${ error instanceof Error ? error . message : 'Unknown error' } `
236+ ) ;
211237 }
212238 }
213239}
0 commit comments