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