UNPKG

pix-client

Version:

Uma lib para facilitar o uso da API do mercado pago para pagamentos via PIX

166 lines 5.87 kB
import { randomUUID } from 'crypto'; import EventEmitter from 'events'; import { Payment } from 'mercadopago'; import { PixError } from '../PixError/index.js'; import { QRCode } from '../QRCode/index.js'; import { PixPaymentErrorMessages, PixPaymentStatusMessages, PixPaymentWarningMessages } from './messages.js'; import { PaymentStatusType } from './types.js'; export class PixPayment extends EventEmitter { pixClient; mpPayment; description; transactionAmount; durationMinutes; payerEmail; status; id = null; qrCode = new QRCode({ content: '', base64: '' }); webhookCallback = null; expireTimeout = null; constructor(pixClient, options) { super(); this.pixClient = pixClient; this.mpPayment = new Payment(pixClient.mpConfig); this.description = options.description; this.transactionAmount = options.transactionAmount; this.durationMinutes = options.durationMinutes || 60; this.payerEmail = options.payerEmail || 'a@b.co'; this.status = 'pending'; } get isComplete() { return (this.status === 'approved' || this.status === 'cancelled'); } get statusObject() { return PixPaymentStatusMessages[this.status]; } async generate() { if (!this.pixClient.webhook.isReady) { console.warn(PixPaymentWarningMessages.WebhookNotReady); await this.pixClient.webhook.listen(); } const response = await this.mpPayment.create({ body: { payment_method_id: 'pix', transaction_amount: this.transactionAmount, notification_url: this.pixClient.webhook.ngrokUrl ?? undefined, payer: { email: this.payerEmail } }, requestOptions: { idempotencyKey: randomUUID() } }); const data = response.point_of_interaction?.transaction_data; if (!data || !response.id) { throw new PixError(PixPaymentErrorMessages.InvalidResponse); } const qrCode = data.qr_code; const qrCodeBase64 = data.qr_code_base64; if (!qrCode || !qrCodeBase64) { throw new PixError(PixPaymentErrorMessages.InvalidResponse); } this.id = response.id.toString(); this.qrCode.content = qrCode; this.qrCode.image.base64 = qrCodeBase64; this.addWebhookListener(); this.startExpirationTimeout(); return this; } addWebhookListener() { if (this.webhookCallback) return; this.webhookCallback = (id) => { if (!id || id === this.id) this.updateStatus(); }; this.pixClient.webhook.on('update', this.webhookCallback); } removeWebhookListener() { if (!this.webhookCallback) return; this.pixClient.webhook.off('update', this.webhookCallback); this.webhookCallback = null; } startExpirationTimeout() { const durationMs = this.durationMinutes * 60 * 1000; this.expireTimeout = setTimeout(async () => { if (this.isComplete) return; try { await this.cancel(); this.emit('expired'); } catch (error) { throw new PixError(PixPaymentErrorMessages.FailedToExpirePayment, error); } }, durationMs); } async updateStatus() { try { const newStatus = await this.getStatus(); const notChanged = newStatus === this.status; if (notChanged) return; this.status = newStatus; if (newStatus === 'approved') { this.emit('paid'); this.cleanup(); } else if (newStatus === 'cancelled') { this.emit('cancelled'); this.cleanup(); } this.emit('status', this.statusObject); } catch (error) { throw new PixError(PixPaymentErrorMessages.FailedToUpdateStatus, error); } } async getStatus() { try { if (!this.id) { throw new PixError(PixPaymentErrorMessages.CanNotGetStatusWithoutId); } const { status } = await this.mpPayment.get({ id: this.id }); if (!status) throw new PixError(PixPaymentErrorMessages.InvalidResponse); const isValidStatus = Object.values(PaymentStatusType) .includes(status); if (!isValidStatus) throw new PixError(PixPaymentErrorMessages.InvalidResponse); return status; } catch (error) { throw new PixError(PixPaymentErrorMessages.FailedToGetStatus, error); } } async cancel() { try { if (!this.id) { throw new PixError(PixPaymentErrorMessages.CanNotCancelPaymentWithoutId); } if (this.isComplete) { throw new PixError(PixPaymentErrorMessages.CanNotCancelCompletePayment); } await this.mpPayment.cancel({ id: this.id }); this.cleanup(); this.status = 'cancelled'; this.emit('cancelled'); this.emit('status', this.statusObject); } catch (error) { await this.updateStatus(); if (!this.isComplete) { throw new PixError(PixPaymentErrorMessages.FailedToCancelPayment, error); } } } cleanup() { this.removeWebhookListener(); if (typeof this.expireTimeout === 'number') { clearTimeout(this.expireTimeout); this.expireTimeout = null; } } } //# sourceMappingURL=PixPayment.js.map