@nicogorga/medusa-payment-mercadopago
Version:
Mercado Pago payment provider plugin for MedusaJS
403 lines • 33.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@medusajs/framework/utils");
const mercadopago_1 = __importStar(require("mercadopago"));
const crypto_1 = require("crypto");
class MercadopagoProviderService extends utils_1.AbstractPaymentProvider {
constructor(container, options) {
super(container, options);
this.options_ = options;
this.client_ = new mercadopago_1.default({
accessToken: options.accessToken,
});
this.logger_ = container.logger;
}
static validateOptions(options) {
if (!options.accessToken || !(0, utils_1.isString)(options.accessToken)) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "Mercado pago 'accessToken' needs to be a non empty string");
}
}
async initiatePayment(input) {
return {
id: "",
data: {
session_id: input.data?.session_id,
amount: input.amount,
},
};
}
async deletePayment(input) {
const paymentId = input.data?.id;
if (paymentId) {
const payment = new mercadopago_1.Payment(this.client_);
await payment.cancel({ id: paymentId });
}
return {};
}
async authorizePayment(input) {
const paymentSessionId = input.data?.session_id;
let data = input.data ?? {};
if (!paymentSessionId) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "No payment session id found in data");
}
const payment = new mercadopago_1.Payment(this.client_);
const results = ((await payment.search({
options: {
external_reference: paymentSessionId,
sort: 'date_approved',
criteria: 'desc'
},
})
// Ideally, this would be done in the call to Mercado Pago, but they don't expose an option
// to filter based on status
)?.results ?? []).filter(payment => payment.status === 'approved');
if (!results.length) {
this.logger_.warn(`No payment found in Mercado Pago for payment session: ${paymentSessionId}\n This could be caused by lag in Mercado Pago's system and doesn't mean the payment was not created`);
}
data = results[0] ?? data;
// Returning PaymentSessionStatus.CAPTURED for auto capture, since for UY card method they are captured automatically
// TODO: Should make it conditionally in cases where a method could be not auto captured (maybe cash or another country)
return {
data: data,
status: utils_1.PaymentSessionStatus.CAPTURED,
};
}
async capturePayment(input) {
// For now, we assume auto capture, so no need to do anything else with Mercado Pago
return { data: input.data };
}
async cancelPayment({ data, context }) {
try {
const id = data?.id;
if (!id) {
return { data: data };
}
const { status } = await this.getPaymentStatus({ data: data });
switch (status) {
case 'authorized':
const paymentClient = new mercadopago_1.Payment(this.client_);
const paymentResponse = await paymentClient.cancel({ id, requestOptions: { idempotencyKey: context?.idempotency_key } });
return { data: paymentResponse };
case 'captured':
const refundClient = new mercadopago_1.PaymentRefund(this.client_);
const refundResponse = await refundClient.total({ payment_id: id, requestOptions: { idempotencyKey: context?.idempotency_key } });
return { data: refundResponse };
default:
return { data };
}
}
catch (error) {
throw new Error(`An error occurred in cancelPayment: ${error}`);
}
}
async getPaymentStatus(input) {
const paymentId = input.data?.id;
const payment = new mercadopago_1.Payment(this.client_);
const paymentData = await payment.get({ id: paymentId });
switch (paymentData.status) {
case "authorized":
return { status: "authorized", data: paymentData };
case "approved":
return { status: "captured", data: paymentData };
case "cancelled":
case "refunded":
return { status: "canceled", data: paymentData };
case "rejected":
return { status: "error", data: {
...paymentData,
error_message: this.sanitizeErrorMessage(paymentData)
} };
default:
return { status: "pending", data: paymentData };
}
}
async refundPayment(input) {
const paymentId = this.getIdOrThrow(input.data);
const payment = new mercadopago_1.Payment(this.client_);
const paymentData = await payment.get({ id: paymentId });
const refundAmount = Number(input.amount);
const isPartial = (paymentData.transaction_amount ?? refundAmount) > refundAmount;
const refund = new mercadopago_1.PaymentRefund(this.client_);
const refundData = await refund.create({
payment_id: paymentId,
body: {
amount: isPartial ? refundAmount : undefined,
},
});
return { data: refundData };
}
async retrievePayment(input) {
const paymentId = this.getIdOrThrow(input.data);
const payment = new mercadopago_1.Payment(this.client_);
const paymentData = await payment.get({ id: paymentId });
return { data: paymentData };
}
updatePayment(input) {
// TODO: Until working with preferences, we won't have a need for this method. After including preferences, it should update preference items and total amount
// when items are added / removed from cart
return Promise.resolve({ data: input.data });
}
async getWebhookActionAndData(payload) {
this.validateWebhookSignature(payload);
const mercadopagoData = payload.data;
const eventType = mercadopagoData.action;
try {
switch (eventType) {
case "payment.created":
case "payment.updated":
const payment = new mercadopago_1.Payment(this.client_);
const paymentData = await payment.get({
id: mercadopagoData.data.id,
});
const paymentSessionId = paymentData.external_reference;
if (!paymentSessionId) {
throw new Error("No external_reference found in mercadopago payload, unable to match against Medusa payment session");
}
if (["authorized", "approved"].includes(paymentData.status ?? "")) {
return {
action: paymentData.status === "approved"
? utils_1.PaymentActions.SUCCESSFUL
: utils_1.PaymentActions.AUTHORIZED,
data: {
session_id: paymentSessionId,
amount: paymentData.transaction_amount,
},
};
}
default:
return { action: "not_supported" };
}
}
catch (error) {
return { action: "failed" };
}
}
async createAccountHolder({ context }) {
const { account_holder, customer, idempotency_key } = context;
const id = account_holder?.data.id;
if (id) {
return { id };
}
if (!customer) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "No customer provided while creating account holder");
}
const customerClient = new mercadopago_1.Customer(this.client_);
let mercadopagoCustomer;
try {
const body = {
email: customer.email,
first_name: customer.first_name || undefined,
last_name: customer.last_name || undefined,
date_registered: new Date().toISOString(),
};
mercadopagoCustomer = await customerClient.create({
body: body,
requestOptions: {
idempotencyKey: idempotency_key,
},
});
}
catch (e) {
const error = e;
if (!!error.cause?.find(c => c.code === '101')) {
const { results } = await customerClient.search({ options: { email: customer.email } });
mercadopagoCustomer = results[0];
}
else {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.UNEXPECTED_STATE, "An error occurred while trying to create a Mercado Pago customer");
}
}
return {
id: mercadopagoCustomer.id,
data: mercadopagoCustomer
};
}
async updateAccountHolder({ context, }) {
const { account_holder, customer, idempotency_key } = context;
const accountHolderId = account_holder.data?.id;
if (!accountHolderId) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "No account holder provided while updating account holder");
}
// No customer, nothing to upodate with third party
if (!customer) {
return {};
}
try {
const customerClient = new mercadopago_1.Customer(this.client_);
const body = {
first_name: customer.first_name ?? undefined,
last_name: customer.last_name ?? undefined,
phone: { number: customer.phone ?? undefined },
identification: account_holder.data.identification ?? undefined,
};
if (!account_holder.data.email) {
body.email = customer.email;
}
const payload = {
customerId: accountHolderId,
body: body,
requestOptions: {
idempotencyKey: idempotency_key,
}
};
const updatedCustomer = await customerClient.update(payload);
return {
data: updatedCustomer,
};
}
catch (e) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.UNEXPECTED_STATE, "An error occurred in updateAccountHolder when updating a Stripe customer");
}
}
async savePaymentMethod({ context, data, }) {
const accountHolderId = context?.account_holder?.data?.id;
if (!accountHolderId) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "Account holder not set while saving a payment method");
}
const paymentMethodData = data;
const card = new mercadopago_1.CustomerCard(this.client_);
// I opened a support ticket with Mercado Pago, as this call randomly fails or succeeds
// for now, the step that calls this method has a try / catch, to allow the Payment to cotinue even if this fails
const created = await card.create({
customerId: accountHolderId,
body: {
token: paymentMethodData.token,
},
requestOptions: {
idempotencyKey: context?.idempotency_key
}
});
return { id: created.id, data: created };
}
async listPaymentMethods({ context, }) {
const accountHolderId = context?.account_holder?.data?.id;
if (!accountHolderId) {
return [];
}
const cardClient = new mercadopago_1.CustomerCard(this.client_);
const paymentMethods = await cardClient.list({
customerId: accountHolderId
});
return paymentMethods.map((method) => ({
id: method.id,
data: method,
}));
}
async createPayment({ paymentSessionId, payload, }) {
const payment = new mercadopago_1.Payment(this.client_);
const paymentResponse = await payment.create({
body: {
...payload,
external_reference: paymentSessionId,
},
});
const errorMessage = this.sanitizeErrorMessage(paymentResponse);
paymentResponse["error_message"] = errorMessage;
return paymentResponse;
}
sanitizeErrorMessage(payment) {
const status = payment.status;
const statusDetail = payment.status_detail;
if (status !== "rejected" || !statusDetail) {
return null;
}
switch (statusDetail) {
case "cc_rejected_bad_filled_card_number":
return "Numero de tarjeta incorrecto.";
case "cc_rejected_bad_filled_date":
return "Numero de expiracion incorrecto.";
case "cc_rejected_bad_filled_security_code":
return "Numero de seguridad incorrecto.";
case "cc_rejected_insufficient_amount":
case "insufficient_amount":
return "Saldo insuficiente.";
case "cc_rejected_max_attempts":
return "Has superado el maximo de intentos, prueba otra tarjeta.";
case "rejected_by_bank":
return "Tu banco ha rechazado el pago.";
default:
return "No hemos podido procesar el pago, intenta nuevamente o prueba otra tarjeta.";
}
}
validateWebhookSignature(data) {
const secret = this.options_.webhookSecret;
// If no webhookSecret is set, the assumption is this protection is not required
if (!secret) {
return;
}
const headers = data.headers;
const xSignature = (headers["x-signature"] ?? "noop");
const xRequestId = (headers["x-request-id"] ?? "noop");
const body = data?.data;
const dataId = (body.data?.id ?? "noop");
const parts = xSignature.split(",");
let timestamp;
let hash;
parts.forEach((part) => {
const [key, val] = part.split("=");
if (key && val) {
const [trimmedKey, trimmedVal] = [key.trim(), val.trim()];
if (trimmedKey === "ts") {
timestamp = trimmedVal;
}
else if (trimmedKey === "v1") {
hash = trimmedVal;
}
}
});
const manifest = `id:${dataId};request-id:${xRequestId};ts:${timestamp};`;
const hmac = (0, crypto_1.createHmac)("sha256", secret);
hmac.update(manifest);
const sha = hmac.digest("hex");
if (sha !== hash) {
this.logger_
.warn(`Unable to verify Mercado Pago authenticity of request with headers:\n
${headers}
`);
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "Invalid signature");
}
}
getIdOrThrow(data) {
const id = String(data?.id);
if (!id) {
throw new utils_1.MedusaError(utils_1.MedusaErrorTypes.INVALID_DATA, "No valid string stored against 'id' key of data object");
}
return id;
}
}
MercadopagoProviderService.identifier = "mercadopago";
exports.default = MercadopagoProviderService;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9wcm92aWRlcnMvbWVyY2Fkby1wYWdvL3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxxREFPbUM7QUFnQ25DLDJEQUFnRztBQUloRyxtQ0FBb0M7QUFXcEMsTUFBTSwwQkFBMkIsU0FBUSwrQkFBMkM7SUFNbEYsWUFBWSxTQUErQixFQUFFLE9BQTJCO1FBQ3RFLEtBQUssQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUM7UUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLHFCQUFpQixDQUFDO1lBQ25DLFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVztTQUNqQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUM7SUFDbEMsQ0FBQztJQUVELE1BQU0sQ0FBQyxlQUFlLENBQUMsT0FBeUI7UUFDOUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLElBQUksQ0FBQyxJQUFBLGdCQUFRLEVBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDM0QsTUFBTSxJQUFJLG1CQUFXLENBQ25CLHdCQUFnQixDQUFDLFlBQVksRUFDN0IsMkRBQTJELENBQzVELENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxlQUFlLENBQ25CLEtBQTJCO1FBRTNCLE9BQU87WUFDTCxFQUFFLEVBQUUsRUFBRTtZQUNOLElBQUksRUFBRTtnQkFDSixVQUFVLEVBQUUsS0FBSyxDQUFDLElBQUksRUFBRSxVQUFVO2dCQUNsQyxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07YUFDckI7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxhQUFhLENB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