diff --git a/app/Extensions/PaymentGateways/PayPal/config.php b/app/Extensions/PaymentGateways/PayPal/config.php new file mode 100644 index 00000000..a8b6779d --- /dev/null +++ b/app/Extensions/PaymentGateways/PayPal/config.php @@ -0,0 +1,12 @@ + "PayPal", + "description" => "PayPal payment gateway", + "RoutesIgnoreCsrf" => [], + ]; +} diff --git a/app/Extensions/PaymentGateways/PayPal/index.php b/app/Extensions/PaymentGateways/PayPal/index.php index aeb2310b..88abc809 100644 --- a/app/Extensions/PaymentGateways/PayPal/index.php +++ b/app/Extensions/PaymentGateways/PayPal/index.php @@ -4,13 +4,10 @@ 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; @@ -30,6 +27,7 @@ function PaypalPay(Request $request) /** @var User $user */ $user = Auth::user(); $shopProduct = ShopProduct::findOrFail($request->shopProduct); + $discount = PartnerDiscount::getDiscount(); // create a new payment $payment = Payment::create([ @@ -39,7 +37,7 @@ function PaypalPay(Request $request) 'type' => $shopProduct->type, 'status' => 'open', 'amount' => $shopProduct->quantity, - 'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100), + 'price' => $shopProduct->price - ($shopProduct->price * $discount / 100), 'tax_value' => $shopProduct->getTaxValue(), 'tax_percent' => $shopProduct->getTaxPercent(), 'total_price' => $shopProduct->getTotalPrice(), @@ -54,7 +52,7 @@ function PaypalPay(Request $request) "purchase_units" => [ [ "reference_id" => uniqid(), - "description" => $shopProduct->display . (PartnerDiscount::getDiscount() ? (" (" . __('Discount') . " " . PartnerDiscount::getDiscount() . '%)') : ""), + "description" => $shopProduct->display . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""), "amount" => [ "value" => $shopProduct->getTotalPrice(), 'currency_code' => strtoupper($shopProduct->currency_code), @@ -176,41 +174,3 @@ 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"), - ], - ], - ]; -} diff --git a/app/Extensions/PaymentGateways/Stripe/config.php b/app/Extensions/PaymentGateways/Stripe/config.php new file mode 100644 index 00000000..f4843a3e --- /dev/null +++ b/app/Extensions/PaymentGateways/Stripe/config.php @@ -0,0 +1,14 @@ + "Stripe", + "description" => "Stripe payment gateway", + "RoutesIgnoreCsrf" => [ + "payment/StripeWebhooks", + ], + ]; +} diff --git a/app/Extensions/PaymentGateways/Stripe/index.php b/app/Extensions/PaymentGateways/Stripe/index.php index ea0369e8..56e898c2 100644 --- a/app/Extensions/PaymentGateways/Stripe/index.php +++ b/app/Extensions/PaymentGateways/Stripe/index.php @@ -4,14 +4,11 @@ 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; @@ -19,6 +16,7 @@ use Stripe\StripeClient; + /** * @param Request $request * @param ShopProduct $shopProduct @@ -34,6 +32,8 @@ function StripePay(Request $request) return; } + $discount = PartnerDiscount::getDiscount(); + // create payment $payment = Payment::create([ @@ -43,7 +43,7 @@ function StripePay(Request $request) 'type' => $shopProduct->type, 'status' => 'open', 'amount' => $shopProduct->quantity, - 'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100), + 'price' => $shopProduct->price - ($shopProduct->price * $discount / 100), 'tax_value' => $shopProduct->getTaxValue(), 'total_price' => $shopProduct->getTotalPrice(), 'tax_percent' => $shopProduct->getTaxPercent(), @@ -58,7 +58,7 @@ function StripePay(Request $request) 'price_data' => [ 'currency' => $shopProduct->currency_code, 'product_data' => [ - 'name' => $shopProduct->display . (PartnerDiscount::getDiscount() ? (' (' . __('Discount') . ' ' . PartnerDiscount::getDiscount() . '%)') : ''), + 'name' => $shopProduct->display . ($discount ? (' (' . __('Discount') . ' ' . $discount . '%)') : ''), 'description' => $shopProduct->description, ], 'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2), @@ -82,6 +82,11 @@ function StripePay(Request $request) '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'), + 'payment_intent_data' => [ + 'metadata' => [ + 'payment_id' => $payment->id, + ], + ], ]); Redirect::to($request->url)->send(); @@ -98,6 +103,8 @@ function StripeSuccess(Request $request) $shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id); + Redirect::route('home')->with('success', 'Please wait for success')->send(); + $stripeClient = getStripeClient(); try { //get stripe data @@ -162,16 +169,17 @@ function StripeSuccess(Request $request) function handleStripePaymentSuccessHook($paymentIntent) { try { - // Get payment db entry - $payment = Payment::where('payment_id', $paymentIntent->id)->first(); + $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(['status' => 'paid']); + $payment->update([ + 'payment_id' => $payment->payment_id ?? $paymentIntent->id, + 'status' => 'paid' + ]); //payment notification $user->notify(new ConfirmPaymentNotification($payment)); @@ -363,40 +371,3 @@ function checkPriceAmount($amount, $currencyCode, $payment_method) ]; 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"), - ], - ]; -} diff --git a/app/Helpers/ExtensionHelper.php b/app/Helpers/ExtensionHelper.php index 884b9f2f..fdf880ab 100644 --- a/app/Helpers/ExtensionHelper.php +++ b/app/Helpers/ExtensionHelper.php @@ -4,44 +4,81 @@ namespace App\Helpers; class ExtensionHelper { - public static function getExtensionConfig($extensionName, $nameSpace) + /** + * Get a config of an extension by its name + * @param string $extensionName + * @param string $configname + */ + public static function getExtensionConfig(string $extensionName, string $configname) { - $extension = app_path() . '/Extensions/' . $nameSpace . "/" . $extensionName . "/index.php"; - // Check if extension exists - if (!file_exists($extension)) { - return null; + $extensions = ExtensionHelper::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"); + } + + + if (isset($config[$configname])) { + return $config[$configname]; + } } - // 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; + return null; } - public static function getPayMethod($extensionName, $nameSpace) + public static function getAllCsrfIgnoredRoutes() { - // 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 + $extensions = ExtensionHelper::getAllExtensions(); - $config = self::getExtensionConfig($extensionName, $nameSpace); + $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"); + } - if ($config == null) { - return null; + if (isset($config['RoutesIgnoreCsrf'])) { + $routes = array_merge($routes, $config['RoutesIgnoreCsrf']); + } + + // add extension/ infront of every route + foreach ($routes as $key => $route) { + $routes[$key] = 'extensions/' . $route; + } } - return $config['payMethod']; + return $routes; + } + + /** + * Get all extensions + * @return array + */ + 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; } } diff --git a/app/Http/Controllers/Admin/PaymentController.php b/app/Http/Controllers/Admin/PaymentController.php index 7ace1eef..bccc64e8 100644 --- a/app/Http/Controllers/Admin/PaymentController.php +++ b/app/Http/Controllers/Admin/PaymentController.php @@ -38,26 +38,24 @@ class PaymentController extends Controller */ public function checkOut(ShopProduct $shopProduct) { - // get all payment gateway extensions - $extensions = glob(app_path() . '/Extensions/PaymentGateways/*', GLOB_ONLYDIR); + $extensions = ExtensionHelper::getAllExtensionsByNamespace('PaymentGateways'); // 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; - } + $payment = new \stdClass(); + $payment->name = ExtensionHelper::getExtensionConfig($extensionName, 'name'); + $payment->image = asset('images/Extensions/PaymentGateways/' . strtolower($extensionName) . '_logo.png'); + $paymentGateways[] = $payment; } + $discount = PartnerDiscount::getDiscount(); + return view('store.checkout')->with([ 'product' => $shopProduct, - 'discountpercent' => PartnerDiscount::getDiscount(), - 'discountvalue' => PartnerDiscount::getDiscount() * $shopProduct->price / 100, + 'discountpercent' => $discount, + 'discountvalue' => $discount * $shopProduct->price / 100, 'discountedprice' => $shopProduct->getPriceAfterDiscount(), 'taxvalue' => $shopProduct->getTaxValue(), 'taxpercent' => $shopProduct->getTaxPercent(), diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 45698844..5fbf7e81 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -2,7 +2,10 @@ namespace App\Http\Middleware; +use App\Helpers\ExtensionHelper; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; +use Illuminate\Contracts\Encryption\Encrypter; +use Illuminate\Contracts\Foundation\Application; class VerifyCsrfToken extends Middleware { @@ -11,7 +14,12 @@ class VerifyCsrfToken extends Middleware * * @var array */ - protected $except = [ - 'payment/StripeWebhooks', - ]; + protected $except = []; + + public function __construct(Application $app, Encrypter $encrypter) + { + $this->app = $app; + $this->encrypter = $encrypter; + $this->except = ExtensionHelper::getAllCsrfIgnoredRoutes(); + } } diff --git a/app/Listeners/CreateInvoice.php b/app/Listeners/CreateInvoice.php index 907b2704..3b447657 100644 --- a/app/Listeners/CreateInvoice.php +++ b/app/Listeners/CreateInvoice.php @@ -19,11 +19,8 @@ class CreateInvoice 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); + $this->createInvoice($event->payment, $event->shopProduct); } } } diff --git a/app/Models/PartnerDiscount.php b/app/Models/PartnerDiscount.php index c1bd50b0..01f93b2d 100644 --- a/app/Models/PartnerDiscount.php +++ b/app/Models/PartnerDiscount.php @@ -18,11 +18,11 @@ class PartnerDiscount extends Model 'referral_system_commission', ]; - public static function getDiscount() + public static function getDiscount(int $user_id = null) { - if ($partnerDiscount = PartnerDiscount::where('user_id', Auth::user()->id)->first()) { + if ($partnerDiscount = PartnerDiscount::where('user_id', $user_id ?? Auth::user()->id)->first()) { return $partnerDiscount->partner_discount; - } elseif ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', Auth::user()->id)->first()) { + } elseif ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user_id ?? Auth::user()->id)->first()) { if ($partnerDiscount = PartnerDiscount::where('user_id', $ref_user->referral_id)->first()) { return $partnerDiscount->registered_user_discount; } diff --git a/app/Traits/Invoiceable.php b/app/Traits/Invoiceable.php index c9073d36..932fa89d 100644 --- a/app/Traits/Invoiceable.php +++ b/app/Traits/Invoiceable.php @@ -3,6 +3,7 @@ namespace App\Traits; use App\Models\PartnerDiscount; +use App\Models\Payment; use App\Models\ShopProduct; use App\Notifications\InvoiceNotification; use Illuminate\Support\Facades\Storage; @@ -14,9 +15,9 @@ use Symfony\Component\Intl\Currencies; trait Invoiceable { - public function createInvoice($user, $payment) + public function createInvoice(Payment $payment, ShopProduct $shopProduct) { - $shopProduct = ShopProduct::where('id', $payment->shop_item_product_id)->first(); + $user = $payment->user; //create invoice $lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id"); $newInvoiceID = $lastInvoiceID + 1; @@ -33,7 +34,6 @@ trait Invoiceable ], ]); - $customer = new Buyer([ 'name' => $user->name, 'custom_fields' => [ @@ -78,7 +78,6 @@ trait Invoiceable $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(),