PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.23
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.23
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / app / Http / Controllers / FrontendControllers / CustomerSubscriptionController.php

CustomerSubscriptionController.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.23, at app/Http/Controllers/FrontendControllers/CustomerSubscriptionController.php

454 lines 16.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Http\Controllers\FrontendControllers;
4
5 use FluentCart\Api\Resource\CustomerResource;
6 use FluentCart\App\App;
7 use FluentCart\App\Helpers\Status;
8 use FluentCart\App\Models\OrderTransaction;
9 use FluentCart\App\Models\Subscription;
10 use FluentCart\App\Modules\PaymentMethods\StripeGateway\SubscriptionsManager;
11 use FluentCart\App\Modules\Subscriptions\Services\EarlyPaymentFeature;
12 use FluentCart\App\Services\OrderService;
13 use FluentCart\Framework\Http\Request\Request;
14 use FluentCart\Framework\Support\Arr;
15
16
17 class CustomerSubscriptionController extends BaseFrontendController
18 {
19 public function getSubscriptions(Request $request): \WP_REST_Response
20 {
21 // Call the method and store the response
22 $errorResponse = $this->checkUserLoggedIn();
23
24 // Check if there is an error response and return it if exists
25 if ($errorResponse !== null) {
26 // Return error if user is not logged in
27 return $this->sendSuccess([
28 'message' => __('Success', 'fluent-cart'),
29 'subscriptions' => [
30 'data' => [],
31 'total' => 0,
32 'per_page' => 10,
33 'current_page' => 1,
34 'last_page' => 1
35 ]
36 ]);
37 }
38
39 $perPage = (int)$request->get('per_page', 10);
40 $page = (int)$request->get('page', 1);
41
42 // Get the current logged-in customer
43 $customer = CustomerResource::getCurrentCustomer();
44
45 if (!$customer) {
46 return $this->sendError([
47 'message' => __('Customer not found', 'fluent-cart')
48 ]);
49 }
50
51
52 // Query the Subscription model to retrieve subscriptions for the logged-in customer
53 // Include associated order and product data using 'with' for eager loading
54 // Sort the results by 'id' in descending order and fetch the data
55 $subscriptions = Subscription::query()
56 ->where('customer_id', $customer->id)
57 ->whereNotIn('status', [Status::SUBSCRIPTION_PENDING, Status::SUBSCRIPTION_INTENDED])
58 ->orderBy('id', 'desc')
59 ->paginate($perPage, ['*'], 'page', $page);
60
61
62 $formattedSubscriptions = $subscriptions->map(function ($subscription) {
63 return OrderService::transformSubscription($subscription);
64 });
65
66 return $this->sendSuccess([
67 'message' => __('Success', 'fluent-cart'),
68 'subscriptions' => [
69 'data' => $formattedSubscriptions,
70 'total' => $subscriptions->total(),
71 'per_page' => $subscriptions->perPage(),
72 'current_page' => $subscriptions->currentPage(),
73 'last_page' => $subscriptions->lastPage()
74 ],
75 ]);
76 }
77
78 public function getSubscription($subscription_uuid): \WP_REST_Response
79 {
80 $errorResponse = $this->checkUserLoggedIn();
81
82 if ($errorResponse !== null) {
83 return $errorResponse;
84 }
85
86 // Get the current logged-in customer
87 $customer = CustomerResource::getCurrentCustomer();
88
89 if (!$customer) {
90 return $this->sendError([
91 'message' => __('Customer not found', 'fluent-cart')
92 ]);
93 }
94
95 $subscription = Subscription::query()
96 ->where('customer_id', $customer->id)
97 ->with(['product', 'variation', 'billing_addresses'])
98 ->where('uuid', $subscription_uuid)
99 ->first();
100
101 if (!$subscription || in_array($subscription->status, [Status::SUBSCRIPTION_PENDING, Status::SUBSCRIPTION_INTENDED])) {
102 return $this->sendError([
103 'message' => __('Subscription not found', 'fluent-cart')
104 ]);
105 }
106
107 $formattedData = [
108 'uuid' => $subscription->uuid,
109 'status' => $subscription->status,
110 'overridden_status' => $subscription->overridden_status,
111 'vendor_subscription_id' => $subscription->vendor_subscription_id,
112 'next_billing_date' => $subscription->next_billing_date,
113 'billing_info' => $subscription->billingInfo,
114 'current_payment_method' => $subscription->current_payment_method,
115 'payment_method' => $subscription->payment_method,
116 'payment_info' => $subscription->payment_info,
117 'bill_times' => $subscription->bill_times,
118 'bill_count' => $subscription->bill_count,
119 'variation_id' => $subscription->variation_id,
120 'product_id' => $subscription->product_id,
121 'config' => $subscription->config,
122 'reactivate_url' => $subscription->getReactivateUrl(),
123 'title' => $subscription->product ? $subscription->product->post_title : $subscription->item_name,
124 'subtitle' => $subscription->variation && $subscription->product ? $subscription->variation->variation_title : '',
125 'can_upgrade' => $subscription->canUpgrade(),
126 'can_switch_payment_method' => $subscription->canSwitchPaymentMethod(),
127 'switchable_payment_methods' => $subscription->switchablePaymentMethods(),
128 'can_update_payment_method' => $subscription->canUpdatePaymentMethod(),
129 'order' => [
130 'uuid' => $subscription->order ? $subscription->order->uuid : ''
131 ],
132 'billing_addresses' => $subscription->billing_addresses,
133 'recurring_amount' => $subscription->recurring_amount,
134 'can_early_pay' => EarlyPaymentFeature::canPay($subscription),
135 'remaining_installments' => max(0, $subscription->bill_times - $subscription->bill_count),
136 ];
137
138
139 // Let's find all the transactions related to this order
140 $orderIds = array_filter([$subscription->order->id, $subscription->order->parent_id]);
141 if ($subscription->order->type === Status::ORDER_TYPE_SUBSCRIPTION) {
142 $renewalOrderIds = $subscription->order->renewals->pluck('id')->toArray();
143 $orderIds = array_merge($orderIds, $renewalOrderIds);
144 $orderIds = array_values(array_unique($orderIds));
145 }
146
147 $transactions = OrderTransaction::query()
148 ->whereIn('order_id', $orderIds)
149 ->with(['order'])
150 ->orderBy('id', 'DESC')
151 ->get();
152
153 $formattedData['transactions'] = $transactions->map(function ($transaction) {
154 return OrderService::transformTransaction($transaction);
155 });
156
157 $formattedData = apply_filters('fluent_cart/customer_portal/subscription_data', $formattedData, [
158 'subscription' => $subscription,
159 'customer' => $customer
160 ]);
161
162 return $this->sendSuccess([
163 'message' => __('Success', 'fluent-cart'),
164 'subscription' => $formattedData
165 ]);
166 }
167
168 public function updatePaymentMethod(Request $request, $subscription_uuid)
169 {
170 $data = $request->input('data');
171 $method = $data['method'];
172 $subscriptionUuid = $subscription_uuid;
173
174 if (!$method || !$subscriptionUuid) {
175 return $this->sendError([
176 'message' => __('Method and Subscription Id required.', 'fluent-cart')
177 ]);
178 }
179
180 $subscription = Subscription::query()->where('uuid', $subscriptionUuid)->first();
181
182 if (empty($subscription)) {
183 return $this->sendError([
184 'message' => __('Subscription not found', 'fluent-cart')
185 ]);
186 }
187
188 if (App::gateway()->has($method)) {
189 try {
190 App::gateway($method)->subscriptions->cardUpdate($data, $subscription->id);
191 } catch (\Exception $e) {
192 return $this->sendError([
193 'message' => $e->getMessage()
194 ]);
195 }
196 }
197
198 return $this->sendError([
199 'message' => __('Could not update payment method', 'fluent-cart')
200 ]);
201 }
202
203 public function getOrCreatePlan(Request $request, $subscription_uuid)
204 {
205 $data = $request->input('data');
206 $method = sanitize_text_field(Arr::get($data, 'method', ''));
207 $reason = sanitize_text_field(Arr::get($data, 'reason', ''));
208
209 if (!$method || !$subscription_uuid) {
210 return $this->sendError([
211 'message' => __('Missing required parameters', 'fluent-cart')
212 ]);
213 }
214
215 $subscription = Subscription::query()->where('uuid', $subscription_uuid)->first();
216 if (empty($subscription)) {
217 return $this->sendError([
218 'message' => __('Subscription not found', 'fluent-cart')
219 ]);
220 }
221
222 if (APP::gateway()->has($method)) {
223 try {
224 APP::gateway($method)->subscriptions->getOrCreateNewPlan($subscription->id, $reason);
225 } catch (\Exception $e) {
226 return $this->sendError([
227 'message' => $e->getMessage()
228 ]);
229 }
230 }
231
232 return $this->sendError([
233 'message' => __('Could not get or create plan', 'fluent-cart')
234 ]);
235 }
236
237 public function switchPaymentMethod(Request $request, $subscription_uuid)
238 {
239 $errorResponse = $this->checkUserLoggedIn();
240
241 if ($errorResponse !== null) {
242 return $errorResponse;
243 }
244
245 $data = $request->input('data');
246 $newPaymentMethod = sanitize_text_field(Arr::get($data, 'newPaymentMethod', ''));
247 $currentPaymentMethod = sanitize_text_field(Arr::get($data, 'currentPaymentMethod', ''));
248
249 if (!$newPaymentMethod || !$currentPaymentMethod || !$subscription_uuid) {
250 return $this->sendError([
251 'message' => __('Missing required parameters', 'fluent-cart')
252 ]);
253 }
254
255 $subscription = Subscription::query()->where('uuid', $subscription_uuid)->first();
256 if (empty($subscription)) {
257 return $this->sendError([
258 'message' => __('Subscription not found', 'fluent-cart')
259 ]);
260 }
261
262 if (App::gateway()->has($newPaymentMethod)) {
263 try {
264 App::gateway($newPaymentMethod)->subscriptions->switchPaymentMethod($data, $subscription->id);
265 } catch (\Exception $e) {
266 return $this->sendError([
267 'message' => $e->getMessage()
268 ]);
269 }
270 }
271
272 return $this->sendError([
273 'message' => __('Could not switch payment method', 'fluent-cart')
274 ]);
275
276 }
277
278 // needed in case of two step switch payment method
279 public function confirmSubscriptionSwitch(Request $request, $subscription_uuid)
280 {
281 $errorResponse = $this->checkUserLoggedIn();
282
283 if ($errorResponse !== null) {
284 return $errorResponse;
285 }
286
287 $data = $request->input('data');
288 $newVendorSubscriptionId = sanitize_text_field(Arr::get($data, 'newVendorSubscriptionId', ''));
289 $method = sanitize_text_field(Arr::get($data, 'method', ''));
290
291 if (!$newVendorSubscriptionId || !$method || !$subscription_uuid) {
292 return $this->sendError([
293 'message' => __('Missing required parameters', 'fluent-cart')
294 ]);
295 }
296
297 $subscription = Subscription::query()->where('uuid', $subscription_uuid)->first();
298 if (empty($subscription)) {
299 return $this->sendError([
300 'message' => __('Subscription not found', 'fluent-cart')
301 ]);
302 }
303
304 if (APP::gateway()->has($method)) {
305 try {
306 APP::gateway($method)->subscriptions->confirmSubscriptionSwitch($data, $subscription->id);
307 } catch (\Exception $e) {
308 return $this->sendError([
309 'message' => $e->getMessage()
310 ]);
311 }
312 }
313
314 return $this->sendError([
315 'message' => __('Could not confirm subscription switch', 'fluent-cart')
316 ]);
317
318 }
319
320 public function cancelAutoRenew(Request $request, $subscription_uuid)
321 {
322 $errorResponse = $this->checkUserLoggedIn();
323 if ($errorResponse !== null) {
324 return $errorResponse;
325 }
326
327 $customer = CustomerResource::getCurrentCustomer();
328 if (!$customer) {
329 return $this->sendError([
330 'message' => __('Customer not found', 'fluent-cart')
331 ]);
332 }
333
334 $subscription = Subscription::query()
335 ->where('uuid', $subscription_uuid)
336 ->where('customer_id', $customer->id)
337 ->first();
338
339 if (empty($subscription)) {
340 return $this->sendError([
341 'message' => __('Subscription not found', 'fluent-cart')
342 ]);
343 }
344
345 $result = $subscription->cancelRemoteSubscription([
346 'reason' => 'cancelled_by_customer',
347 'note' => __('Cancelled by Customer from customer portal', 'fluent-cart'),
348 ]);
349
350 if (is_wp_error($result)) {
351 return $this->sendError([
352 'message' => __('Unable to cancel subscription at this time. Please try again later or contact support.', 'fluent-cart')
353 ]);
354 }
355
356 return [
357 'message' => __('Your subscription has been successfully cancelled', 'fluent-cart')
358 ];
359
360 }
361
362 public function initiateEarlyPayment(Request $request, $subscription_uuid)
363 {
364 $errorResponse = $this->checkUserLoggedIn();
365 if ($errorResponse !== null) {
366 return $errorResponse;
367 }
368
369 $customer = CustomerResource::getCurrentCustomer();
370 if (!$customer) {
371 return $this->sendError([
372 'message' => __('Customer not found', 'fluent-cart')
373 ]);
374 }
375
376 $subscription = Subscription::query()
377 ->where('uuid', $subscription_uuid)
378 ->where('customer_id', $customer->id)
379 ->first();
380
381 if (!$subscription) {
382 return $this->sendError([
383 'message' => __('Subscription not found', 'fluent-cart')
384 ]);
385 }
386
387 if (!EarlyPaymentFeature::canPay($subscription)) {
388 return $this->sendError([
389 'message' => __('Early payment is not available for this subscription.', 'fluent-cart')
390 ]);
391 }
392
393 $url = add_query_arg([
394 'fluent-cart' => 'early-installment-payment',
395 'subscription_hash' => $subscription->uuid,
396 ], home_url('/'));
397
398 return $this->sendSuccess([
399 'message' => __('Early payment URL generated.', 'fluent-cart'),
400 'checkout_url' => $url,
401 ]);
402 }
403
404 private function canEarlyPay(Subscription $subscription): bool
405 {
406 return App::isProActive()
407 && $subscription->bill_times > 0
408 && $subscription->bill_count < $subscription->bill_times
409 && in_array($subscription->status, [Status::SUBSCRIPTION_ACTIVE, Status::SUBSCRIPTION_TRIALING]);
410 }
411
412 public function getSetupIntentRemainingAttempts($subscription_uuid)
413 {
414 $errorResponse = $this->checkUserLoggedIn();
415
416 if ($errorResponse !== null) {
417 return $errorResponse;
418 }
419
420 $customer = CustomerResource::getCurrentCustomer();
421 if (!$customer) {
422 return $this->sendError([
423 'message' => __('Customer not found', 'fluent-cart')
424 ]);
425 }
426
427 $subscription = Subscription::query()
428 ->where('uuid', $subscription_uuid)
429 ->where('customer_id', $customer->id)
430 ->first();
431
432 if (empty($subscription)) {
433 return $this->sendError([
434 'message' => __('Subscription not found', 'fluent-cart')
435 ]);
436 }
437
438 // Only return attempts if current payment method is stripe
439 if ($subscription->current_payment_method !== 'stripe' || empty($subscription->vendor_customer_id)) {
440 return $this->sendError([
441 'message' => __('Payment method not supported', 'fluent-cart')
442 ]);
443 }
444
445 $subscriptionsManager = new SubscriptionsManager();
446 $remaining = $subscriptionsManager->getRemainingRateLimit($subscription->vendor_customer_id);
447
448 return $this->sendSuccess([
449 'remaining' => max(0, $remaining)
450 ]);
451 }
452
453 }
454