mercadopago payment add init

This commit is contained in:
Drylian 2024-01-24 14:48:15 -03:00
parent 3b99ae527f
commit 62149da1f5
7 changed files with 306 additions and 4 deletions

View file

@ -0,0 +1,225 @@
<?php
namespace App\Extensions\PaymentGateways\MercadoPago;
use App\Classes\AbstractExtension;
use App\Enums\PaymentStatus;
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use App\Traits\Coupon as CouponTrait;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;
use App\Notifications\ConfirmPaymentNotification;
/**
* Summary of MercadoPagoExtension
*/
class MercadoPagoExtension extends AbstractExtension
{
use CouponTrait;
public static function getConfig(): array
{
return [
"name" => "MercadoPago",
"RoutesIgnoreCsrf" => [
"payment/MercadoPagoWebhook"
],
];
}
public static function getRedirectUrl(Payment $payment, ShopProduct $shopProduct, string $totalPriceString): string
{
$user = Auth::user();
$user = User::findOrFail($user->id);
$url = 'https://api.mercadopago.com/checkout/preferences';
$settings = new MercadoPagoSettings();
try {
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $settings->access_token,
])->post($url, [
'back_urls' => [
'success' => route('payment.MercadoPagoChecker'),
'failure' => route('payment.Cancel'),
'pending' => route('payment.MercadoPagoChecker'),
],
'notification_url' => route('payment.MercadoPagoWebhook'),
'payer' => [
'email' => $user->email,
],
'items' => [
[
'title' => "Order #{$payment->id} - " . $shopProduct->name,
'quantity' => 1,
'unit_price' => $totalPriceString,
'currency_id' => $shopProduct->currency_code,
],
],
'metadata' => [
'credit_amount' => $shopProduct->quantity,
'user_id' => $user->id,
'crtl_panel_payment_id' => $payment->id,
],
]);
if ($response->successful()) {
// preferenceID
$preferenceId = $response->json()['id'];
// Redirect link
return ("https://www.mercadopago.com/checkout/v1/redirect?preference-id=" . $preferenceId);
} else {
Log::error('MercadoPago Payment: ' . $response->body());
throw new Exception('Payment failed');
}
} catch (Exception $ex) {
Log::error('MercadoPago Payment: ' . $ex->getMessage());
throw new Exception('Payment failed');
}
}
static function Checker(Request $request): void
{
// paymentID (not is preferenceID or paymentID for store)
$paymentId = $request->input('payment_id');
$MpPayment = self::MpPayment($paymentId, false);
switch ($MpPayment) {
case "paid":
Redirect::route('home')->with('success', 'Payment successful')->send();
break;
case "cancelled":
Redirect::route('home')->with('info', 'Your canceled the payment')->send();
break;
case "processing":
Redirect::route('home')->with('info', 'Your payment is being processed')->send();
break;
default:
Redirect::route('home')->with('error', 'Your payment is unknown')->send();
break;
}
}
static function Webhook(Request $request): JsonResponse
{
$topic = $request->input('topic');
$msg = 'unset';
$status = 400;
if ($topic === 'merchant_order') {
$msg = 'ignored';
$status = 200;
} else if ($topic === 'payment') {
$msg = 'ignored';
$status = 200;
} else {
try {
$notificationId = $request->input('data.id') ?? $request->input('id') ?? $request->input('payment_id') ?? 'unknown';
if ($notificationId == 'unknown') {
$msg = 'unknown payment.';
$status = 400;
} else if ($notificationId == '123456') {
$msg = 'MercadoPago api test';
$status = 200;
} else {
$MpPayment = self::MpPayment($notificationId, true);
switch ($MpPayment) {
case "paid":
$msg = $MpPayment;
$status = 200;
break;
case "cancelled":
$msg = $MpPayment;
$status = 200;
break;
case "processing":
$msg = $MpPayment;
$status = 200;
break;
default:
$msg = 'unknown';
$status = 400;
break;
}
}
} catch (Exception $ex) {
Log::error('MercadoPago Webhook(IPN) Payment: ' . $ex->getMessage());
$msg = 'error';
$status = 500;
}
}
$response = new JsonResponse($msg, $status);
return $response;
}
/**
* Mercado Pago Payment checker
*/
private function MpPayment(string $paymentID, bool $notification): string
{
$MpResponse = "unknown";
$payment = "unknown";
$url = "https://api.mercadopago.com/v1/payments/" . $paymentID;
$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
if ($payment->status !== PaymentStatus::PAID) {
$user = User::findOrFail($payment->user_id);
$payment->update([
'status' => PaymentStatus::PAID,
'payment_id' => $paymentID,
]);
$payment->save();
if ($notification) {
$user->notify(new ConfirmPaymentNotification($payment));
}
event(new PaymentEvent($user, $payment, $shopProduct));
event(new UserUpdateCreditsEvent($user));
}
$MpResponse = "paid";
} else {
if ($status == "cancelled") {
$user = User::findOrFail($payment->user_id);
$payment->update([
'status' => PaymentStatus::CANCELED,
'payment_id' => $paymentID,
]);
$payment->save();
event(new PaymentEvent($user, $payment, $shopProduct));
$MpResponse = "cancelled";
} else {
$user = User::findOrFail($payment->user_id);
$payment->update([
'status' => PaymentStatus::PROCESSING,
'payment_id' => $paymentID,
]);
$payment->save();
event(new PaymentEvent($user, $payment, $shopProduct));
$MpResponse = "processing";
}
}
}
return $MpResponse;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace App\Extensions\PaymentGateways\MercadoPago;
use Spatie\LaravelSettings\Settings;
class MercadoPagoSettings extends Settings
{
public bool $enabled = false;
public ?string $access_token;
public static function group(): string
{
return 'mercadopago';
}
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-dollar-sign',
'access_token' => [
'type' => 'string',
'label' => 'Access Token Key',
'description' => 'The Access Token of your Mercado Pago App',
],
'enabled' => [
'type' => 'boolean',
'label' => 'Enabled',
'description' => 'Enable or disable this payment gateway',
],
];
}
}

View file

@ -0,0 +1,18 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
class CreateMercadoPagoSettings extends SettingsMigration
{
public function up(): void
{
$this->migrator->addEncrypted('mpago.access_token', null);
$this->migrator->add('mpago.enabled', false);
}
public function down(): void
{
$this->migrator->delete('mpago.access_token');
$this->migrator->delete('mpago.enabled');
}
}

View file

@ -0,0 +1,18 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Extensions\PaymentGateways\MercadoPago\MercadoPagoExtension;
Route::middleware(['web', 'auth'])->group(function () {
Route::get(
'payment/MercadoPagoChecker',
function () {
MercadoPagoExtension::Checker(request());
}
)->name('payment.MercadoPagoChecker');
});
Route::post('payment/MercadoPagoWebhook', function () {
MercadoPagoExtension::Webhook(request());
})->name('payment.MercadoPagoWebhook');

View file

@ -118,6 +118,9 @@ return [
'settings.paypal.read',
'settings.paypal.write',
'settings.mercadopago.read',
'settings.mercadopago.write',
'settings.stripe.read',
'settings.stripe.write',

View file

@ -258,7 +258,7 @@
@endif
<!-- lol how do i make this shorter? -->
@canany(['settings.discord.read','settings.discord.write','settings.general.read','settings.general.write','settings.invoice.read','settings.invoice.write','settings.locale.read','settings.locale.write','settings.mail.read','settings.mail.write','settings.pterodactyl.read','settings.pterodactyl.write','settings.referral.read','settings.referral.write','settings.server.read','settings.server.write','settings.ticket.read','settings.ticket.write','settings.user.read','settings.user.write','settings.website.read','settings.website.write','settings.paypal.read','settings.paypal.write','settings.stripe.read','settings.stripe.write','settings.mollie.read','settings.mollie.write','admin.overview.read','admin.overview.sync','admin.ticket.read','admin.tickets.write','admin.ticket_blacklist.read','admin.ticket_blacklist.write','admin.roles.read','admin.roles.write','admin.api.read','admin.api.write'])
@canany(['settings.discord.read','settings.discord.write','settings.general.read','settings.general.write','settings.invoice.read','settings.invoice.write','settings.locale.read','settings.locale.write','settings.mail.read','settings.mail.write','settings.pterodactyl.read','settings.pterodactyl.write','settings.referral.read','settings.referral.write','settings.server.read','settings.server.write','settings.ticket.read','settings.ticket.write','settings.user.read','settings.user.write','settings.website.read','settings.website.write','settings.paypal.read','settings.paypal.write','settings.stripe.read','settings.stripe.write','settings.mollie.read','settings.mollie.write','settings.mercadopago.read','settings.mercadopago.write','admin.overview.read','admin.overview.sync','admin.ticket.read','admin.tickets.write','admin.ticket_blacklist.read','admin.ticket_blacklist.write','admin.roles.read','admin.roles.write','admin.api.read','admin.api.write'])
<li class="nav-header">{{ __('Administration') }}</li>
@endcanany
@ -329,7 +329,9 @@
'settings.stripe.read',
'settings.stripe.write',
'settings.mollie.read',
'settings.mollie.write',])
'settings.mollie.write',
'settings.mercadopago.read',
'settings.mercadopago.write',])
<li class="nav-item">
<a href="{{ route('admin.settings.index') }}"
class="nav-link @if (Request::routeIs('admin.settings.*')) active @endif">

View file

@ -258,7 +258,7 @@
@endif
<!-- lol how do i make this shorter? -->
@canany(['settings.discord.read','settings.discord.write','settings.general.read','settings.general.write','settings.invoice.read','settings.invoice.write','settings.locale.read','settings.locale.write','settings.mail.read','settings.mail.write','settings.pterodactyl.read','settings.pterodactyl.write','settings.referral.read','settings.referral.write','settings.server.read','settings.server.write','settings.ticket.read','settings.ticket.write','settings.user.read','settings.user.write','settings.website.read','settings.website.write','settings.paypal.read','settings.paypal.write','settings.stripe.read','settings.stripe.write','settings.mollie.read','settings.mollie.write','admin.overview.read','admin.overview.sync','admin.ticket.read','admin.tickets.write','admin.ticket_blacklist.read','admin.ticket_blacklist.write','admin.roles.read','admin.roles.write','admin.api.read','admin.api.write'])
@canany(['settings.discord.read','settings.discord.write','settings.general.read','settings.general.write','settings.invoice.read','settings.invoice.write','settings.locale.read','settings.locale.write','settings.mail.read','settings.mail.write','settings.pterodactyl.read','settings.pterodactyl.write','settings.referral.read','settings.referral.write','settings.server.read','settings.server.write','settings.ticket.read','settings.ticket.write','settings.user.read','settings.user.write','settings.website.read','settings.website.write','settings.paypal.read','settings.paypal.write','settings.stripe.read','settings.stripe.write','settings.mollie.read','settings.mollie.write','settings.mercadopago.read','settings.mercadopago.write','admin.overview.read','admin.overview.sync','admin.ticket.read','admin.tickets.write','admin.ticket_blacklist.read','admin.ticket_blacklist.write','admin.roles.read','admin.roles.write','admin.api.read','admin.api.write'])
<li class="nav-header">{{ __('Administration') }}</li>
@endcanany
@ -329,7 +329,9 @@
'settings.stripe.read',
'settings.stripe.write',
'settings.mollie.read',
'settings.mollie.write',])
'settings.mollie.write',
'settings.mercadopago.read',
'settings.mercadopago.write',])
<li class="nav-item">
<a href="{{ route('admin.settings.index') }}"
class="nav-link @if (Request::routeIs('admin.settings.*')) active @endif">