From 69246a338f210e4333aca92a0e163e0f669b737d Mon Sep 17 00:00:00 2001 From: Leonardo Matos Date: Fri, 2 Aug 2024 01:34:05 -0300 Subject: [PATCH] fix(pagarme): Read transaction on pagar.me API when webhook signature not verified Overwrite transaction object and status, ignoring received invalidated webhook body --- packages/apps/pagarme/src/pagarme-webhook.ts | 48 ++++++++++++-------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/apps/pagarme/src/pagarme-webhook.ts b/packages/apps/pagarme/src/pagarme-webhook.ts index defb862eb..d3c17d1de 100644 --- a/packages/apps/pagarme/src/pagarme-webhook.ts +++ b/packages/apps/pagarme/src/pagarme-webhook.ts @@ -3,6 +3,7 @@ import { createHmac } from 'node:crypto'; import api from '@cloudcommerce/api'; import * as functions from 'firebase-functions/v1'; import config, { logger } from '@cloudcommerce/firebase/lib/config'; +import axios from 'axios'; import Pagarme from 'pagarme'; import qs from 'qs'; import { parsePagarmeStatus } from './pagarme-utils'; @@ -35,51 +36,63 @@ export const pagarme = { } // https://docs.pagar.me/docs/gerenciando-postbacks - const pagarmeTransaction = req.body && req.body.transaction; - if (pagarmeTransaction && pagarmeTransaction.metadata) { - // const storeId = parseInt(pagarmeTransaction.metadata.store_id, 10); + let pagarmeTransaction = req.body?.transaction; + if (pagarmeTransaction?.metadata) { const orderId = pagarmeTransaction.metadata.order_id as ResourceId | undefined; if (typeof orderId === 'string' && /^[a-f0-9]{24}$/.test(orderId)) { + let pagarmeStatus = req.body.current_status || pagarmeTransaction.status; logger.info(`Order ${orderId}`); + let isVerified = false; if (`${process.env.PAGARME_WEBHOOK_SKIP_SIG}`.toLowerCase() !== 'true') { logger.info(`Validating ${orderId} webhook signature`); const urlSig = req.query.sig; if (urlSig && typeof urlSig === 'string') { const notificationSig = createHmac('sha256', process.env.PAGARME_TOKEN) .update(orderId).digest('hex'); - if (notificationSig !== urlSig) { + isVerified = notificationSig === urlSig; + if (!isVerified) { logger.warn('?sig argument is received with invalid hash'); - res.sendStatus(401); - return; } } else { // validate Pagar.me postback // https://github.com/pagarme/pagarme-js/issues/170#issuecomment-503729557 const headerSig = req.headers['x-hub-signature']; if (!headerSig || typeof headerSig !== 'string') { - res.sendStatus(403); - return; + isVerified = false; + } else { + const verifyBody = qs.stringify(req.body); + const sigHeader = headerSig.replace('sha1=', ''); + isVerified = !!(Pagarme.postback + .verifySignature(process.env.PAGARME_TOKEN, verifyBody, sigHeader)); } - const verifyBody = qs.stringify(req.body); - const sigHeader = headerSig.replace('sha1=', ''); - if ( - !Pagarme.postback - .verifySignature(process.env.PAGARME_TOKEN, verifyBody, sigHeader) - ) { + } + } + if (!isVerified) { + try { + const { data } = await axios({ + url: `https://api.pagar.me/1/transactions/${pagarmeTransaction.id}`, + method: 'get', + data: { api_key: process.env.PAGARME_TOKEN }, + }); + pagarmeTransaction = data; + if (pagarmeTransaction?.metadata?.order_id !== orderId) { res.sendStatus(401); return; } + pagarmeStatus = pagarmeTransaction.status; + } catch { + res.sendStatus(401); + return; } } + try { const order = (await api.get(`orders/${orderId}`)).data; if (order && order.transactions) { // add new transaction status to payment history const transaction = order.transactions?.find(({ intermediator }) => { - return intermediator - && intermediator.transaction_id === String(pagarmeTransaction.id); + return intermediator?.transaction_id === String(pagarmeTransaction.id); }); - const pagarmeStatus = req.body.current_status || pagarmeTransaction.status; const bodyPaymentHistory = { date_time: new Date().toISOString(), status: parsePagarmeStatus(pagarmeStatus), @@ -92,7 +105,6 @@ export const pagarme = { if (req.body.old_status) { bodyPaymentHistory.flags.push(`old:${req.body.old_status}`.substring(0, 20)); } - // return appSdk.apiRequest(storeId, resource, method, body); await api.post(`orders/${orderId}/payments_history`, bodyPaymentHistory); res.status(200).send('OK'); return;