From f57ee4674dce3fd6305a59e77fcee8c0daaff291 Mon Sep 17 00:00:00 2001 From: Drylian <109999325+drylian@users.noreply.github.com> Date: Sat, 25 May 2024 18:42:49 -0300 Subject: [PATCH] Final organization of resources I changed a little the way the Webhook responds to the MercadoPago, better filtering requests and expected values, I also added to getRedirectUrl a check necessary for the MercadoPago Webhook to work. --- .../MercadoPago/MercadoPagoExtension.php | 63 ++++++++++++------- lang/en.json | 1 + 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/app/Extensions/PaymentGateways/MercadoPago/MercadoPagoExtension.php b/app/Extensions/PaymentGateways/MercadoPago/MercadoPagoExtension.php index 5bb1805f..82a06772 100644 --- a/app/Extensions/PaymentGateways/MercadoPago/MercadoPagoExtension.php +++ b/app/Extensions/PaymentGateways/MercadoPago/MercadoPagoExtension.php @@ -39,6 +39,15 @@ class MercadoPagoExtension extends AbstractExtension public static function getRedirectUrl(Payment $payment, ShopProduct $shopProduct, string $totalPriceString): string { + /** + * For Mercado Pago to work correctly, + * it is necessary to use SSL and the app.url must start with "https://", + * this is necessary so that the webhook receives the information and not an error. + */ + if (!str_contains(config('app.url'), 'https://')) { + throw new Exception(__('It is not possible to purchase via MercadoPago: APP_URL does not have HTTPS, required by Mercado Pago.')); + } + $user = Auth::user(); $user = User::findOrFail($user->id); $url = 'https://api.mercadopago.com/checkout/preferences'; @@ -61,7 +70,6 @@ class MercadoPagoExtension extends AbstractExtension [ 'title' => "Order #{$payment->id} - " . $shopProduct->name, 'quantity' => 1, - // convert to float 'unit_price' => floatval($totalPriceString), 'currency_id' => $shopProduct->currency_code, ], @@ -90,42 +98,50 @@ class MercadoPagoExtension extends AbstractExtension $payment = Payment::findOrFail($request->input('payment')); $payment->status = PaymentStatus::PROCESSING; $payment->save(); - Redirect::route('home')->with('success', 'Your payment is being processed')->send(); + Redirect::route('home')->with('success', 'Your payment is being processed!')->send(); return; } static function Webhook(Request $request): JsonResponse { $topic = $request->input('topic'); - if ($topic === 'merchant_order') { - // ignore other types IPN + $action = $request->input('action'); + + /** + * Mercado Pago sends several requests for information in the webhook, + * but most are for other types of API, and that is why it is filtered here. + */ + if ($topic && ($topic === 'merchant_order' || $topic === 'payment')) { return response()->json(['success' => true]); - } else if ($topic === 'payment') { - // ignore other types IPN - return response()->json(['success' => true]); - } else { - try { - $notificationId = $request->input('data_id') || $request->input('data.id') || "unknown"; - if ($notificationId == 'unknown') { - return response()->json(['success' => false]); - } else if ($notificationId == '123456') { - // mercado pago api test - return response()->json(['success' => true]); - } else { + } + + try { + if($action) { + $notification = $request['data']['id']; + + // Filter the API for payments + if (!$notification || !$action) return response()->json(['success' => false], 400); + // Mercado pago test api, for testing webhook request + if ($notification == '123456') return response()->json(['success' => true], 200); + + /** + * Check action have payment.*, + * what is expected for this type of api + */ + if (str_contains($action, 'payment')) { $url = "https://api.mercadopago.com/v1/payments/" . $notificationId; $settings = new MercadoPagoSettings(); $response = Http::withHeaders([ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $settings->access_token, ])->get($url); - if ($response->successful()) { $mercado = $response->json(); $status = $mercado['status']; $payment = Payment::findOrFail($mercado['metadata']['crtl_panel_payment_id']); $shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id); if ($status == "approved") { - // avoids double additions, if the user enters after the webhook has already added the credits + // Avoid double addition of credits, whether due to double requests from the paid market, or a malicious user if ($payment->status !== PaymentStatus::PAID) { $user = User::findOrFail($payment->user_id); $payment->status = PaymentStatus::PAID; @@ -145,12 +161,17 @@ class MercadoPagoExtension extends AbstractExtension $payment->save(); event(new PaymentEvent($user, $payment, $shopProduct)); } + return response()->json(['success' => true]); + } else { + return response()->json(['success' => false]); } + } else { + return response()->json(['success' => false]); } - } catch (Exception $ex) { - Log::error('MercadoPago Webhook(IPN) Payment: ' . $ex->getMessage()); - return response()->json(['success' => false]); } + } catch (Exception $ex) { + Log::error('MercadoPago Webhook(IPN) Payment: ' . $ex->getMessage()); + return response()->json(['success' => false]); } return response()->json(['success' => true]); } diff --git a/lang/en.json b/lang/en.json index 633a4ec2..070a6eb1 100644 --- a/lang/en.json +++ b/lang/en.json @@ -296,6 +296,7 @@ "Everyone": "Everyone", "Clients": "Clients", "Enable Ticketsystem": "Enable Ticketsystem", + "It is not possible to purchase via MercadoPago: APP_URL does not have HTTPS, required by Mercado Pago":"It is not possible to purchase via MercadoPago: APP_URL does not have HTTPS, required by Mercado Pago", "PayPal Client-ID": "PayPal Client-ID", "PayPal Secret-Key": "PayPal Secret-Key", "PayPal Sandbox Client-ID": "PayPal Sandbox Client-ID",