Passed
Pull Request — master (#41)
by Mike
02:12
created

RecordStripeEvent::recordCharge()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 4
nop 2
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
1
<?php namespace GeneaLabs\LaravelMixpanel\Http\Requests;
2
3
use Carbon\Carbon;
4
use GeneaLabs\LaravelMixpanel\Events\MixpanelEvent;
5
use Illuminate\Foundation\Http\FormRequest;
6
7
class RecordStripeEvent extends FormRequest
8
{
9
    public function authorize() : bool
10
    {
11
        return true;
12
    }
13
14
    public function rules() : array
15
    {
16
        return [
17
            //
18
        ];
19
    }
20
21
    public function process()
22
    {
23
        $data = $this->json()->all();
24
25
        if (! $data || ! array_key_exists('data', $data)) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
26
            return;
27
        }
28
29
        $transaction = $data['data']['object'];
30
        $originalValues = array_key_exists('previous_attributes', $data['data'])
31
            ? $data['data']['previous_attributes']
32
            : [];
33
        $stripeCustomerId = $this->findStripeCustomerId($transaction);
34
        $authModel = config('auth.providers.users.model') ?? config('auth.model');
35
        $user = app($authModel)->where('stripe_id', $stripeCustomerId)->first();
1 ignored issue
show
Bug introduced by
The method where() does not exist on Illuminate\Container\Container. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

35
        $user = app($authModel)->/** @scrutinizer ignore-call */ where('stripe_id', $stripeCustomerId)->first();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
36
37
        if (! $user) {
38
            return;
39
        }
40
41
        app('mixpanel')->identify($user->id);
42
43
        if ($transaction['object'] === 'charge' && ! count($originalValues)) {
44
            $this->recordCharge($transaction, $user);
45
        }
46
47
        if ($transaction['object'] === 'subscription') {
48
            $this->recordSubscription($transaction, $user, $originalValues);
49
        }
50
    }
51
52
    private function recordCharge(array $transaction, $user)
53
    {
54
        $charge = 0;
55
        $amount = $transaction['amount'] / 100;
56
        $status = 'Failed';
57
58
        if ($transaction['paid']) {
59
            $status = 'Authorized';
60
61
            if ($transaction['captured']) {
62
                $status = 'Successful';
63
64
                if ($transaction['refunded']) {
65
                    $status = 'Refunded';
66
                }
67
            }
68
        }
69
70
        $trackingData = [
71
            'Payment',
72
            [
73
                'Status' => $status,
74
                'Amount' => $amount,
75
            ],
76
        ];
77
78
        event(new MixpanelEvent($user, $trackingData, $charge));
0 ignored issues
show
Bug introduced by
$trackingData of type array<integer,array<stri...integer|string>|string> is incompatible with the type string expected by parameter $eventName of GeneaLabs\LaravelMixpane...nelEvent::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

78
        event(new MixpanelEvent($user, /** @scrutinizer ignore-type */ $trackingData, $charge));
Loading history...
79
    }
80
81
    private function recordSubscription(array $transaction, $user, array $originalValues = [])
82
    {
83
        $planStatus = array_key_exists('status', $transaction) ? $transaction['status'] : null;
84
        $planName = isset($transaction['plan']['name']) ? $transaction['plan']['name'] : null;
85
        $planStart = array_key_exists('start', $transaction) ? $transaction['start'] : null;
86
        $planAmount = isset($transaction['plan']['amount']) ? $transaction['plan']['amount'] : null;
87
        $oldPlanName = isset($originalValues['plan']['name']) ? $originalValues['plan']['name'] : null;
88
        $oldPlanAmount = isset($originalValues['plan']['amount']) ? $originalValues['plan']['amount'] : null;
89
90
        if ($planStatus === 'canceled') {
91
            $profileData = [
92
                'Subscription' => 'None',
93
                'Churned' => Carbon::parse($transaction['canceled_at'])->format('Y-m-d\Th:i:s'),
94
                'Plan When Churned' => $planName,
95
                'Paid Lifetime' => Carbon::createFromTimestampUTC($planStart)
96
                  ->diffInDays(Carbon::timestamp($transaction['ended_at'])
0 ignored issues
show
Bug Best Practice introduced by
The method Carbon\Carbon::timestamp() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

96
                  ->diffInDays(Carbon::/** @scrutinizer ignore-call */ timestamp($transaction['ended_at'])
Loading history...
97
                  ->timezone('UTC')) . ' days'
98
            ];
99
            $trackingData = [
100
                ['Subscription', ['Status' => 'Canceled', 'Upgraded' => false]],
101
                ['Churn! :-('],
102
            ];
103
        }
104
105
        if (count($originalValues)) {
106
            if ($planAmount && $oldPlanAmount) {
107
                if ($planAmount < $oldPlanAmount) {
108
                    $profileData = [
109
                        'Subscription' => $planName,
110
                        'Churned' => Carbon::timestamp($transaction['ended_at'])
111
                          ->timezone('UTC')
112
                          ->format('Y-m-d\Th:i:s'),
113
                        'Plan When Churned' => $oldPlanName,
114
                    ];
115
                    $trackingData = [
116
                        ['Subscription', [
117
                            'Upgraded' => false,
118
                            'FromPlan' => $oldPlanName,
119
                            'ToPlan' => $planName,
120
                        ]],
121
                        ['Churn! :-('],
122
                    ];
123
                }
124
125 View Code Duplication
                if ($planAmount > $oldPlanAmount) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
                    $profileData = [
127
                        'Subscription' => $planName,
128
                    ];
129
                    $trackingData = [
130
                        ['Subscription', [
131
                            'Upgraded' => true,
132
                            'FromPlan' => $oldPlanName,
133
                            'ToPlan' => $planName,
134
                        ]],
135
                        ['Unchurn! :-)'],
136
                    ];
137
                }
138
            } else {
139 View Code Duplication
                if ($planStatus === 'trialing' && ! $oldPlanName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
                    $profileData = [
141
                        'Subscription' => $planName,
142
                    ];
143
                    $trackingData = [
144
                        ['Subscription', [
145
                            'Upgraded' => true,
146
                            'FromPlan' => 'Trial',
147
                            'ToPlan' => $planName,
148
                        ]],
149
                        ['Unchurn! :-)'],
150
                    ];
151
                }
152
            }
153
        } else {
154 View Code Duplication
            if ($planStatus === 'active') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
                $profileData = [
156
                    'Subscription' => $planName,
157
                ];
158
                $trackingData = [
159
                    ['Subscription', ['Status' => 'Created']],
160
                ];
161
            }
162
163 View Code Duplication
            if ($planStatus === 'trialing') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
                $profileData = [
165
                    'Subscription' => 'Trial',
166
                ];
167
                $trackingData = [
168
                    ['Subscription', ['Status' => 'Trial']],
169
                ];
170
            }
171
        }
172
173
        event(new MixpanelEvent($user, $trackingData, 0, $profileData));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $trackingData does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $profileData does not seem to be defined for all execution paths leading up to this point.
Loading history...
174
    }
175
176
    private function findStripeCustomerId(array $transaction)
177
    {
178
        if (array_key_exists('customer', $transaction)) {
179
            return $transaction['customer'];
180
        }
181
182
        if (array_key_exists('object', $transaction) && $transaction['object'] === 'customer') {
183
            return $transaction['id'];
184
        }
185
186
        if (array_key_exists('subscriptions', $transaction)
187
            && array_key_exists('data', $transaction['subscriptions'])
188
            && array_key_exists(0, $transaction['subscriptions']['data'])
189
            && array_key_exists('customer', $transaction['subscriptions']['data'][0])
190
        ) {
191
            return $transaction['subscriptions']['data'][0]['customer'];
192
        }
193
    }
194
}
195