mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-14 01:41:24 -06:00
feat: send pix button
This commit is contained in:
parent
9f39ec2110
commit
fce3e55e91
@ -94,15 +94,21 @@ export class SendAudioDto extends Metadata {
|
||||
audio: string;
|
||||
}
|
||||
|
||||
export type TypeButton = 'reply' | 'copy' | 'url' | 'call';
|
||||
export type TypeButton = 'reply' | 'copy' | 'url' | 'call' | 'pix';
|
||||
|
||||
export type KeyType = 'phone' | 'email' | 'cpf' | 'cnpj' | 'random';
|
||||
|
||||
export class Button {
|
||||
type: TypeButton;
|
||||
displayText: string;
|
||||
displayText?: string;
|
||||
id?: string;
|
||||
url?: string;
|
||||
copyCode?: string;
|
||||
phoneNumber?: string;
|
||||
currency?: string;
|
||||
name?: string;
|
||||
keyType?: KeyType;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export class SendButtonsDto extends Metadata {
|
||||
|
@ -33,6 +33,7 @@ import { HandleLabelDto, LabelDto } from '@api/dto/label.dto';
|
||||
import {
|
||||
Button,
|
||||
ContactMessage,
|
||||
KeyType,
|
||||
MediaMessage,
|
||||
Options,
|
||||
SendAudioDto,
|
||||
@ -1408,12 +1409,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
});
|
||||
|
||||
const existingChat = await this.prismaRepository.chat.findFirst({
|
||||
where: { instanceId: this.instanceId, remoteJid: message.key.remoteJid },
|
||||
where: { instanceId: this.instanceId, remoteJid: message.remoteJid },
|
||||
});
|
||||
|
||||
if (!!existingChat) {
|
||||
const chatToInsert = {
|
||||
remoteJid: message.key.remoteJid,
|
||||
remoteJid: message.remoteJid,
|
||||
instanceId: this.instanceId,
|
||||
name: message.pushName || '',
|
||||
unreadMessages: 0,
|
||||
@ -2831,6 +2832,15 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
);
|
||||
}
|
||||
|
||||
private generateRandomId(length = 11) {
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private toJSONString(button: Button): string {
|
||||
const toString = (obj: any) => JSON.stringify(obj);
|
||||
|
||||
@ -2844,6 +2854,49 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
url: button.url,
|
||||
merchant_url: button.url,
|
||||
}),
|
||||
pix: () =>
|
||||
toString({
|
||||
currency: button.currency,
|
||||
total_amount: {
|
||||
value: 0,
|
||||
offset: 100
|
||||
},
|
||||
reference_id: this.generateRandomId(),
|
||||
type: "physical-goods",
|
||||
order: {
|
||||
status: "pending",
|
||||
subtotal: {
|
||||
value: 0,
|
||||
offset: 100
|
||||
},
|
||||
order_type: "ORDER",
|
||||
items: [
|
||||
{
|
||||
name: "",
|
||||
amount: {
|
||||
value: 0,
|
||||
offset: 100
|
||||
},
|
||||
quantity: 0,
|
||||
sale_amount: {
|
||||
value: 0,
|
||||
offset: 100
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
payment_settings: [
|
||||
{
|
||||
type: "pix_static_code",
|
||||
pix_static_code: {
|
||||
merchant_name: button.name,
|
||||
key: button.key,
|
||||
key_type: this.mapKeyType.get(button.keyType)
|
||||
}
|
||||
}
|
||||
],
|
||||
share_payment_status: false
|
||||
}),
|
||||
};
|
||||
|
||||
return json[button.type]?.() || '';
|
||||
@ -2854,9 +2907,73 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
['copy', 'cta_copy'],
|
||||
['url', 'cta_url'],
|
||||
['call', 'cta_call'],
|
||||
['pix', 'payment_info'],
|
||||
]);
|
||||
|
||||
private readonly mapKeyType = new Map<KeyType, string>([
|
||||
['phone', 'PHONE'],
|
||||
['email', 'EMAIL'],
|
||||
['cpf', 'CPF'],
|
||||
['cnpj', 'CNPJ'],
|
||||
['random', 'EVP'],
|
||||
]);
|
||||
|
||||
public async buttonMessage(data: SendButtonsDto) {
|
||||
if (data.buttons.length === 0) {
|
||||
throw new BadRequestException('At least one button is required');
|
||||
}
|
||||
|
||||
const hasReplyButtons = data.buttons.some(btn => btn.type === 'reply');
|
||||
|
||||
const hasPixButton = data.buttons.some(btn => btn.type === 'pix');
|
||||
|
||||
const hasOtherButtons = data.buttons.some(btn => btn.type !== 'reply' && btn.type !== 'pix');
|
||||
|
||||
if (hasReplyButtons) {
|
||||
if (data.buttons.length > 3) {
|
||||
throw new BadRequestException('Maximum of 3 reply buttons allowed');
|
||||
}
|
||||
if (hasOtherButtons) {
|
||||
throw new BadRequestException('Reply buttons cannot be mixed with other button types');
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPixButton) {
|
||||
if (data.buttons.length > 1) {
|
||||
throw new BadRequestException('Only one PIX button is allowed');
|
||||
}
|
||||
if (hasOtherButtons) {
|
||||
throw new BadRequestException('PIX button cannot be mixed with other button types');
|
||||
}
|
||||
|
||||
const message: proto.IMessage = {
|
||||
viewOnceMessage: {
|
||||
message: {
|
||||
interactiveMessage: {
|
||||
nativeFlowMessage: {
|
||||
buttons: [{
|
||||
name: this.mapType.get('pix'),
|
||||
buttonParamsJson: this.toJSONString(data.buttons[0]),
|
||||
}],
|
||||
messageParamsJson: JSON.stringify({
|
||||
from: 'api',
|
||||
templateId: v4(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return await this.sendMessageWithTyping(data.number, message, {
|
||||
delay: data?.delay,
|
||||
presence: 'composing',
|
||||
quoted: data?.quoted,
|
||||
mentionsEveryOne: data?.mentionsEveryOne,
|
||||
mentioned: data?.mentioned,
|
||||
});
|
||||
}
|
||||
|
||||
const generate = await (async () => {
|
||||
if (data?.thumbnailUrl) {
|
||||
return await this.prepareMediaMessage({
|
||||
|
@ -413,14 +413,18 @@ export const buttonsMessageSchema: JSONSchema7 = {
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['reply', 'copy', 'url', 'call'],
|
||||
enum: ['reply', 'copy', 'url', 'call', 'pix'],
|
||||
},
|
||||
displayText: { type: 'string' },
|
||||
id: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
phoneNumber: { type: 'string' },
|
||||
currency: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
keyType: { type: 'string', enum: ['phone', 'email', 'cpf', 'cnpj', 'random'] },
|
||||
key: { type: 'string' },
|
||||
},
|
||||
required: ['type', 'displayText'],
|
||||
required: ['type'],
|
||||
...isNotEmpty('id', 'url', 'phoneNumber'),
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user