michangarrito/backups/docs-backup-2026-01-10/docs/02-especificaciones/INTEGRACIONES-EXTERNAS.md
rckrdmrd 928eb795e6 [SIMCO-V38] feat: Actualizar a SIMCO v3.8.0 + cambios apps
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8
- Cambios en backend y frontend

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:53:05 -06:00

1093 lines
25 KiB
Markdown

# MiChangarrito - Integraciones Externas
## Indice de Integraciones
| Integracion | Categoria | Prioridad | Complejidad |
|-------------|-----------|-----------|-------------|
| Stripe | Pagos/Suscripciones | P0 | Media |
| WhatsApp Business API | Mensajeria | P0 | Alta |
| OpenRouter/LLM | Inteligencia Artificial | P0 | Media |
| Mercado Pago Point | Terminal Pago | P1 | Media |
| Firebase FCM | Push Notifications | P1 | Baja |
| Clip | Terminal Pago | P2 | Media |
| CoDi (Banxico) | Pagos QR | P2 | Alta |
| Google Vision | OCR | P2 | Baja |
| OpenAI Whisper | Transcripcion | P2 | Baja |
---
## 1. Stripe
### 1.1 Proposito
- Suscripciones mensuales (Plan Changarrito, Plan Tiendita)
- Pagos con tarjeta
- Referencias de pago OXXO
- Gestion de clientes y facturacion
### 1.2 Documentacion
- Dashboard: https://dashboard.stripe.com/
- API Docs: https://docs.stripe.com/api
- Subscriptions: https://docs.stripe.com/billing/subscriptions
- OXXO: https://docs.stripe.com/payments/oxxo
### 1.3 SDK
```bash
npm install stripe
```
### 1.4 Configuracion
```typescript
// config/stripe.config.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2024-12-18.acacia',
});
export const STRIPE_CONFIG = {
prices: {
changarrito_monthly: process.env.STRIPE_PRICE_CHANGARRITO,
tiendita_monthly: process.env.STRIPE_PRICE_TIENDITA,
tokens_1000: process.env.STRIPE_PRICE_TOKENS_1000,
tokens_3000: process.env.STRIPE_PRICE_TOKENS_3000,
tokens_8000: process.env.STRIPE_PRICE_TOKENS_8000,
tokens_20000: process.env.STRIPE_PRICE_TOKENS_20000,
},
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
};
```
### 1.5 Flujos Principales
#### Crear Suscripcion
```typescript
async function createSubscription(
customerId: string,
priceId: string,
): Promise<Stripe.Subscription> {
return stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
payment_behavior: 'default_incomplete',
payment_settings: {
save_default_payment_method: 'on_subscription',
},
expand: ['latest_invoice.payment_intent'],
});
}
```
#### Crear Pago OXXO
```typescript
async function createOxxoPayment(
amount: number, // en centavos
customerEmail: string,
customerName: string,
): Promise<Stripe.PaymentIntent> {
return stripe.paymentIntents.create({
amount,
currency: 'mxn',
payment_method_types: ['oxxo'],
payment_method_data: {
type: 'oxxo',
billing_details: {
email: customerEmail,
name: customerName,
},
},
});
}
```
#### Webhook Handler
```typescript
// webhooks/stripe.webhook.ts
@Post('webhook/stripe')
async handleStripeWebhook(
@Req() req: RawBodyRequest<Request>,
@Headers('stripe-signature') signature: string,
) {
const event = stripe.webhooks.constructEvent(
req.rawBody,
signature,
STRIPE_CONFIG.webhookSecret,
);
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated':
await this.handleSubscriptionUpdate(event.data.object);
break;
case 'customer.subscription.deleted':
await this.handleSubscriptionCancelled(event.data.object);
break;
case 'invoice.paid':
await this.handleInvoicePaid(event.data.object);
break;
case 'invoice.payment_failed':
await this.handlePaymentFailed(event.data.object);
break;
case 'payment_intent.succeeded':
await this.handlePaymentSucceeded(event.data.object);
break;
}
return { received: true };
}
```
### 1.6 Webhooks a Configurar
| Evento | Accion |
|--------|--------|
| customer.subscription.created | Activar suscripcion, asignar tokens |
| customer.subscription.updated | Actualizar plan |
| customer.subscription.deleted | Cancelar acceso |
| invoice.paid | Renovar periodo, resetear tokens |
| invoice.payment_failed | Notificar, marcar past_due |
| payment_intent.succeeded | Confirmar pago OXXO/tokens |
### 1.7 Comisiones
| Metodo | Comision |
|--------|----------|
| Tarjeta nacional | 3.6% + $3 MXN + IVA |
| OXXO | 3.6% + $3 MXN + IVA |
| Tarjeta internacional | +0.5% |
---
## 2. WhatsApp Business API
### 2.1 Proposito
- Canal principal de comunicacion con duenos
- Gestion del negocio via chat con LLM
- Recepcion de pedidos de clientes
- Envio de notificaciones y recordatorios
### 2.2 Documentacion
- Meta for Developers: https://developers.facebook.com/
- Cloud API: https://developers.facebook.com/docs/whatsapp/cloud-api
- Webhooks: https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks
### 2.3 Configuracion Inicial
1. Crear app en Meta for Developers
2. Agregar producto "WhatsApp"
3. Configurar numero de telefono
4. Verificar negocio
5. Configurar webhooks
### 2.4 Endpoints Principales
```typescript
// config/whatsapp.config.ts
export const WHATSAPP_CONFIG = {
apiVersion: 'v18.0',
baseUrl: 'https://graph.facebook.com',
phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID,
accessToken: process.env.WHATSAPP_ACCESS_TOKEN,
verifyToken: process.env.WHATSAPP_VERIFY_TOKEN,
};
export const getWhatsAppUrl = (endpoint: string) =>
`${WHATSAPP_CONFIG.baseUrl}/${WHATSAPP_CONFIG.apiVersion}/${WHATSAPP_CONFIG.phoneNumberId}/${endpoint}`;
```
### 2.5 Enviar Mensaje
```typescript
// services/whatsapp.service.ts
import axios from 'axios';
export class WhatsAppService {
private readonly headers = {
Authorization: `Bearer ${WHATSAPP_CONFIG.accessToken}`,
'Content-Type': 'application/json',
};
// Mensaje de texto
async sendTextMessage(to: string, text: string): Promise<void> {
await axios.post(
getWhatsAppUrl('messages'),
{
messaging_product: 'whatsapp',
recipient_type: 'individual',
to,
type: 'text',
text: { body: text },
},
{ headers: this.headers },
);
}
// Mensaje con template (para iniciar conversacion)
async sendTemplateMessage(
to: string,
templateName: string,
components?: any[],
): Promise<void> {
await axios.post(
getWhatsAppUrl('messages'),
{
messaging_product: 'whatsapp',
to,
type: 'template',
template: {
name: templateName,
language: { code: 'es_MX' },
components,
},
},
{ headers: this.headers },
);
}
// Mensaje con botones interactivos
async sendButtonMessage(
to: string,
body: string,
buttons: { id: string; title: string }[],
): Promise<void> {
await axios.post(
getWhatsAppUrl('messages'),
{
messaging_product: 'whatsapp',
to,
type: 'interactive',
interactive: {
type: 'button',
body: { text: body },
action: {
buttons: buttons.map((b) => ({
type: 'reply',
reply: { id: b.id, title: b.title },
})),
},
},
},
{ headers: this.headers },
);
}
// Enviar imagen
async sendImageMessage(
to: string,
imageUrl: string,
caption?: string,
): Promise<void> {
await axios.post(
getWhatsAppUrl('messages'),
{
messaging_product: 'whatsapp',
to,
type: 'image',
image: { link: imageUrl, caption },
},
{ headers: this.headers },
);
}
}
```
### 2.6 Webhook Handler
```typescript
// webhooks/whatsapp.webhook.ts
@Controller('webhook/whatsapp')
export class WhatsAppWebhookController {
// Verificacion inicial (GET)
@Get()
verify(@Query() query: any): string {
const mode = query['hub.mode'];
const token = query['hub.verify_token'];
const challenge = query['hub.challenge'];
if (mode === 'subscribe' && token === WHATSAPP_CONFIG.verifyToken) {
return challenge;
}
throw new ForbiddenException('Verification failed');
}
// Recibir mensajes (POST)
@Post()
async handleWebhook(@Body() body: any): Promise<{ status: string }> {
const entry = body.entry?.[0];
const changes = entry?.changes?.[0];
const value = changes?.value;
if (value?.messages) {
for (const message of value.messages) {
await this.processMessage(message, value.contacts?.[0]);
}
}
if (value?.statuses) {
for (const status of value.statuses) {
await this.processStatus(status);
}
}
return { status: 'ok' };
}
private async processMessage(message: any, contact: any): Promise<void> {
const from = message.from;
const messageType = message.type;
const timestamp = message.timestamp;
// Determinar si es dueno o cliente
const tenant = await this.findTenantByPhone(from);
const isOwner = !!tenant;
// Procesar segun tipo
switch (messageType) {
case 'text':
await this.handleTextMessage(from, message.text.body, isOwner, tenant);
break;
case 'image':
await this.handleImageMessage(from, message.image, isOwner, tenant);
break;
case 'audio':
await this.handleAudioMessage(from, message.audio, isOwner, tenant);
break;
case 'interactive':
await this.handleInteractiveMessage(from, message.interactive, tenant);
break;
}
}
}
```
### 2.7 Templates Requeridos
| Template | Proposito | Variables |
|----------|-----------|-----------|
| welcome | Bienvenida a nuevo negocio | {{1}} nombre |
| daily_summary | Resumen diario de ventas | {{1}} fecha, {{2}} total, {{3}} transacciones |
| low_stock_alert | Alerta de stock bajo | {{1}} productos |
| fiado_reminder | Recordatorio de fiado | {{1}} cliente, {{2}} monto, {{3}} dias |
| order_confirmation | Confirmacion de pedido | {{1}} numero, {{2}} total |
| payment_received | Confirmacion de pago | {{1}} monto |
### 2.8 Rate Limits
| Tier | Mensajes/dia |
|------|--------------|
| Inicial | 250 |
| Verificado | 1,000 |
| Escalado | 10,000+ |
---
## 3. OpenRouter / LLM Providers
### 3.1 Proposito
- Asistente IA para duenos (consultas, gestion)
- Atencion a clientes (pedidos, precios)
- Procesamiento de texto natural
- Soporte tecnico automatizado
### 3.2 Documentacion
- OpenRouter: https://openrouter.ai/docs
- OpenAI: https://platform.openai.com/docs
- Anthropic: https://docs.anthropic.com/
### 3.3 Configuracion Agnostica
```typescript
// config/llm.config.ts
export type LLMProvider = 'openrouter' | 'openai' | 'anthropic' | 'ollama';
export interface LLMConfig {
provider: LLMProvider;
apiKey: string;
baseUrl: string;
model: string;
maxTokens: number;
temperature: number;
}
export const getLLMConfig = (): LLMConfig => {
const provider = process.env.LLM_PROVIDER as LLMProvider;
const configs: Record<LLMProvider, Partial<LLMConfig>> = {
openrouter: {
baseUrl: 'https://openrouter.ai/api/v1',
model: 'anthropic/claude-3-haiku',
},
openai: {
baseUrl: 'https://api.openai.com/v1',
model: 'gpt-4o-mini',
},
anthropic: {
baseUrl: 'https://api.anthropic.com/v1',
model: 'claude-3-haiku-20240307',
},
ollama: {
baseUrl: 'http://localhost:11434/api',
model: 'llama3',
},
};
return {
provider,
apiKey: process.env.LLM_API_KEY,
baseUrl: process.env.LLM_BASE_URL || configs[provider].baseUrl,
model: process.env.LLM_MODEL || configs[provider].model,
maxTokens: parseInt(process.env.LLM_MAX_TOKENS || '4096'),
temperature: parseFloat(process.env.LLM_TEMPERATURE || '0.7'),
};
};
```
### 3.4 LLM Service
```typescript
// services/llm.service.ts
import OpenAI from 'openai';
export class LLMService {
private client: OpenAI;
private config: LLMConfig;
constructor() {
this.config = getLLMConfig();
this.client = new OpenAI({
apiKey: this.config.apiKey,
baseURL: this.config.baseUrl,
});
}
async chat(
messages: { role: 'system' | 'user' | 'assistant'; content: string }[],
tools?: any[],
): Promise<{ content: string; toolCalls?: any[]; usage: any }> {
const response = await this.client.chat.completions.create({
model: this.config.model,
messages,
tools,
max_tokens: this.config.maxTokens,
temperature: this.config.temperature,
});
const choice = response.choices[0];
return {
content: choice.message.content || '',
toolCalls: choice.message.tool_calls,
usage: response.usage,
};
}
async chatWithTools(
userMessage: string,
systemPrompt: string,
tools: Tool[],
context: any,
): Promise<string> {
const messages = [
{ role: 'system' as const, content: systemPrompt },
{ role: 'user' as const, content: userMessage },
];
const toolDefinitions = tools.map((t) => ({
type: 'function' as const,
function: {
name: t.name,
description: t.description,
parameters: t.parameters,
},
}));
let response = await this.chat(messages, toolDefinitions);
// Procesar tool calls
while (response.toolCalls && response.toolCalls.length > 0) {
for (const toolCall of response.toolCalls) {
const tool = tools.find((t) => t.name === toolCall.function.name);
if (tool) {
const args = JSON.parse(toolCall.function.arguments);
const result = await tool.execute(args, context);
messages.push({
role: 'assistant',
content: null,
tool_calls: [toolCall],
} as any);
messages.push({
role: 'tool' as any,
content: JSON.stringify(result),
tool_call_id: toolCall.id,
} as any);
}
}
response = await this.chat(messages, toolDefinitions);
}
return response.content;
}
}
```
### 3.5 System Prompts
```typescript
// prompts/owner.prompt.ts
export const OWNER_SYSTEM_PROMPT = `
Eres el asistente virtual de MiChangarrito, ayudando al dueno de un negocio.
CAPACIDADES:
- Consultar ventas del dia, semana o mes
- Consultar ganancias y utilidades
- Ver inventario y stock
- Alertar sobre productos por agotarse
- Registrar ventas por chat
- Agregar o modificar productos
- Generar reportes
- Recordar fiados pendientes
PERSONALIDAD:
- Amigable y servicial
- Respuestas cortas y directas
- Usa lenguaje coloquial mexicano
- Ofrece sugerencias proactivas
CONTEXTO DEL NEGOCIO:
- Nombre: {{business_name}}
- Giro: {{business_type}}
- Plan: {{plan_name}}
Responde siempre en espanol mexicano.
`;
// prompts/customer.prompt.ts
export const CUSTOMER_SYSTEM_PROMPT = `
Eres el asistente de {{business_name}}, atendiendo a un cliente.
CAPACIDADES:
- Informar sobre productos disponibles
- Dar precios
- Tomar pedidos
- Informar horarios
- Informar sobre estado de pedidos
RESTRICCIONES:
- NO puedes dar descuentos sin autorizacion
- NO tienes acceso a informacion financiera
- Redirige consultas complejas al dueno
Responde amablemente y de forma concisa.
`;
```
### 3.6 Costos por Modelo
| Modelo | Input/1M tokens | Output/1M tokens |
|--------|-----------------|------------------|
| GPT-4o-mini | $0.15 | $0.60 |
| Claude 3 Haiku | $0.25 | $1.25 |
| Claude 3.5 Sonnet | $3.00 | $15.00 |
| Llama 3 8B (OpenRouter) | $0.07 | $0.07 |
---
## 4. Mercado Pago Point
### 4.1 Proposito
- Cobros con tarjeta via terminal fisica
- Meses sin intereses
### 4.2 Documentacion
- https://www.mercadopago.com.mx/developers/es/docs/mp-point/overview
### 4.3 SDK
```bash
npm install mercadopago
```
### 4.4 Integracion
```typescript
// services/mercadopago.service.ts
import { MercadoPagoConfig, Payment, PaymentMethod } from 'mercadopago';
export class MercadoPagoService {
private client: MercadoPagoConfig;
constructor() {
this.client = new MercadoPagoConfig({
accessToken: process.env.MERCADOPAGO_ACCESS_TOKEN,
});
}
// Crear payment intent para terminal
async createPointPayment(
amount: number,
description: string,
externalReference: string,
): Promise<any> {
const response = await fetch(
`https://api.mercadopago.com/point/integration-api/devices/${process.env.MERCADOPAGO_DEVICE_ID}/payment-intents`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MERCADOPAGO_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
description,
external_reference: externalReference,
print_on_terminal: true,
}),
},
);
return response.json();
}
// Consultar estado de pago
async getPaymentIntent(paymentIntentId: string): Promise<any> {
const response = await fetch(
`https://api.mercadopago.com/point/integration-api/payment-intents/${paymentIntentId}`,
{
headers: {
Authorization: `Bearer ${process.env.MERCADOPAGO_ACCESS_TOKEN}`,
},
},
);
return response.json();
}
// Cancelar payment intent
async cancelPaymentIntent(paymentIntentId: string): Promise<void> {
await fetch(
`https://api.mercadopago.com/point/integration-api/payment-intents/${paymentIntentId}`,
{
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.MERCADOPAGO_ACCESS_TOKEN}`,
},
},
);
}
}
```
### 4.5 Webhooks
```typescript
@Post('webhook/mercadopago')
async handleMercadoPagoWebhook(@Body() body: any) {
const { type, data } = body;
if (type === 'point_integration_wh') {
const paymentIntent = await this.mpService.getPaymentIntent(data.id);
if (paymentIntent.state === 'FINISHED') {
await this.salesService.confirmPayment(
paymentIntent.external_reference,
'card_mercadopago',
paymentIntent.id,
);
}
}
return { received: true };
}
```
---
## 5. Firebase Cloud Messaging
### 5.1 Proposito
- Push notifications a app movil
- Alertas en tiempo real
### 5.2 Documentacion
- https://firebase.google.com/docs/cloud-messaging
### 5.3 SDK
```bash
npm install firebase-admin
```
### 5.4 Configuracion
```typescript
// config/firebase.config.ts
import * as admin from 'firebase-admin';
const serviceAccount = JSON.parse(
process.env.FIREBASE_SERVICE_ACCOUNT_JSON ||
fs.readFileSync(process.env.FIREBASE_SERVICE_ACCOUNT_PATH, 'utf8'),
);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
export const messaging = admin.messaging();
```
### 5.5 Enviar Notificacion
```typescript
// services/push.service.ts
export class PushNotificationService {
async sendToDevice(
deviceToken: string,
title: string,
body: string,
data?: Record<string, string>,
): Promise<void> {
await messaging.send({
token: deviceToken,
notification: { title, body },
data,
android: {
priority: 'high',
notification: {
sound: 'default',
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
},
},
apns: {
payload: {
aps: {
sound: 'default',
badge: 1,
},
},
},
});
}
async sendToTopic(
topic: string,
title: string,
body: string,
data?: Record<string, string>,
): Promise<void> {
await messaging.send({
topic,
notification: { title, body },
data,
});
}
}
```
---
## 6. Clip
### 6.1 Proposito
- Cobros con tarjeta via terminal Clip
### 6.2 Documentacion
- https://developer.clip.mx/
### 6.3 Integracion (REST API)
```typescript
// services/clip.service.ts
export class ClipService {
private readonly baseUrl = 'https://api-gw.payclip.com';
private readonly apiKey = process.env.CLIP_API_KEY;
async createPaymentRequest(
amount: number,
reference: string,
): Promise<any> {
const response = await fetch(`${this.baseUrl}/paymentrequest/`, {
method: 'POST',
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
currency: 'MXN',
reference,
message: `Cobro ${reference}`,
}),
});
return response.json();
}
async cancelPaymentRequest(code: string): Promise<void> {
await fetch(`${this.baseUrl}/paymentrequest/code/${code}`, {
method: 'DELETE',
headers: { 'x-api-key': this.apiKey },
});
}
async getTransaction(receiptNo: string): Promise<any> {
const response = await fetch(
`${this.baseUrl}/payments/receipt-no/${receiptNo}`,
{
headers: { 'x-api-key': this.apiKey },
},
);
return response.json();
}
}
```
---
## 7. CoDi (via Openpay)
### 7.1 Proposito
- Cobros con QR sin comisiones
### 7.2 Documentacion
- https://site.openpay.mx/docs/codi.html
### 7.3 SDK
```bash
npm install openpay
```
### 7.4 Integracion
```typescript
// services/codi.service.ts
const Openpay = require('openpay');
export class CodiService {
private openpay: any;
constructor() {
this.openpay = new Openpay(
process.env.OPENPAY_MERCHANT_ID,
process.env.OPENPAY_PRIVATE_KEY,
false, // production = false para sandbox
);
}
async createQRCharge(
amount: number,
description: string,
orderId: string,
): Promise<{ qrUrl: string; chargeId: string }> {
return new Promise((resolve, reject) => {
this.openpay.charges.create(
{
method: 'codi',
amount,
description,
order_id: orderId,
codi_options: {
mode: 'QR_CODE',
},
},
(error: any, charge: any) => {
if (error) {
reject(error);
} else {
resolve({
qrUrl: charge.payment_method.barcode_url,
chargeId: charge.id,
});
}
},
);
});
}
async getChargeStatus(chargeId: string): Promise<string> {
return new Promise((resolve, reject) => {
this.openpay.charges.get(chargeId, (error: any, charge: any) => {
if (error) {
reject(error);
} else {
resolve(charge.status);
}
});
});
}
}
```
---
## 8. Google Cloud Vision (OCR)
### 8.1 Proposito
- Leer listas de precios desde fotos
- Procesar notas de compra
- Extraer texto de imagenes
### 8.2 Documentacion
- https://cloud.google.com/vision/docs
### 8.3 Integracion
```typescript
// services/ocr.service.ts
import vision from '@google-cloud/vision';
export class OCRService {
private client: vision.ImageAnnotatorClient;
constructor() {
this.client = new vision.ImageAnnotatorClient({
keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
});
}
async extractText(imageUrl: string): Promise<string> {
const [result] = await this.client.textDetection(imageUrl);
const detections = result.textAnnotations;
if (detections && detections.length > 0) {
return detections[0].description || '';
}
return '';
}
async extractProducts(
imageUrl: string,
): Promise<{ name: string; price: number }[]> {
const text = await this.extractText(imageUrl);
// Usar LLM para estructurar el texto
const structured = await this.llmService.chat([
{
role: 'system',
content:
'Extrae productos y precios del siguiente texto. Responde en JSON: [{"name": "...", "price": 0.00}]',
},
{ role: 'user', content: text },
]);
return JSON.parse(structured.content);
}
}
```
---
## 9. OpenAI Whisper (Transcripcion)
### 9.1 Proposito
- Transcribir audios de WhatsApp
- Comandos de voz
### 9.2 Documentacion
- https://platform.openai.com/docs/guides/speech-to-text
### 9.3 Integracion
```typescript
// services/transcription.service.ts
import OpenAI from 'openai';
import fs from 'fs';
export class TranscriptionService {
private openai: OpenAI;
constructor() {
this.openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
}
async transcribeAudio(audioPath: string): Promise<string> {
const transcription = await this.openai.audio.transcriptions.create({
file: fs.createReadStream(audioPath),
model: 'whisper-1',
language: 'es',
});
return transcription.text;
}
async transcribeFromUrl(audioUrl: string): Promise<string> {
// Descargar audio
const response = await fetch(audioUrl);
const buffer = await response.arrayBuffer();
const tempPath = `/tmp/audio_${Date.now()}.ogg`;
fs.writeFileSync(tempPath, Buffer.from(buffer));
try {
const text = await this.transcribeAudio(tempPath);
return text;
} finally {
fs.unlinkSync(tempPath);
}
}
}
```
---
## Resumen de Variables de Entorno
```env
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# WhatsApp
WHATSAPP_VERIFY_TOKEN=...
WHATSAPP_ACCESS_TOKEN=...
WHATSAPP_PHONE_NUMBER_ID=...
WHATSAPP_BUSINESS_ACCOUNT_ID=...
# LLM
LLM_PROVIDER=openrouter
LLM_API_KEY=...
LLM_MODEL=anthropic/claude-3-haiku
LLM_BASE_URL=https://openrouter.ai/api/v1
# Mercado Pago
MERCADOPAGO_ACCESS_TOKEN=...
MERCADOPAGO_DEVICE_ID=...
# Clip
CLIP_API_KEY=...
CLIP_MERCHANT_ID=...
# CoDi (Openpay)
OPENPAY_MERCHANT_ID=...
OPENPAY_PRIVATE_KEY=...
# Firebase
FIREBASE_SERVICE_ACCOUNT_PATH=./firebase-sa.json
# Google Cloud
GOOGLE_APPLICATION_CREDENTIALS=./gcp-sa.json
# OpenAI (Whisper)
OPENAI_API_KEY=...
```
---
**Version:** 1.0.0
**Fecha:** 2026-01-04