mercadopago payment add init
This commit is contained in:
parent
3b99ae527f
commit
62149da1f5
7 changed files with 306 additions and 4 deletions
|
@ -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.write',
|
||||
|
||||
'settings.mercadopago.read',
|
||||
'settings.mercadopago.write',
|
||||
|
||||
'settings.stripe.read',
|
||||
'settings.stripe.write',
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in a new issue