@@ -33,6 +33,7 @@ import { HandleLabelDto, LabelDto } from '@api/dto/label.dto';
3333import {
3434 Button ,
3535 ContactMessage ,
36+ KeyType ,
3637 MediaMessage ,
3738 Options ,
3839 SendAudioDto ,
@@ -1408,12 +1409,12 @@ export class BaileysStartupService extends ChannelStartupService {
14081409 } ) ;
14091410
14101411 const existingChat = await this . prismaRepository . chat . findFirst ( {
1411- where : { instanceId : this . instanceId , remoteJid : message . key . remoteJid } ,
1412+ where : { instanceId : this . instanceId , remoteJid : message . remoteJid } ,
14121413 } ) ;
14131414
14141415 if ( ! ! existingChat ) {
14151416 const chatToInsert = {
1416- remoteJid : message . key . remoteJid ,
1417+ remoteJid : message . remoteJid ,
14171418 instanceId : this . instanceId ,
14181419 name : message . pushName || '' ,
14191420 unreadMessages : 0 ,
@@ -2831,6 +2832,15 @@ export class BaileysStartupService extends ChannelStartupService {
28312832 ) ;
28322833 }
28332834
2835+ private generateRandomId ( length = 11 ) {
2836+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ;
2837+ let result = '' ;
2838+ for ( let i = 0 ; i < length ; i ++ ) {
2839+ result += characters . charAt ( Math . floor ( Math . random ( ) * characters . length ) ) ;
2840+ }
2841+ return result ;
2842+ }
2843+
28342844 private toJSONString ( button : Button ) : string {
28352845 const toString = ( obj : any ) => JSON . stringify ( obj ) ;
28362846
@@ -2844,6 +2854,49 @@ export class BaileysStartupService extends ChannelStartupService {
28442854 url : button . url ,
28452855 merchant_url : button . url ,
28462856 } ) ,
2857+ pix : ( ) =>
2858+ toString ( {
2859+ currency : button . currency ,
2860+ total_amount : {
2861+ value : 0 ,
2862+ offset : 100
2863+ } ,
2864+ reference_id : this . generateRandomId ( ) ,
2865+ type : "physical-goods" ,
2866+ order : {
2867+ status : "pending" ,
2868+ subtotal : {
2869+ value : 0 ,
2870+ offset : 100
2871+ } ,
2872+ order_type : "ORDER" ,
2873+ items : [
2874+ {
2875+ name : "" ,
2876+ amount : {
2877+ value : 0 ,
2878+ offset : 100
2879+ } ,
2880+ quantity : 0 ,
2881+ sale_amount : {
2882+ value : 0 ,
2883+ offset : 100
2884+ }
2885+ }
2886+ ]
2887+ } ,
2888+ payment_settings : [
2889+ {
2890+ type : "pix_static_code" ,
2891+ pix_static_code : {
2892+ merchant_name : button . name ,
2893+ key : button . key ,
2894+ key_type : this . mapKeyType . get ( button . keyType )
2895+ }
2896+ }
2897+ ] ,
2898+ share_payment_status : false
2899+ } ) ,
28472900 } ;
28482901
28492902 return json [ button . type ] ?.( ) || '' ;
@@ -2854,9 +2907,73 @@ export class BaileysStartupService extends ChannelStartupService {
28542907 [ 'copy' , 'cta_copy' ] ,
28552908 [ 'url' , 'cta_url' ] ,
28562909 [ 'call' , 'cta_call' ] ,
2910+ [ 'pix' , 'payment_info' ] ,
2911+ ] ) ;
2912+
2913+ private readonly mapKeyType = new Map < KeyType , string > ( [
2914+ [ 'phone' , 'PHONE' ] ,
2915+ [ 'email' , 'EMAIL' ] ,
2916+ [ 'cpf' , 'CPF' ] ,
2917+ [ 'cnpj' , 'CNPJ' ] ,
2918+ [ 'random' , 'EVP' ] ,
28572919 ] ) ;
28582920
28592921 public async buttonMessage ( data : SendButtonsDto ) {
2922+ if ( data . buttons . length === 0 ) {
2923+ throw new BadRequestException ( 'At least one button is required' ) ;
2924+ }
2925+
2926+ const hasReplyButtons = data . buttons . some ( btn => btn . type === 'reply' ) ;
2927+
2928+ const hasPixButton = data . buttons . some ( btn => btn . type === 'pix' ) ;
2929+
2930+ const hasOtherButtons = data . buttons . some ( btn => btn . type !== 'reply' && btn . type !== 'pix' ) ;
2931+
2932+ if ( hasReplyButtons ) {
2933+ if ( data . buttons . length > 3 ) {
2934+ throw new BadRequestException ( 'Maximum of 3 reply buttons allowed' ) ;
2935+ }
2936+ if ( hasOtherButtons ) {
2937+ throw new BadRequestException ( 'Reply buttons cannot be mixed with other button types' ) ;
2938+ }
2939+ }
2940+
2941+ if ( hasPixButton ) {
2942+ if ( data . buttons . length > 1 ) {
2943+ throw new BadRequestException ( 'Only one PIX button is allowed' ) ;
2944+ }
2945+ if ( hasOtherButtons ) {
2946+ throw new BadRequestException ( 'PIX button cannot be mixed with other button types' ) ;
2947+ }
2948+
2949+ const message : proto . IMessage = {
2950+ viewOnceMessage : {
2951+ message : {
2952+ interactiveMessage : {
2953+ nativeFlowMessage : {
2954+ buttons : [ {
2955+ name : this . mapType . get ( 'pix' ) ,
2956+ buttonParamsJson : this . toJSONString ( data . buttons [ 0 ] ) ,
2957+ } ] ,
2958+ messageParamsJson : JSON . stringify ( {
2959+ from : 'api' ,
2960+ templateId : v4 ( ) ,
2961+ } ) ,
2962+ } ,
2963+ } ,
2964+ } ,
2965+ } ,
2966+ } ;
2967+
2968+ return await this . sendMessageWithTyping ( data . number , message , {
2969+ delay : data ?. delay ,
2970+ presence : 'composing' ,
2971+ quoted : data ?. quoted ,
2972+ mentionsEveryOne : data ?. mentionsEveryOne ,
2973+ mentioned : data ?. mentioned ,
2974+ } ) ;
2975+ }
2976+
28602977 const generate = await ( async ( ) => {
28612978 if ( data ?. thumbnailUrl ) {
28622979 return await this . prepareMediaMessage ( {
0 commit comments