Browse Source

[FEATURE] Extension Support & Payment Gateways as Extensions 🔥

[FEATURE] Extension Support & Payment Gateways as Extensions 🔥
Dennis 2 years ago
parent
commit
7a91013bd4

+ 31 - 0
app/Events/PaymentEvent.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Events;
+
+use App\Models\Payment;
+use App\Models\ShopProduct;
+use App\Models\User;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+class PaymentEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    public User $user;
+    public Payment $payment;
+    public ShopProduct $shopProduct;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(User $user, Payment $payment, ShopProduct $shopProduct)
+    {
+        $this->user = $user;
+        $this->payment = $payment;
+        $this->shopProduct = $shopProduct;
+    }
+}

+ 216 - 0
app/Extensions/PaymentGateways/PayPal/index.php

@@ -0,0 +1,216 @@
+<?php
+
+use App\Events\PaymentEvent;
+use App\Events\UserUpdateCreditsEvent;
+use App\Models\PartnerDiscount;
+use App\Models\Payment;
+use App\Models\Product;
+use App\Models\ShopProduct;
+use App\Models\User;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Redirect;
+use PayPalCheckoutSdk\Core\PayPalHttpClient;
+use PayPalCheckoutSdk\Core\ProductionEnvironment;
+use PayPalCheckoutSdk\Core\SandboxEnvironment;
+use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
+use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
+use PayPalHttp\HttpException;
+
+
+
+/**
+ * @param Request $request
+ * @param ShopProduct $shopProduct
+ */
+function PaypalPay(Request $request)
+{
+    /** @var User $user */
+    $user = Auth::user();
+    $shopProduct = ShopProduct::findOrFail($request->shopProduct);
+
+    // create a new payment
+    $payment = Payment::create([
+        'user_id' => $user->id,
+        'payment_id' => null,
+        'payment_method' => 'paypal',
+        'type' => $shopProduct->type,
+        'status' => 'open',
+        'amount' => $shopProduct->quantity,
+        'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
+        'tax_value' => $shopProduct->getTaxValue(),
+        'tax_percent' => $shopProduct->getTaxPercent(),
+        'total_price' => $shopProduct->getTotalPrice(),
+        'currency_code' => $shopProduct->currency_code,
+        'shop_item_product_id' => $shopProduct->id,
+    ]);
+
+    $request = new OrdersCreateRequest();
+    $request->prefer('return=representation');
+    $request->body = [
+        "intent" => "CAPTURE",
+        "purchase_units" => [
+            [
+                "reference_id" => uniqid(),
+                "description" => $shopProduct->display . (PartnerDiscount::getDiscount() ? (" (" . __('Discount') . " " . PartnerDiscount::getDiscount() . '%)') : ""),
+                "amount"       => [
+                    "value"         => $shopProduct->getTotalPrice(),
+                    'currency_code' => strtoupper($shopProduct->currency_code),
+                    'breakdown' => [
+                        'item_total' =>
+                        [
+                            'currency_code' => strtoupper($shopProduct->currency_code),
+                            'value' => $shopProduct->getPriceAfterDiscount(),
+                        ],
+                        'tax_total' =>
+                        [
+                            'currency_code' => strtoupper($shopProduct->currency_code),
+                            'value' => $shopProduct->getTaxValue(),
+                        ]
+                    ]
+                ]
+            ]
+        ],
+        "application_context" => [
+            "cancel_url" => route('payment.Cancel'),
+            "return_url" => route('payment.PayPalSuccess', ['payment' => $payment->id]),
+            'brand_name' =>  config('app.name', 'Laravel'),
+            'shipping_preference'  => 'NO_SHIPPING'
+        ]
+
+
+    ];
+
+    try {
+        // Call API with your client and get a response for your call
+        $response = getPayPalClient()->execute($request);
+
+        Redirect::away($response->result->links[1]->href)->send();
+        return;
+    } catch (HttpException $ex) {
+        error_log($ex->statusCode);
+        error_log($ex->getMessage());
+
+        $payment->delete();
+        Redirect::route('payment.Cancel');
+        return;
+    }
+}
+/**
+ * @param Request $laravelRequest
+ */
+function PaypalSuccess(Request $laravelRequest)
+{
+    $user = Auth::user();
+    $user = User::findOrFail($user->id);
+
+    $payment = Payment::findOrFail($laravelRequest->payment);
+    $shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
+
+    $request = new OrdersCaptureRequest($laravelRequest->input('token'));
+    $request->prefer('return=representation');
+
+    try {
+        // Call API with your client and get a response for your call
+        $response = getPayPalClient()->execute($request);
+        if ($response->statusCode == 201 || $response->statusCode == 200) {
+            //update payment
+            $payment->update([
+                'status' => 'paid',
+                'payment_id' => $response->result->id,
+            ]);
+
+            event(new UserUpdateCreditsEvent($user));
+            event(new PaymentEvent($user, $payment, $shopProduct));
+
+            // redirect to the payment success page with success message
+            Redirect::route('home')->with('success', 'Payment successful')->send();
+        } elseif (env('APP_ENV') == 'local') {
+            // If call returns body in response, you can get the deserialized version from the result attribute of the response
+            $payment->delete();
+            dd($response);
+        } else {
+            $payment->update([
+                'status' => 'cancelled',
+                'payment_id' => $response->result->id,
+            ]);
+            abort(500);
+        }
+    } catch (HttpException $ex) {
+        if (env('APP_ENV') == 'local') {
+            echo $ex->statusCode;
+            $payment->delete();
+            dd($ex->getMessage());
+        } else {
+            $payment->update([
+                'status' => 'cancelled',
+                'payment_id' => $response->result->id,
+            ]);
+            abort(422);
+        }
+    }
+}
+/**
+ * @return PayPalHttpClient
+ */
+function getPayPalClient()
+{
+    $environment = env('APP_ENV') == 'local'
+        ? new SandboxEnvironment(getPaypalClientId(), getPaypalClientSecret())
+        : new ProductionEnvironment(getPaypalClientId(), getPaypalClientSecret());
+    return new PayPalHttpClient($environment);
+}
+/**
+ * @return string
+ */
+function getPaypalClientId()
+{
+    return env('APP_ENV') == 'local' ?  config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID") : config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID");
+}
+/**
+ * @return string
+ */
+function getPaypalClientSecret()
+{
+    return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET") : config("SETTINGS::PAYMENTS:PAYPAL:SECRET");
+}
+function getPayPalConfig()
+{
+    return [
+        "name" => "PayPal",
+        "description" => "PayPal payment gateway",
+        "settings" => [
+            "mode" => [
+                "type" => "select",
+                "label" => "Mode",
+                "value" => config("APP_ENV") == 'local' ? "sandbox" : "live",
+                "options" => [
+                    "sandbox" => "Sandbox",
+                    "live" => "Live",
+                ],
+            ],
+            "CLIENT_ID" => [
+                "type" => "text",
+                "label" => "PayPal Client ID",
+                "value" => config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID"),
+            ],
+            "SECRET" => [
+                "type" => "text",
+                "label" => "PayPal Secret",
+                "value" => config("SETTINGS::PAYMENTS:PAYPAL:SECRET"),
+            ],
+            "SANDBOX_CLIENT_ID" => [
+                "type" => "text",
+                "label" => "PayPal Sandbox Client ID",
+                "value" => config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID"),
+            ],
+            "SANDBOX_SECRET" => [
+                "type" => "text",
+                "label" => "PayPal Sandbox Secret",
+                "value" => config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET"),
+            ],
+        ],
+    ];
+}

+ 18 - 0
app/Extensions/PaymentGateways/PayPal/web_routes.php

@@ -0,0 +1,18 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+
+include_once(__DIR__ . '/index.php');
+
+Route::middleware(['web', 'auth'])->group(function () {
+    Route::get('payment/PayPalPay/{shopProduct}', function () {
+        PaypalPay(request());
+    })->name('payment.PayPalPay');
+
+    Route::get(
+        'payment/PayPalSuccess',
+        function () {
+            PaypalSuccess(request());
+        }
+    )->name('payment.PayPalSuccess');
+});

+ 402 - 0
app/Extensions/PaymentGateways/Stripe/index.php

@@ -0,0 +1,402 @@
+<?php
+
+use App\Events\PaymentEvent;
+use App\Events\UserUpdateCreditsEvent;
+use App\Models\PartnerDiscount;
+use App\Models\Payment;
+use App\Models\Product;
+use App\Models\ShopProduct;
+use App\Models\User;
+use App\Notifications\ConfirmPaymentNotification;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Redirect;
+use Stripe\Exception\SignatureVerificationException;
+use Stripe\Stripe;
+use Stripe\StripeClient;
+
+
+
+/**
+ * @param  Request  $request
+ * @param  ShopProduct  $shopProduct
+ */
+function StripePay(Request $request)
+{
+    $user = Auth::user();
+    $shopProduct = ShopProduct::findOrFail($request->shopProduct);
+
+    // check if the price is valid for stripe
+    if (!checkPriceAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), 'stripe')) {
+        Redirect::route('home')->with('error', __('The product you chose can\'t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'))->send();
+        return;
+    }
+
+
+    // create payment
+    $payment = Payment::create([
+        'user_id' => $user->id,
+        'payment_id' => null,
+        'payment_method' => 'stripe',
+        'type' => $shopProduct->type,
+        'status' => 'open',
+        'amount' => $shopProduct->quantity,
+        'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
+        'tax_value' => $shopProduct->getTaxValue(),
+        'total_price' => $shopProduct->getTotalPrice(),
+        'tax_percent' => $shopProduct->getTaxPercent(),
+        'currency_code' => $shopProduct->currency_code,
+        'shop_item_product_id' => $shopProduct->id,
+    ]);
+
+    $stripeClient = getStripeClient();
+    $request = $stripeClient->checkout->sessions->create([
+        'line_items' => [
+            [
+                'price_data' => [
+                    'currency' => $shopProduct->currency_code,
+                    'product_data' => [
+                        'name' => $shopProduct->display . (PartnerDiscount::getDiscount() ? (' (' . __('Discount') . ' ' . PartnerDiscount::getDiscount() . '%)') : ''),
+                        'description' => $shopProduct->description,
+                    ],
+                    'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
+                ],
+                'quantity' => 1,
+            ],
+            [
+                'price_data' => [
+                    'currency' => $shopProduct->currency_code,
+                    'product_data' => [
+                        'name' => __('Tax'),
+                        'description' => $shopProduct->getTaxPercent() . '%',
+                    ],
+                    'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
+                ],
+                'quantity' => 1,
+            ],
+        ],
+
+        'mode' => 'payment',
+        'payment_method_types' => str_getcsv(config('SETTINGS::PAYMENTS:STRIPE:METHODS')),
+        'success_url' => route('payment.StripeSuccess', ['payment' => $payment->id]) . '&session_id={CHECKOUT_SESSION_ID}',
+        'cancel_url' => route('payment.Cancel'),
+    ]);
+
+    Redirect::to($request->url)->send();
+}
+
+/**
+ * @param  Request  $request
+ */
+function StripeSuccess(Request $request)
+{
+    $user = Auth::user();
+    $user = User::findOrFail($user->id);
+    $payment = Payment::findOrFail($request->input('payment'));
+    $shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
+
+
+    $stripeClient = getStripeClient();
+    try {
+        //get stripe data
+        $paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
+        $paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
+
+        //get DB entry of this payment ID if existing
+        $paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
+
+        // check if payment is 100% completed and payment does not exist in db already
+        if ($paymentSession->status == 'complete' && $paymentIntent->status == 'succeeded' && $paymentDbEntry == 0) {
+
+            //update payment
+            $payment->update([
+                'payment_id' => $paymentSession->payment_intent,
+                'status' => 'paid',
+            ]);
+
+            //payment notification
+            $user->notify(new ConfirmPaymentNotification($payment));
+
+            event(new UserUpdateCreditsEvent($user));
+            event(new PaymentEvent($user, $payment, $shopProduct));
+
+            //redirect back to home
+            Redirect::route('home')->with('success', 'Payment successful')->send();
+        } else {
+            if ($paymentIntent->status == 'processing') {
+
+                //update payment
+                $payment->update([
+                    'payment_id' => $paymentSession->payment_intent,
+                    'status' => 'processing',
+                ]);
+
+                event(new PaymentEvent($user, $payment, $shopProduct));
+
+                Redirect::route('home')->with('success', 'Your payment is being processed')->send();
+            }
+
+            if ($paymentDbEntry == 0 && $paymentIntent->status != 'processing') {
+                $stripeClient->paymentIntents->cancel($paymentIntent->id);
+
+                //redirect back to home
+                Redirect::route('home')->with('info', __('Your payment has been canceled!'))->send();
+            } else {
+                abort(402);
+            }
+        }
+    } catch (Exception $e) {
+        if (env('APP_ENV') == 'local') {
+            dd($e->getMessage());
+        } else {
+            abort(422);
+        }
+    }
+}
+
+/**
+ * @param  Request  $request
+ */
+function handleStripePaymentSuccessHook($paymentIntent)
+{
+    try {
+        // Get payment db entry
+        $payment = Payment::where('payment_id', $paymentIntent->id)->first();
+        $user = User::where('id', $payment->user_id)->first();
+        $shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
+
+        if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
+
+
+            //update payment db entry status
+            $payment->update(['status' => 'paid']);
+
+            //payment notification
+            $user->notify(new ConfirmPaymentNotification($payment));
+            event(new UserUpdateCreditsEvent($user));
+            event(new PaymentEvent($user, $payment, $shopProduct));
+        }
+
+        // return 200
+        return response()->json(['success' => true], 200);
+    } catch (Exception $ex) {
+        abort(422);
+    }
+}
+
+/**
+ * @param  Request  $request
+ */
+function StripeWebhooks(Request $request)
+{
+    Stripe::setApiKey(getStripeSecret());
+
+    try {
+        $payload = @file_get_contents('php://input');
+        $sig_header = $request->header('Stripe-Signature');
+        $event = null;
+        $event = \Stripe\Webhook::constructEvent(
+            $payload,
+            $sig_header,
+            getStripeEndpointSecret()
+        );
+    } catch (\UnexpectedValueException $e) {
+        // Invalid payload
+
+        abort(400);
+    } catch (SignatureVerificationException $e) {
+        // Invalid signature
+
+        abort(400);
+    }
+
+    // Handle the event
+    switch ($event->type) {
+        case 'payment_intent.succeeded':
+            $paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
+            handleStripePaymentSuccessHook($paymentIntent);
+            break;
+        default:
+            echo 'Received unknown event type ' . $event->type;
+    }
+}
+
+/**
+ * @return \Stripe\StripeClient
+ */
+function getStripeClient()
+{
+    return new StripeClient(getStripeSecret());
+}
+
+/**
+ * @return string
+ */
+function getStripeSecret()
+{
+    return env('APP_ENV') == 'local'
+        ? config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET')
+        : config('SETTINGS::PAYMENTS:STRIPE:SECRET');
+}
+
+/**
+ * @return string
+ */
+function getStripeEndpointSecret()
+{
+    return env('APP_ENV') == 'local'
+        ? config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET')
+        : config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET');
+}
+/**
+ * @param  $amount
+ * @param  $currencyCode
+ * @param  $payment_method
+ * @return bool
+ * @description check if the amount is higher than the minimum amount for the stripe gateway
+ */
+function checkPriceAmount($amount, $currencyCode, $payment_method)
+{
+    $minimums = [
+        "USD" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "AED" => [
+            "paypal" => 0,
+            "stripe" => 2
+        ],
+        "AUD" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "BGN" => [
+            "paypal" => 0,
+            "stripe" => 1
+        ],
+        "BRL" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "CAD" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "CHF" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "CZK" => [
+            "paypal" => 0,
+            "stripe" => 15
+        ],
+        "DKK" => [
+            "paypal" => 0,
+            "stripe" => 2.5
+        ],
+        "EUR" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "GBP" => [
+            "paypal" => 0,
+            "stripe" => 0.3
+        ],
+        "HKD" => [
+            "paypal" => 0,
+            "stripe" => 4
+        ],
+        "HRK" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "HUF" => [
+            "paypal" => 0,
+            "stripe" => 175
+        ],
+        "INR" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "JPY" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "MXN" => [
+            "paypal" => 0,
+            "stripe" => 10
+        ],
+        "MYR" => [
+            "paypal" => 0,
+            "stripe" => 2
+        ],
+        "NOK" => [
+            "paypal" => 0,
+            "stripe" => 3
+        ],
+        "NZD" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "PLN" => [
+            "paypal" => 0,
+            "stripe" => 2
+        ],
+        "RON" => [
+            "paypal" => 0,
+            "stripe" => 2
+        ],
+        "SEK" => [
+            "paypal" => 0,
+            "stripe" => 3
+        ],
+        "SGD" => [
+            "paypal" => 0,
+            "stripe" => 0.5
+        ],
+        "THB" => [
+            "paypal" => 0,
+            "stripe" => 10
+        ]
+    ];
+    return $amount >= $minimums[$currencyCode][$payment_method];
+}
+
+function getStripeConfig()
+{
+    return [
+        "name" => "Stripe",
+        "description" => "Stripe payment gateway",
+        "mode" => [
+            "type" => "select",
+            "label" => "Mode",
+            "value" => config("APP_ENV") == 'local' ? "sandbox" : "live",
+            "options" => [
+                "sandbox" => "Sandbox",
+                "live" => "Live",
+            ],
+        ],
+        "TEST_SECRET" => [
+            "type" => "text",
+            "label" => "Test Secret Key",
+            "value" => config("SETTINGS::PAYMENTS:STRIPE:TEST_SECRET"),
+        ],
+        "SECRET" => [
+            "type" => "text",
+            "label" => "Live Secret Key",
+            "value" => config("SETTINGS::PAYMENTS:STRIPE:SECRET"),
+        ],
+        "ENDPOINT_TEST_SECRET" => [
+            "type" => "text",
+            "label" => "Test Endpoint Secret",
+            "value" => config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET"),
+        ],
+        "ENDPOINT_SECRET" => [
+            "type" => "text",
+            "label" => "Live Endpoint Secret",
+            "value" => config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET"),
+        ],
+    ];
+}

+ 23 - 0
app/Extensions/PaymentGateways/Stripe/web_routes.php

@@ -0,0 +1,23 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+
+include_once(__DIR__ . '/index.php');
+Route::middleware(['web', 'auth'])->group(function () {
+    Route::get('payment/StripePay/{shopProduct}', function () {
+        StripePay(request());
+    })->name('payment.StripePay');
+
+    Route::get(
+        'payment/StripeSuccess',
+        function () {
+            StripeSuccess(request());
+        }
+    )->name('payment.StripeSuccess');
+});
+
+
+// Stripe WebhookRoute -> validation in Route Handler
+Route::post('payment/StripeWebhooks', function () {
+    StripeWebhooks(request());
+})->name('payment.StripeWebhooks');

+ 47 - 0
app/Helpers/ExtensionHelper.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace App\Helpers;
+
+class ExtensionHelper
+{
+    public static function getExtensionConfig($extensionName, $nameSpace)
+    {
+        $extension = app_path() . '/Extensions/' . $nameSpace . "/" . $extensionName . "/index.php";
+        // Check if extension exists
+        if (!file_exists($extension)) {
+            return null;
+        }
+
+        // call the getConfig function from the index.php file of the extension
+        $config = include_once $extension;
+
+        // Check if the getConfig function exists
+        if (!function_exists('get' . $extensionName . 'Config')) {
+            return null;
+        }
+
+        $config = call_user_func('get' . $extensionName . 'Config');
+
+        // Check if the getConfig function returned an array
+        if (!is_array($config)) {
+            return null;
+        }
+
+        return $config;
+    }
+
+    public static function getPayMethod($extensionName, $nameSpace)
+    {
+        // return the payment method of the extension to be used elsewhere
+        // for example in the payment controller
+        // the function starts with the name of the extension and ends with Pay
+
+        $config = self::getExtensionConfig($extensionName, $nameSpace);
+
+        if ($config == null) {
+            return null;
+        }
+
+        return $config['payMethod'];
+    }
+}

+ 39 - 702
app/Http/Controllers/Admin/PaymentController.php

@@ -6,10 +6,8 @@ use App\Events\UserUpdateCreditsEvent;
 use App\Http\Controllers\Controller;
 use App\Models\PartnerDiscount;
 use App\Models\Payment;
-use App\Models\ShopProduct;
 use App\Models\User;
-use App\Notifications\ConfirmPaymentNotification;
-use App\Notifications\InvoiceNotification;
+use App\Models\ShopProduct;
 use Exception;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\View\Factory;
@@ -18,19 +16,8 @@ use Illuminate\Http\JsonResponse;
 use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Storage;
-use LaravelDaily\Invoices\Classes\Buyer;
-use LaravelDaily\Invoices\Classes\InvoiceItem;
-use LaravelDaily\Invoices\Classes\Party;
-use LaravelDaily\Invoices\Invoice;
-use PayPalCheckoutSdk\Core\PayPalHttpClient;
-use PayPalCheckoutSdk\Core\ProductionEnvironment;
-use PayPalCheckoutSdk\Core\SandboxEnvironment;
-use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
-use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
-use PayPalHttp\HttpException;
-use Symfony\Component\Intl\Currencies;
+use App\Helpers\ExtensionHelper;
+
 
 class PaymentController extends Controller
 {
@@ -49,8 +36,24 @@ class PaymentController extends Controller
      * @param  ShopProduct  $shopProduct
      * @return Application|Factory|View
      */
-    public function checkOut(Request $request, ShopProduct $shopProduct)
-    {
+    public function checkOut(ShopProduct $shopProduct)
+    {
+        // get all payment gateway extensions
+        $extensions = glob(app_path() . '/Extensions/PaymentGateways/*', GLOB_ONLYDIR);
+
+        // build a paymentgateways array that contains the routes for the payment gateways and the image path for the payment gateway which lays in public/images/Extensions/PaymentGateways with the extensionname in lowercase
+        $paymentGateways = [];
+        foreach ($extensions as $extension) {
+            $extensionName = basename($extension);
+            $config = ExtensionHelper::getExtensionConfig($extensionName, 'PaymentGateways');
+            if ($config) {
+                $payment = new \stdClass();
+                $payment->name = $config['name'];
+                $payment->image = asset('images/Extensions/PaymentGateways/' . strtolower($extensionName) . '_logo.png');
+                $paymentGateways[] = $payment;
+            }
+        }
+
         return view('store.checkout')->with([
             'product' => $shopProduct,
             'discountpercent' => PartnerDiscount::getDiscount(),
@@ -59,6 +62,7 @@ class PaymentController extends Controller
             'taxvalue' => $shopProduct->getTaxValue(),
             'taxpercent' => $shopProduct->getTaxPercent(),
             'total' => $shopProduct->getTotalPrice(),
+            'paymentGateways'   => $paymentGateways,
         ]);
     }
 
@@ -67,11 +71,10 @@ class PaymentController extends Controller
      * @param  ShopProduct  $shopProduct
      * @return RedirectResponse
      */
-    public function FreePay(Request $request, ShopProduct $shopProduct)
+    public function FreePay(ShopProduct $shopProduct)
     {
-        //dd($shopProduct);
         //check if the product is really free or the discount is 100%
-        if($shopProduct->getTotalPrice()>0) return redirect()->route('home')->with('error', __('An error ocured. Please try again.'));
+        if ($shopProduct->getTotalPrice() > 0) return redirect()->route('home')->with('error', __('An error ocured. Please try again.'));
 
         //give product
         /** @var User $user */
@@ -80,9 +83,9 @@ class PaymentController extends Controller
         //not updating server limit
 
         //update User with bought item
-        if ($shopProduct->type=="Credits") {
+        if ($shopProduct->type == "Credits") {
             $user->increment('credits', $shopProduct->quantity);
-        }elseif ($shopProduct->type=="Server slots"){
+        } elseif ($shopProduct->type == "Server slots") {
             $user->increment('server_limit', $shopProduct->quantity);
         }
 
@@ -98,7 +101,7 @@ class PaymentController extends Controller
             'type' => $shopProduct->type,
             'status' => 'paid',
             'amount' => $shopProduct->quantity,
-            'price' => $shopProduct->price - ($shopProduct->price*PartnerDiscount::getDiscount()/100),
+            'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
             'tax_value' => $shopProduct->getTaxValue(),
             'tax_percent' => $shopProduct->getTaxPercent(),
             'total_price' => $shopProduct->getTotalPrice(),
@@ -114,196 +117,12 @@ class PaymentController extends Controller
         return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
     }
 
-    /**
-     * @param Request $request
-     * @param ShopProduct $shopProduct
-     * @return RedirectResponse
-     */
-    public function PaypalPay(Request $request, ShopProduct $shopProduct)
-    {
-        if(!$this->checkAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), "paypal")) return redirect()->route('home')->with('error', __('The product you chose can´t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'));
-        $request = new OrdersCreateRequest();
-        $request->prefer('return=representation');
-        $request->body = [
-            'intent' => 'CAPTURE',
-            'purchase_units' => [
-                [
-                    'reference_id' => uniqid(),
-                    'description' => $shopProduct->display.(PartnerDiscount::getDiscount() ? (' ('.__('Discount').' '.PartnerDiscount::getDiscount().'%)') : ''),
-                    'amount' => [
-                        'value' => $shopProduct->getTotalPrice(),
-                        'currency_code' => strtoupper($shopProduct->currency_code),
-                        'breakdown' => [
-                            'item_total' => [
-                                'currency_code' => strtoupper($shopProduct->currency_code),
-                                'value' => $shopProduct->getPriceAfterDiscount(),
-                            ],
-                            'tax_total' => [
-                                'currency_code' => strtoupper($shopProduct->currency_code),
-                                'value' => $shopProduct->getTaxValue(),
-                            ],
-                        ],
-                    ],
-                ],
-            ],
-            'application_context' => [
-                'cancel_url' => route('payment.Cancel'),
-                'return_url' => route('payment.PaypalSuccess', ['product' => $shopProduct->id]),
-                'brand_name' => config('app.name', 'Laravel'),
-                'shipping_preference' => 'NO_SHIPPING',
-            ],
-
-        ];
-
-        try {
-            // Call API with your client and get a response for your call
-            $response = $this->getPayPalClient()->execute($request);
-
-            return redirect()->away($response->result->links[1]->href);
-
-            // If call returns body in response, you can get the deserialized version from the result attribute of the response
-        } catch (HttpException $ex) {
-            echo $ex->statusCode;
-            dd(json_decode($ex->getMessage()));
-        }
-    }
-
-    /**
-     * @return PayPalHttpClient
-     */
-    protected function getPayPalClient()
-    {
-        $environment = env('APP_ENV') == 'local'
-            ? new SandboxEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret())
-            : new ProductionEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret());
-
-        return new PayPalHttpClient($environment);
-    }
-
-    /**
-     * @return string
-     */
-    protected function getPaypalClientId()
-    {
-        return env('APP_ENV') == 'local' ? config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID') : config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID');
-    }
-
-    /**
-     * @return string
-     */
-    protected function getPaypalClientSecret()
-    {
-        return env('APP_ENV') == 'local' ? config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET') : config('SETTINGS::PAYMENTS:PAYPAL:SECRET');
-    }
-
-    /**
-     * @param  Request  $laravelRequest
-     */
-    public function PaypalSuccess(Request $laravelRequest)
+    public function pay(Request $request)
     {
-        /** @var ShopProduct $shopProduct */
-        $shopProduct = ShopProduct::findOrFail($laravelRequest->input('product'));
-
-        /** @var User $user */
-        $user = Auth::user();
-
-        $request = new OrdersCaptureRequest($laravelRequest->input('token'));
-        $request->prefer('return=representation');
-        try {
-            // Call API with your client and get a response for your call
-            $response = $this->getPayPalClient()->execute($request);
-            if ($response->statusCode == 201 || $response->statusCode == 200) {
+        $product = ShopProduct::find($request->product_id);
+        $paymentGateway = $request->payment_method;
 
-                //update server limit
-                if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
-                    if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
-                        $user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
-                    }
-                }
-
-                //update User with bought item
-                if ($shopProduct->type == 'Credits') {
-                    $user->increment('credits', $shopProduct->quantity);
-                } elseif ($shopProduct->type == 'Server slots') {
-                    $user->increment('server_limit', $shopProduct->quantity);
-                }
-
-                //give referral commission always
-                if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits' && config('SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION') == 'true') {
-                    if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
-                        $ref_user = User::findOrFail($ref_user->referral_id);
-                        $increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, '', '');
-                        $ref_user->increment('credits', $increment);
-
-                        //LOGS REFERRALS IN THE ACTIVITY LOG
-                        activity()
-                            ->performedOn($user)
-                            ->causedBy($ref_user)
-                            ->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
-                    }
-                }
-
-                //update role give Referral-reward
-                if ($user->role == 'member') {
-                    $user->update(['role' => 'client']);
-
-                    //give referral commission only on first purchase
-                    if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits' && config('SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION') == 'false') {
-                        if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
-                            $ref_user = User::findOrFail($ref_user->referral_id);
-                            $increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, '', '');
-                            $ref_user->increment('credits', $increment);
-
-                            //LOGS REFERRALS IN THE ACTIVITY LOG
-                            activity()
-                                ->performedOn($user)
-                                ->causedBy($ref_user)
-                                ->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
-                        }
-                    }
-                }
-
-                //store payment
-                $payment = Payment::create([
-                    'user_id' => $user->id,
-                    'payment_id' => $response->result->id,
-                    'payment_method' => 'paypal',
-                    'type' => $shopProduct->type,
-                    'status' => 'paid',
-                    'amount' => $shopProduct->quantity,
-                    'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
-                    'tax_value' => $shopProduct->getTaxValue(),
-                    'tax_percent' => $shopProduct->getTaxPercent(),
-                    'total_price' => $shopProduct->getTotalPrice(),
-                    'currency_code' => $shopProduct->currency_code,
-                    'shop_item_product_id' => $shopProduct->id,
-                ]);
-
-                event(new UserUpdateCreditsEvent($user));
-
-                //only create invoice if SETTINGS::INVOICE:ENABLED is true
-                if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
-                    $this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
-                }
-
-                //redirect back to home
-                return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
-            }
-
-            // If call returns body in response, you can get the deserialized version from the result attribute of the response
-            if (env('APP_ENV') == 'local') {
-                dd($response);
-            } else {
-                abort(500);
-            }
-        } catch (HttpException $ex) {
-            if (env('APP_ENV') == 'local') {
-                echo $ex->statusCode;
-                dd($ex->getMessage());
-            } else {
-                abort(422);
-            }
-        }
+        return redirect()->route('payment.' . $paymentGateway . 'Pay', ['shopProduct' => $product->id]);
     }
 
     /**
@@ -311,491 +130,9 @@ class PaymentController extends Controller
      */
     public function Cancel(Request $request)
     {
-        return redirect()->route('store.index')->with('success', 'Payment was Canceled');
-    }
-
-    /**
-     * @param  Request  $request
-     * @param  ShopProduct  $shopProduct
-     * @return RedirectResponse
-     */
-    public function StripePay(Request $request, ShopProduct $shopProduct)
-    {
-        if(!$this->checkAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), "stripe")) return redirect()->route('home')->with('error', __('The product you chose can´t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'));
-        $stripeClient = $this->getStripeClient();
-
-        $request = $stripeClient->checkout->sessions->create([
-            'line_items' => [
-                [
-                    'price_data' => [
-                        'currency' => $shopProduct->currency_code,
-                        'product_data' => [
-                            'name' => $shopProduct->display.(PartnerDiscount::getDiscount() ? (' ('.__('Discount').' '.PartnerDiscount::getDiscount().'%)') : ''),
-                            'description' => $shopProduct->description,
-                        ],
-                        'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
-                    ],
-                    'quantity' => 1,
-                ],
-                [
-                    'price_data' => [
-                        'currency' => $shopProduct->currency_code,
-                        'product_data' => [
-                            'name' => __('Tax'),
-                            'description' => $shopProduct->getTaxPercent().'%',
-                        ],
-                        'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
-                    ],
-                    'quantity' => 1,
-                ],
-            ],
-
-            'mode' => 'payment',
-            'payment_method_types' => str_getcsv(config('SETTINGS::PAYMENTS:STRIPE:METHODS')),
-            'success_url' => route('payment.StripeSuccess', ['product' => $shopProduct->id]).'&session_id={CHECKOUT_SESSION_ID}',
-            'cancel_url' => route('payment.Cancel'),
-        ]);
-
-        return redirect($request->url, 303);
-    }
-
-    /**
-     * @param  Request  $request
-     */
-    public function StripeSuccess(Request $request)
-    {
-        /** @var ShopProduct $shopProduct */
-        $shopProduct = ShopProduct::findOrFail($request->input('product'));
-
-        /** @var User $user */
-        $user = Auth::user();
-
-        $stripeClient = $this->getStripeClient();
-
-        try {
-            //get stripe data
-            $paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
-            $paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
-
-            //get DB entry of this payment ID if existing
-            $paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
-
-            // check if payment is 100% completed and payment does not exist in db already
-            if ($paymentSession->status == 'complete' && $paymentIntent->status == 'succeeded' && $paymentDbEntry == 0) {
-
-                //update server limit
-                if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
-                    if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
-                        $user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
-                    }
-                }
-
-                //update User with bought item
-                if ($shopProduct->type == 'Credits') {
-                    $user->increment('credits', $shopProduct->quantity);
-                } elseif ($shopProduct->type == 'Server slots') {
-                    $user->increment('server_limit', $shopProduct->quantity);
-                }
-
-                //update role give Referral-reward
-                if ($user->role == 'member') {
-                    $user->update(['role' => 'client']);
-
-                    if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits') {
-                        if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
-                            $ref_user = User::findOrFail($ref_user->referral_id);
-                            $increment = number_format($shopProduct->quantity / 100 * config('SETTINGS::REFERRAL:PERCENTAGE'), 0, '', '');
-                            $ref_user->increment('credits', $increment);
-
-                            //LOGS REFERRALS IN THE ACTIVITY LOG
-                            activity()
-                                ->performedOn($user)
-                                ->causedBy($ref_user)
-                                ->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
-                        }
-                    }
-                }
-
-                //store paid payment
-                $payment = Payment::create([
-                    'user_id' => $user->id,
-                    'payment_id' => $paymentSession->payment_intent,
-                    'payment_method' => 'stripe',
-                    'type' => $shopProduct->type,
-                    'status' => 'paid',
-                    'amount' => $shopProduct->quantity,
-                    'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
-                    'tax_value' => $shopProduct->getTaxValue(),
-                    'total_price' => $shopProduct->getTotalPrice(),
-                    'tax_percent' => $shopProduct->getTaxPercent(),
-                    'currency_code' => $shopProduct->currency_code,
-                    'shop_item_product_id' => $shopProduct->id,
-                ]);
-
-                //payment notification
-                $user->notify(new ConfirmPaymentNotification($payment));
-
-                event(new UserUpdateCreditsEvent($user));
-
-                //only create invoice if SETTINGS::INVOICE:ENABLED is true
-                if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
-                    $this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
-                }
-
-                //redirect back to home
-                return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
-            } else {
-                if ($paymentIntent->status == 'processing') {
-
-                    //store processing payment
-                    $payment = Payment::create([
-                        'user_id' => $user->id,
-                        'payment_id' => $paymentSession->payment_intent,
-                        'payment_method' => 'stripe',
-                        'type' => $shopProduct->type,
-                        'status' => 'processing',
-                        'amount' => $shopProduct->quantity,
-                        'price' => $shopProduct->price,
-                        'tax_value' => $shopProduct->getTaxValue(),
-                        'total_price' => $shopProduct->getTotalPrice(),
-                        'tax_percent' => $shopProduct->getTaxPercent(),
-                        'currency_code' => $shopProduct->currency_code,
-                        'shop_item_product_id' => $shopProduct->id,
-                    ]);
-
-                    //only create invoice if SETTINGS::INVOICE:ENABLED is true
-                    if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
-                        $this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
-                    }
-
-                    //redirect back to home
-                    return redirect()->route('home')->with('success', __('Your payment is being processed!'));
-                }
-                if ($paymentDbEntry == 0 && $paymentIntent->status != 'processing') {
-                    $stripeClient->paymentIntents->cancel($paymentIntent->id);
-
-                    //redirect back to home
-                    return redirect()->route('home')->with('success', __('Your payment has been canceled!'));
-                } else {
-                    abort(402);
-                }
-            }
-        } catch (HttpException $ex) {
-            if (env('APP_ENV') == 'local') {
-                echo $ex->statusCode;
-                dd($ex->getMessage());
-            } else {
-                abort(422);
-            }
-        }
-    }
-
-    /**
-     * @param  Request  $request
-     */
-    protected function handleStripePaymentSuccessHook($paymentIntent)
-    {
-        try {
-            // Get payment db entry
-            $payment = Payment::where('payment_id', $paymentIntent->id)->first();
-            $user = User::where('id', $payment->user_id)->first();
-
-            if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
-
-                //update server limit
-                if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
-                    if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
-                        $user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
-                    }
-                }
-                //update User with bought item
-                if ($shopProduct->type == 'Credits') {
-                    $user->increment('credits', $shopProduct->quantity);
-                } elseif ($shopProduct->type == 'Server slots') {
-                    $user->increment('server_limit', $shopProduct->quantity);
-                }
-
-                //update role give Referral-reward
-                if ($user->role == 'member') {
-                    $user->update(['role' => 'client']);
-
-                    if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits') {
-                        if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
-                            $ref_user = User::findOrFail($ref_user->referral_id);
-                            $increment = number_format($shopProduct->quantity / 100 * config('SETTINGS::REFERRAL:PERCENTAGE'), 0, '', '');
-                            $ref_user->increment('credits', $increment);
-
-                            //LOGS REFERRALS IN THE ACTIVITY LOG
-                            activity()
-                                ->performedOn($user)
-                                ->causedBy($ref_user)
-                                ->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
-                        }
-                    }
-                }
-
-                //update payment db entry status
-                $payment->update(['status' => 'paid']);
-
-                //payment notification
-                $user->notify(new ConfirmPaymentNotification($payment));
-                event(new UserUpdateCreditsEvent($user));
-
-                //only create invoice if SETTINGS::INVOICE:ENABLED is true
-                if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
-                    $this->createInvoice($user, $payment, 'paid', strtoupper($paymentIntent->currency));
-                }
-            }
-        } catch (HttpException $ex) {
-            abort(422);
-        }
-    }
-
-    /**
-     * @param  Request  $request
-     */
-    public function StripeWebhooks(Request $request)
-    {
-        \Stripe\Stripe::setApiKey($this->getStripeSecret());
-
-        try {
-            $payload = @file_get_contents('php://input');
-            $sig_header = $request->header('Stripe-Signature');
-            $event = null;
-            $event = \Stripe\Webhook::constructEvent(
-                $payload,
-                $sig_header,
-                $this->getStripeEndpointSecret()
-            );
-        } catch (\UnexpectedValueException $e) {
-            // Invalid payload
-
-            abort(400);
-        } catch (\Stripe\Exception\SignatureVerificationException $e) {
-            // Invalid signature
-
-            abort(400);
-        }
-
-        // Handle the event
-        switch ($event->type) {
-            case 'payment_intent.succeeded':
-                $paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
-                $this->handleStripePaymentSuccessHook($paymentIntent);
-                break;
-            default:
-                echo 'Received unknown event type '.$event->type;
-        }
-    }
-
-    /**
-     * @return \Stripe\StripeClient
-     */
-    protected function getStripeClient()
-    {
-        return new \Stripe\StripeClient($this->getStripeSecret());
-    }
-
-    /**
-     * @return string
-     */
-    protected function getStripeSecret()
-    {
-        return env('APP_ENV') == 'local'
-            ? config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET')
-            : config('SETTINGS::PAYMENTS:STRIPE:SECRET');
-    }
-
-    /**
-     * @return string
-     */
-    protected function getStripeEndpointSecret()
-    {
-        return env('APP_ENV') == 'local'
-            ? config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET')
-            : config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET');
+        return redirect()->route('store.index')->with('info', 'Payment was Canceled');
     }
 
-    protected function createInvoice($user, $payment, $paymentStatus, $currencyCode)
-    {
-        $shopProduct = ShopProduct::where('id', $payment->shop_item_product_id)->first();
-        //create invoice
-        $lastInvoiceID = \App\Models\Invoice::where('invoice_name', 'like', '%'.now()->format('mY').'%')->count('id');
-        $newInvoiceID = $lastInvoiceID + 1;
-        $logoPath = storage_path('app/public/logo.png');
-
-        $seller = new Party([
-            'name' => config('SETTINGS::INVOICE:COMPANY_NAME'),
-            'phone' => config('SETTINGS::INVOICE:COMPANY_PHONE'),
-            'address' => config('SETTINGS::INVOICE:COMPANY_ADDRESS'),
-            'vat' => config('SETTINGS::INVOICE:COMPANY_VAT'),
-            'custom_fields' => [
-                'E-Mail' => config('SETTINGS::INVOICE:COMPANY_MAIL'),
-                'Web' => config('SETTINGS::INVOICE:COMPANY_WEBSITE'),
-            ],
-        ]);
-
-        $customer = new Buyer([
-            'name' => $user->name,
-            'custom_fields' => [
-                'E-Mail' => $user->email,
-                'Client ID' => $user->id,
-            ],
-        ]);
-        $item = (new InvoiceItem())
-            ->title($shopProduct->description)
-            ->pricePerUnit($shopProduct->price);
-
-        $notes = [
-            __('Payment method').': '.$payment->payment_method,
-        ];
-        $notes = implode('<br>', $notes);
-
-        $invoice = Invoice::make()
-            ->template('controlpanel')
-            ->name(__('Invoice'))
-            ->buyer($customer)
-            ->seller($seller)
-            ->discountByPercent(PartnerDiscount::getDiscount())
-            ->taxRate(floatval($shopProduct->getTaxPercent()))
-            ->shipping(0)
-            ->addItem($item)
-            ->status(__($paymentStatus))
-            ->series(now()->format('mY'))
-            ->delimiter('-')
-            ->sequence($newInvoiceID)
-            ->serialNumberFormat(config('SETTINGS::INVOICE:PREFIX').'{DELIMITER}{SERIES}{SEQUENCE}')
-            ->currencyCode($currencyCode)
-            ->currencySymbol(Currencies::getSymbol($currencyCode))
-            ->notes($notes);
-
-        if (file_exists($logoPath)) {
-            $invoice->logo($logoPath);
-        }
-
-        //Save the invoice in "storage\app\invoice\USER_ID\YEAR"
-        $invoice->filename = $invoice->getSerialNumber().'.pdf';
-        $invoice->render();
-        Storage::disk('local')->put('invoice/'.$user->id.'/'.now()->format('Y').'/'.$invoice->filename, $invoice->output);
-
-        \App\Models\Invoice::create([
-            'invoice_user' => $user->id,
-            'invoice_name' => $invoice->getSerialNumber(),
-            'payment_id' => $payment->payment_id,
-        ]);
-
-        //Send Invoice per Mail
-        $user->notify(new InvoiceNotification($invoice, $user, $payment));
-    }
-
-    public function checkAmount($amount, $currencyCode, $payment_method)
-    {
-        $minimums = [
-            "USD" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "AED" => [
-                "paypal" => 0,
-                "stripe" => 2
-            ],
-            "AUD" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "BGN" => [
-                "paypal" => 0,
-                "stripe" => 1
-            ],
-            "BRL" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "CAD" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "CHF" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "CZK" => [
-                "paypal" => 0,
-                "stripe" => 15
-            ],
-            "DKK" => [
-                "paypal" => 0,
-                "stripe" => 2.5
-            ],
-            "EUR" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "GBP" => [
-                "paypal" => 0,
-                "stripe" => 0.3
-            ],
-            "HKD" => [
-                "paypal" => 0,
-                "stripe" => 4
-            ],
-            "HRK" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "HUF" => [
-                "paypal" => 0,
-                "stripe" => 175
-            ],
-            "INR" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "JPY" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "MXN" => [
-                "paypal" => 0,
-                "stripe" => 10
-            ],
-            "MYR" => [
-                "paypal" => 0,
-                "stripe" => 2
-            ],
-            "NOK" => [
-                "paypal" => 0,
-                "stripe" => 3
-            ],
-            "NZD" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "PLN" => [
-                "paypal" => 0,
-                "stripe" => 2
-            ],
-            "RON" => [
-                "paypal" => 0,
-                "stripe" => 2
-            ],
-            "SEK" => [
-                "paypal" => 0,
-                "stripe" => 3
-            ],
-            "SGD" => [
-                "paypal" => 0,
-                "stripe" => 0.5
-            ],
-            "THB" => [
-                "paypal" => 0,
-                "stripe" => 10
-            ]
-        ];
-        return $amount >= $minimums[$currencyCode][$payment_method];
-    }
-
-
     /**
      * @return JsonResponse|mixed
      *
@@ -808,9 +145,7 @@ class PaymentController extends Controller
         return datatables($query)
 
             ->addColumn('user', function (Payment $payment) {
-                return
-                ($payment->user)?'<a href="'.route('admin.users.show', $payment->user->id).'">'.$payment->user->name.'</a>':__('Unknown user');
-
+                return ($payment->user) ? '<a href="' . route('admin.users.show', $payment->user->id) . '">' . $payment->user->name . '</a>' : __('Unknown user');
             })
             ->editColumn('price', function (Payment $payment) {
                 return $payment->formatToCurrency($payment->price);
@@ -819,18 +154,20 @@ class PaymentController extends Controller
                 return $payment->formatToCurrency($payment->tax_value);
             })
             ->editColumn('tax_percent', function (Payment $payment) {
-                return $payment->tax_percent.' %';
+                return $payment->tax_percent . ' %';
             })
             ->editColumn('total_price', function (Payment $payment) {
                 return $payment->formatToCurrency($payment->total_price);
             })
 
             ->editColumn('created_at', function (Payment $payment) {
-                return ['display' => $payment->created_at ? $payment->created_at->diffForHumans() : '',
-                        'raw' => $payment->created_at ? strtotime($payment->created_at) : ''];
+                return [
+                    'display' => $payment->created_at ? $payment->created_at->diffForHumans() : '',
+                    'raw' => $payment->created_at ? strtotime($payment->created_at) : ''
+                ];
             })
             ->addColumn('actions', function (Payment $payment) {
-                return '<a data-content="'.__('Download').'" data-toggle="popover" data-trigger="hover" data-placement="top"  href="'.route('admin.invoices.downloadSingleInvoice', 'id='.$payment->payment_id).'" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>';
+                return '<a data-content="' . __('Download') . '" data-toggle="popover" data-trigger="hover" data-placement="top"  href="' . route('admin.invoices.downloadSingleInvoice', 'id=' . $payment->payment_id) . '" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>';
             })
             ->rawColumns(['actions', 'user'])
             ->make(true);

+ 31 - 0
app/Listeners/CreateInvoice.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Listeners;
+
+use App\Events\PaymentEvent;
+use App\Traits\Invoiceable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+
+class CreateInvoice implements ShouldQueue
+{
+
+    use Invoiceable;
+
+    /**
+     * Handle the event.
+     *
+     * @param  \App\Events\PaymentEvent  $event
+     * @return void
+     */
+    public function handle(PaymentEvent $event)
+    {
+        if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
+            // get user from payment which does hold the user_id
+            $user = $event->payment->user;
+
+            // create invoice using the trait
+            $this->createInvoice($user, $event->payment);
+        }
+    }
+}

+ 82 - 0
app/Listeners/UserPayment.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace App\Listeners;
+
+use App\Events\PaymentEvent;
+use App\Models\User;
+use Illuminate\Support\Facades\DB;
+use App\Models\PartnerDiscount;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+
+class UserPayment
+{
+    /**
+     * Handle the event.
+     *
+     * @param  \App\Events\PaymentEvent  $event
+     * @return void
+     */
+    public function handle(PaymentEvent $event)
+    {
+        $user = $event->user;
+        $shopProduct = $event->shopProduct;
+
+        // only update user if payment is paid
+        if ($event->payment->status != "paid") {
+            return;
+        }
+
+        //update server limit
+        if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0 && $user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
+            $user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
+        }
+
+        //update User with bought item
+        if ($shopProduct->type == "Credits") {
+            $user->increment('credits', $shopProduct->quantity);
+        } elseif ($shopProduct->type == "Server slots") {
+            $user->increment('server_limit', $shopProduct->quantity);
+        }
+
+        //give referral commission always
+        if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "true") {
+            if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
+                $ref_user = User::findOrFail($ref_user->referral_id);
+                $increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
+                $ref_user->increment('credits', $increment);
+
+                //LOGS REFERRALS IN THE ACTIVITY LOG
+                activity()
+                    ->performedOn($user)
+                    ->causedBy($ref_user)
+                    ->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
+            }
+        }
+        //update role give Referral-reward
+        if ($user->role == 'member') {
+            $user->update(['role' => 'client']);
+
+            //give referral commission only on first purchase
+            if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "false") {
+                if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
+                    $ref_user = User::findOrFail($ref_user->referral_id);
+                    $increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
+                    $ref_user->increment('credits', $increment);
+
+                    //LOGS REFERRALS IN THE ACTIVITY LOG
+                    activity()
+                        ->performedOn($user)
+                        ->causedBy($ref_user)
+                        ->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
+                }
+            }
+        }
+
+        // LOGS PAYMENT IN THE ACTIVITY LOG
+        activity()
+            ->performedOn($user)
+            ->causedBy($user)
+            ->log('bought ' . $shopProduct->quantity . ' ' . $shopProduct->type . ' for ' . $shopProduct->price . $shopProduct->currency_code);
+    }
+}

+ 1 - 10
app/Models/Payment.php

@@ -7,20 +7,11 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use NumberFormatter;
-use Spatie\Activitylog\LogOptions;
-use Spatie\Activitylog\Traits\LogsActivity;
 
 class Payment extends Model
 {
     use HasFactory;
-    use LogsActivity;
-    public function getActivitylogOptions(): LogOptions
-    {
-        return LogOptions::defaults()
-            -> logOnlyDirty()
-            -> logOnly(['*'])
-            -> dontSubmitEmptyLogs();
-    }
+
     public $incrementing = false;
 
     /**

+ 7 - 0
app/Providers/EventServiceProvider.php

@@ -2,8 +2,11 @@
 
 namespace App\Providers;
 
+use App\Events\PaymentEvent;
 use App\Events\UserUpdateCreditsEvent;
+use App\Listeners\CreateInvoice;
 use App\Listeners\UnsuspendServers;
+use App\Listeners\UserPayment;
 use App\Listeners\Verified;
 use Illuminate\Auth\Events\Registered;
 use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
@@ -24,6 +27,10 @@ class EventServiceProvider extends ServiceProvider
         UserUpdateCreditsEvent::class => [
             UnsuspendServers::class,
         ],
+        PaymentEvent::class => [
+            CreateInvoice::class,
+            UserPayment::class,
+        ],
         SocialiteWasCalled::class => [
             // ... other providers
             'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle',

+ 91 - 0
app/Traits/Invoiceable.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace App\Traits;
+
+use App\Models\PartnerDiscount;
+use App\Models\ShopProduct;
+use App\Notifications\InvoiceNotification;
+use Illuminate\Support\Facades\Storage;
+use LaravelDaily\Invoices\Classes\Buyer;
+use LaravelDaily\Invoices\Classes\InvoiceItem;
+use LaravelDaily\Invoices\Classes\Party;
+use LaravelDaily\Invoices\Invoice;
+use Symfony\Component\Intl\Currencies;
+
+trait Invoiceable
+{
+    public function createInvoice($user, $payment)
+    {
+        $shopProduct = ShopProduct::where('id', $payment->shop_item_product_id)->first();
+        //create invoice
+        $lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
+        $newInvoiceID = $lastInvoiceID + 1;
+        $logoPath = storage_path('app/public/logo.png');
+
+        $seller = new Party([
+            'name' => config("SETTINGS::INVOICE:COMPANY_NAME"),
+            'phone' => config("SETTINGS::INVOICE:COMPANY_PHONE"),
+            'address' => config("SETTINGS::INVOICE:COMPANY_ADDRESS"),
+            'vat' => config("SETTINGS::INVOICE:COMPANY_VAT"),
+            'custom_fields' => [
+                'E-Mail' => config("SETTINGS::INVOICE:COMPANY_MAIL"),
+                "Web" => config("SETTINGS::INVOICE:COMPANY_WEBSITE")
+            ],
+        ]);
+
+
+        $customer = new Buyer([
+            'name' => $user->name,
+            'custom_fields' => [
+                'E-Mail' => $user->email,
+                'Client ID' => $user->id,
+            ],
+        ]);
+        $item = (new InvoiceItem())
+            ->title($shopProduct->description)
+            ->pricePerUnit($shopProduct->price);
+
+        $notes = [
+            __("Payment method") . ": " . $payment->payment_method,
+        ];
+        $notes = implode("<br>", $notes);
+
+
+        $invoice = Invoice::make()
+            ->template('controlpanel')
+            ->name(__("Invoice"))
+            ->buyer($customer)
+            ->seller($seller)
+            ->discountByPercent(PartnerDiscount::getDiscount())
+            ->taxRate(floatval($shopProduct->getTaxPercent()))
+            ->shipping(0)
+            ->addItem($item)
+            ->status(__($payment->status))
+            ->series(now()->format('mY'))
+            ->delimiter("-")
+            ->sequence($newInvoiceID)
+            ->serialNumberFormat(config("SETTINGS::INVOICE:PREFIX") . '{DELIMITER}{SERIES}{SEQUENCE}')
+            ->currencyCode(strtoupper($payment->currency_code))
+            ->currencySymbol(Currencies::getSymbol(strtoupper($payment->currency_code)))
+            ->notes($notes);
+
+        if (file_exists($logoPath)) {
+            $invoice->logo($logoPath);
+        }
+
+        //Save the invoice in "storage\app\invoice\USER_ID\YEAR"
+        $invoice->filename = $invoice->getSerialNumber() . '.pdf';
+        $invoice->render();
+        Storage::disk("local")->put("invoice/" . $user->id . "/" . now()->format('Y') . "/" . $invoice->filename, $invoice->output);
+
+
+        \App\Models\Invoice::create([
+            'invoice_user' => $user->id,
+            'invoice_name' => $invoice->getSerialNumber(),
+            'payment_id' => $payment->payment_id,
+        ]);
+
+        //Send Invoice per Mail
+        $user->notify(new InvoiceNotification($invoice, $user, $payment));
+    }
+}

+ 0 - 0
public/images/paypal_logo.png → public/images/Extensions/PaymentGateways/paypal_logo.png


+ 0 - 0
public/images/stripe_logo.png → public/images/Extensions/PaymentGateways/stripe_logo.png


+ 3 - 1
routes/api.php

@@ -34,6 +34,8 @@ Route::middleware('api.token')->group(function () {
     Route::get('/notifications/{user}', [NotificationController::class, 'index']);
     Route::get('/notifications/{user}/{notification}', [NotificationController::class, 'view']);
     Route::post('/notifications', [NotificationController::class, 'send']);
-    Route::delete('/notifications/{user}', [NotificationController::class, 'delete']);
     Route::delete('/notifications/{user}/{notification}', [NotificationController::class, 'deleteOne']);
+    Route::delete('/notifications/{user}', [NotificationController::class, 'delete']);
 });
+
+require __DIR__ . '/extensions_api.php';

+ 21 - 0
routes/extensions_api.php

@@ -0,0 +1,21 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+
+Route::group(['prefix' => 'extensions', 'middleware' => 'api.token'], function () {
+    // get all extensions that are inside App/Extensions
+    // It is important that the extensions are inside a folder with the name of the extension
+    // while those folders are inside Folders with the name of the type of extension like PaymentGateways, Themes, etc.
+    $extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
+    $extensions = [];
+    foreach ($extensionNamespaces as $extensionNamespace) {
+        $extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
+    }
+
+    foreach ($extensions as $extension) {
+        $routesFile = $extension . '/api_routes.php';
+        if (file_exists($routesFile)) {
+            include_once $routesFile;
+        }
+    }
+});

+ 22 - 0
routes/extensions_web.php

@@ -0,0 +1,22 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+
+Route::group(['prefix' => 'extensions'], function () {
+
+    // get all extensions that are inside App/Extensions
+    // It is important that the extensions are inside a folder with the name of the extension
+    // while those folders are inside Folders with the name of the type of extension like PaymentGateways, Themes, etc.
+    $extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
+    $extensions = [];
+    foreach ($extensionNamespaces as $extensionNamespace) {
+        $extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
+    }
+
+    foreach ($extensions as $extension) {
+        $routesFile = $extension . '/web_routes.php';
+        if (file_exists($routesFile)) {
+            include_once $routesFile;
+        }
+    }
+});

+ 6 - 10
routes/web.php

@@ -49,17 +49,14 @@ Route::middleware('guest')->get('/', function () {
 
 Auth::routes(['verify' => true]);
 
-// Stripe WebhookRoute -> validation in Route Handler
-Route::post('payment/StripeWebhooks', [PaymentController::class, 'StripeWebhooks'])->name('payment.StripeWebhooks');
-
 Route::get('/privacy', function () {
-return view('information.privacy');
+    return view('information.privacy');
 })->name('privacy');
 Route::get('/imprint', function () {
-return view('information.imprint');
+    return view('information.imprint');
 })->name('imprint');
 Route::get('/tos', function () {
-return view('information.tos');
+    return view('information.tos');
 })->name('tos');
 
 Route::middleware(['auth', 'checkSuspended'])->group(function () {
@@ -90,10 +87,7 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
 
     //payments
     Route::get('checkout/{shopProduct}', [PaymentController::class, 'checkOut'])->name('checkout');
-    Route::get('payment/PaypalPay/{shopProduct}', [PaymentController::class, 'PaypalPay'])->name('payment.PaypalPay');
-    Route::get('payment/PaypalSuccess', [PaymentController::class, 'PaypalSuccess'])->name('payment.PaypalSuccess');
-    Route::get('payment/StripePay/{shopProduct}', [PaymentController::class, 'StripePay'])->name('payment.StripePay');
-    Route::get('payment/StripeSuccess', [PaymentController::class, 'StripeSuccess'])->name('payment.StripeSuccess');
+    Route::post('payment/pay', [PaymentController::class, 'pay'])->name('payment.pay');
     Route::get('payment/FreePay/{shopProduct}', [PaymentController::class, 'FreePay'])->name('payment.FreePay');
     Route::get('payment/Cancel', [PaymentController::class, 'Cancel'])->name('payment.Cancel');
 
@@ -221,3 +215,5 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
 
     Route::get('/home', [HomeController::class, 'index'])->name('home');
 });
+
+require __DIR__ . '/extensions_web.php';

+ 99 - 130
themes/default/views/store/checkout.blade.php

@@ -10,8 +10,7 @@
                 </div>
                 <div class="col-sm-6">
                     <ol class="breadcrumb float-sm-right">
-                        <li class="breadcrumb-item"><a class=""
-                                href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
+                        <li class="breadcrumb-item"><a class="" href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
                         <li class="breadcrumb-item"><a class="text-muted"
                                 href="{{ route('store.index') }}">{{ __('Store') }}</a>
                         </li>
@@ -23,157 +22,127 @@
     <!-- END CONTENT HEADER -->
 
     <!-- MAIN CONTENT -->
-    <section x-data="serverApp()" x-init="$watch('paymentMethod', value => setPaymentRoute(value))" class="content">
+    <section class="content">
         <div class="container-fluid">
 
             <div class="row">
                 <div class="col-12">
 
-
-                    <!-- Main content -->
-                    <div class="invoice p-3 mb-3">
-                        <!-- title row -->
-                        <div class="row">
-                            <div class="col-12">
-                                <h4>
-                                    <i class="fas fa-globe"></i> {{ config('app.name', 'Laravel') }}
-                                    <small class="float-right">{{ __('Date') }}:
-                                        {{ Carbon\Carbon::now()->isoFormat('LL') }}</small>
-                                </h4>
+                    <form x-data="{ payment_method: '', clicked: false }" action="{{ route('payment.pay') }}" method="POST">
+                        @csrf
+                        @method('post')
+                        <!-- Main content -->
+                        <div class="invoice p-3 mb-3">
+                            <!-- title row -->
+                            <div class="row">
+                                <div class="col-12">
+                                    <h4>
+                                        <i class="fas fa-globe"></i> {{ config('app.name', 'Laravel') }}
+                                        <small class="float-right">{{ __('Date') }}:
+                                            {{ Carbon\Carbon::now()->isoFormat('LL') }}</small>
+                                    </h4>
+                                </div>
+                                <!-- /.col -->
                             </div>
-                            <!-- /.col -->
-                        </div>
 
-                        <!-- Table row -->
-                        <div class="row">
-                            <div class="col-12 table-responsive">
-                                <table class="table table-striped">
-                                    <thead>
-                                        <tr>
-                                            <th>{{ __('Quantity') }}</th>
-                                            <th>{{ __('Product') }}</th>
-                                            <th>{{ __('Description') }}</th>
-                                            <th>{{ __('Subtotal') }}</th>
-                                        </tr>
-                                    </thead>
-                                    <tbody>
-                                        <tr>
-                                            <td>1</td>
-                                            <td><i class="fa fa-coins mr-2"></i>{{ $product->quantity }}
-                                                {{ strtolower($product->type) == 'credits' ? CREDITS_DISPLAY_NAME : $product->type }}
-                                            </td>
-                                            <td>{{ $product->description }}</td>
-                                            <td>{{ $product->formatToCurrency($product->price) }}</td>
-                                        </tr>
-                                    </tbody>
-                                </table>
+                            <!-- Table row -->
+                            <div class="row">
+                                <div class="col-12 table-responsive">
+                                    <table class="table table-striped">
+                                        <thead>
+                                            <tr>
+                                                <th>{{ __('Quantity') }}</th>
+                                                <th>{{ __('Product') }}</th>
+                                                <th>{{ __('Description') }}</th>
+                                                <th>{{ __('Subtotal') }}</th>
+                                            </tr>
+                                        </thead>
+                                        <tbody>
+                                            <tr>
+                                                <td>1</td>
+                                                <td><i class="fa fa-coins mr-2"></i>{{ $product->quantity }}
+                                                    {{ strtolower($product->type) == 'credits' ? CREDITS_DISPLAY_NAME : $product->type }}
+                                                </td>
+                                                <td>{{ $product->description }}</td>
+                                                <td>{{ $product->formatToCurrency($product->price) }}</td>
+                                            </tr>
+                                        </tbody>
+                                    </table>
+                                    <input type="hidden" name="product_id" value="{{ $product->id }}">
+                                </div>
+                                <!-- /.col -->
                             </div>
-                            <!-- /.col -->
-                        </div>
-                        <!-- /.row -->
+                            <!-- /.row -->
 
-                        <div class="row">
-                            <!-- accepted payments column -->
-                            <div class="col-6">
-                                @if($total!=0)
+                            <div class="row">
+                                <!-- accepted payments column -->
+                                <div class="col-6">
                                     <p class="lead">{{ __('Payment Methods') }}:</p>
 
-                                    <div>
-                                        @if (config('SETTINGS::PAYMENTS:PAYPAL:SECRET') || config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET'))
-                                            <label class="text-center " for="paypal">
-                                                <img class="mb-3" height="50"
-                                                    src="{{ url('/images/paypal_logo.png') }}"></br>
-
-                                                <input x-model="paymentMethod" type="radio" id="paypal" value="paypal"
-                                                    name="payment_method">
-                                                </input>
-                                            </label>
-                                        @endif
-                                        @if (config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') || config('SETTINGS::PAYMENTS:STRIPE:SECRET'))
-                                            <label class="ml-5 text-center " for="stripe">
-                                                <img class="mb-3" height="50"
-                                                    src="{{ url('/images/stripe_logo.png') }}" /></br>
-                                                <input x-model="paymentMethod" type="radio" id="stripe" value="stripe"
-                                                    name="payment_method">
-                                                </input>
-                                            </label>
-                                        @endif
+                                    <div class="d-flex flex-wrap  flex-direction-row">
+                                        @foreach ($paymentGateways as $gateway)
+                                            <div class="ml-2">
+                                                <label class="text-center" for="{{ $gateway->name }}">
+                                                    <img class="mb-3" height="50" src="{{ $gateway->image }}"></br>
+                                                    <input x-on:click="console.log(payment_method)" x-model="payment_method"
+                                                        type="radio" id="{{ $gateway->name }}"
+                                                        value="{{ $gateway->name }}">
+                                                    </input>
+                                                </label>
+                                            </div>
+                                        @endforeach
                                     </div>
-                                @else
-                                    <p class="lead" style="text-align: center">{{ __('This product is free for you') }}.</p>
-                                @endif
-                            </div>
-                            <!-- /.col -->
-                            <div class="col-6">
-                                <p class="lead">{{ __('Amount Due') }}
-                                    {{ Carbon\Carbon::now()->isoFormat('LL') }}</p>
 
-                                <div class="table-responsive">
-                                    <table class="table">
-                                        @if($discountpercent&&$discountvalue)
+                                </div>
+                                <!-- /.col -->
+                                <div class="col-6">
+                                    <p class="lead">{{ __('Amount Due') }}
+                                        {{ Carbon\Carbon::now()->isoFormat('LL') }}</p>
+
+                                    <div class="table-responsive">
+                                        <table class="table">
+                                            @if ($discountpercent && $discountvalue)
+                                                <tr>
+                                                    <th>{{ __('Discount') }} ({{ $discountpercent }}%):</th>
+                                                    <td>{{ $product->formatToCurrency($discountvalue) }}</td>
+                                                </tr>
+                                            @endif
                                             <tr>
-                                                <th>{{ __('Discount') }} ({{ $discountpercent }}%):</th>
-                                                <td>{{$product->formatToCurrency($discountvalue)}}</td>
+                                                <th style="width:50%">{{ __('Subtotal') }}:</th>
+                                                <td>{{ $product->formatToCurrency($discountedprice) }}</td>
                                             </tr>
-                                        @endif
-                                        <tr>
-                                            <th style="width:50%">{{ __('Subtotal') }}:</th>
-                                            <td>{{ $product->formatToCurrency($discountedprice) }}</td>
-                                        </tr>
-                                        <tr>
-                                            <th>{{ __('Tax') }} ({{ $taxpercent }}%):</th>
-                                            <td>{{ $product->formatToCurrency($taxvalue) }}</td>
-                                        </tr>
-                                        <tr>
-                                            <th>{{ __('Total') }}:</th>
-                                            <td>{{ $product->formatToCurrency($total) }}</td>
-                                        </tr>
-                                    </table>
+                                            <tr>
+                                                <th>{{ __('Tax') }} ({{ $taxpercent }}%):</th>
+                                                <td>{{ $product->formatToCurrency($taxvalue) }}</td>
+                                            </tr>
+                                            <tr>
+                                                <th>{{ __('Total') }}:</th>
+                                                <td>{{ $product->formatToCurrency($total) }}</td>
+                                            </tr>
+                                        </table>
+                                    </div>
                                 </div>
+                                <!-- /.col -->
                             </div>
-                            <!-- /.col -->
-                        </div>
-                        <!-- /.row -->
+                            <!-- /.row -->
 
-                        <!-- this row will not appear when printing -->
-                        <div class="row no-print">
-                            <div class="col-12">
-                                <a type="button" :href="paymentRoute" :disabled="!paymentRoute"
-                                    :class="!paymentRoute ? 'disabled' : ''" class="btn btn-success float-right"><i
-                                        class="far fa-credit-card mr-2"></i>
-                                    {{ __('Submit Payment') }}
-                                </a>
+                            <!-- this row will not appear when printing -->
+                            <div class="row no-print">
+                                <div class="col-12">
+                                    <button :disabled="!payment_method || clicked"
+                                        :class="!payment_method || clicked ? 'disabled' : ''"
+                                        class="btn btn-success float-right"><i class="far fa-credit-card mr-2"
+                                            @click="clicked = true"></i>
+                                        {{ __('Submit Payment') }}
+                                    </button>
+                                </div>
                             </div>
                         </div>
-                    </div>
+                    </form>
                     <!-- /.invoice -->
                 </div><!-- /.col -->
             </div><!-- /.row -->
         </div>
     </section>
     <!-- END CONTENT -->
-
-    <script>
-        function serverApp() {
-            return {
-                //loading
-                paymentMethod: '',
-                paymentRoute: ({{ $total }} == 0)?('{{ route('payment.FreePay', $product->id) }}'):'',
-                setPaymentRoute(provider) {
-                    switch (provider) {
-                        case 'paypal':
-                            this.paymentRoute = '{{ route('payment.PaypalPay', $product->id) }}';
-                            break;
-                        case 'stripe':
-                            this.paymentRoute = '{{ route('payment.StripePay', $product->id) }}';
-                            break;
-                        default:
-                            this.paymentRoute = '{{ route('payment.PaypalPay', $product->id) }}';
-                    }
-                },
-            }
-        }
-    </script>
-
-
-@endsection
+@endsection