Current File : /home/getxxhzo/app.genicards.com/app/Repositories/SubscriptionRepository.php |
<?php
namespace App\Repositories;
use App\Mail\SubscriptionPaymentSuccessMail;
use App\Models\AffiliateUser;
use App\Models\CouponCode;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Laracasts\Flash\Flash;
use Stripe\Checkout\Session;
use Stripe\Exception\ApiErrorException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use WpOrg\Requests\Exception;
/**
* Class SubscriptionRepository
*/
class SubscriptionRepository extends BaseRepository
{
protected $fieldSearchable = [
'user_id',
'stripe_id',
'stripe_status',
'stripe_plan',
'subscription_plan_id',
'start_date',
'end_date',
'status',
];
/**
* {@inheritDoc}
*/
public function getFieldsSearchable()
{
return $this->fieldSearchable;
}
/**
* {@inheritDoc}
*/
public function model()
{
return Subscription::class;
}
public function purchaseSubscriptionForStripe($input): array
{
$data = $this->manageSubscription($input);
if (! isset($data['plan'])) { // 0 amount plan or try to switch the plan if it is in trial mode
return $data;
}
$result = $this->manageStripeData(
$data['plan'],
[
'amountToPay' => $data['amountToPay'],
'sub_id' => $data['subscription']->id,
]
);
return $result;
}
/**
* @param $input
*/
public function manageSubscription($planData): bool|array
{
$planId = $planData['planId'];
$customFieldId = $planData['customFieldId'] ?? null;
if (isset($planData['couponCodeId'])) {
$couponId = $planData['couponCodeId'];
$coupon = CouponCode::find($couponId);
if ($coupon) {
if ($coupon->coupon_limit != null) {
$newLimit = $coupon->coupon_limit_left - 1;
CouponCode::where('id', $couponId)->update(['coupon_limit_left' => $newLimit]);
}
}
}
/** @var Plan $subscriptionPlan */
$subscriptionPlan = Plan::findOrFail($planId);
if ($subscriptionPlan->frequency == Plan::MONTHLY) {
$newPlanDays = $subscriptionPlan['trial_days'] > 0 ? $subscriptionPlan['trial_days'] : 30;
} else {
if ($subscriptionPlan->frequency == Plan::YEARLY) {
$newPlanDays = $subscriptionPlan['trial_days'] > 0 ? $subscriptionPlan['trial_days'] : 365;
} else {
$newPlanDays = $subscriptionPlan['trial_days'] > 0 ? $subscriptionPlan['trial_days'] : 36500;
}
}
$startsAt = Carbon::now();
$endsAt = $startsAt->copy()->addDays($newPlanDays);
$usedTrialBefore = Subscription::whereTenantId(getLogInUser()->tenant_id)->whereNotNull('trial_ends_at')->exists();
// if the user did not have any trial plan then give them a trial
if (! $usedTrialBefore && $subscriptionPlan->trial_days > 0) {
$endsAt = $startsAt->copy()->addDays($subscriptionPlan->trial_days);
}
// if($planData['customFieldId'] != null){
// $amountToPay = $subscriptionPlan->planCustomFields()->where('id', $planData['customFieldId'])->value('custom_vcard_price');
// }
// else{
// $amountToPay = $subscriptionPlan->price;
// }
$amountToPay = $customFieldId ? $subscriptionPlan->planCustomFields()->where('id', $customFieldId)->value('custom_vcard_price') : $subscriptionPlan->price;
/** @var Subscription $currentSubscription */
$currentSubscription = getCurrentSubscription();
$usedDays = Carbon::parse($currentSubscription->starts_at)->diffInDays($startsAt);
$planIsInTrial = checkIfPlanIsInTrial($currentSubscription);
// switching the plan -- Manage the pro-rating
if (! $currentSubscription->isExpired() && $amountToPay != 0 && ! $planIsInTrial) {
$usedDays = Carbon::parse($currentSubscription->starts_at)->diffInDays($startsAt);
$currentSubsTotalDays = Carbon::parse($currentSubscription->starts_at)->diffInDays($currentSubscription->ends_at);
$currentPlan = $currentSubscription->plan; // TODO: take fields from subscription
// checking if the current active subscription plan has the same price and frequency in order to process the calculation for the proration
$planPrice = $currentPlan->price;
$planFrequency = $currentPlan->frequency;
if ($planPrice != $currentSubscription->plan_amount || $planFrequency != $currentSubscription->plan_frequency) {
$planPrice = $currentSubscription->plan_amount;
$planFrequency = $currentSubscription->plan_frequency;
}
// $frequencyDays = $planFrequency == Plan::MONTHLY ? 30 : 365;
$perDayPrice = round($planPrice / $currentSubsTotalDays, 2);
$isJPYCurrency = ! empty($subscriptionPlan->currency) && isJPYCurrency($subscriptionPlan->currency->currency_code);
$remainingBalance = $planPrice - ($perDayPrice * $usedDays);
$remainingBalance = $isJPYCurrency
? round($remainingBalance) : $remainingBalance;
if ($remainingBalance < $amountToPay) { // adjust the amount in plan i.e. you have to pay for it
$payableAmount = $isJPYCurrency
? round($amountToPay - $remainingBalance)
: round($amountToPay - $remainingBalance, 2);
} else {
$perDayPriceOfNewPlan = round($amountToPay / $newPlanDays, 5);
$totalDays = round($remainingBalance / $perDayPriceOfNewPlan);
$endsAt = Carbon::now()->addDays($totalDays);
$payableAmount = 0;
}
} else {
$payableAmount = $amountToPay;
}
// check that if try to switch the plan
if (! $currentSubscription->isExpired() && $subscriptionPlan->trial_days == 0) {
if ((checkIfPlanIsInTrial($currentSubscription) || ! checkIfPlanIsInTrial($currentSubscription)) && $amountToPay <= 0) {
return ['status' => false, 'subscriptionPlan' => $subscriptionPlan];
}
}
if ($usedDays <= 0) {
$startsAt = $currentSubscription->starts_at;
}
$input = [
'user_id' => getLogInUser()->id,
'plan_id' => $subscriptionPlan->id,
'plan_amount' => $amountToPay,
'payable_amount' => $payableAmount,
'plan_frequency' => $subscriptionPlan->frequency,
'starts_at' => $startsAt,
'ends_at' => $endsAt,
'status' => Subscription::INACTIVE,
'no_of_vcards' => $customFieldId ? $subscriptionPlan->planCustomFields()->where('id', $customFieldId)->value('custom_vcard_number') : $subscriptionPlan->no_of_vcards,
'tenant_id' => getLogInUser()->tenant_id,
'payment_type' => Subscription::STRIPE,
];
if (isset($planData['notes'])) {
$input['notes'] = $planData['notes'];
}
//apply coupon code if exists
if (! empty($planData['couponCodeId'])) {
$couponCode = CouponCode::whereId($planData['couponCodeId'])
->whereCouponName($planData['couponCode'])
->whereStatus(CouponCode::ACTIVE)
->firstOrFail();
if ($couponCode->is_expired) {
Flash::error('Invalid Coupon code');
return [];
}
$couponCodeRepo = App(CouponCodeRepository::class);
$data['planId'] = $subscriptionPlan->id;
$data['planPrice'] = $amountToPay;
$data['couponCode'] = $planData['couponCode'];
$data['customFieldId'] = $customFieldId;
$couponData = $couponCodeRepo->getAfterDiscountData($data);
$payableAmount = $input['payable_amount'] = $couponData['afterDiscount']['amountToPay'];
$input['coupon_code_meta'] = $couponData['afterDiscount'];
$input['coupon_code_meta']['discount'] = $couponCode->discount;
unset($input['coupon_code_meta']['amountToPay']);
$input['discount'] = $couponData['afterDiscount']['discount'];
}
$subscription = Subscription::create($input);
if (isset($planData['attachment']) && ! empty($planData['attachment'])) {
$subscription->addMedia($planData['attachment'])->toMediaCollection(
Subscription::ATTACHMENT_PATH,
config('app.media_disc')
);
}
if ($amountToPay <= 0 || $payableAmount == 0) {
// De-Active all other subscription
Subscription::whereTenantId(getLogInTenantId())
->where('id', '!=', $subscription->id)
->update([
'status' => Subscription::INACTIVE,
]);
Subscription::findOrFail($subscription->id)->update(['status' => Subscription::ACTIVE]);
return ['status' => true, 'subscriptionPlan' => $subscriptionPlan];
}
session(['subscription_plan_id' => $subscription->id]);
session(['from_pricing' => request()->get('from_pricing')]);
return [
'plan' => $subscriptionPlan,
'amountToPay' => $payableAmount,
'subscription' => $subscription,
];
}
public function manageStripeData($subscriptionPlan, $data): array
{
$amountToPay = $data['amountToPay'];
$subscriptionID = $data['sub_id'];
if (! empty($subscriptionPlan->currency) && in_array(
$subscriptionPlan->currency->currency_code,
zeroDecimalCurrencies()
)) {
$planAmount = intval($amountToPay);
} else {
$planAmount = $amountToPay * 100;
}
setStripeApiKey();
$session = Session::create([
'payment_method_types' => ['card'],
'customer_email' => Auth::user()->email,
'line_items' => [
[
'price_data' => [
'product_data' => [
'name' => $subscriptionPlan->name,
'description' => 'Subscribing for the plan named ' . $subscriptionPlan->name,
],
'unit_amount' => $planAmount,
'currency' => $subscriptionPlan->currency->currency_code,
],
'quantity' => 1,
],
],
'client_reference_id' => $subscriptionID,
'metadata' => [
'payment_type' => Transaction::STRIPE,
'amount' => $planAmount,
'plan_currency' => $subscriptionPlan->currency->currency_code,
],
'mode' => 'payment',
'success_url' => url('payment-success') . '?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => url('failed-payment?error=payment_cancelled'),
]);
$result = [
'sessionId' => $session['id'],
];
return $result;
}
/**
* @throws ApiErrorException
*/
public function paymentUpdate($request)
{
try {
setStripeApiKey();
// Current User Subscription
// New Plan Subscribe
$stripe = new \Stripe\StripeClient(
getSelectedPaymentGateway('stripe_secret')
);
$sessionData = $stripe->checkout->sessions->retrieve(
$request->session_id,
[]
);
// where, $sessionData->client_reference_id = the subscription id
Subscription::findOrFail($sessionData->client_reference_id)->update(['status' => Subscription::ACTIVE]);
// De-Active all other subscription
Subscription::whereTenantId(getLogInTenantId())
->where('id', '!=', $sessionData->client_reference_id)
->where('status', '!=', Subscription::REJECT)
->update([
'status' => Subscription::INACTIVE,
]);
$paymentAmount = null;
if ($sessionData->metadata->plan_currency != null && in_array(
$sessionData->metadata->plan_currency,
zeroDecimalCurrencies()
)) {
$paymentAmount = $sessionData->amount_total;
} else {
$paymentAmount = $sessionData->amount_total / 100;
}
$transaction = Transaction::create([
'transaction_id' => $request->session_id,
'type' => $sessionData->metadata->payment_type,
'amount' => $paymentAmount,
'tenant_id' => getLogInTenantId(),
'status' => Transaction::SUCCESS,
'meta' => json_encode($sessionData),
]);
$subscription = Subscription::findOrFail($sessionData->client_reference_id);
$subscription->update(['transaction_id' => $transaction->id]);
$affiliateAmount = getSuperAdminSettingValue('affiliation_amount');
$affiliateAmountType = getSuperAdminSettingValue('affiliation_amount_type');
if ($affiliateAmountType == 1) {
AffiliateUser::whereUserId(getLogInUserId())->where('amount', 0)->withoutGlobalScopes()->update(['amount' => $affiliateAmount, 'is_verified' => 1]);
} else if ($affiliateAmountType == 2) {
$amount = $paymentAmount * $affiliateAmount / 100;
AffiliateUser::whereUserId(getLogInUserId())->where('amount', 0)->withoutGlobalScopes()->update(['amount' => $amount, 'is_verified' => 1]);
}
$userEmail = getLogInUser()->email;
$planName = $subscription->plan->name;
$firstName = getLogInUser()->first_name;
$lastName = getLogInUser()->last_name;
$emailData = [
'subscriptionId' => $sessionData->client_reference_id,
'subscriptionAmount' => $paymentAmount,
'transactionID' => $request->session_id,
'planName' => $planName,
'first_name' => $firstName,
'last_name' => $lastName,
];
Mail::to($userEmail)->send(new SubscriptionPaymentSuccessMail($emailData));
DB::commit();
$subscription->load('plan');
return $subscription;
} catch (\Exception $e) {
DB::rollBack();
throw new UnprocessableEntityHttpException($e->getMessage());
}
}
public function paymentFailed($planId)
{
$subscriptionPlan = Subscription::findOrFail($planId);
$subscriptionPlan->delete();
}
public function downloadAttachment($subscription): array
{
try {
$documentMedia = $subscription->media[0];
$documentPath = $documentMedia->getPath();
if (config('app.media_disc') === 'public') {
$documentPath = (Str::after($documentMedia->getUrl(), '/uploads'));
}
$file = Storage::disk(config('app.media_disc'))->get($documentPath);
$headers = [
'Content-Type' => $subscription->media[0]->mime_type,
'Content-Description' => 'File Transfer',
'Content-Disposition' => "attachment; filename={$subscription->media[0]->file_name}",
'filename' => $subscription->media[0]->file_name,
];
return [$file, $headers];
} catch (Exception $e) {
throw new UnprocessableEntityHttpException($e->getMessage());
}
}
}