Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 152
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
SubscriptionController
0.00% covered (danger)
0.00%
0 / 152
0.00% covered (danger)
0.00%
0 / 8
1482
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 current
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 1
380
 isOnPremiumPlan
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 cancelSubscription
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 resumeSubscription
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 getInvoices
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCoupon
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 trialSubscription
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace App\Http\Controllers\v1\Stripe;
4
5use Exception;
6use Stripe\StripeClient;
7use App\Http\Models\Plans;
8use Illuminate\Http\Request;
9use App\Http\Models\Shortcut;
10use App\Http\Models\Auth\User;
11use App\Http\Models\UserReferral;
12use App\Traits\SubscriptionTrait;
13use Illuminate\Http\JsonResponse;
14use App\Http\Controllers\Controller;
15use App\Http\Models\Admin\CompanyLicenses;
16use App\Http\Models\ShortcutCategory;
17use App\Http\Models\ShortcutSubCategoryLv1;
18use App\Http\Requests\ValidateCouponRequest;
19use Illuminate\Support\Facades\Log;
20use Carbon\Carbon;
21
22class SubscriptionController extends Controller
23{
24    use SubscriptionTrait;
25
26    public function index(Request $request): JsonResponse
27    {
28        $plans = Plans::select('title', 'stripe_obj', 'identifier', 'features', 'currency', 'interval', 'unit_amount')
29            ->WhereNull('pricing_version')
30            ->get();
31
32        $data = [];
33
34        $plans->each(function ($plan) use (&$data) {
35            $data[] = [
36                'title' => $plan->title,
37                'identifier' => $plan->identifier,
38                'features' => $plan->features,
39                'currency' => $plan->currency,
40                'interval' => $plan->interval,
41                'unit_amount' => $plan->unit_amount,
42            ];
43        });
44
45        return response()->json($data);
46    }
47    public function current(Request $request)
48    {
49        $user = $request->user();
50
51        $current_plan = $this->getCurrentPlan($user)->toArray();
52
53        $features = $current_plan['features'];
54
55        // User statistics as per there plan
56        $categories_count = ShortcutCategory::where('user_id', request()->user()->id)->count();
57        $sub_categories_count = ShortcutSubCategoryLv1::whereHas('ShortcutCategory')->where('user_id', request()->user()->id)->count();
58        $flyplates_count = Shortcut::where(['user_defined' => false])->count();
59        $flycuts_counts = Shortcut::count();
60        $total_allowed_characters = $features['Number of Characters that can be used per flycut'];
61        $spell_check = $features['Spell Check (Language)'];
62        $total_flyplates_allowed = $features['FlyPlates'];
63        $total_allowed_categories = $features['Categories'];
64
65        $total_allowed_shortcuts = $features['Number of FlyCuts that can be created'];
66
67        // for flyrewards restrictions
68
69        $is_rewardable = (isset($user->rewardable)) ? $user->rewardable : '';
70        $rewards_level = (isset($user->rewards_level)) ? $user->rewards_level : '';
71
72        if ($is_rewardable) {
73            switch ($rewards_level) {
74                case 1:
75                    $total_allowed_shortcuts = -1;
76                    $features['Number of FlyCuts that can be created'] = $total_allowed_shortcuts;
77                    break;
78
79                case 2:
80                    $total_allowed_shortcuts = -1;
81                    $features['Number of FlyCuts that can be created'] = $total_allowed_shortcuts;
82
83                    $total_allowed_characters = -1;
84                    $features['Number of Characters that can be used per flycut'] = $total_allowed_characters;
85                    break;
86
87                case 3:
88                    $total_allowed_shortcuts = -1;
89                    $features['Number of FlyCuts that can be created'] = $total_allowed_shortcuts;
90
91                    $total_allowed_characters = -1;
92                    $features['Number of Characters that can be used per flycut'] = $total_allowed_characters;
93
94                    // check the plan and accordingly allowed user for the extra category
95                    if (
96                        $current_plan['identifier'] == Plans::FREEMIUM_IDENTIFIER ||
97                        $current_plan['identifier'] == Plans::STARTER_MONTHLY_IDENTIFIER ||
98                        $current_plan['identifier'] == Plans::STARTER_YEARLY_IDENTIFIER
99                    ) {
100                        $total_allowed_categories = $total_allowed_categories + 1;
101                        $features['Categories'] = $total_allowed_categories;
102                    }
103                    break;
104
105                case $rewards_level >= 4:
106                    $total_allowed_shortcuts = -1;
107                    $features['Number of FlyCuts that can be created'] = $total_allowed_shortcuts;
108
109                    $total_allowed_characters = -1;
110                    $features['Number of Characters that can be used per flycut'] = $total_allowed_characters;
111
112                    $total_flyplates_allowed = -1;
113                    $features['FlyPlates'] = $total_flyplates_allowed;
114
115                    // check the plan and accordingly allowed user for the extra category
116                    if (
117                        $current_plan['identifier'] == Plans::FREEMIUM_IDENTIFIER ||
118                        $current_plan['identifier'] == Plans::STARTER_MONTHLY_IDENTIFIER ||
119                        $current_plan['identifier'] == Plans::STARTER_YEARLY_IDENTIFIER
120                    ) {
121                        $total_allowed_categories = $total_allowed_categories + 1;
122                        $features['Categories'] = $total_allowed_categories;
123                    }
124                    break;
125            }
126        }
127
128        $current_plan['features'] = $features;
129
130        $current_plan['statistics']['sub_categories'] = $sub_categories_count;
131        $current_plan['statistics']['sub_categories_available'] = $features['Subcategory'];
132        $current_plan['statistics']['categories'] = $categories_count;
133        $current_plan['statistics']['categories_available'] = $total_allowed_categories;
134        $current_plan['statistics']['flyplates'] = $flyplates_count;
135        $current_plan['statistics']['flyplates_available'] = $total_flyplates_allowed;
136        $current_plan['statistics']['flycuts'] = $flycuts_counts;
137        $current_plan['statistics']['flycuts_available'] = $total_allowed_shortcuts;
138        $current_plan['statistics']['characters'] = $total_allowed_characters;
139        $current_plan['statistics']['spell_check_language'] = $spell_check;
140
141        $current_plan["referrals"] = UserReferral::firstWhere('user_id', $user->id);
142
143        $userId = $user->getKey();
144        $referral_key = $user->referral_key;
145        if (! $referral_key) {
146            $referral_key = uniqid();
147            User::where('_id', $userId)->update([
148                'referral_key' => $referral_key,
149            ]);
150        }
151        $current_plan["referral_key"] = $referral_key;
152        $current_plan['used_trial'] = $user->subscriptionTrials()->where('plan_identifier', 'sales-pro-monthly')->exists();
153        $current_plan['on_trial'] = false;
154
155        $userDetails = User::withTrashed()->firstWhere('email', $user->email);
156        $userDetails['is_poc'] = $userDetails->isPOC();
157        $current_plan['user_details'] = $userDetails;
158
159        $current_plan['already_used_pro_trial'] = $user->subscriptionTrials()->exists();
160
161        if ($current_plan['identifier'] == Plans::FREEMIUM_IDENTIFIER) {
162            return $current_plan;
163        }
164
165        $current_plan['on_trial'] = $user->subscription('main')->onTrial();
166        $current_plan['plan_status'] = $user->subscription('main')->stripe_status;
167        $plan_ends_on = $current_plan['on_trial'] ? $user->subscription('main')->trial_ends_at : $user->subscription('main')->ends_at ?? null;
168
169        // TODO MA figure this out
170        // if ($plan_ends_on) {
171        //     $plan_ends_on = Carbon::createFromFormat('Y-m-d H:i:s', $plan_ends_on->toDateTimeString())->format('Y-m-d');
172        // }
173        if ($user->company_id) {
174            $company_license = CompanyLicenses::where('company_id', $user->company_id)
175                ->active()
176                ->first();
177
178            if ($company_license) {
179                $plan_ends_on = Carbon::createFromFormat('Y-m-d H:i:s', $company_license->contract_end_date);
180            }
181        }
182
183        $current_plan['ends_at'] = $plan_ends_on;
184        $current_plan['used_trial'] = $user->subscriptionTrials()->where('plan_identifier', $current_plan['identifier'])->exists();
185
186        return $current_plan;
187    }
188
189    public function isOnPremiumPlan(Request $request): JsonResponse
190    {
191        $current_subscription = $request->user()->subscription('main');
192
193        if ($current_subscription) {
194            return response()->json(true);
195        }
196
197        return response()->json(false);
198    }
199
200    public function cancelSubscription(Request $request): JsonResponse
201    {
202        try {
203            if (!$request->user()->subscribed('main')) {
204                throw new Exception('You are not subscribed to any plans');
205            }
206
207            $request->user()->subscription('main')->cancel();
208
209            return response()->json(['message' => 'You subscription has been successfully cancelled']);
210        } catch (Exception $e) {
211            return response()->json(['error' => $e->getMessage()], 422);
212        }
213    }
214
215    public function resumeSubscription(Request $request): JsonResponse
216    {
217        try {
218            if ($request->user()->subscribed('main') && !$request->user()->subscription('main')->onGracePeriod()) {
219                throw new Exception('You are already subscribed to plan');
220            }
221
222            $request->user()->subscription('main')->resume();
223
224            return response()->json(['message' => 'You subscription has been successfully resumed']);
225        } catch (Exception $e) {
226            return response()->json(['error' => $e->getMessage()], 422);
227        }
228    }
229
230    public function getInvoices(Request $request): JsonResponse
231    {
232        return response()->json($request->user()->invoicesIncludingPending(), 200);
233    }
234
235    /**
236     * Retrieve coupon object
237     */
238    public function getCoupon(ValidateCouponRequest $request): JsonResponse
239    {
240        $plan = Plans::select('stripe_obj')->whereNull('pricing_version')->firstWhere('identifier', $request->plan_identifier);
241
242        try {
243            $coupon = $this->retrieveCouponObject($request->coupon);
244            $coupon->valid = in_array($plan->stripe_obj['product'] ?? '', $coupon->applies_to->products);
245            $coupon = $coupon->valid ? $coupon : ['valid' => false];
246
247            return response()->json($coupon);
248        } catch (\Exception $e) {
249            return response()->json(['message' => $e->getMessage()], 422);
250        }
251    }
252
253    public function trialSubscription(Request $request)
254    {
255        $user = $request->user();
256
257        if ($this->getCurrentPlan($user)->identifier !== Plans::FREEMIUM_IDENTIFIER) {
258            return response()->json(['message' => 'You are not eligible for a trial plan'], 422);
259        }
260
261        $planIdentifier = 'sales-pro-monthly';
262
263        if ($user->subscriptionTrials()->where('plan_identifier', $planIdentifier)->exists()) {
264            return response()->json(['message' => 'You have used your trial period for this plan already!'], 403);
265        }
266
267        $trialPeriodDays = 14;
268        $planId = Plans::select('stripe_id')->whereNull('pricing_version')->firstWhere('identifier', $planIdentifier)->stripe_id;
269
270        try {
271            if (!$user->hasStripeId()) {
272                $user->createAsStripeCustomer([
273                    'name' => $user->first_name . ' ' . $user->last_name,
274                    'email' => $user->email,
275                ]);
276            }
277
278            $stripe = new StripeClient(config('services.stripe.secret'));
279
280            $stripe->subscriptions->create([
281                'customer' => $user->stripe_id,
282                'items' => [['price' => $planId]],
283                'trial_period_days' => $trialPeriodDays,
284                'payment_settings' => ['save_default_payment_method' => 'on_subscription'],
285                'trial_settings' => ['end_behavior' => ['missing_payment_method' => 'cancel']],
286            ]);
287
288            return response()->json(['message' => "You have subscribed to {$planIdentifier} on trial for {$trialPeriodDays}"]);
289        } catch (Exception $e) {
290            return response()->json(['message' => $e->getMessage()], 422);
291        }
292    }
293}