Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 120 |
|
0.00% |
0 / 16 |
CRAP | |
0.00% |
0 / 1 |
WebhookController | |
0.00% |
0 / 120 |
|
0.00% |
0 / 16 |
2352 | |
0.00% |
0 / 1 |
newSubscriptionName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
handleCustomerSubscriptionUpdated | |
0.00% |
0 / 43 |
|
0.00% |
0 / 1 |
306 | |||
handleCustomerCreated | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
handleCustomerSubscriptionDeleted | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
handleCustomerSubscriptionCreated | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
72 | |||
updateSubscriptionDetails | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
handlePaymentIntentSucceeded | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
handlePaymentIntentRequiresAction | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
handlePaymentIntentPartiallyFunded | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
handlePaymentIntentPaymentFailed | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
handlePaymentIntentCanceled | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
handlePaymentIntentProcessing | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
updatePaymentStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCurrentPlan | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getStatus | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
getSubsciptionStatus | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace App\Http\Controllers\v1; |
4 | |
5 | use Carbon\Carbon; |
6 | use App\Http\Models\Plans; |
7 | use App\Http\Models\Auth\User; |
8 | use App\Http\Models\Subscription; |
9 | use Stripe\Subscription as StripeSubscription; |
10 | use Symfony\Component\HttpFoundation\Response; |
11 | use Laravel\Cashier\Http\Controllers\WebhookController as CashierController; |
12 | |
13 | class WebhookController extends CashierController |
14 | { |
15 | protected $current_plan; |
16 | |
17 | protected function newSubscriptionName(array $payload) |
18 | { |
19 | return 'main'; |
20 | } |
21 | |
22 | /** |
23 | * Handle customer subscription updated. |
24 | */ |
25 | protected function handleCustomerSubscriptionUpdated(array $payload): Response |
26 | { |
27 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
28 | $data = $payload['data']['object']; |
29 | |
30 | $user->subscriptions->filter(function (Subscription $subscription) use ($data) { |
31 | return $subscription->stripe_id === $data['id']; |
32 | })->each(function (Subscription $subscription) use ($data, $user, $payload) { |
33 | if ( |
34 | isset($data['status']) && |
35 | $data['status'] === StripeSubscription::STATUS_INCOMPLETE_EXPIRED |
36 | ) { |
37 | $subscription->items()->delete(); |
38 | $subscription->delete(); |
39 | return; |
40 | } |
41 | |
42 | $firstItem = $data['items']['data'][0]; |
43 | $isSinglePlan = count($data['items']['data']) === 1; |
44 | |
45 | // Plan... |
46 | $subscription->stripe_plan = $isSinglePlan ? $firstItem['plan']['id'] : null; |
47 | |
48 | // Quantity... |
49 | $subscription->quantity = $isSinglePlan && isset($firstItem['quantity']) ? $firstItem['quantity'] : null; |
50 | |
51 | // Trial ending date... |
52 | if (isset($data['trial_end'])) { |
53 | $trialEnd = Carbon::createFromTimestamp($data['trial_end']); |
54 | |
55 | if (! $subscription->trial_ends_at || $subscription->trial_ends_at->ne($trialEnd)) { |
56 | $subscription->trial_ends_at = $trialEnd; |
57 | } |
58 | } |
59 | |
60 | // Cancellation date... |
61 | if (isset($data['cancel_at_period_end'])) { |
62 | if ($data['cancel_at_period_end']) { |
63 | $subscription->ends_at = $subscription->onTrial() |
64 | ? $subscription->trial_ends_at |
65 | : Carbon::createFromTimestamp($data['current_period_end']); |
66 | } elseif (isset($data['cancel_at'])) { |
67 | $subscription->ends_at = Carbon::createFromTimestamp($data['cancel_at']); |
68 | } else { |
69 | $subscription->ends_at = null; |
70 | } |
71 | } |
72 | |
73 | // Status... |
74 | if (isset($data['status'])) { |
75 | $subscription->stripe_status = $data['status']; |
76 | } |
77 | |
78 | $subscription->save(); |
79 | |
80 | // Update subscription items... |
81 | if (isset($data['items'])) { |
82 | $plans = []; |
83 | |
84 | foreach ($data['items']['data'] as $item) { |
85 | $plans[] = $item['plan']['id']; |
86 | |
87 | $subscription->items()->updateOrCreate([ |
88 | 'stripe_id' => $item['id'], |
89 | ], [ |
90 | 'stripe_plan' => $item['plan']['id'], |
91 | 'quantity' => $item['quantity'] ?? null, |
92 | ]); |
93 | } |
94 | // Delete items that aren't attached to the subscription anymore... |
95 | $subscription->items()->whereNotIn('stripe_plan', $plans)->delete(); |
96 | } |
97 | |
98 | $this->updateSubscriptionDetails($user, $payload); |
99 | }); |
100 | } |
101 | return $this->successMethod(); |
102 | } |
103 | |
104 | protected function handleCustomerCreated(array $payload) |
105 | { |
106 | if ($user = $this->getUserByStripeId($payload['data']['object']['id'])) { |
107 | } |
108 | |
109 | return $this->successMethod(); |
110 | } |
111 | |
112 | /** |
113 | * Handle a cancelled customer from a Stripe subscription. |
114 | */ |
115 | protected function handleCustomerSubscriptionDeleted(array $payload): Response |
116 | { |
117 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
118 | $user->subscriptions->filter(function ($subscription) use ($payload) { |
119 | return $subscription->stripe_id === $payload['data']['object']['id']; |
120 | })->each(function ($subscription) { |
121 | $subscription->markAsCancelled(); |
122 | }); |
123 | } |
124 | |
125 | return $this->successMethod(); |
126 | } |
127 | |
128 | /** |
129 | * Handle customer subscription created. |
130 | */ |
131 | protected function handleCustomerSubscriptionCreated(array $payload): Response |
132 | { |
133 | $user = $this->getUserByStripeId($payload['data']['object']['customer']); |
134 | |
135 | if ($user) { |
136 | $data = $payload['data']['object']; |
137 | |
138 | if (! $user->subscriptions->contains('stripe_id', $data['id'])) { |
139 | if (isset($data['trial_end'])) { |
140 | $trialEndsAt = Carbon::createFromTimestamp($data['trial_end']); |
141 | } else { |
142 | $trialEndsAt = null; |
143 | } |
144 | |
145 | $firstItem = $data['items']['data'][0]; |
146 | $isSinglePlan = count($data['items']['data']) === 1; |
147 | $coupon = null; |
148 | $coupon = $user->discount ?? $user->discount()->coupon(); |
149 | |
150 | $subscription = $user->subscriptions()->create([ |
151 | 'name' => $data['metadata']['name'] ?? $this->newSubscriptionName($payload), |
152 | 'stripe_id' => $data['id'], |
153 | 'stripe_status' => $data['status'], |
154 | 'stripe_plan' => $isSinglePlan ? $firstItem['plan']['id'] : null, |
155 | 'quantity' => $isSinglePlan && isset($firstItem['quantity']) ? $firstItem['quantity'] : null, |
156 | 'trial_ends_at' => $trialEndsAt, |
157 | 'ends_at' => null, |
158 | 'coupon' => $coupon->id, |
159 | ]); |
160 | |
161 | foreach ($data['items']['data'] as $item) { |
162 | $subscription->items()->create([ |
163 | 'stripe_id' => $item['id'], |
164 | 'stripe_plan' => $item['plan']['id'], |
165 | 'quantity' => $item['quantity'] ?? null, |
166 | ]); |
167 | } |
168 | } |
169 | $this->updateSubscriptionDetails($user, $payload); |
170 | } |
171 | |
172 | return $this->successMethod(); |
173 | } |
174 | |
175 | protected function updateSubscriptionDetails(User $user, $payload) |
176 | { |
177 | $this->current_plan = $this->getCurrentPlan($user); |
178 | } |
179 | |
180 | protected function handlePaymentIntentSucceeded(array $payload) |
181 | { |
182 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
183 | $this->updatePaymentStatus($user, $payload['data']['object']['status']); |
184 | } |
185 | } |
186 | |
187 | protected function handlePaymentIntentRequiresAction(array $payload) |
188 | { |
189 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
190 | $this->updatePaymentStatus($user, $payload['data']['object']['status']); |
191 | } |
192 | } |
193 | |
194 | protected function handlePaymentIntentPartiallyFunded(array $payload) |
195 | { |
196 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
197 | $this->updatePaymentStatus($user, $payload['data']['object']['status']); |
198 | } |
199 | } |
200 | |
201 | protected function handlePaymentIntentPaymentFailed(array $payload) |
202 | { |
203 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
204 | $this->updatePaymentStatus($user, $payload['data']['object']['status']); |
205 | } |
206 | } |
207 | |
208 | protected function handlePaymentIntentCanceled(array $payload) |
209 | { |
210 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
211 | $this->updatePaymentStatus($user, $payload['data']['object']['status']); |
212 | } |
213 | } |
214 | |
215 | protected function handlePaymentIntentProcessing(array $payload) |
216 | { |
217 | if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) { |
218 | $this->updatePaymentStatus($user, $payload['data']['object']['status']); |
219 | } |
220 | } |
221 | |
222 | protected function updatePaymentStatus(User $user, $status) {} |
223 | |
224 | public function getCurrentPlan(User $user) |
225 | { |
226 | $current_subscription = $user->subscription('main'); |
227 | |
228 | if ($current_subscription) { |
229 | return $current_subscription->plan; |
230 | } |
231 | |
232 | return Plans::select('title', 'identifier', 'features', 'currency', 'interval', 'unit_amount') |
233 | ->where('identifier', 'freemium')->first(); |
234 | } |
235 | |
236 | public function getStatus($status) |
237 | { |
238 | $status_arr = [ |
239 | StripeSubscription::STATUS_ACTIVE => 'Succeeded', |
240 | StripeSubscription::STATUS_CANCELED => 'Succeeded', |
241 | StripeSubscription::STATUS_INCOMPLETE => 'Failed', |
242 | StripeSubscription::STATUS_INCOMPLETE_EXPIRED => 'Failed', |
243 | StripeSubscription::STATUS_PAST_DUE => 'Failed', |
244 | StripeSubscription::STATUS_TRIALING => 'Pending', |
245 | StripeSubscription::STATUS_UNPAID => 'Succeeded', |
246 | ]; |
247 | |
248 | return @$status_arr[$status]; |
249 | } |
250 | |
251 | protected function getSubsciptionStatus($status) |
252 | { |
253 | $status_arr = [ |
254 | StripeSubscription::STATUS_ACTIVE => 'Active', |
255 | StripeSubscription::STATUS_PAST_DUE => 'Past due', |
256 | StripeSubscription::STATUS_UNPAID => 'Unpaid', |
257 | StripeSubscription::STATUS_UNPAID => 'Canceled', |
258 | StripeSubscription::STATUS_INCOMPLETE_EXPIRED => 'Expired', |
259 | StripeSubscription::STATUS_INCOMPLETE_EXPIRED => 'Incomplete', |
260 | ]; |
261 | |
262 | return @$status_arr[$status]; |
263 | } |
264 | } |