Browse Source

Merge branch 'ControlPanel-gg:development' into development

Dennis 2 years ago
parent
commit
bb0243df47
34 changed files with 1529 additions and 992 deletions
  1. 146 0
      app/Extensions/PaymentGateways/Mollie/MollieExtension.php
  2. 41 0
      app/Extensions/PaymentGateways/Mollie/MollieSettings.php
  3. 18 0
      app/Extensions/PaymentGateways/Mollie/migrations/2023_03_26_215801_create_mollie_settings.php
  4. 22 0
      app/Extensions/PaymentGateways/Mollie/web_routes.php
  5. 197 0
      app/Extensions/PaymentGateways/PayPal/PayPalExtension.php
  6. 0 11
      app/Extensions/PaymentGateways/PayPal/config.php
  7. 0 195
      app/Extensions/PaymentGateways/PayPal/index.php
  8. 3 4
      app/Extensions/PaymentGateways/PayPal/web_routes.php
  9. 390 0
      app/Extensions/PaymentGateways/Stripe/StripeExtension.php
  10. 0 13
      app/Extensions/PaymentGateways/Stripe/config.php
  11. 0 376
      app/Extensions/PaymentGateways/Stripe/index.php
  12. 4 4
      app/Extensions/PaymentGateways/Stripe/web_routes.php
  13. 9 0
      app/Helpers/AbstractExtension.php
  14. 131 75
      app/Helpers/ExtensionHelper.php
  15. 4 1
      app/Http/Controllers/Admin/SettingsController.php
  16. 3 1
      app/Listeners/CreateInvoice.php
  17. 6 6
      app/Settings/PterodactylSettings.php
  18. 28 27
      composer.json
  19. 343 137
      composer.lock
  20. 14 0
      docker/docker-compose.yml
  21. 1 1
      package-lock.json
  22. 3 1
      package.json
  23. 1 0
      public/build/assets/app-0fd5dfcd.js
  24. 0 0
      public/build/assets/app-26e8174e.css
  25. 8 3
      public/build/manifest.json
  26. BIN
      public/images/Extensions/PaymentGateways/mollie_logo.png
  27. BIN
      public/images/Extensions/PaymentGateways/paypal_logo.png
  28. BIN
      public/images/Extensions/PaymentGateways/stripe_logo.png
  29. 0 2
      storage/framework/cache/data/.gitignore
  30. 19 0
      themes/default/sass/app.scss
  31. 0 11
      themes/default/views/admin/store/index.blade.php
  32. 1 1
      themes/default/views/layouts/main.blade.php
  33. 133 111
      themes/default/views/store/checkout.blade.php
  34. 4 12
      themes/default/vite.config.js

+ 146 - 0
app/Extensions/PaymentGateways/Mollie/MollieExtension.php

@@ -0,0 +1,146 @@
+<?php
+
+namespace App\Extensions\PaymentGateways\Mollie;
+
+use App\Helpers\AbstractExtension;
+use App\Events\PaymentEvent;
+use App\Events\UserUpdateCreditsEvent;
+use App\Models\PartnerDiscount;
+use App\Models\Payment;
+use App\Models\ShopProduct;
+use App\Models\User;
+use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Redirect;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Http;
+
+/**
+ * Summary of PayPalExtension
+ */
+class MollieExtension extends AbstractExtension
+{
+    public static function getConfig(): array
+    {
+        return [
+            "name" => "Mollie",
+            "RoutesIgnoreCsrf" => [
+                "payment/MollieWebhook"
+            ],
+        ];
+    }
+
+    static function pay(Request $request): void
+    {
+        $url = 'https://api.mollie.com/v2/payments';
+        $settings = new MollieSettings();
+
+        $user = Auth::user();
+        $shopProduct = ShopProduct::findOrFail($request->shopProduct);
+        $discount = PartnerDiscount::getDiscount();
+
+        // create a new payment
+        $payment = Payment::create([
+            'user_id' => $user->id,
+            'payment_id' => null,
+            'payment_method' => 'mollie',
+            'type' => $shopProduct->type,
+            'status' => 'open',
+            'amount' => $shopProduct->quantity,
+            'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
+            'tax_value' => $shopProduct->getTaxValue(),
+            'tax_percent' => $shopProduct->getTaxPercent(),
+            'total_price' => $shopProduct->getTotalPrice(),
+            'currency_code' => $shopProduct->currency_code,
+            'shop_item_product_id' => $shopProduct->id,
+        ]);
+
+        try {
+            $response = Http::withHeaders([
+                'Content-Type' => 'application/json',
+                'Authorization' => 'Bearer ' . $settings->api_key,
+            ])->post($url, [
+                'amount' => [
+                    'currency' => $shopProduct->currency_code,
+                    'value' => number_format($shopProduct->getTotalPrice(), 2, '.', ''),
+                ],
+                'description' => "Order #{$payment->id} - " . $shopProduct->name,
+                'redirectUrl' => route('payment.MollieSuccess'),
+                'cancelUrl' => route('payment.Cancel'),
+                'webhookUrl' => url('/extensions/payment/MollieWebhook'),
+                'metadata' => [
+                    'payment_id' => $payment->id,
+                ],
+            ]);
+
+            if ($response->status() != 201) {
+                Log::error('Mollie Payment: ' . $response->body());
+                $payment->delete();
+
+                Redirect::route('store.index')->with('error', __('Payment failed'))->send();
+                return;
+            }
+
+            $payment->update([
+                'payment_id' => $response->json()['id'],
+            ]);
+
+            Redirect::away($response->json()['_links']['checkout']['href'])->send();
+            return;
+        } catch (Exception $ex) {
+            Log::error('Mollie Payment: ' . $ex->getMessage());
+            $payment->delete();
+
+            Redirect::route('store.index')->with('error', __('Payment failed'))->send();
+            return;
+        }
+    }
+
+    static function success(Request $request): void
+    {
+        $payment = Payment::findOrFail($request->input('payment'));
+        $payment->status = 'pending';
+
+        Redirect::route('home')->with('success', 'Your payment is being processed')->send();
+        return;
+    }
+
+    static function webhook(Request $request): JsonResponse
+    {
+        $url = 'https://api.mollie.com/v2/payments/' . $request->id;
+        $settings = new MollieSettings();
+
+        try {
+            $response = Http::withHeaders([
+                'Content-Type' => 'application/json',
+                'Authorization' => 'Bearer ' . $settings->api_key,
+            ])->get($url);
+            if ($response->status() != 200) {
+                Log::error('Mollie Payment Webhook: ' . $response->json()['title']);
+                return response()->json(['success' => false]);
+            }
+
+            $payment = Payment::findOrFail($response->json()['metadata']['payment_id']);
+            $payment->status->update([
+                'status' => $response->json()['status'],
+            ]);
+
+            $shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
+            event(new PaymentEvent($payment, $payment, $shopProduct));
+
+            if ($response->json()['status'] == 'paid') {
+                $user = User::findOrFail($payment->user_id);
+                event(new UserUpdateCreditsEvent($user));
+            }
+        } catch (Exception $ex) {
+            Log::error('Mollie Payment Webhook: ' . $ex->getMessage());
+            return response()->json(['success' => false]);
+        }
+
+        // return a 200 status code
+        return response()->json(['success' => true]);
+    }
+}

+ 41 - 0
app/Extensions/PaymentGateways/Mollie/MollieSettings.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Extensions\PaymentGateways\Mollie;
+
+use Spatie\LaravelSettings\Settings;
+
+class MollieSettings extends Settings
+{
+
+    public bool $enabled = false;
+    public ?string $api_key;
+
+    public static function group(): string
+    {
+        return 'mollie';
+    }
+
+    public static function encrypted(): array
+    {
+        return [
+            'api_key',
+        ];
+    }
+
+    public static function getOptionInputData()
+    {
+        return [
+            'category_icon' => 'fas fa-dollar-sign',
+            'api_key' => [
+                'type' => 'string',
+                'label' => 'API Key',
+                'description' => 'The API Key of your Mollie App',
+            ],
+            'enabled' => [
+                'type' => 'boolean',
+                'label' => 'Enabled',
+                'description' => 'Enable or disable this payment gateway',
+            ],
+        ];
+    }
+}

+ 18 - 0
app/Extensions/PaymentGateways/Mollie/migrations/2023_03_26_215801_create_mollie_settings.php

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

+ 22 - 0
app/Extensions/PaymentGateways/Mollie/web_routes.php

@@ -0,0 +1,22 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+use App\Extensions\PaymentGateways\Mollie\MollieExtension;
+
+Route::middleware(['web', 'auth'])->group(function () {
+    Route::get('payment/MolliePay/{shopProduct}', function () {
+        MollieExtension::pay(request());
+    })->name('payment.MolliePay');
+
+    Route::get(
+        'payment/MollieSuccess',
+        function () {
+            MollieExtension::success(request());
+        }
+    )->name('payment.MollieSuccess');
+});
+
+
+Route::post('payment/MollieWebhook', function () {
+    MollieExtension::webhook(request());
+})->name('payment.MollieWebhook');

+ 197 - 0
app/Extensions/PaymentGateways/PayPal/PayPalExtension.php

@@ -0,0 +1,197 @@
+<?php
+
+namespace App\Extensions\PaymentGateways\PayPal;
+
+use App\Helpers\AbstractExtension;
+use App\Events\PaymentEvent;
+use App\Events\UserUpdateCreditsEvent;
+use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
+use App\Models\PartnerDiscount;
+use App\Models\Payment;
+use App\Models\ShopProduct;
+use App\Models\User;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Redirect;
+use Illuminate\Support\Facades\Log;
+use PayPalCheckoutSdk\Core\PayPalHttpClient;
+use PayPalCheckoutSdk\Core\ProductionEnvironment;
+use PayPalCheckoutSdk\Core\SandboxEnvironment;
+use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
+use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
+use PayPalHttp\HttpException;
+
+
+/**
+ * Summary of PayPalExtension
+ */
+class PayPalExtension extends AbstractExtension
+{
+    public static function getConfig(): array
+    {
+        return [
+            "name" => "PayPal",
+            "RoutesIgnoreCsrf" => [],
+        ];
+    }
+
+    static function PaypalPay(Request $request): void
+    {
+        /** @var User $user */
+        $user = Auth::user();
+        $shopProduct = ShopProduct::findOrFail($request->shopProduct);
+        $discount = PartnerDiscount::getDiscount();
+
+        // 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 * $discount / 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 . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""),
+                    "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', 'Controlpanel.GG'),
+                'shipping_preference'  => 'NO_SHIPPING'
+            ]
+
+
+        ];
+
+        try {
+            // Call API with your client and get a response for your call
+            $response = self::getPayPalClient()->execute($request);
+
+            // check for any errors in the response
+            if ($response->statusCode != 201) {
+                throw new \Exception($response->statusCode);
+            }
+
+            // make sure the link is not empty
+            if (empty($response->result->links[1]->href)) {
+                throw new \Exception('No redirect link found');
+            }
+
+            Redirect::away($response->result->links[1]->href)->send();
+            return;
+        } catch (HttpException $ex) {
+            Log::error('PayPal Payment: ' . $ex->getMessage());
+            $payment->delete();
+
+            Redirect::route('store.index')->with('error', __('Payment failed'))->send();
+            return;
+        }
+    }
+
+    static function PaypalSuccess(Request $laravelRequest): void
+    {
+        $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 = self::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);
+            }
+        }
+    }
+
+    static function getPayPalClient(): PayPalHttpClient
+    {
+        $environment = env('APP_ENV') == 'local'
+            ? new SandboxEnvironment(self::getPaypalClientId(), self::getPaypalClientSecret())
+            : new ProductionEnvironment(self::getPaypalClientId(), self::getPaypalClientSecret());
+        return new PayPalHttpClient($environment);
+    }
+    /**
+     * @return string
+     */
+    static function getPaypalClientId(): string
+    {
+        $settings = new PayPalSettings();
+        return env('APP_ENV') == 'local' ?  $settings->sandbox_client_id : $settings->client_id;
+    }
+    /**
+     * @return string
+     */
+    static function getPaypalClientSecret(): string
+    {
+        $settings = new PayPalSettings();
+        return env('APP_ENV') == 'local' ? $settings->sandbox_client_secret : $settings->client_secret;
+    }
+}

+ 0 - 11
app/Extensions/PaymentGateways/PayPal/config.php

@@ -1,11 +0,0 @@
-<?php
-
-namespace App\Extensions\PaymentGateways\PayPal;
-
-function getConfig()
-{
-    return [
-        "name" => "PayPal",
-        "RoutesIgnoreCsrf" => [],
-    ];
-}

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

@@ -1,195 +0,0 @@
-<?php
-
-use App\Events\PaymentEvent;
-use App\Events\UserUpdateCreditsEvent;
-use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
-use App\Models\PartnerDiscount;
-use App\Models\Payment;
-use App\Models\ShopProduct;
-use App\Models\User;
-use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\Redirect;
-use Illuminate\Support\Facades\Log;
-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)
-{
-    $settings = new PayPalSettings();
-
-    /** @var User $user */
-    $user = Auth::user();
-    $shopProduct = ShopProduct::findOrFail($request->shopProduct);
-    $discount = PartnerDiscount::getDiscount();
-
-    // 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 * $discount / 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 . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""),
-                "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', 'Controlpanel.GG'),
-            'shipping_preference'  => 'NO_SHIPPING'
-        ]
-
-
-    ];
-
-    try {
-        // Call API with your client and get a response for your call
-        $response = getPayPalClient()->execute($request);
-
-        // check for any errors in the response
-        if ($response->statusCode != 201) {
-            throw new \Exception($response->statusCode);
-        }
-
-        // make sure the link is not empty
-        if (empty($response->result->links[1]->href)) {
-            throw new \Exception('No redirect link found');
-        }
-
-        Redirect::away($response->result->links[1]->href)->send();
-        return;
-    } catch (HttpException $ex) {
-        Log::error('PayPal Payment: ' . $ex->getMessage());
-        $payment->delete();
-
-        Redirect::route('store.index')->with('error', __('Payment failed'))->send();
-        return;
-    }
-}
-/**
- * @param Request $laravelRequest
- */
-function PaypalSuccess(Request $laravelRequest)
-{
-    $settings = new PayPalSettings();
-
-    $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()
-{
-    $settings = new PayPalSettings();
-
-    $environment = env('APP_ENV') == 'local'
-        ? new SandboxEnvironment(getPaypalClientId(), getPaypalClientSecret())
-        : new ProductionEnvironment(getPaypalClientId(), getPaypalClientSecret());
-    return new PayPalHttpClient($environment);
-}
-/**
- * @return string
- */
-function getPaypalClientId()
-{
-    $settings = new PayPalSettings();
-    return env('APP_ENV') == 'local' ?  $settings->sandbox_client_id : $settings->client_id;
-}
-/**
- * @return string
- */
-function getPaypalClientSecret()
-{
-    $settings = new PayPalSettings();
-    return env('APP_ENV') == 'local' ? $settings->sandbox_client_secret : $settings->client_secret;
-}

+ 3 - 4
app/Extensions/PaymentGateways/PayPal/web_routes.php

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

+ 390 - 0
app/Extensions/PaymentGateways/Stripe/StripeExtension.php

@@ -0,0 +1,390 @@
+<?php
+
+namespace App\Extensions\PaymentGateways\Stripe;
+
+use App\Helpers\AbstractExtension;
+use App\Events\PaymentEvent;
+use App\Events\UserUpdateCreditsEvent;
+use App\Extensions\PaymentGateways\Stripe\StripeSettings;
+use App\Models\PartnerDiscount;
+use App\Models\Payment;
+use App\Models\ShopProduct;
+use App\Models\User;
+use App\Notifications\ConfirmPaymentNotification;
+use Exception;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Redirect;
+use Stripe\Exception\SignatureVerificationException;
+use Stripe\Stripe;
+use Stripe\StripeClient;
+
+class StripeExtension extends AbstractExtension
+{
+    public static function getConfig(): array
+    {
+        return [
+            "name" => "Stripe",
+            "RoutesIgnoreCsrf" => [
+                "payment/StripeWebhooks",
+            ],
+        ];
+    }
+
+    /**
+     * @param  Request  $request
+     * @param  ShopProduct  $shopProduct
+     */
+    public static function StripePay(Request $request)
+    {
+        $user = Auth::user();
+        $shopProduct = ShopProduct::findOrFail($request->shopProduct);
+
+        // check if the price is valid for stripe
+        if (!self::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;
+        }
+
+        $discount = PartnerDiscount::getDiscount();
+
+
+        // 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 * $discount / 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 = self::getStripeClient();
+        $request = $stripeClient->checkout->sessions->create([
+            'line_items' => [
+                [
+                    'price_data' => [
+                        'currency' => $shopProduct->currency_code,
+                        'product_data' => [
+                            'name' => $shopProduct->display . ($discount ? (' (' . __('Discount') . ' ' . $discount . '%)') : ''),
+                            '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',
+            'success_url' => route('payment.StripeSuccess', ['payment' => $payment->id]) . '&session_id={CHECKOUT_SESSION_ID}',
+            'cancel_url' => route('payment.Cancel'),
+            'payment_intent_data' => [
+                'metadata' => [
+                    'payment_id' => $payment->id,
+                ],
+            ],
+        ]);
+
+        Redirect::to($request->url)->send();
+    }
+
+    /**
+     * @param  Request  $request
+     */
+    public static 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);
+
+
+        Redirect::route('home')->with('success', 'Please wait for success')->send();
+
+        $stripeClient = self::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
+     */
+    public static function handleStripePaymentSuccessHook($paymentIntent)
+    {
+        try {
+            $payment = Payment::where('id', $paymentIntent->metadata->payment_id)->with('user')->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([
+                    'payment_id' => $payment->payment_id ?? $paymentIntent->id,
+                    '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
+     */
+    public static function StripeWebhooks(Request $request)
+    {
+        Stripe::setApiKey(self::getStripeSecret());
+
+        try {
+            $payload = @file_get_contents('php://input');
+            $sig_header = $request->header('Stripe-Signature');
+            $event = null;
+            $event = \Stripe\Webhook::constructEvent(
+                $payload,
+                $sig_header,
+                self::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
+                self::handleStripePaymentSuccessHook($paymentIntent);
+                break;
+            default:
+                echo 'Received unknown event type ' . $event->type;
+        }
+    }
+
+    /**
+     * @return \Stripe\StripeClient
+     */
+    public static function getStripeClient()
+    {
+        return new StripeClient(self::getStripeSecret());
+    }
+
+    /**
+     * @return string
+     */
+    public static function getStripeSecret()
+    {
+        $settings = new StripeSettings();
+
+        return env('APP_ENV') == 'local'
+            ? $settings->test_secret_key
+            : $settings->secret_key;
+    }
+
+    /**
+     * @return string
+     */
+    public static function getStripeEndpointSecret()
+    {
+        $settings = new StripeSettings();
+        return env('APP_ENV') == 'local'
+            ? $settings->test_endpoint_secret
+            : $settings->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
+     */
+    public static 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];
+    }
+}

+ 0 - 13
app/Extensions/PaymentGateways/Stripe/config.php

@@ -1,13 +0,0 @@
-<?php
-
-namespace App\Extensions\PaymentGateways\Stripe;
-
-function getConfig()
-{
-    return [
-        "name" => "Stripe",
-        "RoutesIgnoreCsrf" => [
-            "payment/StripeWebhooks",
-        ],
-    ];
-}

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

@@ -1,376 +0,0 @@
-<?php
-
-use App\Events\PaymentEvent;
-use App\Events\UserUpdateCreditsEvent;
-use App\Extensions\PaymentGateways\Stripe\StripeSettings;
-use App\Models\PartnerDiscount;
-use App\Models\Payment;
-use App\Models\ShopProduct;
-use App\Models\User;
-use App\Notifications\ConfirmPaymentNotification;
-use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Auth;
-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;
-    }
-
-    $discount = PartnerDiscount::getDiscount();
-
-
-    // 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 * $discount / 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 . ($discount ? (' (' . __('Discount') . ' ' . $discount . '%)') : ''),
-                        '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',
-        'success_url' => route('payment.StripeSuccess', ['payment' => $payment->id]) . '&session_id={CHECKOUT_SESSION_ID}',
-        'cancel_url' => route('payment.Cancel'),
-        'payment_intent_data' => [
-            'metadata' => [
-                'payment_id' => $payment->id,
-            ],
-        ],
-    ]);
-
-    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);
-
-
-    Redirect::route('home')->with('success', 'Please wait for success')->send();
-
-    $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 {
-        $payment = Payment::where('id', $paymentIntent->metadata->payment_id)->with('user')->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([
-                'payment_id' => $payment->payment_id ?? $paymentIntent->id,
-                '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()
-{
-    $settings = new StripeSettings();
-
-    return env('APP_ENV') == 'local'
-        ? $settings->test_secret_key
-        : $settings->secret_key;
-}
-
-/**
- * @return string
- */
-function getStripeEndpointSecret()
-{
-    $settings = new StripeSettings();
-    return env('APP_ENV') == 'local'
-        ? $settings->test_endpoint_secret
-        : $settings->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];
-}

+ 4 - 4
app/Extensions/PaymentGateways/Stripe/web_routes.php

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

+ 9 - 0
app/Helpers/AbstractExtension.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace App\Helpers;
+
+// create a abstract class for the extension that will contain all the methods that will be used in the extension
+abstract class AbstractExtension
+{
+    abstract public static function getConfig(): array;
+}

+ 131 - 75
app/Helpers/ExtensionHelper.php

@@ -8,88 +8,154 @@ namespace App\Helpers;
 class ExtensionHelper
 {
     /**
-     * Get a config of an extension by its name
-     * @param string $extensionName
-     * @param string $configname
+     * Get all extensions
+     * @return array array of all extensions e.g. ["App\Extensions\PayPal", "App\Extensions\Stripe"]
      */
-    public static function getExtensionConfig(string $extensionName, string $configname)
+    public static function getAllExtensions()
+    {
+        $extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
+        $extensions = [];
+        foreach ($extensionNamespaces as $extensionNamespace) {
+            $extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
+        }
+        // remove base path from every extension but keep app/Extensions/...
+        $extensions = array_map(fn ($item) => str_replace('/', '\\', str_replace(app_path() . '/', 'App/', $item)), $extensions);
+
+        return $extensions;
+    }
+
+    /**
+     * Get all extensions by namespace
+     * @param string $namespace case sensitive namespace of the extension e.g. PaymentGateways
+     * @return array array of all extensions e.g. ["App\Extensions\PayPal", "App\Extensions\Stripe"]
+     */
+    public static function getAllExtensionsByNamespace(string $namespace)
     {
-        $extensions = ExtensionHelper::getAllExtensions();
+        $extensions = glob(app_path() . '/Extensions/' . $namespace . '/*', GLOB_ONLYDIR);
+        // remove base path from every extension but keep app/Extensions/...
+        $extensions = array_map(fn ($item) => str_replace('/', '\\', str_replace(app_path() . '/', 'App/', $item)), $extensions);
+
+        return $extensions;
+    }
+
+    /**
+     * Get an extension by its name
+     * @param string $extensionName case sensitive name of the extension e.g. PayPal
+     * @return string|null the path of the extension e.g. App\Extensions\PayPal
+     */
+    public static function getExtension(string $extensionName)
+    {
+        $extensions = self::getAllExtensions();
+        // filter the extensions by the extension name
+        $extensions = array_filter($extensions, fn ($item) => basename($item) == $extensionName);
+
+        // return the only extension
+        return array_shift($extensions);
+    }
+
+    /**
+     * Get all extension classes
+     * @return array array of all extension classes e.g. ["App\Extensions\PayPal\PayPalExtension", "App\Extensions\Stripe\StripeExtension"]
+     */
+    public static function getAllExtensionClasses()
+    {
+        $extensions = self::getAllExtensions();
+        // add the ExtensionClass to the end of the namespace 
+        $extensions = array_map(fn ($item) => $item . '\\' . basename($item) . 'Extension', $extensions);
+        // filter out non existing extension classes
+        $extensions = array_filter($extensions, fn ($item) => class_exists($item));
+
+        return $extensions;
+    }
+
+    /**
+     * Get all extension classes by namespace
+     * @param string $namespace case sensitive namespace of the extension e.g. PaymentGateways
+     * @return array array of all extension classes e.g. ["App\Extensions\PayPal\PayPalExtension", "App\Extensions\Stripe\StripeExtension"]
+     */
+    public static function getAllExtensionClassesByNamespace(string $namespace)
+    {
+        $extensions = self::getAllExtensionsByNamespace($namespace);
+        // add the ExtensionClass to the end of the namespace
+        $extensions = array_map(fn ($item) => $item . '\\' . basename($item) . 'Extension', $extensions);
+        // filter out non existing extension classes
+        $extensions = array_filter($extensions, fn ($item) => class_exists($item));
+
+        return $extensions;
+    }
+
+    /**
+     * Get the class of an extension by its name
+     * @param string $extensionName case sensitive name of the extension e.g. PayPal
+     * @return string|null the class name of the extension e.g. App\Extensions\PayPal\PayPalExtension
+     */
+    public static function getExtensionClass(string $extensionName)
+    {
+        $extensions = self::getAllExtensions();
 
-        // call the getConfig function of the config file of the extension like that
-        // call_user_func("App\\Extensions\\PaymentGateways\\Stripe" . "\\getConfig");
         foreach ($extensions as $extension) {
             if (!(basename($extension) ==  $extensionName)) {
                 continue;
             }
 
-            $configFile = $extension . '/config.php';
-            if (file_exists($configFile)) {
-                include_once $configFile;
-                $config = call_user_func('App\\Extensions\\' . basename(dirname($extension)) . '\\' . basename($extension) . "\\getConfig");
-            }
+            $extensionClass = $extension . '\\' . $extensionName . 'Extension';
+            return $extensionClass;
+        }
+    }
 
 
-            if (isset($config[$configname])) {
-                return $config[$configname];
-            }
+
+
+    /**
+     * Get a config of an extension by its name
+     * @param string $extensionName
+     * @param string $configname
+     */
+    public static function getExtensionConfig(string $extensionName, string $configname)
+    {
+
+        $extension = self::getExtensionClass($extensionName);
+
+        $config = $extension::getConfig();
+
+
+
+        if (isset($config[$configname])) {
+            return $config[$configname];
         }
 
+
         return null;
     }
 
     public static function getAllCsrfIgnoredRoutes()
     {
-        $extensions = ExtensionHelper::getAllExtensions();
+        $extensions = self::getAllExtensionClasses();
 
         $routes = [];
+
         foreach ($extensions as $extension) {
-            $configFile = $extension . '/config.php';
-            if (file_exists($configFile)) {
-                include_once $configFile;
-                $config = call_user_func('App\\Extensions\\' . basename(dirname($extension)) . '\\' . basename($extension) . "\\getConfig");
-            }
+            $config = $extension::getConfig();
 
             if (isset($config['RoutesIgnoreCsrf'])) {
                 $routes = array_merge($routes, $config['RoutesIgnoreCsrf']);
             }
-
-            // map over the routes and add the extension name as prefix
-            $result = array_map(fn ($item) => "extensions/{$item}", $routes);
         }
+        // map over the routes and add the extension name as prefix
+        $result = array_map(fn ($item) => "extensions/{$item}", $routes);
 
         return $result;
     }
 
-    /**
-     * Get all extensions
-     * @return array of all extension paths look like: app/Extensions/ExtensionNamespace/ExtensionName
-     */
-    public static function getAllExtensions()
-    {
-        $extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
-        $extensions = [];
-        foreach ($extensionNamespaces as $extensionNamespace) {
-            $extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
-        }
-
-        return $extensions;
-    }
-
-    public static function getAllExtensionsByNamespace(string $namespace)
-    {
-        $extensions = glob(app_path() . '/Extensions/' . $namespace . '/*', GLOB_ONLYDIR);
-
-        return $extensions;
-    }
-
     /**
      * Summary of getAllExtensionMigrations
      * @return array of all migration paths look like: app/Extensions/ExtensionNamespace/ExtensionName/migrations/
      */
     public static function getAllExtensionMigrations()
     {
-        $extensions = ExtensionHelper::getAllExtensions();
+        $extensions = self::getAllExtensions();
+        // Transform the extensions to a path
+        $extensions = array_map(fn ($item) => self::extensionNameToPath($item), $extensions);
 
         // get all migration directories of the extensions and return them as array
         $migrations = [];
@@ -109,21 +175,15 @@ class ExtensionHelper
      */
     public static function getAllExtensionSettingsClasses()
     {
-        $extensions = ExtensionHelper::getAllExtensions();
+        $extensions = self::getAllExtensions();
 
         $settings = [];
         foreach ($extensions as $extension) {
 
             $extensionName = basename($extension);
-            $settingFile = $extension . '/' . $extensionName . 'Settings.php';
-            if (file_exists($settingFile)) {
-                // remove the base path from the setting file path to get the namespace
-
-                $settingFile = str_replace(app_path() . '/', '', $settingFile);
-                $settingFile = str_replace('.php', '', $settingFile);
-                $settingFile = str_replace('/', '\\', $settingFile);
-                $settingFile = 'App\\' . $settingFile;
-                $settings[] = $settingFile;
+            $settingsClass = $extension . '\\' . $extensionName . 'Settings';
+            if (class_exists($settingsClass)) {
+                $settings[] = $settingsClass;
             }
         }
 
@@ -132,25 +192,21 @@ class ExtensionHelper
 
     public static function getExtensionSettings(string $extensionName)
     {
-        $extensions = ExtensionHelper::getAllExtensions();
+        $extension = self::getExtension($extensionName);
+        $settingClass = $extension . '\\' . $extensionName . 'Settings';
 
-        // find the setting file of the extension and return an instance of it
-        foreach ($extensions as $extension) {
-            if (!(basename($extension) ==  $extensionName)) {
-                continue;
-            }
-
-            $extensionName = basename($extension);
-            $settingFile = $extension . '/' . $extensionName . 'Settings.php';
-            if (file_exists($settingFile)) {
-                // remove the base path from the setting file path to get the namespace
-
-                $settingFile = str_replace(app_path() . '/', '', $settingFile);
-                $settingFile = str_replace('.php', '', $settingFile);
-                $settingFile = str_replace('/', '\\', $settingFile);
-                $settingFile = 'App\\' . $settingFile;
-                return new $settingFile();
-            }
+        if (class_exists($settingClass)) {
+            return new $settingClass();
         }
     }
+
+    /**
+     * Transforms a extension name to a path
+     * @param string $extensionName e.g. App\Extensions\PaymentGateways\PayPal
+     * @return string e.g. C:\xampp\htdocs\laravel\app/Extensions/PaymentGateways/PayPal
+     */
+    private static function extensionNameToPath(string $extensionName)
+    {
+        return app_path() . '/' . str_replace('\\', '/', str_replace('App\\', '', $extensionName));
+    }
 }

+ 4 - 1
app/Http/Controllers/Admin/SettingsController.php

@@ -109,7 +109,7 @@ class SettingsController extends Controller
         $settingsClass = new $settings_class();
 
         foreach ($settingsClass->toArray() as $key => $value) {
-            switch (gettype($value)) {
+            switch (gettype($request->input($key))) {
                 case 'boolean':
                     $settingsClass->$key = $request->has($key);
                     break;
@@ -125,6 +125,9 @@ class SettingsController extends Controller
                 case 'double':
                     $settingsClass->$key = $request->input($key) ?? 0.0;
                     break;
+                case 'NULL':
+                    $settingsClass->$key = null;
+                    break;
             }
         }
 

+ 3 - 1
app/Listeners/CreateInvoice.php

@@ -11,6 +11,7 @@ class CreateInvoice
     use Invoiceable;
 
     private $invoice_enabled;
+    private $invoice_settings;
 
     /**
      * Create the event listener.
@@ -20,6 +21,7 @@ class CreateInvoice
     public function __construct(InvoiceSettings $invoice_settings)
     {
         $this->invoice_enabled = $invoice_settings->enabled;
+        $this->invoice_settings = $invoice_settings;
     }
 
     /**
@@ -32,7 +34,7 @@ class CreateInvoice
     {
         if ($this->invoice_enabled) {
             // create invoice using the trait
-            $this->createInvoice($event->payment, $event->shopProduct);
+            $this->createInvoice($event->payment, $event->shopProduct, $this->invoice_settings);
         }
     }
 }

+ 6 - 6
app/Settings/PterodactylSettings.php

@@ -6,11 +6,11 @@ use Spatie\LaravelSettings\Settings;
 
 class PterodactylSettings extends Settings
 {
-    public string $admin_token;
+    public ?string $admin_token;
 
-    public string $user_token;
+    public ?string $user_token;
 
-    public string $panel_url;
+    public ?string $panel_url;
 
     public int $per_page_limit;
 
@@ -44,9 +44,9 @@ class PterodactylSettings extends Settings
     public static function getValidations()
     {
         return [
-            'panel_url' => 'required|string|url',
-            'admin_token' => 'required|string',
-            'user_token' => 'required|string',
+            'panel_url' => 'nullable|string|url',
+            'admin_token' => 'nullable|string',
+            'user_token' => 'nullable|string',
             'per_page_limit' => 'required|integer|min:1|max:10000',
         ];
     }

+ 28 - 27
composer.json

@@ -11,37 +11,38 @@
         "php": "^8.1",
         "ext-intl": "*",
         "biscolab/laravel-recaptcha": "^5.4",
-        "doctrine/dbal": "^3.1",
-        "guzzlehttp/guzzle": "^7.2",
-        "hidehalo/nanoid-php": "^1.1",
+        "doctrine/dbal": "^3.5.3",
+        "guzzlehttp/guzzle": "^7.5",
+        "hidehalo/nanoid-php": "^1.1.12",
         "kkomelin/laravel-translatable-string-exporter": "^1.18",
-        "laravel/framework": "^9.46",
-        "laravel/tinker": "^2.7",
-        "laravel/ui": "^3.3",
-        "laraveldaily/laravel-invoices": "^3.0",
-        "league/flysystem-aws-s3-v3": "^3.0",
-        "paypal/paypal-checkout-sdk": "^1.0",
-        "paypal/rest-api-sdk-php": "^1.14",
-        "qirolab/laravel-themer": "^2.0",
-        "socialiteproviders/discord": "^4.1",
-        "spatie/laravel-activitylog": "^4.4",
-        "spatie/laravel-query-builder": "^5.0",
+        "laravel/framework": "^9.50.2",
+        "laravel/tinker": "^2.8",
+        "laravel/ui": "^3.4.6",
+        "laraveldaily/laravel-invoices": "^3.0.2",
+        "league/flysystem-aws-s3-v3": "^3.12.2",
+        "paypal/paypal-checkout-sdk": "^1.0.2",
+        "paypal/rest-api-sdk-php": "^1.14.0",
+        "predis/predis": "*",
+        "qirolab/laravel-themer": "^2.0.2",
+        "socialiteproviders/discord": "^4.1.2",
+        "spatie/laravel-activitylog": "^4.7.3",
+        "spatie/laravel-query-builder": "^5.1.2",
         "spatie/laravel-settings": "^2.7",
-        "spatie/laravel-validation-rules": "^3.2",
-        "stripe/stripe-php": "^7.107",
-        "symfony/http-client": "^6.2",
-        "symfony/intl": "^6.0",
-        "symfony/mailgun-mailer": "^6.2",
-        "yajra/laravel-datatables-oracle": "^9.19"
+        "spatie/laravel-validation-rules": "^3.2.2",
+        "stripe/stripe-php": "^7.128",
+        "symfony/http-client": "^6.2.6",
+        "symfony/intl": "^6.2.5",
+        "symfony/mailgun-mailer": "^6.2.5",
+        "yajra/laravel-datatables-oracle": "^9.21.2"
     },
     "require-dev": {
-        "barryvdh/laravel-debugbar": "^3.6",
-        "fakerphp/faker": "^1.9.1",
-        "laravel/sail": "^1.15",
-        "mockery/mockery": "^1.4.4",
-        "nunomaduro/collision": "^6.3",
-        "phpunit/phpunit": "^9.5.10",
-        "spatie/laravel-ignition": "^1.4"
+        "barryvdh/laravel-debugbar": "^3.7",
+        "fakerphp/faker": "^1.21",
+        "laravel/sail": "^1.19",
+        "mockery/mockery": "^1.5.1",
+        "nunomaduro/collision": "^6.4",
+        "phpunit/phpunit": "^9.6",
+        "spatie/laravel-ignition": "^1.6"
     },
     "config": {
         "optimize-autoloader": true,

File diff suppressed because it is too large
+ 343 - 137
composer.lock


+ 14 - 0
docker/docker-compose.yml

@@ -61,5 +61,19 @@ services:
     networks:
       - laravel
 
+  redis:
+    image: "redis:alpine"
+    command: redis-server --requirepass sOmE_sEcUrE_pAsS
+    ports:
+      - "6379:6379"
+    volumes:
+      - $PWD/redis-data:/var/lib/redis
+      - $PWD/redis.conf:/usr/local/etc/redis/redis.conf
+    environment:
+      - REDIS_REPLICATION_MODE=master
+    networks:
+      - laravel
+
+
 volumes:
   mysql:

+ 1 - 1
package-lock.json

@@ -1,5 +1,5 @@
 {
-    "name": "controlpanel",
+    "name": "controllpanelgg",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {

+ 3 - 1
package.json

@@ -2,7 +2,9 @@
     "private": true,
     "scripts": {
         "development": "vite",
-        "production": "vite build"
+        "production": "vite build",
+        "dev:default": "vite --config themes/default/vite.config.js",
+        "build:default": "vite build --config themes/default/vite.config.js"
     },
     "devDependencies": {
         "axios": "^0.25",

+ 1 - 0
public/build/assets/app-0fd5dfcd.js

@@ -0,0 +1 @@
+require("./adminlte");require("./slim.kickstart.min");require("./bootstrap");

File diff suppressed because it is too large
+ 0 - 0
public/build/assets/app-26e8174e.css


+ 8 - 3
public/build/manifest.json

@@ -1,7 +1,12 @@
 {
+  "themes/default/js/app.js": {
+    "file": "assets/app-0fd5dfcd.js",
+    "isEntry": true,
+    "src": "themes/default/js/app.js"
+  },
   "themes/default/sass/app.scss": {
-    "file": "assets/app-bac23d88.css",
-    "src": "themes/default/sass/app.scss",
-    "isEntry": true
+    "file": "assets/app-26e8174e.css",
+    "isEntry": true,
+    "src": "themes/default/sass/app.scss"
   }
 }

BIN
public/images/Extensions/PaymentGateways/mollie_logo.png


BIN
public/images/Extensions/PaymentGateways/paypal_logo.png


BIN
public/images/Extensions/PaymentGateways/stripe_logo.png


+ 0 - 2
storage/framework/cache/data/.gitignore

@@ -1,2 +0,0 @@
-*
-!.gitignore

+ 19 - 0
themes/default/sass/app.scss

@@ -5,3 +5,22 @@
 @import "../css/stylesheet.css";
 @import "../css/adminlte.min.css";
 @import "../css/slim.min.css";
+
+.checkout-gateways {
+    // make the radio button clickable
+    cursor: pointer;
+
+    // add some space between all gateway divs bit the last one
+    &:not(:last-child) {
+        margin-bottom: 1rem;
+    }
+}
+
+.checkout-gateway-label {
+    // make the label clickable
+    cursor: pointer;
+    // center the label
+    display: flex;
+    justify-content: start;
+    align-items: center;
+}

+ 0 - 11
themes/default/views/admin/store/index.blade.php

@@ -24,18 +24,7 @@
     <section class="content">
         <div class="container-fluid">
 
-            <div class="row">
-                <div class="col-lg-4">
-                    @if ($isPaymentSetup == false)
-                        <div class="callout callout-danger">
-                            <h4>{{ __('No payment method is configured.') }}</h4>
-                            <p>{{ __('To configure the payment methods, head to the settings-page and add the required options for your prefered payment method.') }}
-                            </p>
-                        </div>
-                    @endif
 
-                </div>
-            </div>
 
             <div class="card">
 

+ 1 - 1
themes/default/views/layouts/main.blade.php

@@ -40,7 +40,7 @@
     </noscript>
     <script src="{{ asset('js/app.js') }}"></script>
     <!-- tinymce -->
-    <script src={{ asset('plugins/tinymce/js/tinymce/tinymce.min.js') }}></script>
+    <script src="{{ asset('plugins/tinymce/js/tinymce/tinymce.min.js') }}"></script>
     @vite('themes/default/sass/app.scss')
 </head>
 

+ 133 - 111
themes/default/views/store/checkout.blade.php

@@ -24,133 +24,155 @@
     <!-- MAIN CONTENT -->
     <section class="content">
         <div class="container-fluid">
-
-            <div class="row">
-                <div class="col-12">
-
-                    <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>
+            <form x-data="{ payment_method: '', clicked: false }" action="{{ route('payment.pay') }}" method="POST">
+                @csrf
+                @method('post')
+                <div class="row d-flex justify-content-center flex-wrap">
+                    @if (!$productIsFree)
+                        <div class="col-xl-4">
+                            <div class="card">
+                                <div class="card-header">
+                                    <h4 class="mb-0">
+                                        <i class="fas fa-money-check-alt"></i>
+                                        Payment Methods
                                     </h4>
                                 </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>
+                                <div class="card-body">
                                     <input type="hidden" name="product_id" value="{{ $product->id }}">
+                                    <input type="hidden" name="payment_method" :value="payment_method"
+                                        x-model="payment_method">
+                                    <div class="row">
+                                        <div class="col-lg-12">
+                                            @foreach ($paymentGateways as $gateway)
+                                                <div class="row checkout-gateways">
+                                                    <div class="col-12 d-flex justify-content-between">
+                                                        <label class="form-check-label h4 checkout-gateway-label"
+                                                            for="{{ $gateway->name }}">
+                                                            <span class="mr-3">{{ $gateway->name }}</span>
+                                                        </label>
+                                                        <button class="btn btn-primary rounded" type="button"
+                                                            name="payment-method" id="{{ $gateway->name }}"
+                                                            value="{{ $gateway->name }}"
+                                                            :class="payment_method === '{{ $gateway->name }}' ?
+                                                                'active' : ''"
+                                                            @click="payment_method = '{{ $gateway->name }}'; clicked = true;"
+                                                            x-text="payment_method == '{{ $gateway->name }}' ? 'Selected' : 'Select'">Select</button>
+                                                        </button>
+
+                                                    </div>
+                                                </div>
+                                            @endforeach
+                                        </div>
+                                    </div>
                                 </div>
-                                <!-- /.col -->
                             </div>
-                            <!-- /.row -->
+                        </div>
+                    @endif
+                    <div class="col-xl-3">
+                        <div class="card">
+                            <div class="card-header">
+                                <h4 class="mb-0 text-center">
+                                    <i class="fas fa-shopping-cart"></i>
+                                    Checkout details
+                                </h4>
+                            </div>
 
-                            <div class="row">
-                                <!-- accepted payments column -->
-                                <div class="col-6">
-                                    @if (!$productIsFree)
-                                        <p class="lead">{{ __('Payment Methods') }}:</p>
+                            <div class="card-body">
+                                <ul class="list-group mb-3">
+                                    <li class="list-group-item">
+                                        <div>
+                                            <h5 class="my-0">{{ __('Product details') }}</h5>
+                                        </div>
+                                        <ul class="pl-0">
+                                            <li class="d-flex justify-content-between">
+                                                <span class="text-muted d-inline-block">{{ __('Type') }}</span>
+                                                <span
+                                                    class="text-muted d-inline-block">{{ strtolower($product->type) == 'credits' ? $credits_display_name : $product->type }}</span>
+                                            </li>
+                                            <li class="d-flex justify-content-between">
+                                                <span class="text-muted d-inline-block">{{ __('Amount') }}</span>
+                                                <span class="text-muted d-inline-block">{{ $product->quantity }}</span>
+                                            </li>
+                                            <li class="d-flex justify-content-between">
+                                                <span class="text-muted d-inline-block">{{ __('Total Amount') }}</span>
+                                                <span class="text-muted d-inline-block">{{ $product->quantity }}</span>
+                                            </li>
+                                        </ul>
 
-                                        <div class="d-flex flex-wrap  flex-direction-row">
+                                    </li>
 
-                                            @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>
-                                    @endif
 
-                                </div>
-                                <!-- /.col -->
-                                <div class="col-6">
-                                    <p class="lead">{{ __('Amount Due') }}
-                                        {{ Carbon\Carbon::now()->isoFormat('LL') }}</p>
+                                    </li>
+                                    <li class="list-group-item d-flex justify-content-between lh-condensed">
+                                        <div>
+                                            <h6 class="my-0">{{ __('Description') }}</h6>
+                                            <span class="text-muted">
+                                                {{ $product->description }}
+                                            </span>
+                                        </div>
+                                    </li>
+                                    <li class="list-group-item">
+                                        <div>
+                                            <h5 class="my-0">{{ __('Pricing') }}</h5>
+                                        </div>
 
-                                    <div class="table-responsive">
-                                        <table class="table">
+                                        <ul class="pl-0">
+                                            <li class="d-flex justify-content-between">
+                                                <span class="text-muted d-inline-block">{{ __('Subtotal') }}</span>
+                                                <span class="text-muted d-inline-block">
+                                                    {{ $product->formatToCurrency($discountedprice) }}</span>
+                                            </li>
+                                            <div class="d-flex justify-content-between">
+                                                <span class="text-muted d-inline-block">{{ __('Tax') }}
+                                                    @if ($taxpercent > 0)
+                                                        ({{ $taxpercent }}%):
+                                                    @endif
+                                                </span>
+                                                <span class="text-muted d-inline-block">
+                                                    + {{ $product->formatToCurrency($taxvalue) }}</span>
+                                            </div>
                                             @if ($discountpercent && $discountvalue)
-                                                <tr>
-                                                    <th>{{ __('Discount') }} ({{ $discountpercent }}%):</th>
-                                                    <td>{{ $product->formatToCurrency($discountvalue) }}</td>
-                                                </tr>
+                                                <div class="d-flex justify-content-between">
+                                                    <span class="text-muted d-inline-block">{{ __('Discount') }}
+                                                        ({{ $discountpercent }}%):</span>
+                                                    <span
+                                                        class="text-muted d-inline-block">-{{ $product->formatToCurrency($discountvalue) }}</span>
+                                                </div>
                                             @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>
-                                    </div>
-                                </div>
-                                <!-- /.col -->
-                            </div>
-                            <!-- /.row -->
+                                            <hr class="text-white border-secondary">
+                                            <div class="d-flex justify-content-between">
+                                                <span class="text-muted d-inline-block">{{ __('Total') }}</span>
+                                                <span
+                                                    class="text-muted d-inline-block">{{ $product->formatToCurrency($total) }}</span>
+                                            </div>
+                                            <template x-if="payment_method">
+                                                <div class="d-flex justify-content-between">
+                                                    <span class="text-muted d-inline-block">{{ __('Pay with') }}</span>
+                                                    <span class="text-muted d-inline-block" x-text="payment_method"></span>
+                                                </div>
+                                            </template>
+                                        </ul>
+                                    </li>
+                                </ul>
 
-                            <!-- this row will not appear when printing -->
-                            <div class="row no-print">
-                                <div class="col-12">
-                                    <button :disabled="(!payment_method || clicked) && {{ !$productIsFree }}"
-                                        :class="(!payment_method || clicked) && {{ !$productIsFree }} ? 'disabled' : ''"
-                                        class="btn btn-success float-right"><i class="far fa-credit-card mr-2"
-                                            @click="clicked = true"></i>
-                                        @if ($productIsFree)
-                                            {{ __('Get for free') }}
-                                        @else
-                                            {{ __('Submit Payment') }}
-                                        @endif
-                                    </button>
-                                </div>
+                                <button :disabled="(!payment_method || !clicked) && {{ !$productIsFree }}"
+                                    :class="(!payment_method || !clicked) && {{ !$productIsFree }} ? 'disabled' : ''"
+                                    class="btn btn-success float-right w-100">
+                                    <i class="far fa-credit-card mr-2" @click="clicked == true"></i>
+                                    @if ($productIsFree)
+                                        {{ __('Get for free') }}
+                                    @else
+                                        {{ __('Submit Payment') }}
+                                    @endif
+
+                                </button>
                             </div>
                         </div>
-                    </form>
-                    <!-- /.invoice -->
-                </div><!-- /.col -->
-            </div><!-- /.row -->
+                    </div>
+                </div>
+            </form>
         </div>
+
     </section>
     <!-- END CONTENT -->
 @endsection

+ 4 - 12
themes/default/vite.config.js

@@ -2,19 +2,12 @@ import { defineConfig } from "vite";
 import laravel from "laravel-vite-plugin";
 import path from "path";
 
-
-
 export default defineConfig({
     plugins: [
         laravel({
-            input: [
-                "themes/default/sass/app.scss",
-                "themes/default/js/app.js"
-            ],
+            input: ["themes/default/sass/app.scss", "themes/default/js/app.js"],
             buildDirectory: "build",
         }),
-        
-        
         {
             name: "blade",
             handleHotUpdate({ file, server }) {
@@ -29,9 +22,8 @@ export default defineConfig({
     ],
     resolve: {
         alias: {
-            '@': '/themes/default/js',
-            '~bootstrap': path.resolve('node_modules/bootstrap'),
-        }
+            "@": "/themes/default/js",
+            "~bootstrap": path.resolve("node_modules/bootstrap"),
+        },
     },
-    
 });

Some files were not shown because too many files changed in this diff