PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.0
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.5.0, at app/Http/Controllers/FrontendControllers/CustomerSubscriptionController.php

506 lines 18.2 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 $sectionParts = apply_filters('fluent_cart/customer/subscription_details_section_parts', [
163 'end_of_subscription' => ''
164 ], [
165 'subscription' => $subscription,
166 'formattedData' => $formattedData
167 ]);
168
169 $sectionParts = array_map('wp_kses_post', $sectionParts);
170
171 return $this->sendSuccess([
172 'message' => __('Success', 'fluent-cart'),
173 'subscription' => $formattedData,
174 'section_parts' => $sectionParts
175 ]);
176 }
177
178 public function updatePaymentMethod(Request $request, $subscription_uuid)
179 {
180 $errorResponse = $this->checkUserLoggedIn();
181 if ($errorResponse !== null) {
182 return $errorResponse;
183 }
184
185 $customer = CustomerResource::getCurrentCustomer();
186 if (!$customer) {
187 return $this->sendError(['message' => __('Customer not found', 'fluent-cart')]);
188 }
189
190 $data = $request->input('data');
191 $method = $data['method'];
192 $subscriptionUuid = $subscription_uuid;
193
194 if (!$method || !$subscriptionUuid) {
195 return $this->sendError([
196 'message' => __('Method and Subscription Id required.', 'fluent-cart')
197 ]);
198 }
199
200 $subscription = Subscription::query()
201 ->where('uuid', $subscriptionUuid)
202 ->where('customer_id', $customer->id)
203 ->first();
204
205 if (empty($subscription)) {
206 return $this->sendError([
207 'message' => __('Subscription not found', 'fluent-cart')
208 ]);
209 }
210
211 if (App::gateway()->has($method)) {
212 try {
213 App::gateway($method)->subscriptions->cardUpdate($data, $subscription->id);
214 } catch (\Exception $e) {
215 return $this->sendError([
216 'message' => $e->getMessage()
217 ]);
218 }
219 }
220
221 return $this->sendError([
222 'message' => __('Could not update payment method', 'fluent-cart')
223 ]);
224 }
225
226 public function getOrCreatePlan(Request $request, $subscription_uuid)
227 {
228 $errorResponse = $this->checkUserLoggedIn();
229 if ($errorResponse !== null) {
230 return $errorResponse;
231 }
232
233 $customer = CustomerResource::getCurrentCustomer();
234 if (!$customer) {
235 return $this->sendError(['message' => __('Customer not found', 'fluent-cart')]);
236 }
237
238 $data = $request->input('data');
239 $method = sanitize_text_field(Arr::get($data, 'method', ''));
240 $reason = sanitize_text_field(Arr::get($data, 'reason', ''));
241
242 if (!$method || !$subscription_uuid) {
243 return $this->sendError([
244 'message' => __('Missing required parameters', 'fluent-cart')
245 ]);
246 }
247
248 $subscription = Subscription::query()
249 ->where('uuid', $subscription_uuid)
250 ->where('customer_id', $customer->id)
251 ->first();
252 if (empty($subscription)) {
253 return $this->sendError([
254 'message' => __('Subscription not found', 'fluent-cart')
255 ]);
256 }
257
258 if (APP::gateway()->has($method)) {
259 try {
260 APP::gateway($method)->subscriptions->getOrCreateNewPlan($subscription->id, $reason);
261 } catch (\Exception $e) {
262 return $this->sendError([
263 'message' => $e->getMessage()
264 ]);
265 }
266 }
267
268 return $this->sendError([
269 'message' => __('Could not get or create plan', 'fluent-cart')
270 ]);
271 }
272
273 public function switchPaymentMethod(Request $request, $subscription_uuid)
274 {
275 $errorResponse = $this->checkUserLoggedIn();
276
277 if ($errorResponse !== null) {
278 return $errorResponse;
279 }
280
281 $data = $request->input('data');
282 $newPaymentMethod = sanitize_text_field(Arr::get($data, 'newPaymentMethod', ''));
283 $currentPaymentMethod = sanitize_text_field(Arr::get($data, 'currentPaymentMethod', ''));
284
285 $customer = CustomerResource::getCurrentCustomer();
286 if (!$customer) {
287 return $this->sendError(['message' => __('Customer not found', 'fluent-cart')]);
288 }
289
290 if (!$newPaymentMethod || !$currentPaymentMethod || !$subscription_uuid) {
291 return $this->sendError([
292 'message' => __('Missing required parameters', 'fluent-cart')
293 ]);
294 }
295
296 $subscription = Subscription::query()
297 ->where('uuid', $subscription_uuid)
298 ->where('customer_id', $customer->id)
299 ->first();
300 if (empty($subscription)) {
301 return $this->sendError([
302 'message' => __('Subscription not found', 'fluent-cart')
303 ]);
304 }
305
306 if (App::gateway()->has($newPaymentMethod)) {
307 try {
308 App::gateway($newPaymentMethod)->subscriptions->switchPaymentMethod($data, $subscription->id);
309 } catch (\Exception $e) {
310 return $this->sendError([
311 'message' => $e->getMessage()
312 ]);
313 }
314 }
315
316 return $this->sendError([
317 'message' => __('Could not switch payment method', 'fluent-cart')
318 ]);
319
320 }
321
322 // needed in case of two step switch payment method
323 public function confirmSubscriptionSwitch(Request $request, $subscription_uuid)
324 {
325 $errorResponse = $this->checkUserLoggedIn();
326
327 if ($errorResponse !== null) {
328 return $errorResponse;
329 }
330
331 $data = $request->input('data');
332 $newVendorSubscriptionId = sanitize_text_field(Arr::get($data, 'newVendorSubscriptionId', ''));
333 $method = sanitize_text_field(Arr::get($data, 'method', ''));
334
335 $customer = CustomerResource::getCurrentCustomer();
336 if (!$customer) {
337 return $this->sendError(['message' => __('Customer not found', 'fluent-cart')]);
338 }
339
340 if (!$newVendorSubscriptionId || !$method || !$subscription_uuid) {
341 return $this->sendError([
342 'message' => __('Missing required parameters', 'fluent-cart')
343 ]);
344 }
345
346 $subscription = Subscription::query()
347 ->where('uuid', $subscription_uuid)
348 ->where('customer_id', $customer->id)
349 ->first();
350 if (empty($subscription)) {
351 return $this->sendError([
352 'message' => __('Subscription not found', 'fluent-cart')
353 ]);
354 }
355
356 if (APP::gateway()->has($method)) {
357 try {
358 APP::gateway($method)->subscriptions->confirmSubscriptionSwitch($data, $subscription->id);
359 } catch (\Exception $e) {
360 return $this->sendError([
361 'message' => $e->getMessage()
362 ]);
363 }
364 }
365
366 return $this->sendError([
367 'message' => __('Could not confirm subscription switch', 'fluent-cart')
368 ]);
369
370 }
371
372 public function cancelAutoRenew(Request $request, $subscription_uuid)
373 {
374 $errorResponse = $this->checkUserLoggedIn();
375 if ($errorResponse !== null) {
376 return $errorResponse;
377 }
378
379 $customer = CustomerResource::getCurrentCustomer();
380 if (!$customer) {
381 return $this->sendError([
382 'message' => __('Customer not found', 'fluent-cart')
383 ]);
384 }
385
386 $subscription = Subscription::query()
387 ->where('uuid', $subscription_uuid)
388 ->where('customer_id', $customer->id)
389 ->first();
390
391 if (empty($subscription)) {
392 return $this->sendError([
393 'message' => __('Subscription not found', 'fluent-cart')
394 ]);
395 }
396
397 $result = $subscription->cancelRemoteSubscription([
398 'reason' => 'cancelled_by_customer',
399 'note' => __('Cancelled by Customer from customer portal', 'fluent-cart'),
400 ]);
401
402 if (is_wp_error($result)) {
403 return $this->sendError([
404 'message' => __('Unable to cancel subscription at this time. Please try again later or contact support.', 'fluent-cart')
405 ]);
406 }
407
408 return [
409 'message' => __('Your subscription has been successfully cancelled', 'fluent-cart')
410 ];
411
412 }
413
414 public function initiateEarlyPayment(Request $request, $subscription_uuid)
415 {
416 $errorResponse = $this->checkUserLoggedIn();
417 if ($errorResponse !== null) {
418 return $errorResponse;
419 }
420
421 $customer = CustomerResource::getCurrentCustomer();
422 if (!$customer) {
423 return $this->sendError([
424 'message' => __('Customer not found', 'fluent-cart')
425 ]);
426 }
427
428 $subscription = Subscription::query()
429 ->where('uuid', $subscription_uuid)
430 ->where('customer_id', $customer->id)
431 ->first();
432
433 if (!$subscription) {
434 return $this->sendError([
435 'message' => __('Subscription not found', 'fluent-cart')
436 ]);
437 }
438
439 if (!EarlyPaymentFeature::canPay($subscription)) {
440 return $this->sendError([
441 'message' => __('Early payment is not available for this subscription.', 'fluent-cart')
442 ]);
443 }
444
445 $url = add_query_arg([
446 'fluent-cart' => 'early-installment-payment',
447 'subscription_hash' => $subscription->uuid,
448 ], home_url('/'));
449
450 return $this->sendSuccess([
451 'message' => __('Early payment URL generated.', 'fluent-cart'),
452 'checkout_url' => $url,
453 ]);
454 }
455
456 private function canEarlyPay(Subscription $subscription): bool
457 {
458 return App::isProActive()
459 && $subscription->bill_times > 0
460 && $subscription->bill_count < $subscription->bill_times
461 && in_array($subscription->status, [Status::SUBSCRIPTION_ACTIVE, Status::SUBSCRIPTION_TRIALING]);
462 }
463
464 public function getSetupIntentRemainingAttempts($subscription_uuid)
465 {
466 $errorResponse = $this->checkUserLoggedIn();
467
468 if ($errorResponse !== null) {
469 return $errorResponse;
470 }
471
472 $customer = CustomerResource::getCurrentCustomer();
473 if (!$customer) {
474 return $this->sendError([
475 'message' => __('Customer not found', 'fluent-cart')
476 ]);
477 }
478
479 $subscription = Subscription::query()
480 ->where('uuid', $subscription_uuid)
481 ->where('customer_id', $customer->id)
482 ->first();
483
484 if (empty($subscription)) {
485 return $this->sendError([
486 'message' => __('Subscription not found', 'fluent-cart')
487 ]);
488 }
489
490 // Only return attempts if current payment method is stripe
491 if ($subscription->current_payment_method !== 'stripe' || empty($subscription->vendor_customer_id)) {
492 return $this->sendError([
493 'message' => __('Payment method not supported', 'fluent-cart')
494 ]);
495 }
496
497 $subscriptionsManager = new SubscriptionsManager();
498 $remaining = $subscriptionsManager->getRemainingRateLimit($subscription->vendor_customer_id);
499
500 return $this->sendSuccess([
501 'remaining' => max(0, $remaining)
502 ]);
503 }
504
505 }
506