[FEATURE] Extension Support & Payment Gateways as Extensions 🔥
[FEATURE] Extension Support & Payment Gateways as Extensions 🔥
This commit is contained in:
commit
7a91013bd4
19 changed files with 1140 additions and 854 deletions
31
app/Events/PaymentEvent.php
Normal file
31
app/Events/PaymentEvent.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PaymentEvent
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public User $user;
|
||||
public Payment $payment;
|
||||
public ShopProduct $shopProduct;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, Payment $payment, ShopProduct $shopProduct)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->payment = $payment;
|
||||
$this->shopProduct = $shopProduct;
|
||||
}
|
||||
}
|
216
app/Extensions/PaymentGateways/PayPal/index.php
Normal file
216
app/Extensions/PaymentGateways/PayPal/index.php
Normal file
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use PayPalCheckoutSdk\Core\PayPalHttpClient;
|
||||
use PayPalCheckoutSdk\Core\ProductionEnvironment;
|
||||
use PayPalCheckoutSdk\Core\SandboxEnvironment;
|
||||
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
|
||||
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
|
||||
use PayPalHttp\HttpException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ShopProduct $shopProduct
|
||||
*/
|
||||
function PaypalPay(Request $request)
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
|
||||
|
||||
// create a new payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => null,
|
||||
'payment_method' => 'paypal',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'open',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
$request = new OrdersCreateRequest();
|
||||
$request->prefer('return=representation');
|
||||
$request->body = [
|
||||
"intent" => "CAPTURE",
|
||||
"purchase_units" => [
|
||||
[
|
||||
"reference_id" => uniqid(),
|
||||
"description" => $shopProduct->display . (PartnerDiscount::getDiscount() ? (" (" . __('Discount') . " " . PartnerDiscount::getDiscount() . '%)') : ""),
|
||||
"amount" => [
|
||||
"value" => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'breakdown' => [
|
||||
'item_total' =>
|
||||
[
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'value' => $shopProduct->getPriceAfterDiscount(),
|
||||
],
|
||||
'tax_total' =>
|
||||
[
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'value' => $shopProduct->getTaxValue(),
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
"application_context" => [
|
||||
"cancel_url" => route('payment.Cancel'),
|
||||
"return_url" => route('payment.PayPalSuccess', ['payment' => $payment->id]),
|
||||
'brand_name' => config('app.name', 'Laravel'),
|
||||
'shipping_preference' => 'NO_SHIPPING'
|
||||
]
|
||||
|
||||
|
||||
];
|
||||
|
||||
try {
|
||||
// Call API with your client and get a response for your call
|
||||
$response = getPayPalClient()->execute($request);
|
||||
|
||||
Redirect::away($response->result->links[1]->href)->send();
|
||||
return;
|
||||
} catch (HttpException $ex) {
|
||||
error_log($ex->statusCode);
|
||||
error_log($ex->getMessage());
|
||||
|
||||
$payment->delete();
|
||||
Redirect::route('payment.Cancel');
|
||||
return;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param Request $laravelRequest
|
||||
*/
|
||||
function PaypalSuccess(Request $laravelRequest)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$user = User::findOrFail($user->id);
|
||||
|
||||
$payment = Payment::findOrFail($laravelRequest->payment);
|
||||
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
|
||||
|
||||
$request = new OrdersCaptureRequest($laravelRequest->input('token'));
|
||||
$request->prefer('return=representation');
|
||||
|
||||
try {
|
||||
// Call API with your client and get a response for your call
|
||||
$response = getPayPalClient()->execute($request);
|
||||
if ($response->statusCode == 201 || $response->statusCode == 200) {
|
||||
//update payment
|
||||
$payment->update([
|
||||
'status' => 'paid',
|
||||
'payment_id' => $response->result->id,
|
||||
]);
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
|
||||
// redirect to the payment success page with success message
|
||||
Redirect::route('home')->with('success', 'Payment successful')->send();
|
||||
} elseif (env('APP_ENV') == 'local') {
|
||||
// If call returns body in response, you can get the deserialized version from the result attribute of the response
|
||||
$payment->delete();
|
||||
dd($response);
|
||||
} else {
|
||||
$payment->update([
|
||||
'status' => 'cancelled',
|
||||
'payment_id' => $response->result->id,
|
||||
]);
|
||||
abort(500);
|
||||
}
|
||||
} catch (HttpException $ex) {
|
||||
if (env('APP_ENV') == 'local') {
|
||||
echo $ex->statusCode;
|
||||
$payment->delete();
|
||||
dd($ex->getMessage());
|
||||
} else {
|
||||
$payment->update([
|
||||
'status' => 'cancelled',
|
||||
'payment_id' => $response->result->id,
|
||||
]);
|
||||
abort(422);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return PayPalHttpClient
|
||||
*/
|
||||
function getPayPalClient()
|
||||
{
|
||||
$environment = env('APP_ENV') == 'local'
|
||||
? new SandboxEnvironment(getPaypalClientId(), getPaypalClientSecret())
|
||||
: new ProductionEnvironment(getPaypalClientId(), getPaypalClientSecret());
|
||||
return new PayPalHttpClient($environment);
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function getPaypalClientId()
|
||||
{
|
||||
return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID") : config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID");
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function getPaypalClientSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET") : config("SETTINGS::PAYMENTS:PAYPAL:SECRET");
|
||||
}
|
||||
function getPayPalConfig()
|
||||
{
|
||||
return [
|
||||
"name" => "PayPal",
|
||||
"description" => "PayPal payment gateway",
|
||||
"settings" => [
|
||||
"mode" => [
|
||||
"type" => "select",
|
||||
"label" => "Mode",
|
||||
"value" => config("APP_ENV") == 'local' ? "sandbox" : "live",
|
||||
"options" => [
|
||||
"sandbox" => "Sandbox",
|
||||
"live" => "Live",
|
||||
],
|
||||
],
|
||||
"CLIENT_ID" => [
|
||||
"type" => "text",
|
||||
"label" => "PayPal Client ID",
|
||||
"value" => config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID"),
|
||||
],
|
||||
"SECRET" => [
|
||||
"type" => "text",
|
||||
"label" => "PayPal Secret",
|
||||
"value" => config("SETTINGS::PAYMENTS:PAYPAL:SECRET"),
|
||||
],
|
||||
"SANDBOX_CLIENT_ID" => [
|
||||
"type" => "text",
|
||||
"label" => "PayPal Sandbox Client ID",
|
||||
"value" => config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID"),
|
||||
],
|
||||
"SANDBOX_SECRET" => [
|
||||
"type" => "text",
|
||||
"label" => "PayPal Sandbox Secret",
|
||||
"value" => config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET"),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
18
app/Extensions/PaymentGateways/PayPal/web_routes.php
Normal file
18
app/Extensions/PaymentGateways/PayPal/web_routes.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
include_once(__DIR__ . '/index.php');
|
||||
|
||||
Route::middleware(['web', 'auth'])->group(function () {
|
||||
Route::get('payment/PayPalPay/{shopProduct}', function () {
|
||||
PaypalPay(request());
|
||||
})->name('payment.PayPalPay');
|
||||
|
||||
Route::get(
|
||||
'payment/PayPalSuccess',
|
||||
function () {
|
||||
PaypalSuccess(request());
|
||||
}
|
||||
)->name('payment.PayPalSuccess');
|
||||
});
|
402
app/Extensions/PaymentGateways/Stripe/index.php
Normal file
402
app/Extensions/PaymentGateways/Stripe/index.php
Normal file
|
@ -0,0 +1,402 @@
|
|||
<?php
|
||||
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ConfirmPaymentNotification;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Stripe\Exception\SignatureVerificationException;
|
||||
use Stripe\Stripe;
|
||||
use Stripe\StripeClient;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ShopProduct $shopProduct
|
||||
*/
|
||||
function StripePay(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
|
||||
|
||||
// check if the price is valid for stripe
|
||||
if (!checkPriceAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), 'stripe')) {
|
||||
Redirect::route('home')->with('error', __('The product you chose can\'t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'))->send();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// create payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => null,
|
||||
'payment_method' => 'stripe',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'open',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
$stripeClient = getStripeClient();
|
||||
$request = $stripeClient->checkout->sessions->create([
|
||||
'line_items' => [
|
||||
[
|
||||
'price_data' => [
|
||||
'currency' => $shopProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => $shopProduct->display . (PartnerDiscount::getDiscount() ? (' (' . __('Discount') . ' ' . PartnerDiscount::getDiscount() . '%)') : ''),
|
||||
'description' => $shopProduct->description,
|
||||
],
|
||||
'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
|
||||
],
|
||||
'quantity' => 1,
|
||||
],
|
||||
[
|
||||
'price_data' => [
|
||||
'currency' => $shopProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => __('Tax'),
|
||||
'description' => $shopProduct->getTaxPercent() . '%',
|
||||
],
|
||||
'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
|
||||
],
|
||||
'quantity' => 1,
|
||||
],
|
||||
],
|
||||
|
||||
'mode' => 'payment',
|
||||
'payment_method_types' => str_getcsv(config('SETTINGS::PAYMENTS:STRIPE:METHODS')),
|
||||
'success_url' => route('payment.StripeSuccess', ['payment' => $payment->id]) . '&session_id={CHECKOUT_SESSION_ID}',
|
||||
'cancel_url' => route('payment.Cancel'),
|
||||
]);
|
||||
|
||||
Redirect::to($request->url)->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
function StripeSuccess(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$user = User::findOrFail($user->id);
|
||||
$payment = Payment::findOrFail($request->input('payment'));
|
||||
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
|
||||
|
||||
|
||||
$stripeClient = getStripeClient();
|
||||
try {
|
||||
//get stripe data
|
||||
$paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
|
||||
$paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
|
||||
|
||||
//get DB entry of this payment ID if existing
|
||||
$paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
|
||||
|
||||
// check if payment is 100% completed and payment does not exist in db already
|
||||
if ($paymentSession->status == 'complete' && $paymentIntent->status == 'succeeded' && $paymentDbEntry == 0) {
|
||||
|
||||
//update payment
|
||||
$payment->update([
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'status' => 'paid',
|
||||
]);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
|
||||
//redirect back to home
|
||||
Redirect::route('home')->with('success', 'Payment successful')->send();
|
||||
} else {
|
||||
if ($paymentIntent->status == 'processing') {
|
||||
|
||||
//update payment
|
||||
$payment->update([
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'status' => 'processing',
|
||||
]);
|
||||
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
|
||||
Redirect::route('home')->with('success', 'Your payment is being processed')->send();
|
||||
}
|
||||
|
||||
if ($paymentDbEntry == 0 && $paymentIntent->status != 'processing') {
|
||||
$stripeClient->paymentIntents->cancel($paymentIntent->id);
|
||||
|
||||
//redirect back to home
|
||||
Redirect::route('home')->with('info', __('Your payment has been canceled!'))->send();
|
||||
} else {
|
||||
abort(402);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (env('APP_ENV') == 'local') {
|
||||
dd($e->getMessage());
|
||||
} else {
|
||||
abort(422);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
function handleStripePaymentSuccessHook($paymentIntent)
|
||||
{
|
||||
try {
|
||||
// Get payment db entry
|
||||
$payment = Payment::where('payment_id', $paymentIntent->id)->first();
|
||||
$user = User::where('id', $payment->user_id)->first();
|
||||
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
|
||||
|
||||
if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
|
||||
|
||||
|
||||
//update payment db entry status
|
||||
$payment->update(['status' => 'paid']);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
}
|
||||
|
||||
// return 200
|
||||
return response()->json(['success' => true], 200);
|
||||
} catch (Exception $ex) {
|
||||
abort(422);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
function StripeWebhooks(Request $request)
|
||||
{
|
||||
Stripe::setApiKey(getStripeSecret());
|
||||
|
||||
try {
|
||||
$payload = @file_get_contents('php://input');
|
||||
$sig_header = $request->header('Stripe-Signature');
|
||||
$event = null;
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
$payload,
|
||||
$sig_header,
|
||||
getStripeEndpointSecret()
|
||||
);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
// Invalid payload
|
||||
|
||||
abort(400);
|
||||
} catch (SignatureVerificationException $e) {
|
||||
// Invalid signature
|
||||
|
||||
abort(400);
|
||||
}
|
||||
|
||||
// Handle the event
|
||||
switch ($event->type) {
|
||||
case 'payment_intent.succeeded':
|
||||
$paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
|
||||
handleStripePaymentSuccessHook($paymentIntent);
|
||||
break;
|
||||
default:
|
||||
echo 'Received unknown event type ' . $event->type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Stripe\StripeClient
|
||||
*/
|
||||
function getStripeClient()
|
||||
{
|
||||
return new StripeClient(getStripeSecret());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function getStripeSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local'
|
||||
? config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET')
|
||||
: config('SETTINGS::PAYMENTS:STRIPE:SECRET');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function getStripeEndpointSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local'
|
||||
? config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET')
|
||||
: config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET');
|
||||
}
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $currencyCode
|
||||
* @param $payment_method
|
||||
* @return bool
|
||||
* @description check if the amount is higher than the minimum amount for the stripe gateway
|
||||
*/
|
||||
function checkPriceAmount($amount, $currencyCode, $payment_method)
|
||||
{
|
||||
$minimums = [
|
||||
"USD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"AED" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"AUD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"BGN" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 1
|
||||
],
|
||||
"BRL" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"CAD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"CHF" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"CZK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 15
|
||||
],
|
||||
"DKK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2.5
|
||||
],
|
||||
"EUR" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"GBP" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.3
|
||||
],
|
||||
"HKD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 4
|
||||
],
|
||||
"HRK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"HUF" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 175
|
||||
],
|
||||
"INR" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"JPY" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"MXN" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 10
|
||||
],
|
||||
"MYR" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"NOK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 3
|
||||
],
|
||||
"NZD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"PLN" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"RON" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"SEK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 3
|
||||
],
|
||||
"SGD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"THB" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 10
|
||||
]
|
||||
];
|
||||
return $amount >= $minimums[$currencyCode][$payment_method];
|
||||
}
|
||||
|
||||
function getStripeConfig()
|
||||
{
|
||||
return [
|
||||
"name" => "Stripe",
|
||||
"description" => "Stripe payment gateway",
|
||||
"mode" => [
|
||||
"type" => "select",
|
||||
"label" => "Mode",
|
||||
"value" => config("APP_ENV") == 'local' ? "sandbox" : "live",
|
||||
"options" => [
|
||||
"sandbox" => "Sandbox",
|
||||
"live" => "Live",
|
||||
],
|
||||
],
|
||||
"TEST_SECRET" => [
|
||||
"type" => "text",
|
||||
"label" => "Test Secret Key",
|
||||
"value" => config("SETTINGS::PAYMENTS:STRIPE:TEST_SECRET"),
|
||||
],
|
||||
"SECRET" => [
|
||||
"type" => "text",
|
||||
"label" => "Live Secret Key",
|
||||
"value" => config("SETTINGS::PAYMENTS:STRIPE:SECRET"),
|
||||
],
|
||||
"ENDPOINT_TEST_SECRET" => [
|
||||
"type" => "text",
|
||||
"label" => "Test Endpoint Secret",
|
||||
"value" => config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET"),
|
||||
],
|
||||
"ENDPOINT_SECRET" => [
|
||||
"type" => "text",
|
||||
"label" => "Live Endpoint Secret",
|
||||
"value" => config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET"),
|
||||
],
|
||||
];
|
||||
}
|
23
app/Extensions/PaymentGateways/Stripe/web_routes.php
Normal file
23
app/Extensions/PaymentGateways/Stripe/web_routes.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
include_once(__DIR__ . '/index.php');
|
||||
Route::middleware(['web', 'auth'])->group(function () {
|
||||
Route::get('payment/StripePay/{shopProduct}', function () {
|
||||
StripePay(request());
|
||||
})->name('payment.StripePay');
|
||||
|
||||
Route::get(
|
||||
'payment/StripeSuccess',
|
||||
function () {
|
||||
StripeSuccess(request());
|
||||
}
|
||||
)->name('payment.StripeSuccess');
|
||||
});
|
||||
|
||||
|
||||
// Stripe WebhookRoute -> validation in Route Handler
|
||||
Route::post('payment/StripeWebhooks', function () {
|
||||
StripeWebhooks(request());
|
||||
})->name('payment.StripeWebhooks');
|
47
app/Helpers/ExtensionHelper.php
Normal file
47
app/Helpers/ExtensionHelper.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class ExtensionHelper
|
||||
{
|
||||
public static function getExtensionConfig($extensionName, $nameSpace)
|
||||
{
|
||||
$extension = app_path() . '/Extensions/' . $nameSpace . "/" . $extensionName . "/index.php";
|
||||
// Check if extension exists
|
||||
if (!file_exists($extension)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// call the getConfig function from the index.php file of the extension
|
||||
$config = include_once $extension;
|
||||
|
||||
// Check if the getConfig function exists
|
||||
if (!function_exists('get' . $extensionName . 'Config')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$config = call_user_func('get' . $extensionName . 'Config');
|
||||
|
||||
// Check if the getConfig function returned an array
|
||||
if (!is_array($config)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function getPayMethod($extensionName, $nameSpace)
|
||||
{
|
||||
// return the payment method of the extension to be used elsewhere
|
||||
// for example in the payment controller
|
||||
// the function starts with the name of the extension and ends with Pay
|
||||
|
||||
$config = self::getExtensionConfig($extensionName, $nameSpace);
|
||||
|
||||
if ($config == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $config['payMethod'];
|
||||
}
|
||||
}
|
|
@ -6,10 +6,8 @@ use App\Events\UserUpdateCreditsEvent;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\Payment;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ConfirmPaymentNotification;
|
||||
use App\Notifications\InvoiceNotification;
|
||||
use App\Models\ShopProduct;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
|
@ -18,19 +16,8 @@ use Illuminate\Http\JsonResponse;
|
|||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use LaravelDaily\Invoices\Classes\Buyer;
|
||||
use LaravelDaily\Invoices\Classes\InvoiceItem;
|
||||
use LaravelDaily\Invoices\Classes\Party;
|
||||
use LaravelDaily\Invoices\Invoice;
|
||||
use PayPalCheckoutSdk\Core\PayPalHttpClient;
|
||||
use PayPalCheckoutSdk\Core\ProductionEnvironment;
|
||||
use PayPalCheckoutSdk\Core\SandboxEnvironment;
|
||||
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
|
||||
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
|
||||
use PayPalHttp\HttpException;
|
||||
use Symfony\Component\Intl\Currencies;
|
||||
use App\Helpers\ExtensionHelper;
|
||||
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
|
@ -49,8 +36,24 @@ class PaymentController extends Controller
|
|||
* @param ShopProduct $shopProduct
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function checkOut(Request $request, ShopProduct $shopProduct)
|
||||
public function checkOut(ShopProduct $shopProduct)
|
||||
{
|
||||
// get all payment gateway extensions
|
||||
$extensions = glob(app_path() . '/Extensions/PaymentGateways/*', GLOB_ONLYDIR);
|
||||
|
||||
// build a paymentgateways array that contains the routes for the payment gateways and the image path for the payment gateway which lays in public/images/Extensions/PaymentGateways with the extensionname in lowercase
|
||||
$paymentGateways = [];
|
||||
foreach ($extensions as $extension) {
|
||||
$extensionName = basename($extension);
|
||||
$config = ExtensionHelper::getExtensionConfig($extensionName, 'PaymentGateways');
|
||||
if ($config) {
|
||||
$payment = new \stdClass();
|
||||
$payment->name = $config['name'];
|
||||
$payment->image = asset('images/Extensions/PaymentGateways/' . strtolower($extensionName) . '_logo.png');
|
||||
$paymentGateways[] = $payment;
|
||||
}
|
||||
}
|
||||
|
||||
return view('store.checkout')->with([
|
||||
'product' => $shopProduct,
|
||||
'discountpercent' => PartnerDiscount::getDiscount(),
|
||||
|
@ -59,6 +62,7 @@ class PaymentController extends Controller
|
|||
'taxvalue' => $shopProduct->getTaxValue(),
|
||||
'taxpercent' => $shopProduct->getTaxPercent(),
|
||||
'total' => $shopProduct->getTotalPrice(),
|
||||
'paymentGateways' => $paymentGateways,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -67,11 +71,10 @@ class PaymentController extends Controller
|
|||
* @param ShopProduct $shopProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function FreePay(Request $request, ShopProduct $shopProduct)
|
||||
public function FreePay(ShopProduct $shopProduct)
|
||||
{
|
||||
//dd($shopProduct);
|
||||
//check if the product is really free or the discount is 100%
|
||||
if($shopProduct->getTotalPrice()>0) return redirect()->route('home')->with('error', __('An error ocured. Please try again.'));
|
||||
if ($shopProduct->getTotalPrice() > 0) return redirect()->route('home')->with('error', __('An error ocured. Please try again.'));
|
||||
|
||||
//give product
|
||||
/** @var User $user */
|
||||
|
@ -80,9 +83,9 @@ class PaymentController extends Controller
|
|||
//not updating server limit
|
||||
|
||||
//update User with bought item
|
||||
if ($shopProduct->type=="Credits") {
|
||||
if ($shopProduct->type == "Credits") {
|
||||
$user->increment('credits', $shopProduct->quantity);
|
||||
}elseif ($shopProduct->type=="Server slots"){
|
||||
} elseif ($shopProduct->type == "Server slots") {
|
||||
$user->increment('server_limit', $shopProduct->quantity);
|
||||
}
|
||||
|
||||
|
@ -98,7 +101,7 @@ class PaymentController extends Controller
|
|||
'type' => $shopProduct->type,
|
||||
'status' => 'paid',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price*PartnerDiscount::getDiscount()/100),
|
||||
'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
|
@ -114,196 +117,12 @@ class PaymentController extends Controller
|
|||
return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ShopProduct $shopProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function PaypalPay(Request $request, ShopProduct $shopProduct)
|
||||
public function pay(Request $request)
|
||||
{
|
||||
if(!$this->checkAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), "paypal")) return redirect()->route('home')->with('error', __('The product you chose can´t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'));
|
||||
$request = new OrdersCreateRequest();
|
||||
$request->prefer('return=representation');
|
||||
$request->body = [
|
||||
'intent' => 'CAPTURE',
|
||||
'purchase_units' => [
|
||||
[
|
||||
'reference_id' => uniqid(),
|
||||
'description' => $shopProduct->display.(PartnerDiscount::getDiscount() ? (' ('.__('Discount').' '.PartnerDiscount::getDiscount().'%)') : ''),
|
||||
'amount' => [
|
||||
'value' => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'breakdown' => [
|
||||
'item_total' => [
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'value' => $shopProduct->getPriceAfterDiscount(),
|
||||
],
|
||||
'tax_total' => [
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'value' => $shopProduct->getTaxValue(),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'application_context' => [
|
||||
'cancel_url' => route('payment.Cancel'),
|
||||
'return_url' => route('payment.PaypalSuccess', ['product' => $shopProduct->id]),
|
||||
'brand_name' => config('app.name', 'Laravel'),
|
||||
'shipping_preference' => 'NO_SHIPPING',
|
||||
],
|
||||
$product = ShopProduct::find($request->product_id);
|
||||
$paymentGateway = $request->payment_method;
|
||||
|
||||
];
|
||||
|
||||
try {
|
||||
// Call API with your client and get a response for your call
|
||||
$response = $this->getPayPalClient()->execute($request);
|
||||
|
||||
return redirect()->away($response->result->links[1]->href);
|
||||
|
||||
// If call returns body in response, you can get the deserialized version from the result attribute of the response
|
||||
} catch (HttpException $ex) {
|
||||
echo $ex->statusCode;
|
||||
dd(json_decode($ex->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PayPalHttpClient
|
||||
*/
|
||||
protected function getPayPalClient()
|
||||
{
|
||||
$environment = env('APP_ENV') == 'local'
|
||||
? new SandboxEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret())
|
||||
: new ProductionEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret());
|
||||
|
||||
return new PayPalHttpClient($environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getPaypalClientId()
|
||||
{
|
||||
return env('APP_ENV') == 'local' ? config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID') : config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getPaypalClientSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local' ? config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET') : config('SETTINGS::PAYMENTS:PAYPAL:SECRET');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $laravelRequest
|
||||
*/
|
||||
public function PaypalSuccess(Request $laravelRequest)
|
||||
{
|
||||
/** @var ShopProduct $shopProduct */
|
||||
$shopProduct = ShopProduct::findOrFail($laravelRequest->input('product'));
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$request = new OrdersCaptureRequest($laravelRequest->input('token'));
|
||||
$request->prefer('return=representation');
|
||||
try {
|
||||
// Call API with your client and get a response for your call
|
||||
$response = $this->getPayPalClient()->execute($request);
|
||||
if ($response->statusCode == 201 || $response->statusCode == 200) {
|
||||
|
||||
//update server limit
|
||||
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
|
||||
if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
|
||||
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
|
||||
}
|
||||
}
|
||||
|
||||
//update User with bought item
|
||||
if ($shopProduct->type == 'Credits') {
|
||||
$user->increment('credits', $shopProduct->quantity);
|
||||
} elseif ($shopProduct->type == 'Server slots') {
|
||||
$user->increment('server_limit', $shopProduct->quantity);
|
||||
}
|
||||
|
||||
//give referral commission always
|
||||
if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits' && config('SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION') == 'true') {
|
||||
if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
|
||||
$ref_user = User::findOrFail($ref_user->referral_id);
|
||||
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, '', '');
|
||||
$ref_user->increment('credits', $increment);
|
||||
|
||||
//LOGS REFERRALS IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($ref_user)
|
||||
->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
|
||||
}
|
||||
}
|
||||
|
||||
//update role give Referral-reward
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
|
||||
//give referral commission only on first purchase
|
||||
if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits' && config('SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION') == 'false') {
|
||||
if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
|
||||
$ref_user = User::findOrFail($ref_user->referral_id);
|
||||
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, '', '');
|
||||
$ref_user->increment('credits', $increment);
|
||||
|
||||
//LOGS REFERRALS IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($ref_user)
|
||||
->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//store payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => $response->result->id,
|
||||
'payment_method' => 'paypal',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'paid',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
//only create invoice if SETTINGS::INVOICE:ENABLED is true
|
||||
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
|
||||
$this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
|
||||
}
|
||||
|
||||
//redirect back to home
|
||||
return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
|
||||
}
|
||||
|
||||
// If call returns body in response, you can get the deserialized version from the result attribute of the response
|
||||
if (env('APP_ENV') == 'local') {
|
||||
dd($response);
|
||||
} else {
|
||||
abort(500);
|
||||
}
|
||||
} catch (HttpException $ex) {
|
||||
if (env('APP_ENV') == 'local') {
|
||||
echo $ex->statusCode;
|
||||
dd($ex->getMessage());
|
||||
} else {
|
||||
abort(422);
|
||||
}
|
||||
}
|
||||
return redirect()->route('payment.' . $paymentGateway . 'Pay', ['shopProduct' => $product->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,491 +130,9 @@ class PaymentController extends Controller
|
|||
*/
|
||||
public function Cancel(Request $request)
|
||||
{
|
||||
return redirect()->route('store.index')->with('success', 'Payment was Canceled');
|
||||
return redirect()->route('store.index')->with('info', 'Payment was Canceled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ShopProduct $shopProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function StripePay(Request $request, ShopProduct $shopProduct)
|
||||
{
|
||||
if(!$this->checkAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), "stripe")) return redirect()->route('home')->with('error', __('The product you chose can´t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'));
|
||||
$stripeClient = $this->getStripeClient();
|
||||
|
||||
$request = $stripeClient->checkout->sessions->create([
|
||||
'line_items' => [
|
||||
[
|
||||
'price_data' => [
|
||||
'currency' => $shopProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => $shopProduct->display.(PartnerDiscount::getDiscount() ? (' ('.__('Discount').' '.PartnerDiscount::getDiscount().'%)') : ''),
|
||||
'description' => $shopProduct->description,
|
||||
],
|
||||
'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
|
||||
],
|
||||
'quantity' => 1,
|
||||
],
|
||||
[
|
||||
'price_data' => [
|
||||
'currency' => $shopProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => __('Tax'),
|
||||
'description' => $shopProduct->getTaxPercent().'%',
|
||||
],
|
||||
'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
|
||||
],
|
||||
'quantity' => 1,
|
||||
],
|
||||
],
|
||||
|
||||
'mode' => 'payment',
|
||||
'payment_method_types' => str_getcsv(config('SETTINGS::PAYMENTS:STRIPE:METHODS')),
|
||||
'success_url' => route('payment.StripeSuccess', ['product' => $shopProduct->id]).'&session_id={CHECKOUT_SESSION_ID}',
|
||||
'cancel_url' => route('payment.Cancel'),
|
||||
]);
|
||||
|
||||
return redirect($request->url, 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function StripeSuccess(Request $request)
|
||||
{
|
||||
/** @var ShopProduct $shopProduct */
|
||||
$shopProduct = ShopProduct::findOrFail($request->input('product'));
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$stripeClient = $this->getStripeClient();
|
||||
|
||||
try {
|
||||
//get stripe data
|
||||
$paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
|
||||
$paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
|
||||
|
||||
//get DB entry of this payment ID if existing
|
||||
$paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
|
||||
|
||||
// check if payment is 100% completed and payment does not exist in db already
|
||||
if ($paymentSession->status == 'complete' && $paymentIntent->status == 'succeeded' && $paymentDbEntry == 0) {
|
||||
|
||||
//update server limit
|
||||
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
|
||||
if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
|
||||
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
|
||||
}
|
||||
}
|
||||
|
||||
//update User with bought item
|
||||
if ($shopProduct->type == 'Credits') {
|
||||
$user->increment('credits', $shopProduct->quantity);
|
||||
} elseif ($shopProduct->type == 'Server slots') {
|
||||
$user->increment('server_limit', $shopProduct->quantity);
|
||||
}
|
||||
|
||||
//update role give Referral-reward
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
|
||||
if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits') {
|
||||
if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
|
||||
$ref_user = User::findOrFail($ref_user->referral_id);
|
||||
$increment = number_format($shopProduct->quantity / 100 * config('SETTINGS::REFERRAL:PERCENTAGE'), 0, '', '');
|
||||
$ref_user->increment('credits', $increment);
|
||||
|
||||
//LOGS REFERRALS IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($ref_user)
|
||||
->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//store paid payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'payment_method' => 'stripe',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'paid',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
//only create invoice if SETTINGS::INVOICE:ENABLED is true
|
||||
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
|
||||
$this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
|
||||
}
|
||||
|
||||
//redirect back to home
|
||||
return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
|
||||
} else {
|
||||
if ($paymentIntent->status == 'processing') {
|
||||
|
||||
//store processing payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'payment_method' => 'stripe',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'processing',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price,
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
//only create invoice if SETTINGS::INVOICE:ENABLED is true
|
||||
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
|
||||
$this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
|
||||
}
|
||||
|
||||
//redirect back to home
|
||||
return redirect()->route('home')->with('success', __('Your payment is being processed!'));
|
||||
}
|
||||
if ($paymentDbEntry == 0 && $paymentIntent->status != 'processing') {
|
||||
$stripeClient->paymentIntents->cancel($paymentIntent->id);
|
||||
|
||||
//redirect back to home
|
||||
return redirect()->route('home')->with('success', __('Your payment has been canceled!'));
|
||||
} else {
|
||||
abort(402);
|
||||
}
|
||||
}
|
||||
} catch (HttpException $ex) {
|
||||
if (env('APP_ENV') == 'local') {
|
||||
echo $ex->statusCode;
|
||||
dd($ex->getMessage());
|
||||
} else {
|
||||
abort(422);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
protected function handleStripePaymentSuccessHook($paymentIntent)
|
||||
{
|
||||
try {
|
||||
// Get payment db entry
|
||||
$payment = Payment::where('payment_id', $paymentIntent->id)->first();
|
||||
$user = User::where('id', $payment->user_id)->first();
|
||||
|
||||
if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
|
||||
|
||||
//update server limit
|
||||
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
|
||||
if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
|
||||
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
|
||||
}
|
||||
}
|
||||
//update User with bought item
|
||||
if ($shopProduct->type == 'Credits') {
|
||||
$user->increment('credits', $shopProduct->quantity);
|
||||
} elseif ($shopProduct->type == 'Server slots') {
|
||||
$user->increment('server_limit', $shopProduct->quantity);
|
||||
}
|
||||
|
||||
//update role give Referral-reward
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
|
||||
if ((config('SETTINGS::REFERRAL:MODE') == 'commission' || config('SETTINGS::REFERRAL:MODE') == 'both') && $shopProduct->type == 'Credits') {
|
||||
if ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user->id)->first()) {
|
||||
$ref_user = User::findOrFail($ref_user->referral_id);
|
||||
$increment = number_format($shopProduct->quantity / 100 * config('SETTINGS::REFERRAL:PERCENTAGE'), 0, '', '');
|
||||
$ref_user->increment('credits', $increment);
|
||||
|
||||
//LOGS REFERRALS IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($ref_user)
|
||||
->log('gained '.$increment.' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update payment db entry status
|
||||
$payment->update(['status' => 'paid']);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
//only create invoice if SETTINGS::INVOICE:ENABLED is true
|
||||
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
|
||||
$this->createInvoice($user, $payment, 'paid', strtoupper($paymentIntent->currency));
|
||||
}
|
||||
}
|
||||
} catch (HttpException $ex) {
|
||||
abort(422);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function StripeWebhooks(Request $request)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->getStripeSecret());
|
||||
|
||||
try {
|
||||
$payload = @file_get_contents('php://input');
|
||||
$sig_header = $request->header('Stripe-Signature');
|
||||
$event = null;
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
$payload,
|
||||
$sig_header,
|
||||
$this->getStripeEndpointSecret()
|
||||
);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
// Invalid payload
|
||||
|
||||
abort(400);
|
||||
} catch (\Stripe\Exception\SignatureVerificationException $e) {
|
||||
// Invalid signature
|
||||
|
||||
abort(400);
|
||||
}
|
||||
|
||||
// Handle the event
|
||||
switch ($event->type) {
|
||||
case 'payment_intent.succeeded':
|
||||
$paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
|
||||
$this->handleStripePaymentSuccessHook($paymentIntent);
|
||||
break;
|
||||
default:
|
||||
echo 'Received unknown event type '.$event->type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Stripe\StripeClient
|
||||
*/
|
||||
protected function getStripeClient()
|
||||
{
|
||||
return new \Stripe\StripeClient($this->getStripeSecret());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getStripeSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local'
|
||||
? config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET')
|
||||
: config('SETTINGS::PAYMENTS:STRIPE:SECRET');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getStripeEndpointSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local'
|
||||
? config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET')
|
||||
: config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET');
|
||||
}
|
||||
|
||||
protected function createInvoice($user, $payment, $paymentStatus, $currencyCode)
|
||||
{
|
||||
$shopProduct = ShopProduct::where('id', $payment->shop_item_product_id)->first();
|
||||
//create invoice
|
||||
$lastInvoiceID = \App\Models\Invoice::where('invoice_name', 'like', '%'.now()->format('mY').'%')->count('id');
|
||||
$newInvoiceID = $lastInvoiceID + 1;
|
||||
$logoPath = storage_path('app/public/logo.png');
|
||||
|
||||
$seller = new Party([
|
||||
'name' => config('SETTINGS::INVOICE:COMPANY_NAME'),
|
||||
'phone' => config('SETTINGS::INVOICE:COMPANY_PHONE'),
|
||||
'address' => config('SETTINGS::INVOICE:COMPANY_ADDRESS'),
|
||||
'vat' => config('SETTINGS::INVOICE:COMPANY_VAT'),
|
||||
'custom_fields' => [
|
||||
'E-Mail' => config('SETTINGS::INVOICE:COMPANY_MAIL'),
|
||||
'Web' => config('SETTINGS::INVOICE:COMPANY_WEBSITE'),
|
||||
],
|
||||
]);
|
||||
|
||||
$customer = new Buyer([
|
||||
'name' => $user->name,
|
||||
'custom_fields' => [
|
||||
'E-Mail' => $user->email,
|
||||
'Client ID' => $user->id,
|
||||
],
|
||||
]);
|
||||
$item = (new InvoiceItem())
|
||||
->title($shopProduct->description)
|
||||
->pricePerUnit($shopProduct->price);
|
||||
|
||||
$notes = [
|
||||
__('Payment method').': '.$payment->payment_method,
|
||||
];
|
||||
$notes = implode('<br>', $notes);
|
||||
|
||||
$invoice = Invoice::make()
|
||||
->template('controlpanel')
|
||||
->name(__('Invoice'))
|
||||
->buyer($customer)
|
||||
->seller($seller)
|
||||
->discountByPercent(PartnerDiscount::getDiscount())
|
||||
->taxRate(floatval($shopProduct->getTaxPercent()))
|
||||
->shipping(0)
|
||||
->addItem($item)
|
||||
->status(__($paymentStatus))
|
||||
->series(now()->format('mY'))
|
||||
->delimiter('-')
|
||||
->sequence($newInvoiceID)
|
||||
->serialNumberFormat(config('SETTINGS::INVOICE:PREFIX').'{DELIMITER}{SERIES}{SEQUENCE}')
|
||||
->currencyCode($currencyCode)
|
||||
->currencySymbol(Currencies::getSymbol($currencyCode))
|
||||
->notes($notes);
|
||||
|
||||
if (file_exists($logoPath)) {
|
||||
$invoice->logo($logoPath);
|
||||
}
|
||||
|
||||
//Save the invoice in "storage\app\invoice\USER_ID\YEAR"
|
||||
$invoice->filename = $invoice->getSerialNumber().'.pdf';
|
||||
$invoice->render();
|
||||
Storage::disk('local')->put('invoice/'.$user->id.'/'.now()->format('Y').'/'.$invoice->filename, $invoice->output);
|
||||
|
||||
\App\Models\Invoice::create([
|
||||
'invoice_user' => $user->id,
|
||||
'invoice_name' => $invoice->getSerialNumber(),
|
||||
'payment_id' => $payment->payment_id,
|
||||
]);
|
||||
|
||||
//Send Invoice per Mail
|
||||
$user->notify(new InvoiceNotification($invoice, $user, $payment));
|
||||
}
|
||||
|
||||
public function checkAmount($amount, $currencyCode, $payment_method)
|
||||
{
|
||||
$minimums = [
|
||||
"USD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"AED" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"AUD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"BGN" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 1
|
||||
],
|
||||
"BRL" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"CAD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"CHF" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"CZK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 15
|
||||
],
|
||||
"DKK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2.5
|
||||
],
|
||||
"EUR" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"GBP" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.3
|
||||
],
|
||||
"HKD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 4
|
||||
],
|
||||
"HRK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"HUF" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 175
|
||||
],
|
||||
"INR" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"JPY" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"MXN" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 10
|
||||
],
|
||||
"MYR" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"NOK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 3
|
||||
],
|
||||
"NZD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"PLN" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"RON" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 2
|
||||
],
|
||||
"SEK" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 3
|
||||
],
|
||||
"SGD" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 0.5
|
||||
],
|
||||
"THB" => [
|
||||
"paypal" => 0,
|
||||
"stripe" => 10
|
||||
]
|
||||
];
|
||||
return $amount >= $minimums[$currencyCode][$payment_method];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return JsonResponse|mixed
|
||||
*
|
||||
|
@ -808,9 +145,7 @@ class PaymentController extends Controller
|
|||
return datatables($query)
|
||||
|
||||
->addColumn('user', function (Payment $payment) {
|
||||
return
|
||||
($payment->user)?'<a href="'.route('admin.users.show', $payment->user->id).'">'.$payment->user->name.'</a>':__('Unknown user');
|
||||
|
||||
return ($payment->user) ? '<a href="' . route('admin.users.show', $payment->user->id) . '">' . $payment->user->name . '</a>' : __('Unknown user');
|
||||
})
|
||||
->editColumn('price', function (Payment $payment) {
|
||||
return $payment->formatToCurrency($payment->price);
|
||||
|
@ -819,18 +154,20 @@ class PaymentController extends Controller
|
|||
return $payment->formatToCurrency($payment->tax_value);
|
||||
})
|
||||
->editColumn('tax_percent', function (Payment $payment) {
|
||||
return $payment->tax_percent.' %';
|
||||
return $payment->tax_percent . ' %';
|
||||
})
|
||||
->editColumn('total_price', function (Payment $payment) {
|
||||
return $payment->formatToCurrency($payment->total_price);
|
||||
})
|
||||
|
||||
->editColumn('created_at', function (Payment $payment) {
|
||||
return ['display' => $payment->created_at ? $payment->created_at->diffForHumans() : '',
|
||||
'raw' => $payment->created_at ? strtotime($payment->created_at) : ''];
|
||||
return [
|
||||
'display' => $payment->created_at ? $payment->created_at->diffForHumans() : '',
|
||||
'raw' => $payment->created_at ? strtotime($payment->created_at) : ''
|
||||
];
|
||||
})
|
||||
->addColumn('actions', function (Payment $payment) {
|
||||
return '<a data-content="'.__('Download').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.invoices.downloadSingleInvoice', 'id='.$payment->payment_id).'" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>';
|
||||
return '<a data-content="' . __('Download') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.invoices.downloadSingleInvoice', 'id=' . $payment->payment_id) . '" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>';
|
||||
})
|
||||
->rawColumns(['actions', 'user'])
|
||||
->make(true);
|
||||
|
|
31
app/Listeners/CreateInvoice.php
Normal file
31
app/Listeners/CreateInvoice.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Traits\Invoiceable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
class CreateInvoice implements ShouldQueue
|
||||
{
|
||||
|
||||
use Invoiceable;
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \App\Events\PaymentEvent $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(PaymentEvent $event)
|
||||
{
|
||||
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
|
||||
// get user from payment which does hold the user_id
|
||||
$user = $event->payment->user;
|
||||
|
||||
// create invoice using the trait
|
||||
$this->createInvoice($user, $event->payment);
|
||||
}
|
||||
}
|
||||
}
|
82
app/Listeners/UserPayment.php
Normal file
82
app/Listeners/UserPayment.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\PartnerDiscount;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
class UserPayment
|
||||
{
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \App\Events\PaymentEvent $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(PaymentEvent $event)
|
||||
{
|
||||
$user = $event->user;
|
||||
$shopProduct = $event->shopProduct;
|
||||
|
||||
// only update user if payment is paid
|
||||
if ($event->payment->status != "paid") {
|
||||
return;
|
||||
}
|
||||
|
||||
//update server limit
|
||||
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0 && $user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
|
||||
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
|
||||
}
|
||||
|
||||
//update User with bought item
|
||||
if ($shopProduct->type == "Credits") {
|
||||
$user->increment('credits', $shopProduct->quantity);
|
||||
} elseif ($shopProduct->type == "Server slots") {
|
||||
$user->increment('server_limit', $shopProduct->quantity);
|
||||
}
|
||||
|
||||
//give referral commission always
|
||||
if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "true") {
|
||||
if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
|
||||
$ref_user = User::findOrFail($ref_user->referral_id);
|
||||
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
|
||||
$ref_user->increment('credits', $increment);
|
||||
|
||||
//LOGS REFERRALS IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($ref_user)
|
||||
->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
|
||||
}
|
||||
}
|
||||
//update role give Referral-reward
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
|
||||
//give referral commission only on first purchase
|
||||
if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "false") {
|
||||
if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
|
||||
$ref_user = User::findOrFail($ref_user->referral_id);
|
||||
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
|
||||
$ref_user->increment('credits', $increment);
|
||||
|
||||
//LOGS REFERRALS IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($ref_user)
|
||||
->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOGS PAYMENT IN THE ACTIVITY LOG
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->causedBy($user)
|
||||
->log('bought ' . $shopProduct->quantity . ' ' . $shopProduct->type . ' for ' . $shopProduct->price . $shopProduct->currency_code);
|
||||
}
|
||||
}
|
|
@ -7,20 +7,11 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use NumberFormatter;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
|
||||
class Payment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use LogsActivity;
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
-> logOnlyDirty()
|
||||
-> logOnly(['*'])
|
||||
-> dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Listeners\CreateInvoice;
|
||||
use App\Listeners\UnsuspendServers;
|
||||
use App\Listeners\UserPayment;
|
||||
use App\Listeners\Verified;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
|
@ -24,6 +27,10 @@ class EventServiceProvider extends ServiceProvider
|
|||
UserUpdateCreditsEvent::class => [
|
||||
UnsuspendServers::class,
|
||||
],
|
||||
PaymentEvent::class => [
|
||||
CreateInvoice::class,
|
||||
UserPayment::class,
|
||||
],
|
||||
SocialiteWasCalled::class => [
|
||||
// ... other providers
|
||||
'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle',
|
||||
|
|
91
app/Traits/Invoiceable.php
Normal file
91
app/Traits/Invoiceable.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Notifications\InvoiceNotification;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use LaravelDaily\Invoices\Classes\Buyer;
|
||||
use LaravelDaily\Invoices\Classes\InvoiceItem;
|
||||
use LaravelDaily\Invoices\Classes\Party;
|
||||
use LaravelDaily\Invoices\Invoice;
|
||||
use Symfony\Component\Intl\Currencies;
|
||||
|
||||
trait Invoiceable
|
||||
{
|
||||
public function createInvoice($user, $payment)
|
||||
{
|
||||
$shopProduct = ShopProduct::where('id', $payment->shop_item_product_id)->first();
|
||||
//create invoice
|
||||
$lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
|
||||
$newInvoiceID = $lastInvoiceID + 1;
|
||||
$logoPath = storage_path('app/public/logo.png');
|
||||
|
||||
$seller = new Party([
|
||||
'name' => config("SETTINGS::INVOICE:COMPANY_NAME"),
|
||||
'phone' => config("SETTINGS::INVOICE:COMPANY_PHONE"),
|
||||
'address' => config("SETTINGS::INVOICE:COMPANY_ADDRESS"),
|
||||
'vat' => config("SETTINGS::INVOICE:COMPANY_VAT"),
|
||||
'custom_fields' => [
|
||||
'E-Mail' => config("SETTINGS::INVOICE:COMPANY_MAIL"),
|
||||
"Web" => config("SETTINGS::INVOICE:COMPANY_WEBSITE")
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
$customer = new Buyer([
|
||||
'name' => $user->name,
|
||||
'custom_fields' => [
|
||||
'E-Mail' => $user->email,
|
||||
'Client ID' => $user->id,
|
||||
],
|
||||
]);
|
||||
$item = (new InvoiceItem())
|
||||
->title($shopProduct->description)
|
||||
->pricePerUnit($shopProduct->price);
|
||||
|
||||
$notes = [
|
||||
__("Payment method") . ": " . $payment->payment_method,
|
||||
];
|
||||
$notes = implode("<br>", $notes);
|
||||
|
||||
|
||||
$invoice = Invoice::make()
|
||||
->template('controlpanel')
|
||||
->name(__("Invoice"))
|
||||
->buyer($customer)
|
||||
->seller($seller)
|
||||
->discountByPercent(PartnerDiscount::getDiscount())
|
||||
->taxRate(floatval($shopProduct->getTaxPercent()))
|
||||
->shipping(0)
|
||||
->addItem($item)
|
||||
->status(__($payment->status))
|
||||
->series(now()->format('mY'))
|
||||
->delimiter("-")
|
||||
->sequence($newInvoiceID)
|
||||
->serialNumberFormat(config("SETTINGS::INVOICE:PREFIX") . '{DELIMITER}{SERIES}{SEQUENCE}')
|
||||
->currencyCode(strtoupper($payment->currency_code))
|
||||
->currencySymbol(Currencies::getSymbol(strtoupper($payment->currency_code)))
|
||||
->notes($notes);
|
||||
|
||||
if (file_exists($logoPath)) {
|
||||
$invoice->logo($logoPath);
|
||||
}
|
||||
|
||||
//Save the invoice in "storage\app\invoice\USER_ID\YEAR"
|
||||
$invoice->filename = $invoice->getSerialNumber() . '.pdf';
|
||||
$invoice->render();
|
||||
Storage::disk("local")->put("invoice/" . $user->id . "/" . now()->format('Y') . "/" . $invoice->filename, $invoice->output);
|
||||
|
||||
|
||||
\App\Models\Invoice::create([
|
||||
'invoice_user' => $user->id,
|
||||
'invoice_name' => $invoice->getSerialNumber(),
|
||||
'payment_id' => $payment->payment_id,
|
||||
]);
|
||||
|
||||
//Send Invoice per Mail
|
||||
$user->notify(new InvoiceNotification($invoice, $user, $payment));
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
@ -34,6 +34,8 @@ Route::middleware('api.token')->group(function () {
|
|||
Route::get('/notifications/{user}', [NotificationController::class, 'index']);
|
||||
Route::get('/notifications/{user}/{notification}', [NotificationController::class, 'view']);
|
||||
Route::post('/notifications', [NotificationController::class, 'send']);
|
||||
Route::delete('/notifications/{user}', [NotificationController::class, 'delete']);
|
||||
Route::delete('/notifications/{user}/{notification}', [NotificationController::class, 'deleteOne']);
|
||||
Route::delete('/notifications/{user}', [NotificationController::class, 'delete']);
|
||||
});
|
||||
|
||||
require __DIR__ . '/extensions_api.php';
|
||||
|
|
21
routes/extensions_api.php
Normal file
21
routes/extensions_api.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group(['prefix' => 'extensions', 'middleware' => 'api.token'], function () {
|
||||
// get all extensions that are inside App/Extensions
|
||||
// It is important that the extensions are inside a folder with the name of the extension
|
||||
// while those folders are inside Folders with the name of the type of extension like PaymentGateways, Themes, etc.
|
||||
$extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
|
||||
$extensions = [];
|
||||
foreach ($extensionNamespaces as $extensionNamespace) {
|
||||
$extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
|
||||
}
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
$routesFile = $extension . '/api_routes.php';
|
||||
if (file_exists($routesFile)) {
|
||||
include_once $routesFile;
|
||||
}
|
||||
}
|
||||
});
|
22
routes/extensions_web.php
Normal file
22
routes/extensions_web.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group(['prefix' => 'extensions'], function () {
|
||||
|
||||
// get all extensions that are inside App/Extensions
|
||||
// It is important that the extensions are inside a folder with the name of the extension
|
||||
// while those folders are inside Folders with the name of the type of extension like PaymentGateways, Themes, etc.
|
||||
$extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
|
||||
$extensions = [];
|
||||
foreach ($extensionNamespaces as $extensionNamespace) {
|
||||
$extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
|
||||
}
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
$routesFile = $extension . '/web_routes.php';
|
||||
if (file_exists($routesFile)) {
|
||||
include_once $routesFile;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -49,17 +49,14 @@ Route::middleware('guest')->get('/', function () {
|
|||
|
||||
Auth::routes(['verify' => true]);
|
||||
|
||||
// Stripe WebhookRoute -> validation in Route Handler
|
||||
Route::post('payment/StripeWebhooks', [PaymentController::class, 'StripeWebhooks'])->name('payment.StripeWebhooks');
|
||||
|
||||
Route::get('/privacy', function () {
|
||||
return view('information.privacy');
|
||||
return view('information.privacy');
|
||||
})->name('privacy');
|
||||
Route::get('/imprint', function () {
|
||||
return view('information.imprint');
|
||||
return view('information.imprint');
|
||||
})->name('imprint');
|
||||
Route::get('/tos', function () {
|
||||
return view('information.tos');
|
||||
return view('information.tos');
|
||||
})->name('tos');
|
||||
|
||||
Route::middleware(['auth', 'checkSuspended'])->group(function () {
|
||||
|
@ -90,10 +87,7 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
|
|||
|
||||
//payments
|
||||
Route::get('checkout/{shopProduct}', [PaymentController::class, 'checkOut'])->name('checkout');
|
||||
Route::get('payment/PaypalPay/{shopProduct}', [PaymentController::class, 'PaypalPay'])->name('payment.PaypalPay');
|
||||
Route::get('payment/PaypalSuccess', [PaymentController::class, 'PaypalSuccess'])->name('payment.PaypalSuccess');
|
||||
Route::get('payment/StripePay/{shopProduct}', [PaymentController::class, 'StripePay'])->name('payment.StripePay');
|
||||
Route::get('payment/StripeSuccess', [PaymentController::class, 'StripeSuccess'])->name('payment.StripeSuccess');
|
||||
Route::post('payment/pay', [PaymentController::class, 'pay'])->name('payment.pay');
|
||||
Route::get('payment/FreePay/{shopProduct}', [PaymentController::class, 'FreePay'])->name('payment.FreePay');
|
||||
Route::get('payment/Cancel', [PaymentController::class, 'Cancel'])->name('payment.Cancel');
|
||||
|
||||
|
@ -221,3 +215,5 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
|
|||
|
||||
Route::get('/home', [HomeController::class, 'index'])->name('home');
|
||||
});
|
||||
|
||||
require __DIR__ . '/extensions_web.php';
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a class=""
|
||||
href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
|
||||
<li class="breadcrumb-item"><a class="" href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
|
||||
<li class="breadcrumb-item"><a class="text-muted"
|
||||
href="{{ route('store.index') }}">{{ __('Store') }}</a>
|
||||
</li>
|
||||
|
@ -23,157 +22,127 @@
|
|||
<!-- END CONTENT HEADER -->
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<section x-data="serverApp()" x-init="$watch('paymentMethod', value => setPaymentRoute(value))" class="content">
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="invoice p-3 mb-3">
|
||||
<!-- title row -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4>
|
||||
<i class="fas fa-globe"></i> {{ config('app.name', 'Laravel') }}
|
||||
<small class="float-right">{{ __('Date') }}:
|
||||
{{ Carbon\Carbon::now()->isoFormat('LL') }}</small>
|
||||
</h4>
|
||||
<form x-data="{ payment_method: '', clicked: false }" action="{{ route('payment.pay') }}" method="POST">
|
||||
@csrf
|
||||
@method('post')
|
||||
<!-- Main content -->
|
||||
<div class="invoice p-3 mb-3">
|
||||
<!-- title row -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4>
|
||||
<i class="fas fa-globe"></i> {{ config('app.name', 'Laravel') }}
|
||||
<small class="float-right">{{ __('Date') }}:
|
||||
{{ Carbon\Carbon::now()->isoFormat('LL') }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
<!-- Table row -->
|
||||
<div class="row">
|
||||
<div class="col-12 table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Quantity') }}</th>
|
||||
<th>{{ __('Product') }}</th>
|
||||
<th>{{ __('Description') }}</th>
|
||||
<th>{{ __('Subtotal') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td><i class="fa fa-coins mr-2"></i>{{ $product->quantity }}
|
||||
{{ strtolower($product->type) == 'credits' ? CREDITS_DISPLAY_NAME : $product->type }}
|
||||
</td>
|
||||
<td>{{ $product->description }}</td>
|
||||
<td>{{ $product->formatToCurrency($product->price) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Table row -->
|
||||
<div class="row">
|
||||
<div class="col-12 table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Quantity') }}</th>
|
||||
<th>{{ __('Product') }}</th>
|
||||
<th>{{ __('Description') }}</th>
|
||||
<th>{{ __('Subtotal') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td><i class="fa fa-coins mr-2"></i>{{ $product->quantity }}
|
||||
{{ strtolower($product->type) == 'credits' ? CREDITS_DISPLAY_NAME : $product->type }}
|
||||
</td>
|
||||
<td>{{ $product->description }}</td>
|
||||
<td>{{ $product->formatToCurrency($product->price) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<input type="hidden" name="product_id" value="{{ $product->id }}">
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<!-- accepted payments column -->
|
||||
<div class="col-6">
|
||||
@if($total!=0)
|
||||
<div class="row">
|
||||
<!-- accepted payments column -->
|
||||
<div class="col-6">
|
||||
<p class="lead">{{ __('Payment Methods') }}:</p>
|
||||
|
||||
<div>
|
||||
@if (config('SETTINGS::PAYMENTS:PAYPAL:SECRET') || config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET'))
|
||||
<label class="text-center " for="paypal">
|
||||
<img class="mb-3" height="50"
|
||||
src="{{ url('/images/paypal_logo.png') }}"></br>
|
||||
|
||||
<input x-model="paymentMethod" type="radio" id="paypal" value="paypal"
|
||||
name="payment_method">
|
||||
</input>
|
||||
</label>
|
||||
@endif
|
||||
@if (config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') || config('SETTINGS::PAYMENTS:STRIPE:SECRET'))
|
||||
<label class="ml-5 text-center " for="stripe">
|
||||
<img class="mb-3" height="50"
|
||||
src="{{ url('/images/stripe_logo.png') }}" /></br>
|
||||
<input x-model="paymentMethod" type="radio" id="stripe" value="stripe"
|
||||
name="payment_method">
|
||||
</input>
|
||||
</label>
|
||||
@endif
|
||||
<div class="d-flex flex-wrap flex-direction-row">
|
||||
@foreach ($paymentGateways as $gateway)
|
||||
<div class="ml-2">
|
||||
<label class="text-center" for="{{ $gateway->name }}">
|
||||
<img class="mb-3" height="50" src="{{ $gateway->image }}"></br>
|
||||
<input x-on:click="console.log(payment_method)" x-model="payment_method"
|
||||
type="radio" id="{{ $gateway->name }}"
|
||||
value="{{ $gateway->name }}">
|
||||
</input>
|
||||
</label>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<p class="lead" style="text-align: center">{{ __('This product is free for you') }}.</p>
|
||||
@endif
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-6">
|
||||
<p class="lead">{{ __('Amount Due') }}
|
||||
{{ Carbon\Carbon::now()->isoFormat('LL') }}</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
@if($discountpercent&&$discountvalue)
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-6">
|
||||
<p class="lead">{{ __('Amount Due') }}
|
||||
{{ Carbon\Carbon::now()->isoFormat('LL') }}</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
@if ($discountpercent && $discountvalue)
|
||||
<tr>
|
||||
<th>{{ __('Discount') }} ({{ $discountpercent }}%):</th>
|
||||
<td>{{ $product->formatToCurrency($discountvalue) }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
<tr>
|
||||
<th>{{ __('Discount') }} ({{ $discountpercent }}%):</th>
|
||||
<td>{{$product->formatToCurrency($discountvalue)}}</td>
|
||||
<th style="width:50%">{{ __('Subtotal') }}:</th>
|
||||
<td>{{ $product->formatToCurrency($discountedprice) }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
<tr>
|
||||
<th style="width:50%">{{ __('Subtotal') }}:</th>
|
||||
<td>{{ $product->formatToCurrency($discountedprice) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ __('Tax') }} ({{ $taxpercent }}%):</th>
|
||||
<td>{{ $product->formatToCurrency($taxvalue) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ __('Total') }}:</th>
|
||||
<td>{{ $product->formatToCurrency($total) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<tr>
|
||||
<th>{{ __('Tax') }} ({{ $taxpercent }}%):</th>
|
||||
<td>{{ $product->formatToCurrency($taxvalue) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ __('Total') }}:</th>
|
||||
<td>{{ $product->formatToCurrency($total) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<!-- this row will not appear when printing -->
|
||||
<div class="row no-print">
|
||||
<div class="col-12">
|
||||
<button :disabled="!payment_method || clicked"
|
||||
:class="!payment_method || clicked ? 'disabled' : ''"
|
||||
class="btn btn-success float-right"><i class="far fa-credit-card mr-2"
|
||||
@click="clicked = true"></i>
|
||||
{{ __('Submit Payment') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<!-- this row will not appear when printing -->
|
||||
<div class="row no-print">
|
||||
<div class="col-12">
|
||||
<a type="button" :href="paymentRoute" :disabled="!paymentRoute"
|
||||
:class="!paymentRoute ? 'disabled' : ''" class="btn btn-success float-right"><i
|
||||
class="far fa-credit-card mr-2"></i>
|
||||
{{ __('Submit Payment') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- /.invoice -->
|
||||
</div><!-- /.col -->
|
||||
</div><!-- /.row -->
|
||||
</div>
|
||||
</section>
|
||||
<!-- END CONTENT -->
|
||||
|
||||
<script>
|
||||
function serverApp() {
|
||||
return {
|
||||
//loading
|
||||
paymentMethod: '',
|
||||
paymentRoute: ({{ $total }} == 0)?('{{ route('payment.FreePay', $product->id) }}'):'',
|
||||
setPaymentRoute(provider) {
|
||||
switch (provider) {
|
||||
case 'paypal':
|
||||
this.paymentRoute = '{{ route('payment.PaypalPay', $product->id) }}';
|
||||
break;
|
||||
case 'stripe':
|
||||
this.paymentRoute = '{{ route('payment.StripePay', $product->id) }}';
|
||||
break;
|
||||
default:
|
||||
this.paymentRoute = '{{ route('payment.PaypalPay', $product->id) }}';
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@endsection
|
||||
@endsection
|
||||
|
|
Loading…
Add table
Reference in a new issue