Merge pull request #328 from IceToast/stripe_integration
feature: Stripe integration
This commit is contained in:
commit
c614e03526
25 changed files with 2292 additions and 1229 deletions
14
.env.example
14
.env.example
|
@ -1,4 +1,4 @@
|
|||
APP_NAME=Dashboard
|
||||
APP_NAME=Controlpanel.gg
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
|
@ -25,6 +25,18 @@ PAYPAL_SECRET=
|
|||
PAYPAL_CLIENT_ID=
|
||||
PAYPAL_EMAIL=
|
||||
|
||||
|
||||
#stripe details, you only need "test" for testing! you can do this by setting the APP_ENV to local
|
||||
STRIPE_TEST_SECRET=
|
||||
STRIPE_SECRET=
|
||||
#https://dashboard.stripe.com/webhooks -> webhook route: <your.controlpanel.gg>/payment/StripeWebhooks
|
||||
STRIPE_ENDPOINT_TEST_SECRET=
|
||||
STRIPE_ENDPOINT_SECRET=
|
||||
#stripe payment methods, comma seperated list of methods you want to support:
|
||||
#read into https://stripe.com/docs/payments/payment-methods/integration-options and/or https://stripe.com/docs/payments/payment-methods/overview
|
||||
#Method needs to support the currency
|
||||
STRIPE_METHODS=
|
||||
|
||||
#set-up for extra discord verification
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
### Features
|
||||
|
||||
- PayPal Integration
|
||||
- Stripe Integration
|
||||
- Email Verification
|
||||
- Audit Log
|
||||
- Admin Dashboard
|
||||
|
@ -10,16 +11,22 @@
|
|||
- and so much more!
|
||||
|
||||
# ControlPanel-gg
|
||||
|
||||
![controlpanel](https://user-images.githubusercontent.com/45005889/123518824-06b05000-d6a8-11eb-91b9-d1ed36bd2317.png)
|
||||
|
||||
![](https://img.shields.io/github/stars/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/forks/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/tag/ControlPanel-gg/dashboard) [![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg) ![](https://img.shields.io/github/issues/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/license/ControlPanel-gg/dashboard) ![](https://img.shields.io/discord/787829714483019826)
|
||||
## About
|
||||
|
||||
ControlPanel's Dashboard is a dashboard application designed to offer clients a management tool to manage their pterodactyl servers. This dashboard comes with a credit-based billing solution that credits users hourly for each server they have and suspends them if they run out of credits.
|
||||
|
||||
This dashboard offers an easy to use and free billing solution for all starting and experienced hosting providers. This dashboard has many customization options and added discord 0auth verification to offer a solid link between your discord server and your dashboard.
|
||||
|
||||
### [Installation](https://controlpanel.gg/docs/intro "Installation")
|
||||
|
||||
### [Updating](https://controlpanel.gg/docs/Installation/updating "Updating")
|
||||
|
||||
### [Discord](https://discord.gg/4Y6HjD2uyU "discord")
|
||||
|
||||
### [Contributing](https://controlpanel.gg/docs/Contributing/contributing "Contributing")
|
||||
|
||||
### [Donating](https://controlpanel.gg/docs/Contributing/donating "Donating")
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\PaypalProduct;
|
||||
use App\Models\CreditProduct;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
@ -12,7 +12,7 @@ use Illuminate\Http\Response;
|
|||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PaypalProductController extends Controller
|
||||
class CreditProductController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
|
@ -21,11 +21,16 @@ class PaypalProductController extends Controller
|
|||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$isPaypalSetup = false;
|
||||
if (env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID')) $isPaypalSetup = true;
|
||||
$isPaymentSetup = false;
|
||||
|
||||
if (
|
||||
env('APP_ENV') == 'local' ||
|
||||
env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID') ||
|
||||
env('STRIPE_SECRET') && env('STRIPE_ENDPOINT_SECRET') && env('STRIPE_METHODS')
|
||||
) $isPaymentSetup = true;
|
||||
|
||||
return view('admin.store.index', [
|
||||
'isPaypalSetup' => $isPaypalSetup
|
||||
'isPaymentSetup' => $isPaymentSetup
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -60,7 +65,7 @@ class PaypalProductController extends Controller
|
|||
]);
|
||||
|
||||
$disabled = !is_null($request->input('disabled'));
|
||||
PaypalProduct::create(array_merge($request->all(), ['disabled' => $disabled]));
|
||||
CreditProduct::create(array_merge($request->all(), ['disabled' => $disabled]));
|
||||
|
||||
return redirect()->route('admin.store.index')->with('success', __('Store item has been created!'));
|
||||
}
|
||||
|
@ -68,10 +73,10 @@ class PaypalProductController extends Controller
|
|||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return Response
|
||||
*/
|
||||
public function show(PaypalProduct $paypalProduct)
|
||||
public function show(CreditProduct $creditProduct)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
@ -79,14 +84,14 @@ class PaypalProductController extends Controller
|
|||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return Application|Factory|View|Response
|
||||
*/
|
||||
public function edit(PaypalProduct $paypalProduct)
|
||||
public function edit(CreditProduct $creditProduct)
|
||||
{
|
||||
return view('admin.store.edit', [
|
||||
'currencyCodes' => config('currency_codes'),
|
||||
'paypalProduct' => $paypalProduct
|
||||
'creditProduct' => $creditProduct
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -94,10 +99,10 @@ class PaypalProductController extends Controller
|
|||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function update(Request $request, PaypalProduct $paypalProduct)
|
||||
public function update(Request $request, CreditProduct $creditProduct)
|
||||
{
|
||||
$request->validate([
|
||||
"disabled" => "nullable",
|
||||
|
@ -110,19 +115,19 @@ class PaypalProductController extends Controller
|
|||
]);
|
||||
|
||||
$disabled = !is_null($request->input('disabled'));
|
||||
$paypalProduct->update(array_merge($request->all(), ['disabled' => $disabled]));
|
||||
$creditProduct->update(array_merge($request->all(), ['disabled' => $disabled]));
|
||||
|
||||
return redirect()->route('admin.store.index')->with('success', __('Store item has been updated!'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function disable(Request $request, PaypalProduct $paypalProduct)
|
||||
public function disable(Request $request, CreditProduct $creditProduct)
|
||||
{
|
||||
$paypalProduct->update(['disabled' => !$paypalProduct->disabled]);
|
||||
$creditProduct->update(['disabled' => !$creditProduct->disabled]);
|
||||
|
||||
return redirect()->route('admin.store.index')->with('success', __('Product has been updated!'));
|
||||
}
|
||||
|
@ -130,50 +135,50 @@ class PaypalProductController extends Controller
|
|||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function destroy(PaypalProduct $paypalProduct)
|
||||
public function destroy(CreditProduct $creditProduct)
|
||||
{
|
||||
$paypalProduct->delete();
|
||||
$creditProduct->delete();
|
||||
return redirect()->back()->with('success', __('Store item has been removed!'));
|
||||
}
|
||||
|
||||
|
||||
public function dataTable()
|
||||
{
|
||||
$query = PaypalProduct::query();
|
||||
$query = CreditProduct::query();
|
||||
|
||||
return datatables($query)
|
||||
->addColumn('actions', function (PaypalProduct $paypalProduct) {
|
||||
->addColumn('actions', function (CreditProduct $creditProduct) {
|
||||
return '
|
||||
<a data-content="'.__("Edit").'" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.store.edit', $paypalProduct->id) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
|
||||
<a data-content="' . __("Edit") . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.store.edit', $creditProduct->id) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
|
||||
|
||||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.store.destroy', $paypalProduct->id) . '">
|
||||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.store.destroy', $creditProduct->id) . '">
|
||||
' . csrf_field() . '
|
||||
' . method_field("DELETE") . '
|
||||
<button data-content="' . __("Delete") . '" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
';
|
||||
})
|
||||
->addColumn('disabled', function (PaypalProduct $paypalProduct) {
|
||||
$checked = $paypalProduct->disabled == false ? "checked" : "";
|
||||
->addColumn('disabled', function (CreditProduct $creditProduct) {
|
||||
$checked = $creditProduct->disabled == false ? "checked" : "";
|
||||
return '
|
||||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.store.disable', $paypalProduct->id) . '">
|
||||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.store.disable', $creditProduct->id) . '">
|
||||
' . csrf_field() . '
|
||||
' . method_field("PATCH") . '
|
||||
<div class="custom-control custom-switch">
|
||||
<input ' . $checked . ' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch' . $paypalProduct->id . '">
|
||||
<label class="custom-control-label" for="switch' . $paypalProduct->id . '"></label>
|
||||
<input ' . $checked . ' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch' . $creditProduct->id . '">
|
||||
<label class="custom-control-label" for="switch' . $creditProduct->id . '"></label>
|
||||
</div>
|
||||
</form>
|
||||
';
|
||||
})
|
||||
->editColumn('created_at', function (PaypalProduct $paypalProduct) {
|
||||
return $paypalProduct->created_at ? $paypalProduct->created_at->diffForHumans() : '';
|
||||
->editColumn('created_at', function (CreditProduct $creditProduct) {
|
||||
return $creditProduct->created_at ? $creditProduct->created_at->diffForHumans() : '';
|
||||
})
|
||||
->editColumn('price', function (PaypalProduct $paypalProduct) {
|
||||
return $paypalProduct->formatToCurrency($paypalProduct->price);
|
||||
->editColumn('price', function (CreditProduct $creditProduct) {
|
||||
return $creditProduct->formatToCurrency($creditProduct->price);
|
||||
})
|
||||
->rawColumns(['actions', 'disabled'])
|
||||
->make();
|
|
@ -7,9 +7,10 @@ use App\Http\Controllers\Controller;
|
|||
use App\Models\Configuration;
|
||||
use App\Models\InvoiceSettings;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaypalProduct;
|
||||
use App\Models\CreditProduct;
|
||||
use App\Models\User;
|
||||
use App\Notifications\InvoiceNotification;
|
||||
use App\Notifications\ConfirmPaymentNotification;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
|
@ -29,6 +30,8 @@ use PayPalCheckoutSdk\Core\SandboxEnvironment;
|
|||
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
|
||||
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
|
||||
use PayPalHttp\HttpException;
|
||||
use Stripe\Stripe;
|
||||
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
|
@ -45,25 +48,25 @@ class PaymentController extends Controller
|
|||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function checkOut(Request $request, PaypalProduct $paypalProduct)
|
||||
public function checkOut(Request $request, CreditProduct $creditProduct)
|
||||
{
|
||||
return view('store.checkout')->with([
|
||||
'product' => $paypalProduct,
|
||||
'taxvalue' => $paypalProduct->getTaxValue(),
|
||||
'taxpercent' => $paypalProduct->getTaxPercent(),
|
||||
'total' => $paypalProduct->getTotalPrice()
|
||||
'product' => $creditProduct,
|
||||
'taxvalue' => $creditProduct->getTaxValue(),
|
||||
'taxpercent' => $creditProduct->getTaxPercent(),
|
||||
'total' => $creditProduct->getTotalPrice()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param PaypalProduct $paypalProduct
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function pay(Request $request, PaypalProduct $paypalProduct)
|
||||
public function PaypalPay(Request $request, CreditProduct $creditProduct)
|
||||
{
|
||||
$request = new OrdersCreateRequest();
|
||||
$request->prefer('return=representation');
|
||||
|
@ -72,28 +75,28 @@ class PaymentController extends Controller
|
|||
"purchase_units" => [
|
||||
[
|
||||
"reference_id" => uniqid(),
|
||||
"description" => $paypalProduct->description,
|
||||
"description" => $creditProduct->description,
|
||||
"amount" => [
|
||||
"value" => $paypalProduct->getTotalPrice(),
|
||||
'currency_code' => strtoupper($paypalProduct->currency_code),
|
||||
"value" => $creditProduct->getTotalPrice(),
|
||||
'currency_code' => strtoupper($creditProduct->currency_code),
|
||||
'breakdown' => [
|
||||
'item_total' =>
|
||||
[
|
||||
'currency_code' => strtoupper($paypalProduct->currency_code),
|
||||
'value' => $paypalProduct->price,
|
||||
'currency_code' => strtoupper($creditProduct->currency_code),
|
||||
'value' => $creditProduct->price,
|
||||
],
|
||||
'tax_total' =>
|
||||
[
|
||||
'currency_code' => strtoupper($paypalProduct->currency_code),
|
||||
'value' => $paypalProduct->getTaxValue(),
|
||||
'currency_code' => strtoupper($creditProduct->currency_code),
|
||||
'value' => $creditProduct->getTaxValue(),
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
"application_context" => [
|
||||
"cancel_url" => route('payment.cancel'),
|
||||
"return_url" => route('payment.success', ['product' => $paypalProduct->id]),
|
||||
"cancel_url" => route('payment.Cancel'),
|
||||
"return_url" => route('payment.PaypalSuccess', ['product' => $creditProduct->id]),
|
||||
'brand_name' => config('app.name', 'Laravel'),
|
||||
'shipping_preference' => 'NO_SHIPPING'
|
||||
]
|
||||
|
@ -112,7 +115,6 @@ class PaymentController extends Controller
|
|||
echo $ex->statusCode;
|
||||
dd(json_decode($ex->getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,8 +123,8 @@ class PaymentController extends Controller
|
|||
protected function getPayPalClient()
|
||||
{
|
||||
$environment = env('APP_ENV') == 'local'
|
||||
? new SandboxEnvironment($this->getClientId(), $this->getClientSecret())
|
||||
: new ProductionEnvironment($this->getClientId(), $this->getClientSecret());
|
||||
? new SandboxEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret())
|
||||
: new ProductionEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret());
|
||||
|
||||
return new PayPalHttpClient($environment);
|
||||
}
|
||||
|
@ -130,7 +132,7 @@ class PaymentController extends Controller
|
|||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientId()
|
||||
protected function getPaypalClientId()
|
||||
{
|
||||
return env('APP_ENV') == 'local' ? env('PAYPAL_SANDBOX_CLIENT_ID') : env('PAYPAL_CLIENT_ID');
|
||||
}
|
||||
|
@ -138,7 +140,7 @@ class PaymentController extends Controller
|
|||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret()
|
||||
protected function getPaypalClientSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local' ? env('PAYPAL_SANDBOX_SECRET') : env('PAYPAL_SECRET');
|
||||
}
|
||||
|
@ -146,10 +148,11 @@ class PaymentController extends Controller
|
|||
/**
|
||||
* @param Request $laravelRequest
|
||||
*/
|
||||
public function success(Request $laravelRequest)
|
||||
public function PaypalSuccess(Request $laravelRequest)
|
||||
{
|
||||
/** @var PaypalProduct $paypalProduct */
|
||||
$paypalProduct = PaypalProduct::findOrFail($laravelRequest->input('product'));
|
||||
/** @var CreditProduct $creditProduct */
|
||||
$creditProduct = CreditProduct::findOrFail($laravelRequest->input('product'));
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
|
@ -161,7 +164,7 @@ class PaymentController extends Controller
|
|||
if ($response->statusCode == 201 || $response->statusCode == 200) {
|
||||
|
||||
//update credits
|
||||
$user->increment('credits', $paypalProduct->quantity);
|
||||
$user->increment('credits', $creditProduct->quantity);
|
||||
|
||||
//update server limit
|
||||
if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
|
||||
|
@ -179,21 +182,316 @@ class PaymentController extends Controller
|
|||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => $response->result->id,
|
||||
'payer_id' => $laravelRequest->input('PayerID'),
|
||||
'payment_method' => 'paypal',
|
||||
'type' => 'Credits',
|
||||
'status' => $response->result->status,
|
||||
'amount' => $paypalProduct->quantity,
|
||||
'price' => $paypalProduct->price,
|
||||
'tax_value' => $paypalProduct->getTaxValue(),
|
||||
'tax_percent' => $paypalProduct->getTaxPercent(),
|
||||
'total_price' => $paypalProduct->getTotalPrice(),
|
||||
'currency_code' => $paypalProduct->currency_code,
|
||||
'payer' => json_encode($response->result->payer),
|
||||
'status' => 'paid',
|
||||
'amount' => $creditProduct->quantity,
|
||||
'price' => $creditProduct->price,
|
||||
'tax_value' => $creditProduct->getTaxValue(),
|
||||
'tax_percent' => $creditProduct->getTaxPercent(),
|
||||
'total_price' => $creditProduct->getTotalPrice(),
|
||||
'currency_code' => $creditProduct->currency_code,
|
||||
'credit_product_id' => $creditProduct->id,
|
||||
]);
|
||||
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
$this->createInvoice($user, $payment, 'paid');
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function Cancel(Request $request)
|
||||
{
|
||||
return redirect()->route('store.index')->with('success', 'Payment was Canceled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param CreditProduct $creditProduct
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function StripePay(Request $request, CreditProduct $creditProduct)
|
||||
{
|
||||
$stripeClient = $this->getStripeClient();
|
||||
|
||||
|
||||
$request = $stripeClient->checkout->sessions->create([
|
||||
'line_items' => [
|
||||
[
|
||||
'price_data' => [
|
||||
'currency' => $creditProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => $creditProduct->display,
|
||||
'description' => $creditProduct->description,
|
||||
],
|
||||
'unit_amount_decimal' => round($creditProduct->price * 100, 2),
|
||||
],
|
||||
'quantity' => 1,
|
||||
],
|
||||
[
|
||||
'price_data' => [
|
||||
'currency' => $creditProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => 'Product Tax',
|
||||
'description' => $creditProduct->getTaxPercent() . "%",
|
||||
],
|
||||
'unit_amount_decimal' => round($creditProduct->getTaxValue(), 2) * 100,
|
||||
],
|
||||
'quantity' => 1,
|
||||
]
|
||||
],
|
||||
|
||||
'mode' => 'payment',
|
||||
"payment_method_types" => str_getcsv(env('STRIPE_METHODS')),
|
||||
'success_url' => route('payment.StripeSuccess', ['product' => $creditProduct->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 CreditProduct $creditProduct */
|
||||
$creditProduct = CreditProduct::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 credits
|
||||
$user->increment('credits', $creditProduct->quantity);
|
||||
|
||||
//update server limit
|
||||
if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
|
||||
if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
|
||||
$user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
|
||||
}
|
||||
}
|
||||
|
||||
//update role
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
}
|
||||
|
||||
//store paid payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'payment_method' => 'stripe',
|
||||
'type' => 'Credits',
|
||||
'status' => 'paid',
|
||||
'amount' => $creditProduct->quantity,
|
||||
'price' => $creditProduct->price,
|
||||
'tax_value' => $creditProduct->getTaxValue(),
|
||||
'total_price' => $creditProduct->getTotalPrice(),
|
||||
'tax_percent' => $creditProduct->getTaxPercent(),
|
||||
'currency_code' => $creditProduct->currency_code,
|
||||
'credit_product_id' => $creditProduct->id,
|
||||
]);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
$this->createInvoice($user, $payment, 'paid');
|
||||
|
||||
//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' => 'Credits',
|
||||
'status' => 'processing',
|
||||
'amount' => $creditProduct->quantity,
|
||||
'price' => $creditProduct->price,
|
||||
'tax_value' => $creditProduct->getTaxValue(),
|
||||
'total_price' => $creditProduct->getTotalPrice(),
|
||||
'tax_percent' => $creditProduct->getTaxPercent(),
|
||||
'currency_code' => $creditProduct->currency_code,
|
||||
'credit_product_id' => $creditProduct->id,
|
||||
]);
|
||||
|
||||
$this->createInvoice($user, $payment, 'processing');
|
||||
|
||||
//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') {
|
||||
// Increment User Credits
|
||||
$user->increment('credits', $payment->amount);
|
||||
|
||||
//update server limit
|
||||
if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
|
||||
if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
|
||||
$user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
|
||||
}
|
||||
}
|
||||
|
||||
//update role
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
}
|
||||
|
||||
//update payment db entry status
|
||||
$payment->update(['status' => 'paid']);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
$this->createInvoice($user, $payment, 'paid');
|
||||
}
|
||||
} 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'
|
||||
? env('STRIPE_TEST_SECRET')
|
||||
: env('STRIPE_SECRET');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getStripeEndpointSecret()
|
||||
{
|
||||
return env('APP_ENV') == 'local'
|
||||
? env('STRIPE_ENDPOINT_TEST_SECRET')
|
||||
: env('STRIPE_ENDPOINT_SECRET');
|
||||
}
|
||||
|
||||
|
||||
protected function createInvoice($user, $payment, $paymentStatus)
|
||||
{
|
||||
$creditProduct = CreditProduct::where('id', $payment->credit_product_id)->first();
|
||||
//create invoice
|
||||
$lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
|
||||
$newInvoiceID = $lastInvoiceID + 1;
|
||||
|
@ -220,8 +518,8 @@ class PaymentController extends Controller
|
|||
],
|
||||
]);
|
||||
$item = (new InvoiceItem())
|
||||
->title($paypalProduct->description)
|
||||
->pricePerUnit($paypalProduct->price);
|
||||
->title($creditProduct->description)
|
||||
->pricePerUnit($creditProduct->price);
|
||||
|
||||
$invoice = Invoice::make()
|
||||
->template('controlpanel')
|
||||
|
@ -229,10 +527,10 @@ class PaymentController extends Controller
|
|||
->buyer($customer)
|
||||
->seller($seller)
|
||||
->discountByPercent(0)
|
||||
->taxRate(floatval($paypalProduct->getTaxPercent()))
|
||||
->taxRate(floatval($creditProduct->getTaxPercent()))
|
||||
->shipping(0)
|
||||
->addItem($item)
|
||||
->status(__('Paid'))
|
||||
->status(__($paymentStatus))
|
||||
->series(now()->format('mY'))
|
||||
->delimiter("-")
|
||||
->sequence($newInvoiceID)
|
||||
|
@ -241,6 +539,7 @@ class PaymentController extends Controller
|
|||
if (file_exists($logoPath)) {
|
||||
$invoice->logo($logoPath);
|
||||
}
|
||||
|
||||
//Save the invoice in "storage\app\invoice\USER_ID\YEAR"
|
||||
$invoice->filename = $invoice->getSerialNumber() . '.pdf';
|
||||
$invoice->render();
|
||||
|
@ -255,41 +554,8 @@ class PaymentController extends Controller
|
|||
|
||||
//Send Invoice per Mail
|
||||
$user->notify(new InvoiceNotification($invoice, $user, $payment));
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
return redirect()->route('store.index')->with('success', __('Payment was Canceled'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return JsonResponse|mixed
|
||||
* @throws Exception
|
||||
|
@ -308,9 +574,13 @@ class PaymentController extends Controller
|
|||
->editColumn('tax_value', function (Payment $payment) {
|
||||
return $payment->formatToCurrency($payment->tax_value);
|
||||
})
|
||||
->editColumn('tax_percent', function (Payment $payment) {
|
||||
return $payment->tax_percent . ' %';
|
||||
})
|
||||
->editColumn('total_price', function (Payment $payment) {
|
||||
return $payment->formatToCurrency($payment->total_price);
|
||||
})
|
||||
|
||||
->editColumn('created_at', function (Payment $payment) {
|
||||
return $payment->created_at ? $payment->created_at->diffForHumans() : '';
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Configuration;
|
||||
use App\Models\PaypalProduct;
|
||||
use App\Models\CreditProduct;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class StoreController extends Controller
|
||||
|
@ -11,10 +11,13 @@ class StoreController extends Controller
|
|||
/** Display a listing of the resource. */
|
||||
public function index()
|
||||
{
|
||||
$isPaypalSetup = false;
|
||||
if (env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID')) $isPaypalSetup = true;
|
||||
if (env('APP_ENV', 'local') == 'local') $isPaypalSetup = true;
|
||||
$isPaymentSetup = false;
|
||||
|
||||
if (
|
||||
env('APP_ENV') == 'local' ||
|
||||
env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID') ||
|
||||
env('STRIPE_SECRET') && env('STRIPE_ENDPOINT_SECRET') && env('STRIPE_METHODS')
|
||||
) $isPaymentSetup = true;
|
||||
|
||||
//Required Verification for creating an server
|
||||
if (Configuration::getValueByKey('FORCE_EMAIL_VERIFICATION', false) === 'true' && !Auth::user()->hasVerifiedEmail()) {
|
||||
|
@ -27,8 +30,8 @@ class StoreController extends Controller
|
|||
}
|
||||
|
||||
return view('store.index')->with([
|
||||
'products' => PaypalProduct::where('disabled', '=', false)->orderBy('price', 'asc')->get(),
|
||||
'isPaypalSetup' => $isPaypalSetup
|
||||
'products' => CreditProduct::where('disabled', '=', false)->orderBy('price', 'asc')->get(),
|
||||
'isPaymentSetup' => $isPaymentSetup,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware
|
|||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
'payment/StripeWebhooks'
|
||||
];
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use NumberFormatter;
|
|||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use App\Models\Configuration;
|
||||
|
||||
class PaypalProduct extends Model
|
||||
class CreditProduct extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
/**
|
||||
|
@ -33,10 +33,10 @@ class PaypalProduct extends Model
|
|||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function (PaypalProduct $paypalProduct) {
|
||||
static::creating(function (CreditProduct $creditProduct) {
|
||||
$client = new Client();
|
||||
|
||||
$paypalProduct->{$paypalProduct->getKeyName()} = $client->generateId($size = 21);
|
||||
$creditProduct->{$creditProduct->getKeyName()} = $client->generateId($size = 21);
|
||||
});
|
||||
}
|
||||
|
|
@ -23,8 +23,7 @@ class Payment extends Model
|
|||
'id',
|
||||
'user_id',
|
||||
'payment_id',
|
||||
'payer_id',
|
||||
'payer',
|
||||
'payment_method',
|
||||
'status',
|
||||
'type',
|
||||
'amount',
|
||||
|
@ -33,6 +32,7 @@ class Payment extends Model
|
|||
'total_price',
|
||||
'tax_percent',
|
||||
'currency_code',
|
||||
'credit_product_id',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "laravel/laravel",
|
||||
"name": "cpgg/dashboard",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"description": "A billing and control panel made for Pterodactyl.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
|
@ -26,6 +26,7 @@
|
|||
"spatie/laravel-activitylog": "^3.16",
|
||||
"spatie/laravel-query-builder": "^3.6",
|
||||
"spatie/laravel-validation-rules": "^3.0",
|
||||
"stripe/stripe-php": "^7.107",
|
||||
"yajra/laravel-datatables-oracle": "~9.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
2339
composer.lock
generated
2339
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
|
|||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaypalProductsTable extends Migration
|
||||
class CreateCreditProductsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
|
@ -13,7 +13,7 @@ class CreatePaypalProductsTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('paypal_products', function (Blueprint $table) {
|
||||
Schema::create('credit_products', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('type');
|
||||
$table->decimal('price')->default(0);
|
||||
|
@ -32,6 +32,6 @@ class CreatePaypalProductsTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('paypal_products');
|
||||
Schema::dropIfExists('credit_products');
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
|
|||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddDisplayToPaypalProductsTable extends Migration
|
||||
class AddDisplayToCreditProductsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
|
@ -13,7 +13,7 @@ class AddDisplayToPaypalProductsTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('paypal_products', function (Blueprint $table) {
|
||||
Schema::table('credit_products', function (Blueprint $table) {
|
||||
$table->string('display');
|
||||
});
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class AddDisplayToPaypalProductsTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('paypal_products', function (Blueprint $table) {
|
||||
Schema::table('credit_products', function (Blueprint $table) {
|
||||
$table->dropColumn('display');
|
||||
});
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
|
||||
class UpdateToPaymentsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('payments', function (Blueprint $table) {
|
||||
$table->string('payment_method');
|
||||
$table->dropColumn('payer');
|
||||
$table->dropColumn('payer_id');
|
||||
$table->string('credit_product_id');
|
||||
});
|
||||
|
||||
DB::statement('UPDATE payments SET payment_method="paypal"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('payments', function (Blueprint $table) {
|
||||
$table->dropColumn('payment_method');
|
||||
$table->string('payer_id')->nullable();
|
||||
$table->text('payer')->nullable();
|
||||
$table->dropColumn('credit_product_id');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
namespace Database\Seeders;
|
||||
|
||||
use Database\Seeders\Seeds\ProductSeeder;
|
||||
use Database\Seeders\Seeds\PaypalProductSeeder;
|
||||
use Database\Seeders\Seeds\CreditProductSeeder;
|
||||
use Database\Seeders\Seeds\ApplicationApiSeeder;
|
||||
use Database\Seeders\Seeds\UsefulLinksSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
@ -19,7 +19,7 @@ class ExampleItemsSeeder extends Seeder
|
|||
{
|
||||
$this->call([
|
||||
ProductSeeder::class,
|
||||
PaypalProductSeeder::class,
|
||||
CreditProductSeeder::class,
|
||||
ApplicationApiSeeder::class,
|
||||
UsefulLinksSeeder::class
|
||||
]);
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace Database\Seeders\Seeds;
|
||||
|
||||
use App\Models\PaypalProduct;
|
||||
use App\Models\CreditProduct;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class PaypalProductSeeder extends Seeder
|
||||
class CreditProductSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
|
@ -14,7 +14,7 @@ class PaypalProductSeeder extends Seeder
|
|||
*/
|
||||
public function run()
|
||||
{
|
||||
PaypalProduct::create([
|
||||
CreditProduct::create([
|
||||
'type' => 'Credits',
|
||||
'display' => '350',
|
||||
'description' => 'Adds 350 credits to your account',
|
||||
|
@ -24,7 +24,7 @@ class PaypalProductSeeder extends Seeder
|
|||
'disabled' => false,
|
||||
]);
|
||||
|
||||
PaypalProduct::create([
|
||||
CreditProduct::create([
|
||||
'type' => 'Credits',
|
||||
'display' => '875 + 125',
|
||||
'description' => 'Adds 1000 credits to your account',
|
||||
|
@ -34,7 +34,7 @@ class PaypalProductSeeder extends Seeder
|
|||
'disabled' => false,
|
||||
]);
|
||||
|
||||
PaypalProduct::create([
|
||||
CreditProduct::create([
|
||||
'type' => 'Credits',
|
||||
'display' => '1750 + 250',
|
||||
'description' => 'Adds 2000 credits to your account',
|
||||
|
@ -44,7 +44,7 @@ class PaypalProductSeeder extends Seeder
|
|||
'disabled' => false,
|
||||
]);
|
||||
|
||||
PaypalProduct::create([
|
||||
CreditProduct::create([
|
||||
'type' => 'Credits',
|
||||
'display' => '3500 + 500',
|
||||
'description' => 'Adds 4000 credits to your account',
|
BIN
public/images/paypal_logo.png
Normal file
BIN
public/images/paypal_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
public/images/stripe_logo.png
Normal file
BIN
public/images/stripe_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
|
@ -95,8 +95,8 @@
|
|||
"This is what the user sees at checkout": "Dies ist die Beschreibung auf der Rechnung und was der Kunde beim kauf sieht",
|
||||
"Adds 1000 credits to your account": "Fügt deinem Account 1000 Credits hinzu",
|
||||
"Active": "Aktiv",
|
||||
"Paypal is not configured.": "Paypal ist nicht konfiguriert!",
|
||||
"To configure PayPal, head to the .env and add your PayPal’s client id and secret.": "Um Paypal zu konfigurieren, füge deine Paypal client ID und Secretkey in deine .env-Datei hinzu",
|
||||
"No payment method is configured.": "Keine Bezahlmethode konfiguriert!",
|
||||
"To configure the payment methods, head to the .env and add the required options for your prefered payment method.": "Um die Bezahlmethoden einzustellen, setze die passenden Variablen, für eine oder mehrere Bezahlmethoden, in der .env",
|
||||
"Useful Links": "Nützliche Links",
|
||||
"Icon class name": "Icon Klassen-Name",
|
||||
"You can find available free icons": "Hier gibt es kostenlose Icons",
|
||||
|
|
|
@ -106,8 +106,8 @@
|
|||
"Adds 1000 credits to your account": "Adds 1000 credits to your account",
|
||||
|
||||
"Active": "Active",
|
||||
"Paypal is not configured.": "Paypal is not configured.",
|
||||
"To configure PayPal, head to the .env and add your PayPal’s client id and secret.": "To configure PayPal, head to the .env and add your PayPal’s client id and secret.",
|
||||
"No payment method is configured.": "No payment method is configured.",
|
||||
"To configure the payment methods, head to the .env and add the required options for your prefered payment method.": "To configure the payment methods, head to the .env and add the required options for your prefered payment method.",
|
||||
|
||||
"Useful Links": "Useful Links",
|
||||
"Icon class name": "Icon class name",
|
||||
|
@ -366,6 +366,4 @@
|
|||
"Your Payment was successful!": "Your Payment was successful!",
|
||||
"Hello": "Hello",
|
||||
"Your payment was processed successfully!": "Your payment was processed successfully!"
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -34,15 +34,14 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('ID') }}</th>
|
||||
<th>{{__('User')}}</th>
|
||||
<th>{{ __('Type') }}</th>
|
||||
<th>{{ __('Amount') }}</th>
|
||||
<th>{{ __('Product Price') }}</th>
|
||||
<th>{{__('Tax')}}</th>
|
||||
<th>{{__('Tax')}}(%)</th>
|
||||
<th>{{ __('Tax Value') }}</th>
|
||||
<th>{{ __('Tax Percentage') }}</th>
|
||||
<th>{{ __('Total Price') }}</th>
|
||||
<th>{{__('Payment_ID')}}</th>
|
||||
<th>{{__('Payer_ID')}}</th>
|
||||
<th>{{ __('Payment ID') }}</th>
|
||||
<th>{{ __('Payment Method') }}</th>
|
||||
<th>{{ __('Created at') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -70,7 +69,6 @@
|
|||
ajax: "{{ route('admin.payments.datatable') }}",
|
||||
columns: [
|
||||
{data: 'id',name: 'payments.id'},
|
||||
{data: 'user', sortable: false},
|
||||
{data: 'type'},
|
||||
{data: 'amount'},
|
||||
{data: 'price'},
|
||||
|
@ -78,12 +76,12 @@
|
|||
{data: 'tax_percent'},
|
||||
{data: 'total_price'},
|
||||
{data: 'payment_id'},
|
||||
{data: 'payer_id'},
|
||||
{data: 'payment_method'},
|
||||
{data: 'created_at'},
|
||||
],
|
||||
fnDrawCallback: function(oSettings) {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.store.index') }}">{{ __('Store') }}</a></li>
|
||||
<li class="breadcrumb-item"><a class="text-muted"
|
||||
href="{{route('admin.store.edit' , $paypalProduct->id)}}">{{__('Edit')}}</a>
|
||||
href="{{ route('admin.store.edit', $creditProduct->id) }}">{{ __('Edit') }}</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
@ -30,18 +30,15 @@
|
|||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form action="{{route('admin.store.update' , $paypalProduct->id)}}" method="POST">
|
||||
<form action="{{ route('admin.store.update', $creditProduct->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" @if($paypalProduct->disabled) checked
|
||||
@endif name="disabled"
|
||||
class="custom-control-input custom-control-input-danger"
|
||||
id="switch1">
|
||||
<input type="checkbox" @if ($creditProduct->disabled) checked @endif name="disabled"
|
||||
class="custom-control-input custom-control-input-danger" id="switch1">
|
||||
<label class="custom-control-label" for="switch1">{{ __('Disabled') }} <i
|
||||
data-toggle="popover"
|
||||
data-trigger="hover"
|
||||
data-toggle="popover" data-trigger="hover"
|
||||
data-content="{{ __('Will hide this option from being selected') }}"
|
||||
class="fas fa-info-circle"></i></label>
|
||||
</div>
|
||||
|
@ -65,8 +62,8 @@
|
|||
<select required name="currency_code" id="currency_code"
|
||||
class="custom-select @error('name') is-invalid @enderror">
|
||||
@foreach ($currencyCodes as $code)
|
||||
<option @if($paypalProduct->currency_code == $code) selected
|
||||
@endif value="{{$code}}">{{$code}}</option>
|
||||
<option @if ($creditProduct->currency_code == $code) selected @endif value="{{ $code }}">
|
||||
{{ $code }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('currency_code')
|
||||
|
@ -82,13 +79,10 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="price">{{__('Price')}}</label>
|
||||
<input value="{{$paypalProduct->price}}" id="price" name="price"
|
||||
type="number"
|
||||
placeholder="10.00"
|
||||
step="any"
|
||||
class="form-control @error('price') is-invalid @enderror"
|
||||
required="required">
|
||||
<label for="price">Price</label>
|
||||
<input value="{{ $creditProduct->price }}" id="price" name="price" type="number"
|
||||
placeholder="10.00" step="any"
|
||||
class="form-control @error('price') is-invalid @enderror" required="required">
|
||||
@error('price')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
|
@ -97,12 +91,10 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="quantity">{{__('Quantity')}}</label>
|
||||
<input value="{{$paypalProduct->quantity}}" id="quantity" name="quantity"
|
||||
type="number"
|
||||
placeholder="1000"
|
||||
class="form-control @error('quantity') is-invalid @enderror"
|
||||
required="required">
|
||||
<label for="quantity">Quantity</label>
|
||||
<input value="{{ $creditProduct->quantity }}" id="quantity" name="quantity"
|
||||
type="number" placeholder="1000"
|
||||
class="form-control @error('quantity') is-invalid @enderror" required="required">
|
||||
@error('quantity')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
|
@ -114,11 +106,9 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="display">{{__('Display')}}</label>
|
||||
<input value="{{$paypalProduct->display}}" id="display" name="display"
|
||||
type="text"
|
||||
placeholder="750 + 250"
|
||||
class="form-control @error('display') is-invalid @enderror"
|
||||
<label for="display">Display</label>
|
||||
<input value="{{ $creditProduct->display }}" id="display" name="display" type="text"
|
||||
placeholder="750 + 250" class="form-control @error('display') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('display')
|
||||
<div class="invalid-feedback">
|
||||
|
@ -131,12 +121,10 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">{{__('Description')}}</label>
|
||||
<input value="{{$paypalProduct->description}}" id="description" name="description"
|
||||
type="text"
|
||||
placeholder="{{__('Adds 1000 credits to your account')}}"
|
||||
class="form-control @error('description') is-invalid @enderror"
|
||||
required="required">
|
||||
<label for="description">Description</label>
|
||||
<input value="{{ $creditProduct->description }}" id="description" name="description"
|
||||
type="text" placeholder="{{ __('Adds 1000 credits to your account') }}"
|
||||
class="form-control @error('description') is-invalid @enderror" required="required">
|
||||
@error('description')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
|
|
|
@ -26,12 +26,14 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
@if($isPaypalSetup == false)
|
||||
@if ($isPaymentSetup == false)
|
||||
<div class="callout callout-danger">
|
||||
<h4>{{__('Paypal is not configured.')}}</h4>
|
||||
<p>{{__('To configure PayPal, head to the .env and add your PayPal’s client id and secret.')}}</p>
|
||||
<h4>{{ __('No payment method is configured.') }}</h4>
|
||||
<p>{{ __('To configure the payment methods, head to the .env and add the required options for your prefered payment method.') }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -81,21 +83,37 @@
|
|||
document.addEventListener("DOMContentLoaded", function() {
|
||||
$('#datatable').DataTable({
|
||||
language: {
|
||||
url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{config("app.datatable_locale")}}.json'
|
||||
url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{ config('app.datatable_locale') }}.json'
|
||||
},
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
stateSave: true,
|
||||
ajax: "{{ route('admin.store.datatable') }}",
|
||||
order: [[ 2, "desc" ]],
|
||||
columns: [
|
||||
{data: 'disabled'},
|
||||
{data: 'type'},
|
||||
{data: 'price'},
|
||||
{data: 'display'},
|
||||
{data: 'description'},
|
||||
{data: 'created_at'},
|
||||
{data: 'actions', sortable: false},
|
||||
order: [
|
||||
[2, "desc"]
|
||||
],
|
||||
columns: [{
|
||||
data: 'disabled'
|
||||
},
|
||||
{
|
||||
data: 'type'
|
||||
},
|
||||
{
|
||||
data: 'price'
|
||||
},
|
||||
{
|
||||
data: 'display'
|
||||
},
|
||||
{
|
||||
data: 'description'
|
||||
},
|
||||
{
|
||||
data: 'created_at'
|
||||
},
|
||||
{
|
||||
data: 'actions',
|
||||
sortable: false
|
||||
},
|
||||
],
|
||||
fnDrawCallback: function(oSettings) {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
</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="text-muted" href="{{route('store.index')}}">{{__('Store')}}</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>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,7 +23,7 @@
|
|||
<!-- END CONTENT HEADER -->
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<section class="content">
|
||||
<section x-data="serverApp()" x-init="$watch('paymentMethod', value => setPaymentRoute(value))" class="content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
|
@ -34,7 +37,8 @@
|
|||
<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>
|
||||
<small class="float-right">{{ __('Date') }}:
|
||||
{{ Carbon\Carbon::now()->isoFormat('LL') }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
|
@ -44,7 +48,7 @@
|
|||
<div class="col-sm-4 invoice-col">
|
||||
{{ __('To') }}
|
||||
<address>
|
||||
<strong>{{config('app.name' , 'Laravel')}}</strong><br>
|
||||
<strong>{{ config('app.name', 'Controlpanel.GG') }}</strong><br>
|
||||
{{ __('Email') }}: {{ env('PAYPAL_EMAIL', env('MAIL_FROM_NAME')) }}
|
||||
</address>
|
||||
</div>
|
||||
|
@ -81,7 +85,9 @@
|
|||
<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><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>
|
||||
|
@ -97,15 +103,33 @@
|
|||
<div class="col-6">
|
||||
<p class="lead">{{ __('Payment Methods') }}:</p>
|
||||
|
||||
<img src="https://www.paypalobjects.com/digitalassets/c/website/logo/full-text/pp_fc_hl.svg" alt="Paypal">
|
||||
<div>
|
||||
@if (env('PAYPAL_SANDBOX_SECRET') || env('PAYPAL_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 (env('STRIPE_TEST_SECRET') || env('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>
|
||||
|
||||
<p class="text-muted well well-sm shadow-none" style="margin-top: 10px;">
|
||||
{{__('By purchasing this product you agree and accept our terms of service')}}</a>
|
||||
</p>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-6">
|
||||
<p class="lead">{{__('Amount due')}} {{Carbon\Carbon::now()->isoFormat('LL')}}</p>
|
||||
<p class="lead">{{ __('Amount Due') }}
|
||||
{{ Carbon\Carbon::now()->isoFormat('LL') }}</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
|
@ -135,7 +159,10 @@
|
|||
<!-- this row will not appear when printing -->
|
||||
<div class="row no-print">
|
||||
<div class="col-12">
|
||||
<a href="{{route('payment.pay' , $product->id)}}" type="button" class="btn btn-success float-right"><i class="far fa-credit-card mr-2"></i> {{__('Submit Payment')}}
|
||||
<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>
|
||||
|
@ -143,10 +170,35 @@
|
|||
<!-- /.invoice -->
|
||||
</div><!-- /.col -->
|
||||
</div><!-- /.row -->
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<!-- END CONTENT -->
|
||||
|
||||
<script>
|
||||
function serverApp() {
|
||||
return {
|
||||
//loading
|
||||
paymentMethod: '',
|
||||
paymentRoute: '',
|
||||
|
||||
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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@extends('layouts.main')
|
||||
<?php use App\Models\PaypalProduct; ?>
|
||||
<?php use App\Models\CreditProduct; ?>
|
||||
|
||||
@section('content')
|
||||
<!-- CONTENT HEADER -->
|
||||
|
@ -11,8 +11,10 @@
|
|||
</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="text-muted" href="{{route('store.index')}}">{{__('Store')}}</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>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,7 +32,7 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
@if($isPaypalSetup && $products->count() > 0)
|
||||
@if ($isPaymentSetup && $products->count() > 0)
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
|
@ -47,13 +49,16 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php /** @var $product PaypalProduct */?>
|
||||
<?php /** @var $product CreditProduct */
|
||||
?>
|
||||
@foreach ($products as $product)
|
||||
<tr>
|
||||
<td>{{ $product->formatToCurrency($product->price) }}</td>
|
||||
<td>{{strtolower($product->type) == 'credits' ? CREDITS_DISPLAY_NAME : $product->type}}</td>
|
||||
<td>{{ strtolower($product->type) == 'credits' ? CREDITS_DISPLAY_NAME : $product->type }}
|
||||
</td>
|
||||
<td><i class="fa fa-coins mr-2"></i>{{ $product->display }}</td>
|
||||
<td><a href="{{route('checkout' , $product->id)}}" class="btn btn-info">{{__('Purchase')}}</a>
|
||||
<td><a href="{{ route('checkout', $product->id) }}"
|
||||
class="btn btn-info">{{ __('Purchase') }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
|
@ -5,7 +5,7 @@ use App\Http\Controllers\Admin\ApplicationApiController;
|
|||
use App\Http\Controllers\Admin\ConfigurationController;
|
||||
use App\Http\Controllers\Admin\OverViewController;
|
||||
use App\Http\Controllers\Admin\PaymentController;
|
||||
use App\Http\Controllers\Admin\PaypalProductController;
|
||||
use App\Http\Controllers\Admin\CreditProductController;
|
||||
use App\Http\Controllers\Admin\ProductController;
|
||||
use App\Http\Controllers\Admin\ServerController as AdminServerController;
|
||||
use App\Http\Controllers\Admin\SettingsController;
|
||||
|
@ -40,6 +40,9 @@ 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::middleware(['auth', 'checkSuspended'])->group(function () {
|
||||
#resend verification email
|
||||
Route::get('/email/verification-notification', function (Request $request) {
|
||||
|
@ -61,10 +64,14 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
|
|||
Route::get('/products/products/{egg?}/{node?}', [FrontProductController::class, 'getProductsBasedOnNode'])->name('products.products.node');
|
||||
|
||||
#payments
|
||||
Route::get('checkout/{paypalProduct}', [PaymentController::class, 'checkOut'])->name('checkout');
|
||||
Route::get('payment/success', [PaymentController::class, 'success'])->name('payment.success');
|
||||
Route::get('payment/cancel', [PaymentController::class, 'cancel'])->name('payment.cancel');
|
||||
Route::get('payment/pay/{paypalProduct}', [PaymentController::class, 'pay'])->name('payment.pay');
|
||||
Route::get('checkout/{creditProduct}', [PaymentController::class, 'checkOut'])->name('checkout');
|
||||
Route::get('payment/PaypalPay/{creditProduct}', [PaymentController::class, 'PaypalPay'])->name('payment.PaypalPay');
|
||||
Route::get('payment/PaypalSuccess', [PaymentController::class, 'PaypalSuccess'])->name('payment.PaypalSuccess');
|
||||
Route::get('payment/StripePay/{creditProduct}', [PaymentController::class, 'StripePay'])->name('payment.StripePay');
|
||||
Route::get('payment/StripeSuccess', [PaymentController::class, 'StripeSuccess'])->name('payment.StripeSuccess');
|
||||
Route::get('payment/Cancel', [PaymentController::class, 'Cancel'])->name('payment.Cancel');
|
||||
|
||||
|
||||
|
||||
Route::get('users/logbackin', [UserController::class, 'logBackIn'])->name('users.logbackin');
|
||||
|
||||
|
@ -100,10 +107,10 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
|
|||
Route::patch('products/disable/{product}', [ProductController::class, 'disable'])->name('products.disable');
|
||||
Route::resource('products', ProductController::class);
|
||||
|
||||
Route::get('store/datatable', [PaypalProductController::class, 'datatable'])->name('store.datatable');
|
||||
Route::patch('store/disable/{paypalProduct}', [PaypalProductController::class, 'disable'])->name('store.disable');
|
||||
Route::resource('store', PaypalProductController::class)->parameters([
|
||||
'store' => 'paypalProduct',
|
||||
Route::get('store/datatable', [CreditProductController::class, 'datatable'])->name('store.datatable');
|
||||
Route::patch('store/disable/{creditProduct}', [CreditProductController::class, 'disable'])->name('store.disable');
|
||||
Route::resource('store', CreditProductController::class)->parameters([
|
||||
'store' => 'creditProduct',
|
||||
]);
|
||||
|
||||
Route::get('payments/datatable', [PaymentController::class, 'datatable'])->name('payments.datatable');
|
||||
|
|
Loading…
Reference in a new issue