File "SubscriptionService.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Services/SubscriptionService.php
File size: 39.1 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Services;
use App\Models\PaymentConfiguration;
use App\Models\Students;
use App\Models\Subscription;
use App\Models\SubscriptionBill;
use App\Models\User;
use App\Repositories\AddonSubscription\AddonSubscriptionInterface;
use App\Repositories\Package\PackageInterface;
use App\Repositories\PaymentTransaction\PaymentTransactionInterface;
use App\Repositories\Staff\StaffInterface;
use App\Repositories\Subscription\SubscriptionInterface;
use App\Repositories\SubscriptionBill\SubscriptionBillInterface;
use App\Repositories\SubscriptionFeature\SubscriptionFeatureInterface;
use App\Repositories\User\UserInterface;
use Auth;
use Carbon\Carbon;
use DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Stripe\Stripe;
use Stripe\Checkout\Session as StripeSession;
use Unicodeveloper\Paystack\Facades\Paystack;
use Illuminate\Support\Facades\Log;
use App\Services\ResponseService;
class SubscriptionService
{
private UserInterface $user;
private SubscriptionInterface $subscription;
private PackageInterface $package;
private SubscriptionFeatureInterface $subscriptionFeature;
private CachingService $cache;
private AddonSubscriptionInterface $addonSubscription;
private StaffInterface $staff;
private SubscriptionBillInterface $subscriptionBill;
private PaymentTransactionInterface $paymentTransaction;
public function __construct(UserInterface $user, SubscriptionInterface $subscription, PackageInterface $package, SubscriptionFeatureInterface $subscriptionFeature, CachingService $cache, AddonSubscriptionInterface $addonSubscription, StaffInterface $staff, SubscriptionBillInterface $subscriptionBill, PaymentTransactionInterface $paymentTransaction)
{
$this->user = $user;
$this->subscription = $subscription;
$this->package = $package;
$this->subscriptionFeature = $subscriptionFeature;
$this->cache = $cache;
$this->addonSubscription = $addonSubscription;
$this->staff = $staff;
$this->subscriptionBill = $subscriptionBill;
$this->paymentTransaction = $paymentTransaction;
}
/**
* @param $package_id
* @param $school_id
* @param $isCurrentPlan
* @return Model|null
*/
public function createSubscription($package_id, $school_id = null, $subscription_id = null, $isCurrentPlan = null)
{
// package_id => Create that package
// school_id => if super admin can assign package, then school id is compulsory
// subscription_id => if school admin already set upcoming plan update only that plan
// isCurrentPlan => school admin can set current plan & upcoming plan also
$settings = $this->cache->getSystemSettings();
$package = $this->package->builder()->with('package_feature')->where('id', $package_id)->first();
$end_date = '';
if (!$school_id) {
$school_id = Auth::user()->school_id;
}
if ($package->is_trial) {
$end_date = Carbon::now()->addDays(($settings['trial_days']))->format('Y-m-d');
} else {
$end_date = Carbon::now()->addDays(($package->days - 1))->format('Y-m-d');
}
$start_date = Carbon::now()->format('Y-m-d');
// If not current subscription plan
if (!$isCurrentPlan) {
// Attempt to get the current active subscription
$current_subscription = $this->active_subscription($school_id);
// Check if a current subscription was found
if ($current_subscription) {
$start_date = Carbon::parse($current_subscription->end_date)->addDays()->format('Y-m-d');
$end_date = Carbon::parse($start_date)->addDays(($package->days - 1))->format('Y-m-d');
} else {
// Handle the case where there is no active subscription
Log::warning("No active subscription found for school_id: {$school_id}");
// You might want to set default dates or handle this case differently
$start_date = Carbon::now()->format('Y-m-d');
$end_date = Carbon::now()->addDays($package->days - 1)->format('Y-m-d');
}
}
$subscription_data = [
'package_id' => $package->id,
'name' => $package->name,
'student_charge' => $package->student_charge,
'staff_charge' => $package->staff_charge,
'start_date' => $start_date,
'end_date' => $end_date,
'package_type' => $package->type,
'no_of_students' => $package->no_of_students,
'no_of_staffs' => $package->no_of_staffs,
'billing_cycle' => $package->days,
'school_id' => $school_id,
'charges' => $package->charges
];
// Check subscription update or create
// If school has already set upcoming plan
if ($subscription_id) {
$subscription = $this->subscription->update($subscription_id, $subscription_data);
} else {
$subscription = $this->subscription->create($subscription_data);
}
// If current subscription plan then set package features
if ($isCurrentPlan) {
$subscription_features = array();
foreach ($package->package_feature as $key => $feature) {
$subscription_features[] = [
'subscription_id' => $subscription->id,
'feature_id' => $feature->feature_id
];
}
$this->subscriptionFeature->upsert($subscription_features, ['subscription_id', 'feature_id'], ['subscription_id', 'feature_id']);
$this->cache->removeSchoolCache(config('constants.CACHE.SCHOOL.FEATURES'), $subscription->school_id);
// If prepaid plan generate bill first
if ($package->type == 0) {
$subscription_bill[] = [
'subscription_id' => $subscription->id,
'amount' => $package->charges,
'total_student' => $package->no_of_students,
'total_staff' => $package->no_of_staffs,
'due_date' => Carbon::now(),
'school_id' => $subscription->school_id
];
if (Auth::user() && !Auth::user()->hasRole('School Admin')) {
$billData = [
'user_id' => $subscription->school->admin_id,
'amount' => $package->charges,
'payment_gateway' => 'Cash',
'school_id' => $subscription->school_id,
'payment_status' => 'succeed'
];
$paymentTransaction = $this->paymentTransaction->create($billData);
$subscription_bill[0]['payment_transaction_id'] = $paymentTransaction->id;
}
// $subscription_bill = $this->subscriptionBill->create($subscription_bill);
SubscriptionBill::upsert($subscription_bill, ['subscription_id', 'school_id'], ['amount', 'total_student', 'total_staff', 'due_date']);
// return $subscription_bill = $this->subscriptionBill->upsert($subscription_bill,['subscription_id','school_id'],['amount','total_student','total_staff','due_date']);
}
}
return $subscription;
}
/**
* @param $generateBill
* @return Model|null
*/
public function createSubscriptionBill($subscription, $generateBill = null)
{
// GenerateBill [ null => Generate immediate bill, 1 => Generate regular bill ]
// Set school database connection for getting user counts
Config::set('database.connections.school.database', $subscription->school->database_name);
DB::purge('school');
DB::connection('school')->reconnect();
DB::setDefaultConnection('school');
// $students = User::on('school')->withTrashed()->where(function ($q) use ($subscription) {
// $q->whereBetween('deleted_at', [$subscription->start_date, $subscription->end_date]);
// })->orWhereNull('deleted_at')->role('Student')->where('school_id', $subscription->school_id)->count();
$students = Students::on('school')->whereHas('user', function ($q) use ($subscription) {
$q->withTrashed()->where(function ($q) use ($subscription) {
$q->whereBetween('deleted_at', [$subscription->start_date, $subscription->end_date]);
})->orWhereNull('deleted_at')->where('school_id', $subscription->school_id);
})->has('user')->count();
$staffs = $this->staff->builder()->whereHas('user', function ($q) use ($subscription) {
$q->where(function ($q) use ($subscription) {
$q->withTrashed()->whereBetween('deleted_at', [$subscription->start_date, $subscription->end_date])
->orWhereNull('deleted_at');
})->where('school_id', $subscription->school_id);
})->count();
DB::setDefaultConnection('mysql');
$today_date = Carbon::now()->format('Y-m-d');
$start_date = Carbon::parse($subscription->start_date);
if ($generateBill) {
$usage_days = $start_date->diffInDays($subscription->end_date) + 1;
} else {
$usage_days = $start_date->diffInDays($today_date) + 1;
}
$bill_cycle_days = $subscription->billing_cycle;
// Get addon total
$addons = $this->addonSubscription->builder()->where('subscription_id', $subscription->id)->sum('price');
$student_charges = (($usage_days * $subscription->student_charge) / $bill_cycle_days) * $students;
$staff_charges = (($usage_days * $subscription->staff_charge) / $bill_cycle_days) * $staffs;
$systemSettings = $this->cache->getSystemSettings();
$subscription_bill = [
'subscription_id' => $subscription->id,
'amount' => number_format(($student_charges + $staff_charges + $addons), 2, '.', ''),
'total_student' => $students,
'total_staff' => $staffs,
'due_date' => Carbon::now()->addDays($systemSettings['additional_billing_days'])->format('Y-m-d'),
'school_id' => $subscription->school_id
];
// Create bill for active plan
return $subscription_bill = $this->subscriptionBill->create($subscription_bill);
}
// Check subscription pending bills
public function subscriptionPendingBill()
{
$subscriptionBill = $this->subscriptionBill->builder()->whereHas('transaction', function ($q) {
$q->whereNot('payment_status', "succeed");
})->orDoesntHave('transaction')->where('school_id', Auth::user()->school_id)->whereNot('amount', 0)->first();
return $subscriptionBill;
}
// Stripe payment gateway
public function stripe_payment($subscriptionBill_id = null, $package_id = null, $type = null, $subscription_id = null, $isCurrentPlan = null)
{
try {
Log::info('Starting Stripe payment process');
$settings = app(CachingService::class)->getSystemSettings();
$name = 'Subscription';
$amount = 0;
if ($subscriptionBill_id) {
$subscriptionBill = $this->subscriptionBill->findById($subscriptionBill_id);
$name = $subscriptionBill->subscription->name;
$amount = $subscriptionBill->amount;
$package_id = -1;
Log::info('Processing subscription bill payment', [
'subscription_bill_id' => $subscriptionBill_id,
'subscription_name' => $name,
'amount' => $amount
]);
}
if ($package_id && $package_id != -1) {
$package = $this->package->findById($package_id);
$name = $package->name;
$amount = $package->charges;
$subscriptionBill_id = -1;
Log::info('Processing package payment', [
'package_id' => $package_id,
'package_name' => $name,
'amount' => $amount
]);
$name = $package->name;
}
if ($type == null) {
$type = -1;
}
if (!$subscription_id) {
$subscription_id = -1;
}
if (!$isCurrentPlan) {
$isCurrentPlan = -1;
}
// Get payment configuration
DB::setDefaultConnection('mysql');
$paymentConfiguration = PaymentConfiguration::where('school_id', null)
->where('payment_method', 'stripe')
->where('status', 1)
->first();
if (!$paymentConfiguration) {
Log::error('Stripe payment configuration not found or disabled');
return redirect()->back()->with('error', trans('Stripe payment is not available'));
}
$stripe_secret_key = $paymentConfiguration->secret_key ?? null;
if (empty($stripe_secret_key)) {
Log::error('Stripe secret key is missing');
return redirect()->back()->with('error', trans('Stripe API key is not configured'));
}
$currency = $paymentConfiguration->currency_code;
Log::info('Processing payment with currency: ' . $currency);
// Validate minimum amount
$checkAmount = $this->checkMinimumAmount(strtoupper($currency), $amount);
$checkAmount = (float) $checkAmount;
$checkAmount = round($checkAmount, 2);
Log::info('Validated amount: ' . $checkAmount . ' (' . $currency . ')');
// Validate that we have a product name
// if (empty($name)) {
// Log::error('Product name is empty for Stripe payment', [
// 'subscription_bill_id' => $subscriptionBill_id,
// 'package_id' => $package_id
// ]);
// return redirect()->back()->with('error', trans('Unable to process payment: Product name is missing'));
// }
// Stripe payment
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.stripe.com/v1/checkout/sessions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $stripe_secret_key,
'Content-Type: application/x-www-form-urlencoded',
'Stripe-Version: 2022-11-15'
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'line_items[0][price_data][currency]' => strtolower($currency),
'line_items[0][price_data][product_data][name]' => $name,
'line_items[0][price_data][unit_amount]' => (int) ($checkAmount * 100),
'line_items[0][quantity]' => 1,
'mode' => 'payment',
'success_url' => url('subscriptions/payment/success') . '/{CHECKOUT_SESSION_ID}' . '/' . $subscriptionBill_id . '/' . $package_id . '/' . $type . '/' . $subscription_id . '/' . $isCurrentPlan,
'cancel_url' => url('subscriptions/payment/cancel') . '/' . $subscriptionBill_id,
'metadata[subscription_bill_id]' => $subscriptionBill_id,
'metadata[package_id]' => $package_id,
'metadata[type]' => $type,
'metadata[subscription_id]' => $subscription_id,
'metadata[is_current_plan]' => $isCurrentPlan,
'metadata[school_id]' => Auth::user()->school_id ?? null
])
]);
// Execute cURL request
$response = curl_exec($ch);
// Check for cURL errors
if (curl_errno($ch)) {
Log::error('Curl Error: ' . curl_error($ch));
curl_close($ch);
return redirect()->back()->with('error', trans('Connection error occurred'));
}
curl_close($ch);
$session = json_decode($response, true);
if (isset($session['error'])) {
Log::error('Stripe API Error: ' . $session['error']['message']);
return redirect()->back()->with('error', trans('Stripe error: ') . $session['error']['message']);
}
return redirect()->away($session['url'])->with('success', trans('The stripe payment has been successful'));
} catch (\Stripe\Exception\ApiErrorException $e) {
Log::error('Stripe API Error: ' . $e->getMessage());
Log::error('Stripe API Error Code: ' . $e->getStripeCode());
Log::error('Stripe API Error Type: ' . $e->getStripeType());
return redirect()->back()->with('error', trans('Stripe payment error: ') . $e->getMessage());
} catch (\Exception $e) {
Log::error('General Error in Stripe Payment: ' . $e->getMessage());
Log::error('Error trace: ' . $e->getTraceAsString());
return redirect()->back()->with('error', trans('server_not_responding'));
}
}
// Paystack Payment
public function paystack_payment($subscriptionBill_id = null, $package_id = null, $type = null, $subscription_id = null, $isCurrentPlan = null)
{
try {
$settings = app(CachingService::class)->getSystemSettings();
// dd($settings);
// die;
$name = '';
$amount = 0;
if ($subscriptionBill_id) {
$subscriptionBill = $this->subscriptionBill->findById($subscriptionBill_id);
$name = $subscriptionBill->subscription->name;
$amount = $subscriptionBill->amount;
}
if ($package_id && $package_id != -1) {
$package = $this->package->findById($package_id);
$name = $package->name;
$amount = $package->charges;
$subscriptionBill_id = -1;
}
if ($type == null) {
$type = -1;
}
if (!$subscription_id) {
$subscription_id = -1;
}
if (!$isCurrentPlan) {
$isCurrentPlan = -1;
}
// Set default values
$type = $type ?? -1;
$subscription_id = $subscription_id ?? -1;
$isCurrentPlan = $isCurrentPlan ?? -1;
// Access the model directly via data for super admin data
DB::setDefaultConnection('mysql');
$paymentConfiguration = PaymentConfiguration::where('school_id', null)->where('payment_method', 'Paystack')->where('status', 1)->first();
if ($paymentConfiguration && !$paymentConfiguration->status) {
return redirect()->back()->with('error', trans('Current Paystack payment not available'));
}
$paystack_secret_key = $paymentConfiguration->secret_key ?? null;
if (empty($paystack_secret_key)) {
return redirect()->back()->with('error', trans('No API key provided'));
}
$currency = $paymentConfiguration->currency_code;
\Log::info('Paystack currency: ' . $currency);
$checkAmount = $this->checkMinimumAmount(strtoupper($currency), $amount);
$checkAmount = (float) $checkAmount;
$checkAmount = round($checkAmount, 2);
// Prepare request data
$data = [
'amount' => ($checkAmount * 100),
'email' => Auth::user()->email,
'currency' => $currency,
'redirect_url' => url('subscriptions/payment/success') . '/{CHECKOUT_SESSION_ID}' . '/' . $subscriptionBill_id . '/' . $package_id . '/' . $type . '/' . $subscription_id . '/' . $isCurrentPlan,
'callback_url' => url('subscriptions/history'),
'metadata' => [
'package_id' => $package_id,
'type' => 'package',
'name' => 'package', // Using name instead of package_type to match webhook handling
'subscription_id' => $subscription_id,
'is_current_plan' => $isCurrentPlan,
'school_id' => Auth::user()->school_id ?? null,
'user_id' => Auth::user()->id,
'payment_transaction_id' => $paymentTransaction->id ?? null
]
];
// Make HTTP request to Paystack API
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $paystack_secret_key,
'Content-Type' => 'application/json',
'Cache-Control' => 'no-cache'
])->post('https://api.paystack.co/transaction/initialize', $data);
\Log::info('Paystack response: ' . $response->body());
// Check if request was successful
if (!$response->successful()) {
Log::error('Paystack API Error: ' . $response->body());
return redirect()->back()->with('error', trans('Paystack error: ') . $response->json()['message']);
}
$result = $response->json();
// if ($result['status']) {
// // update flutterwave payment transaction table
// $paymentTransactionData = array(
// 'user_id' => Auth::user()->id,
// 'amount' => $amount,
// 'payment_gateway' => 'Paystack',
// 'order_id' => $result['data']['reference'],
// 'payment_status' => 'pending',
// 'school_id' => Auth::user()->school_id,
// 'created_at' => now(),
// 'updated_at' => now(),
// );
// $this->paymentTransaction->create($paymentTransactionData);
// }
Log::info('Paystack payment initialized successfully: ' . $result['data']['reference']);
return redirect()->away($result['data']['authorization_url'])->with('success', trans('The paystack payment has been successful'));
} catch (\Exception $e) {
Log::error('General Error in Paystack Payment: ' . $e->getMessage());
Log::error('Error trace: ' . $e->getTraceAsString());
return redirect()->back()->with('error', trans('server_not_responding'));
}
}
// Flutterwave Payment
public function flutterwave_payment($subscriptionBill_id = null, $package_id = null, $type = null, $subscription_id = null, $isCurrentPlan = null)
{
try {
DB::beginTransaction();
$settings = app(CachingService::class)->getSystemSettings();
$name = '';
$amount = 0;
if ($subscriptionBill_id) {
$subscriptionBill = $this->subscriptionBill->findById($subscriptionBill_id);
$name = $subscriptionBill->subscription->name;
$amount = $subscriptionBill->amount;
$package_id = -1;
}
if ($package_id != -1) {
$package = $this->package->findById($package_id);
$name = $package->name;
$amount = $package->charges;
$subscriptionBill_id = -1;
}
if ($type == null) {
$type = -1;
}
if (!$subscription_id) {
$subscription_id = -1;
}
if (!$isCurrentPlan) {
$isCurrentPlan = -1;
}
// Get payment configuration
DB::setDefaultConnection('mysql');
$paymentConfiguration = PaymentConfiguration::where('school_id', null)
->where('status', 1)
->first();
\Log::info("Flutterwave Payment Configuration: " . json_encode($paymentConfiguration));
if (!$paymentConfiguration || !$paymentConfiguration->status) {
throw new \Exception(trans('Current Flutterwave payment not available'));
}
if (empty($paymentConfiguration->api_key)) {
throw new \Exception(trans('No API key provided for Flutterwave'));
}
$currency = $paymentConfiguration->currency_code;
$checkAmount = (float) $this->checkMinimumAmount(strtoupper($currency), $amount);
\Log::info("Flutterwave Check Amount: " . $checkAmount);
// Create payment transaction record first
$paymentTransactionData = [
'user_id' => Auth::user()->id,
'amount' => $amount,
'payment_gateway' => 'Flutterwave',
'order_id' => 'tx_' . time(),
'payment_status' => 'pending',
'school_id' => Auth::user()->school_id,
'created_at' => now(),
'updated_at' => now(),
];
$paymentTransaction = $this->paymentTransaction->create($paymentTransactionData);
if (!$paymentTransaction) {
throw new \Exception(trans('Failed to create payment transaction'));
}
// Update subscription bill with payment transaction ID
if ($subscriptionBill_id != -1) {
$this->subscriptionBill->update($subscriptionBill_id, [
'payment_transaction_id' => $paymentTransaction->id
]);
}
// Prepare Flutterwave API request
$request = [
'tx_ref' => $paymentTransaction->order_id,
'amount' => $checkAmount,
'currency' => strtoupper($currency),
'email' => Auth::user()->email,
'order_id' => $subscriptionBill_id,
'order_name' => $name,
'customer' => [
'email' => Auth::user()->email,
'name' => Auth::user()->full_name,
'mobile' => Auth::user()->mobile,
],
'meta' => [
'subscription_bill_id' => $subscriptionBill_id,
'package_id' => $package_id,
'type' => 'package',
'subscription_id' => $subscription_id,
'is_current_plan' => $isCurrentPlan,
'school_id' => Auth::user()->school_id ?? null,
'user_id' => Auth::user()->id,
'payment_transaction_id' => $paymentTransaction->id
],
'redirect_url' => url('subscriptions/history'),
'cancel_url' => url('subscriptions/payment/cancel') . '/' . $subscriptionBill_id,
];
// Make request to Flutterwave API
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $paymentConfiguration->secret_key,
'Content-Type' => 'application/json',
])->post('https://api.flutterwave.com/v3/payments', $request);
$res = $response->json();
\Log::info('Flutterwave response: ' . json_encode($res));
if ($res['status'] !== 'success') {
ResponseService::errorResponse($res['message']);
}
DB::commit();
return redirect()->away($res['data']['link'])->with('success', trans('The flutterwave payment has been successful'));
} catch (\Exception $e) {
DB::rollBack();
\Log::error('Flutterwave Payment Error: ' . $e->getMessage());
\Log::error($e->getTraceAsString());
return redirect()->back()->with('error', $e->getMessage());
}
}
public function active_subscription($schoolId)
{
$today_date = Carbon::now()->format('Y-m-d');
$subscription = Subscription::where('school_id', $schoolId)->whereDate('start_date', '<=', $today_date)->whereDate('end_date', '>=', $today_date)->latest()->first();
if ($subscription) {
if ($subscription->package_type == 1) {
// Postpaid
$subscription = Subscription::where('school_id', $schoolId)->where('package_type', 1)->whereDate('start_date', '<=', $today_date)->whereDate('end_date', '>=', $today_date)->with('subscription_feature.feature', 'addons.feature', 'package.package_feature.feature')->doesntHave('subscription_bill')->has('subscription_feature')->latest()->first();
} else {
// Prepaid
$subscription = Subscription::where('school_id', $schoolId)->where('package_type', 0)->whereDate('start_date', '<=', $today_date)->whereDate('end_date', '>=', $today_date)->with('package.package_feature.feature')->has('subscription_bill')->with('subscription_feature.feature')->with([
'addons' => function ($q) {
$q->has('transaction')->with('feature')->whereHas('transaction', function ($q) {
$q->where('payment_status', "succeed");
});
}
])->whereHas('subscription_bill.transaction', function ($q) {
$q->where('payment_status', "succeed");
})->has('subscription_feature')->latest()->first();
}
} else {
return null;
}
return $subscription;
}
public function check_user_limit($subscription, $type)
{
// type [ Students / Staffs ]
if ($type == "Students") {
$students = $this->user->builder()->where('status', 1)->role('Student')->where('school_id', $subscription->school_id)->count();
if ($students >= $subscription->no_of_students) {
return false;
}
return true;
} else {
$staffs = $this->staff->builder()->whereHas('user', function ($q) use ($subscription) {
$q->where('status', 1)->where('school_id', $subscription->school_id);
})->count();
if ($staffs >= $subscription->no_of_staffs) {
return false;
}
return true;
}
}
public function prepaid_addon_payment($addonSubscriptionId)
{
try {
$settings = app(CachingService::class)->getSystemSettings();
// $subscriptionBill = $this->subscriptionBill->findById($subscriptionBill_id);
$addonSubscription = $this->addonSubscription->findById($addonSubscriptionId);
// Access the model directly via data for super admin data, use the interface builder for school-specific data.
DB::setDefaultConnection('mysql');
$paymentConfiguration = PaymentConfiguration::where('school_id', null)->where('status', 1)->first();
if ($paymentConfiguration && !$paymentConfiguration->status) {
return redirect()->back()->with('error', trans('Current payment method not available'));
}
// Stripe Payment
if ($paymentConfiguration->payment_method == 'Stripe') {
$stripe_secret_key = $paymentConfiguration->secret_key ?? null;
if (empty($stripe_secret_key)) {
return redirect()->back()->with('error', trans('No API key provided'));
}
$amount = $addonSubscription->price;
$currency = $paymentConfiguration->currency_code;
// Validate minimum amount
$checkAmount = $this->checkMinimumAmount(strtoupper($currency), $amount);
$checkAmount = (float) $checkAmount;
$checkAmount = round($checkAmount, 2);
Log::info('Validated amount: ' . $checkAmount * 100);
Stripe::setApiKey($stripe_secret_key);
$session = StripeSession::create([
'line_items' => [
[
'price_data' => [
'currency' => $currency,
'product_data' => [
'name' => $addonSubscription->feature->name,
'images' => [$settings['horizontal_logo'] ?? 'logo.svg'],
],
'unit_amount' => $checkAmount * 100,
],
'quantity' => 1,
],
],
'mode' => 'payment',
'success_url' => url('addons/payment/success') . '/{CHECKOUT_SESSION_ID}' . '/' . $addonSubscriptionId,
'cancel_url' => url('addons/payment/cancel'),
]);
return redirect()->away($session->url);
}
// Paystack Payment
if ($paymentConfiguration->payment_method == 'Paystack') {
try {
$paystack_secret_key = $paymentConfiguration->secret_key ?? null;
$currency = $paymentConfiguration->currency_code;
$checkAmount = $this->checkMinimumAmount(strtoupper($currency), $addonSubscription->price);
$amount = (float) str_replace(',', '', number_format($checkAmount, 2));
if (empty($paystack_secret_key)) {
return redirect()->back()->with('error', trans('No API key provided'));
}
$data = [
'amount' => $amount * 100,
'email' => Auth::user()->email,
'currency' => strtoupper($currency),
'metadata' => [
'type' => 'addon',
'subscription_id' => $addonSubscription->subscription_id,
'feature_id' => $addonSubscription->feature_id,
'addon_subscription_id' => $addonSubscriptionId,
'school_id' => Auth::user()->school_id,
'user_id' => Auth::user()->id,
'price' => $addonSubscription->price,
'end_date' => $addonSubscription->end_date,
],
'callback_url' => route('addons.payment.success_callback')
];
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $paystack_secret_key,
'Content-Type' => 'application/json',
])->post('https://api.paystack.co/transaction/initialize', $data);
$res = $response->json();
\Log::info('Paystack response: ' . json_encode($res));
if (!$res['status']) {
return redirect()->back()->with('error', $res['message']);
}
if ($res['status']) {
return redirect()->away($res['data']['authorization_url'])->with('reload', true)->with('success', trans('The paystack payment has been successful'));
}
return redirect('addons/plan')->back()->with('error', trans('Paystack payment failed'));
} catch (\Throwable $th) {
\Log::error('Paystack Payment Error: ' . $th->getMessage());
DB::rollBack();
return redirect()->back()->with('error', trans('server_not_responding'));
}
}
// Flutterwave Payment
if ($paymentConfiguration->payment_method == 'Flutterwave') {
$flutterwave_secret_key = $paymentConfiguration->secret_key ?? null;
$currency = $paymentConfiguration->currency_code;
$amount = (float) number_format((float) ceil((float) $addonSubscription->price * 100) / 100, 2);
if (empty($flutterwave_secret_key)) {
return redirect()->back()->with('error', trans('No API key provided'));
}
$data = [
'tx_ref' => 'tx_' . time(),
'amount' => $amount * 100,
'currency' => strtoupper($currency),
'email' => Auth::user()->email,
'customer' => [
'email' => Auth::user()->email,
'name' => Auth::user()->full_name,
'mobile' => Auth::user()->mobile,
],
'meta' => [
'type' => 'addon',
'subscription_id' => $addonSubscription->subscription_id,
'feature_id' => $addonSubscription->feature_id,
'addon_subscription_id' => $addonSubscriptionId,
'school_id' => Auth::user()->school_id,
'user_id' => Auth::user()->id,
],
'redirect_url' => url('addons/payment/success'),
];
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $flutterwave_secret_key,
'Content-Type' => 'application/json',
])->post('https://api.flutterwave.com/v3/payments', $data);
$res = $response->json();
\Log::info('Flutterwave response: ' . json_encode($res));
if (!$response->successful()) {
throw new \Exception('Flutterwave API request failed: ' . ($res['message'] ?? 'Unknown error'));
}
if (!$res['status']) {
return redirect()->back()->with('error', $res['message']);
}
if ($res['status']) {
return redirect()->away($res['data']['link'])->with('reload', true)->with('success', trans('The flutterwave payment has been successful'));
}
}
} catch (\Throwable $th) {
DB::rollBack();
return redirect()->back()->with('error', trans('server_not_responding'));
}
}
/**
* @param string|float $currency
* @param string|float $amount
* @return float
*/
public function checkMinimumAmount($currency, $amount)
{
$currencies = array(
'USD' => 0.50,
'AED' => 2.00,
'AUD' => 0.50,
'BGN' => 1.00,
'BRL' => 0.50,
'CAD' => 0.50,
'CHF' => 0.50,
'CZK' => 15.00,
'DKK' => 2.50,
'EUR' => 0.50,
'GBP' => 0.30,
'HKD' => 4.00,
'HUF' => 175.00,
'INR' => 0.50,
'JPY' => 50,
'MXN' => 10,
'MYR' => 2.00,
'NOK' => 3.00,
'NZD' => 0.50,
'PLN' => 2.00,
'RON' => 2.00,
'SEK' => 3.00,
'SGD' => 0.50,
'THB' => 10,
'ZAR' => 10,
);
if ($amount != 0) {
if (array_key_exists($currency, $currencies)) {
if ($currencies[$currency] >= $amount) {
return $currencies[$currency];
} else {
return $amount;
}
} else {
return $amount;
}
}
return 0;
}
}