mercadopago payment add init
This commit is contained in:
parent
3b99ae527f
commit
62149da1f5
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
18
app/Extensions/PaymentGateways/MercadoPago/web_routes.php
Normal file
18
app/Extensions/PaymentGateways/MercadoPago/web_routes.php
Normal 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');
|
|
@ -118,6 +118,9 @@ return [
|
||||||
'settings.paypal.read',
|
'settings.paypal.read',
|
||||||
'settings.paypal.write',
|
'settings.paypal.write',
|
||||||
|
|
||||||
|
'settings.mercadopago.read',
|
||||||
|
'settings.mercadopago.write',
|
||||||
|
|
||||||
'settings.stripe.read',
|
'settings.stripe.read',
|
||||||
'settings.stripe.write',
|
'settings.stripe.write',
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,7 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- lol how do i make this shorter? -->
|
<!-- 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>
|
<li class="nav-header">{{ __('Administration') }}</li>
|
||||||
@endcanany
|
@endcanany
|
||||||
|
|
||||||
|
@ -329,7 +329,9 @@
|
||||||
'settings.stripe.read',
|
'settings.stripe.read',
|
||||||
'settings.stripe.write',
|
'settings.stripe.write',
|
||||||
'settings.mollie.read',
|
'settings.mollie.read',
|
||||||
'settings.mollie.write',])
|
'settings.mollie.write',
|
||||||
|
'settings.mercadopago.read',
|
||||||
|
'settings.mercadopago.write',])
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{{ route('admin.settings.index') }}"
|
<a href="{{ route('admin.settings.index') }}"
|
||||||
class="nav-link @if (Request::routeIs('admin.settings.*')) active @endif">
|
class="nav-link @if (Request::routeIs('admin.settings.*')) active @endif">
|
||||||
|
|
|
@ -258,7 +258,7 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- lol how do i make this shorter? -->
|
<!-- 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>
|
<li class="nav-header">{{ __('Administration') }}</li>
|
||||||
@endcanany
|
@endcanany
|
||||||
|
|
||||||
|
@ -329,7 +329,9 @@
|
||||||
'settings.stripe.read',
|
'settings.stripe.read',
|
||||||
'settings.stripe.write',
|
'settings.stripe.write',
|
||||||
'settings.mollie.read',
|
'settings.mollie.read',
|
||||||
'settings.mollie.write',])
|
'settings.mollie.write',
|
||||||
|
'settings.mercadopago.read',
|
||||||
|
'settings.mercadopago.write',])
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{{ route('admin.settings.index') }}"
|
<a href="{{ route('admin.settings.index') }}"
|
||||||
class="nav-link @if (Request::routeIs('admin.settings.*')) active @endif">
|
class="nav-link @if (Request::routeIs('admin.settings.*')) active @endif">
|
||||||
|
|
Loading…
Reference in a new issue