Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 249
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
SubscriptionService
0.00% covered (danger)
0.00%
0 / 249
0.00% covered (danger)
0.00%
0 / 9
7832
0.00% covered (danger)
0.00%
0 / 1
 created
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 updateSubscriptionInfo
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 initFreemiumSubscription
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 endFreemiumSubscription
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 startSubscription
0.00% covered (danger)
0.00%
0 / 111
0.00% covered (danger)
0.00%
0 / 1
1640
 endSubscription
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 1
306
 checkPremiumSubscriptionsActive
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getCountryName
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 parseFields
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
156
1<?php
2
3namespace App\Services\UserInfo;
4
5use App\Http\Models\Auth\User;
6use App\Http\Models\Plans;
7use App\Http\Models\Subscription;
8use App\Http\Models\UserInfo;
9use App\Traits\ObjectMapper;
10use Carbon\Carbon;
11use Illuminate\Support\Str;
12use MongoDB\BSON\UTCDateTime;
13
14class SubscriptionService
15{
16    use ObjectMapper;
17
18    public function created(Subscription $subscription): void
19    {
20        $created = $subscription->toArray();
21
22        $parsedFields = $this->parseFields($created, $subscription);
23
24        $this->updateSubscriptionInfo($parsedFields);
25    }
26
27    public function updateSubscriptionInfo(array $data)
28    {
29        $subscriptionInfo = UserInfo::firstOrNew(['user_id' => $data['user_id']]);
30        $subscriptionInfo->fill($data);
31        $subscriptionInfo->save();
32    }
33
34    public function initFreemiumSubscription(string $user_id, $cancellation_date = null): array
35    {
36        $now = Carbon::now();
37        if (! empty($cancellation_date)) {
38            $now = $cancellation_date;
39        }
40
41        $user = User::find($user_id);
42        $plan = Plans::where('identifier', 'freemium')->first();
43        $result = [
44            'subscription_type' => $plan->hubspot_name,
45            'plan_name' => $plan->title,
46            'plan_id' => $plan->id,
47        ];
48
49        if ($plan->hubspot_subscription_status) {
50            $result[$plan->hubspot_subscription_status] = 'Active';
51        }
52
53        if ($plan->hubspot_subscription_status_updated_on) {
54            $result[$plan->hubspot_subscription_status_updated_on] = new UTCDateTime($now->timestamp * 1000);
55        }
56
57        if ($plan->hubspot_subscription_start_date) {
58            $result[$plan->hubspot_subscription_start_date] = new UTCDateTime($user->created_at->timestamp * 1000);
59        }
60
61        return $result;
62    }
63
64    public function endFreemiumSubscription(string $user_id, $date): array
65    {
66        $user = User::withTrashed()->find($user_id);
67        $plan = Plans::where('identifier', 'freemium')->first();
68        $result = [];
69
70        if ($plan->hubspot_subscription_status) {
71            $result[$plan->hubspot_subscription_status] = 'Canceled';
72        }
73
74        if ($plan->hubspot_subscription_status_updated_on) {
75            $result[$plan->hubspot_subscription_status_updated_on] = new UTCDateTime($date->timestamp * 1000);
76        }
77
78        if ($plan->hubspot_subscription_start_date) {
79            $result[$plan->hubspot_subscription_start_date] = new UTCDateTime($user->created_at->timestamp * 1000);
80        }
81
82        if ($plan->hubspot_cancel_subscription_date) {
83            $result[$plan->hubspot_cancel_subscription_date] = new UTCDateTime($date->timestamp * 1000);
84        }
85
86        if ($plan->hubspot_subscription_churn_date) {
87            $result[$plan->hubspot_subscription_churn_date] = new UTCDateTime($date->timestamp * 1000);
88        }
89
90        return $result;
91    }
92
93    public function startSubscription(array $fields, Subscription $subscription): array
94    {
95        $map = [
96            'stripe_plan' => ['rename' => 'subscription_type', 'transform' => fn ($value) => $value ? Plans::where('stripe_id', $value)->first()?->hubspot_name : null],
97        ];
98        $result = $this->mapObject($fields, $map);
99
100        if (! empty($fields['stripe_plan'])) {
101            $user = User::find($subscription->user_id);
102            $plan = Plans::where('stripe_id', $fields['stripe_plan'])->first();
103            $result['subscription_type'] = $plan->hubspot_name;
104            $result['flymsg_last_product_purchased'] = $plan->hubspot_last_product;
105            $result['plan_name'] = $plan->title;
106            $result['plan_id'] = $plan->id;
107
108            if ($plan->hubspot_payment_status) {
109                $result[$plan->hubspot_payment_status] = 'Succeeded';
110            }
111
112            if ($plan->hubspot_subscription_status) {
113                $result[$plan->hubspot_subscription_status] = $subscription->trial_ends_at ? 'Trial' : 'Active';
114            }
115
116            if ($plan->hubspot_subscription_status_updated_on) {
117                $result[$plan->hubspot_subscription_status_updated_on] = new UTCDateTime($subscription->created_at->timestamp * 1000);
118            }
119
120            if ($plan->hubspot_subscription_start_date) {
121                $result[$plan->hubspot_subscription_start_date] = new UTCDateTime($subscription->created_at->timestamp * 1000);
122            }
123
124            if ($plan->hubspot_subscription_frequency) {
125                if (Str::contains($plan->identifier, 'pro')) {
126                    $frequency = match (true) {
127                        Str::contains($plan->identifier, 'monthly') => 'Monthly',
128                        Str::contains($plan->identifier, 'appsumo-growth-lifetime') => 'Lifetime - AppSumo',
129                        Str::contains($plan->identifier, 'dealfuel-growth-lifetime') => 'Lifetime - DealFuel',
130                        default => 'Annual',
131                    };
132                } else {
133                    $frequency = match (true) {
134                        Str::contains($plan->identifier, 'yearly') => 'Annual',
135                        Str::contains($plan->identifier, 'appsumo-growth-lifetime') => 'Lifetime - AppSumo',
136                        Str::contains($plan->identifier, 'dealfuel-growth-lifetime') => 'Lifetime - DealFuel',
137                        default => 'Monthly',
138                    };
139                }
140
141                $result[$plan->hubspot_subscription_frequency] = $frequency;
142            }
143
144            if ($plan->hubspot_subscription_expiration_date) {
145                $startsAt = $subscription->created_at;
146                $cycleEndsAt = $subscription->ends_at;
147
148                if ($result[$plan->hubspot_subscription_frequency] === 'Monthly') {
149                    $cycleEndsAt = $startsAt->addMonth();
150                    while ($cycleEndsAt->isBefore(now()->addDay())) {
151                        $cycleEndsAt = $cycleEndsAt->addMonth();
152                    }
153                }
154
155                if ($result[$plan->hubspot_subscription_frequency] === 'Annual') {
156                    $cycleEndsAt = $startsAt->addYear();
157                    while ($cycleEndsAt->isBefore(now()->addDay())) {
158                        $cycleEndsAt = $cycleEndsAt->addYear();
159                    }
160                }
161
162                $result[$plan->hubspot_subscription_expiration_date] = match (true) {
163                    ! empty($subscription->trial_ends_at) => new UTCDateTime($subscription->trial_ends_at->timestamp * 1000),
164                    Str::contains($plan->identifier, 'lifetime') => new UTCDateTime(4070880000000),
165                    ! empty($subscription->ends_at) => new UTCDateTime($subscription->ends_at->timestamp * 1000),
166                    $result[$plan->hubspot_subscription_frequency] === 'Monthly' || $result[$plan->hubspot_subscription_frequency] === 'Annual' => new UTCDateTime($cycleEndsAt->timestamp * 1000),
167                    default => null,
168                };
169            }
170
171            $result['appsumo_flymsg_lifetime_access'] = Str::contains($plan->identifier, 'appsumo-growth-lifetime') ? 'true' : 'false';
172
173            if (config('app.env') === 'production') {
174                if ($plan->hubspot_subscription_monthly_recurring_revenue && $plan->hubspot_subscription_annual_recurring_revenue) {
175                    $trialEndsAt = $subscription->trial_ends_at;
176                    $invoices = $user->invoices()->toArray();
177                    $invoices = array_reverse($invoices);
178
179                    // Two  diffrent plans handle
180                    foreach ($invoices as $i => $invoice) {
181                        $invoiceDataIndex = $invoice['lines']['total_count'] > 0 ? $invoice['lines']['total_count'] - 1 : 0;
182                        if (empty($invoice['lines']['data'][$invoiceDataIndex]['subscription']) && $invoice['billing_reason'] == 'manual') {
183                            continue;
184                        }
185
186                        $interval = $invoice['lines']['data'][$invoiceDataIndex]['plan']['interval'];
187
188                        $price = $invoice['lines']['data'][$invoiceDataIndex]['price']['unit_amount_decimal'] / 100;
189
190                        if ($interval == 'month') {
191                            $interval = 'Monthly';
192                            $annual_recurring_revenue = $price * 12;
193                            $monthly_recurring_revenue = $price;
194                        } else {
195                            $interval = 'Annual';
196                            $annual_recurring_revenue = $price;
197                            $monthly_recurring_revenue = $price / 12;
198                        }
199
200                        if (isset($plan->identifier) && $plan->identifier == 'sales-pro-monthly' && isset($trialEndsAt)) {
201                            $annual_recurring_revenue = 0;
202                            $monthly_recurring_revenue = 0;
203                        }
204                    }
205
206                    if (! empty($monthly_recurring_revenue)) {
207                        $result[$plan->hubspot_subscription_monthly_recurring_revenue] = $monthly_recurring_revenue;
208                    }
209
210                    if (! empty($annual_recurring_revenue)) {
211                        $result[$plan->hubspot_subscription_annual_recurring_revenue] = $annual_recurring_revenue;
212                    }
213
214                    if ($plan->identifier == 'appsumo-growth-lifetime') {
215                        $result['flymsg_growth_subscription_monthly_recurring_revenue'] = 4;
216                        $result['growth_subscription_annual_recurring_revenue'] = 48;
217                    }
218                }
219            }
220
221            if ($plan->hubspot_subscription_plan_type) {
222                $planType = 'SMB';
223                if (! Str::contains($plan->identifier, 'smb')) {
224                    $planType = 'Enterprise';
225                }
226                $result[$plan->hubspot_subscription_plan_type] = $planType;
227            }
228
229            if ($plan->hubspot_user_type) {
230                $userRoles = $user->roles();
231
232                $result[$plan->hubspot_user_type] = implode(';', array_map(function ($role) {
233                    $newRole = match ($role) {
234                        'Reporting Admin' => 'Reporting POC',
235                        'Group Admin' => 'Group Admin Manager',
236                        default => $role,
237                    };
238
239                    if (empty($newRole)) {
240                        return 'User';
241                    }
242
243                    return $newRole;
244                }, $userRoles));
245
246                if (empty($result[$plan->hubspot_user_type])) {
247                    $result[$plan->hubspot_user_type] = 'User';
248                }
249            }
250
251            if ($plan->hubspot_cancel_subscription_date) {
252                $result[$plan->hubspot_cancel_subscription_date] = '';
253            }
254
255            if ($plan->hubspot_subscription_churn_date) {
256                $result[$plan->hubspot_subscription_churn_date] = '';
257            }
258
259            if ($subscription->trial_ends_at) {
260                $result['trail_period_start_date'] = new UTCDateTime($subscription->created_at->timestamp * 1000);
261                $result['trail_period_end_date'] = new UTCDateTime($subscription->trial_ends_at->timestamp * 1000);
262            }
263
264            if ($plan->identifier !== 'freemium') {
265                $freemium = $this->endFreemiumSubscription($subscription->user_id, $subscription->created_at);
266                $result = array_merge($result, $freemium);
267            }
268        }
269
270        return $result;
271    }
272
273    public function endSubscription(array $fields, Subscription $subscription, bool $deleteUser = false, $cancellation_date = null): array
274    {
275        $map = [
276            // 'stripe_plan' => ['rename' => 'subscription_type', 'transform' => fn($value) => $value ? Plans::where('stripe_id', $value)->first()?->hubspot_name : null],
277        ];
278        $result = $this->mapObject($fields, $map);
279
280        if (! empty($fields['stripe_plan'])) {
281            $now = Carbon::now();
282
283            if (! empty($cancellation_date)) {
284                $now = $cancellation_date;
285            }
286
287            $plan = Plans::where('stripe_id', $fields['stripe_plan'])->first();
288
289            if (! $plan) {
290                return $result;
291            }
292
293            if ($plan->hubspot_subscription_status) {
294                $result[$plan->hubspot_subscription_status] = $subscription->trial_ends_at ? 'Expired' : 'Canceled';
295            }
296
297            if ($plan->hubspot_subscription_status_updated_on) {
298                $result[$plan->hubspot_subscription_status_updated_on] = new UTCDateTime($now->timestamp * 1000);
299            }
300
301            if ($plan->hubspot_subscription_start_date) {
302                $result[$plan->hubspot_subscription_start_date] = new UTCDateTime($subscription->created_at->timestamp * 1000);
303            }
304
305            if ($plan->hubspot_subscription_frequency) {
306                if (Str::contains($plan->identifier, 'pro')) {
307                    $frequency = match (true) {
308                        Str::contains($plan->identifier, 'monthly') => 'Monthly',
309                        Str::contains($plan->identifier, 'appsumo-growth-lifetime') => 'Lifetime - AppSumo',
310                        Str::contains($plan->identifier, 'dealfuel-growth-lifetime') => 'Lifetime - DealFuel',
311                        default => 'Annual',
312                    };
313                } else {
314                    $frequency = match (true) {
315                        Str::contains($plan->identifier, 'yearly') => 'Annual',
316                        Str::contains($plan->identifier, 'appsumo-growth-lifetime') => 'Lifetime - AppSumo',
317                        Str::contains($plan->identifier, 'dealfuel-growth-lifetime') => 'Lifetime - DealFuel',
318                        default => 'Monthly',
319                    };
320                }
321
322                $result[$plan->hubspot_subscription_frequency] = $frequency;
323            }
324
325            if ($plan->hubspot_subscription_expiration_date) {
326                $result[$plan->hubspot_subscription_expiration_date] = match (true) {
327                    ! empty($subscription->trial_ends_at) => new UTCDateTime($subscription->trial_ends_at->timestamp * 1000),
328                    Str::contains($plan->identifier, 'lifetime') => new UTCDateTime(4070880000000),
329                    ! empty($subscription->ends_at) => new UTCDateTime($subscription->ends_at->timestamp * 1000),
330                    default => null,
331                };
332            }
333
334            if ($plan->hubspot_cancel_subscription_date) {
335                $result[$plan->hubspot_cancel_subscription_date] = new UTCDateTime($now->timestamp * 1000);
336            }
337
338            if ($plan->hubspot_subscription_churn_date) {
339                $result[$plan->hubspot_subscription_churn_date] = new UTCDateTime($now->timestamp * 1000);
340            }
341
342            if ($subscription->trial_ends_at) {
343                $result['trail_period_canceled_date'] = new UTCDateTime($now->timestamp * 1000);
344                $result['trail_period_start_date'] = new UTCDateTime($subscription->created_at->timestamp * 1000);
345                $result['trail_period_end_date'] = new UTCDateTime($subscription->trial_ends_at->timestamp * 1000);
346            }
347
348            // if there is no other premium subscription
349            if (! $this->checkPremiumSubscriptionsActive($subscription->user_id, $plan->hubspot_subscription_status) && ! $deleteUser) {
350                $freemium = $this->initFreemiumSubscription($subscription->user_id, $cancellation_date);
351                $result = array_merge($result, $freemium);
352
353                // check another premium active subscription
354                $sub = Subscription::where('user_id', $subscription->user_id)->where('trial_ends_at', null)->where('stripe_status', 'active')->first();
355                if (! empty($sub)) {
356                    $keepSub = $this->startSubscription($sub->toArray(), $sub);
357                    $result = array_merge($result, $keepSub);
358                }
359            }
360        }
361
362        return $result;
363    }
364
365    public function checkPremiumSubscriptionsActive(string $userId, string $planId): bool
366    {
367        $plans = Plans::where('identifier', '!=', 'freemium')->where('hubspot_subscription_status', '!=', $planId)->get();
368
369        $userInfo = UserInfo::firstWhere('user_id', $userId);
370
371        if (! $userInfo) {
372            return false;
373        }
374
375        foreach ($plans as $plan) {
376            if ($userInfo->{$plan->hubspot_subscription_status} === 'Active') {
377                return true;
378            }
379        }
380
381        return false;
382    }
383
384    public function getCountryName($default_payment): string
385    {
386        $names = json_decode(file_get_contents(base_path('names.json')), true);
387        $countryName = '';
388        if (isset($default_payment->billing_details->address->country)) {
389            $countryName = $names[$default_payment->billing_details->address->country];
390        }
391
392        return $countryName;
393    }
394
395    public function parseFields(array $fields, Subscription $subscription): array
396    {
397        $data = [
398            'user_id' => $subscription->user_id,
399            'subscription_id' => $subscription->id,
400        ];
401
402        if (! empty($fields['stripe_status']) && ($fields['stripe_status'] === 'active' || $fields['stripe_status'] === 'trialing')) {
403            $data = array_merge($data, $this->startSubscription($fields, $subscription));
404            $user = User::find($subscription->user_id);
405            $default_payment = $user->defaultPaymentMethod();
406
407            $data['subscription_owner'] = $default_payment->billing_details->name ?? '';
408            $data['billing_address_line_1'] = $default_payment->billing_details->address->line1 ?? '';
409            $data['billing_address_line_2'] = $default_payment->billing_details->address->line2 ?? '';
410            $data['billing_city'] = $default_payment->billing_details->address->city ?? '';
411            $data['billing_state'] = $default_payment->billing_details->address->state ?? '';
412            $data['billing_zip'] = $default_payment->billing_details->address->postal_code ?? '';
413            $data['billing_country'] = $this->getCountryName($default_payment);
414            $data['number_of_completed_payments'] = $user->invoices()->count();
415            $data['number_of_expected_payments'] = $user->invoicesIncludingPending()->count();
416            $data['payment_method'] = 'Credit Card';
417
418            if ($user->invoices()->count() > 0) {
419                $invoices = $user->invoices()->toArray();
420                $data['last_total_invoice_amount'] = $invoices[0]['total'] / 100;
421                $data['flymsg_total_sales'] = collect($invoices)->sum('amount_paid') / 100;
422
423                if ($invoices && ! empty($invoices[0]) && ! empty($invoices[0]['discount']) && ! empty($invoices[0]['discount']['coupon'])) {
424                    $coupon = $invoices[0]['discount']['coupon'];
425                    if ($coupon) {
426                        $data['coupon_name'] = $coupon['name'];
427                        $data['coupon_code'] = $coupon['id'];
428                        $data['coupon_type'] = $coupon['percent_off'] ? 'Percentage' : 'Fixed Amout';
429                        $data['duration'] = $coupon['duration'];
430                        $data['coupon_redemption_limits'] = $coupon['max_redemptions'];
431                        $data['coupon_value__discount_'] = $coupon['percent_off'] != null ? $coupon['percent_off'] : $coupon['amount_off'];
432                    }
433                }
434            }
435        } else {
436            $data = array_merge($data, $this->endSubscription($fields, $subscription));
437        }
438
439        return $data;
440    }
441}