PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.3
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.3
1.6.6 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 All 49 releases
fluent-cart / app / Modules / PaymentMethods / StripeGateway / Confirmations.php

Confirmations.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.5.3, at app/Modules/PaymentMethods/StripeGateway/Confirmations.php

656 lines 26.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\Modules\PaymentMethods\StripeGateway;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Helpers\CurrenciesHelper;
7 use FluentCart\App\Helpers\Status;
8 use FluentCart\App\Helpers\StatusHelper;
9 use FluentCart\App\Models\Customer;
10 use FluentCart\App\Models\Order;
11 use FluentCart\App\Models\OrderTransaction;
12 use FluentCart\App\Models\Subscription;
13 use FluentCart\App\Modules\PaymentMethods\StripeGateway\API\API;
14 use FluentCart\App\Modules\Subscriptions\Services\SubscriptionService;
15 use FluentCart\App\Services\DateTime\DateTime;
16 use FluentCart\App\Services\Payments\PaymentHelper;
17 use FluentCart\Framework\Support\Arr;
18
19 class Confirmations
20 {
21 public function init()
22 {
23 add_action('wp_ajax_nopriv_fluent_cart_confirm_stripe_payment', [$this, 'confirmStripePayment']);
24 add_action('wp_ajax_fluent_cart_confirm_stripe_payment', [$this, 'confirmStripePayment']);
25
26 add_filter('fluent_cart/form_disable_stripe_connect', function ($value, $args) {
27 if (defined('FCT_STRIPE_LIVE_PUBLIC_KEY') || defined('FCT_STRIPE_TEST_PUBLIC_KEY')) {
28 return true;
29 }
30
31 return $value;
32 }, 10, 2);
33
34
35 if (isset($_REQUEST['fct_stripe_hosted']) && isset($_REQUEST['trx_hash'])) {
36 $transaction = OrderTransaction::query()->where('uuid', sanitize_text_field(App::request()->get('trx_hash')))->first();
37 if (!$transaction || $transaction->status === Status::TRANSACTION_SUCCEEDED) {
38 return;
39 }
40
41 // Get session ID from transaction meta
42 $sessionId = Arr::get($transaction->meta, 'session_id');
43
44 if ($sessionId) {
45 $this->confirmByCheckoutSession($sessionId, $transaction);
46 } else {
47 return;
48 }
49 }
50
51 }
52
53 private function confirmByCheckoutSession($sessionId, $transaction)
54 {
55
56 $api = new API();
57
58 $session = $api->getStripeObject('checkout/sessions/' . $sessionId, [
59 'expand' => ['payment_intent', 'subscription.latest_invoice.payment_intent.latest_charge']
60 ]);
61
62
63 if (is_wp_error($session)) {
64 fluent_cart_add_log(__('Stripe Session Retrieval Failed', 'fluent-cart'), $session->get_error_message(), 'error', [
65 'module_name' => 'order',
66 'module_id' => $transaction->order_id,
67 ]);
68 if ($transaction->subscription_id) {
69 $subscription = Subscription::query()->find($transaction->subscription_id);
70 if ($subscription) {
71 $subscription->addLog(__('Stripe Session Retrieval Failed', 'fluent-cart'), $session->get_error_message(), 'error');
72 }
73 }
74 return;
75 }
76
77 $paymentStatus = Arr::get($session, 'payment_status');
78 $mode = Arr::get($session, 'mode');
79
80 if ($mode === 'subscription') {
81 $vendorSubscription = Arr::get($session, 'subscription');
82 $vendorSubscriptionId = is_array($vendorSubscription) ? Arr::get($vendorSubscription, 'id') : $vendorSubscription;
83
84 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
85
86 if ($subscription && $vendorSubscriptionId) {
87 $updateData = [
88 'vendor_subscription_id' => $vendorSubscriptionId,
89 'vendor_customer_id' => Arr::get($vendorSubscription, 'customer'),
90 ];
91
92
93 if (is_array($vendorSubscription)) {
94 if (Arr::get($vendorSubscription, 'current_period_end')) {
95 $updateData['next_billing_date'] = gmdate('Y-m-d H:i:s', (int) Arr::get($vendorSubscription, 'current_period_end'));
96 }
97
98 if (Arr::get($vendorSubscription, 'trial_end')) {
99 $updateData['trial_ends_at'] = gmdate('Y-m-d H:i:s', (int) Arr::get($vendorSubscription, 'trial_end'));
100 }
101 }
102
103 $subscription->update($updateData);
104 }
105
106 $paymentIntent = null;
107 $billingInfo = [];
108
109
110 if (is_array($vendorSubscription)) {
111 $paymentIntent = Arr::get($vendorSubscription, 'latest_invoice.payment_intent');
112 }
113
114
115 if (!$paymentIntent && Arr::get($session, 'invoice')) {
116 $invoiceId = Arr::get($session, 'invoice');
117 $invoice = $api->getStripeObject('invoices/' . $invoiceId, [
118 'expand' => ['payment_intent.latest_charge']
119 ]);
120 if (!is_wp_error($invoice)) {
121 $paymentIntent = Arr::get($invoice, 'payment_intent.latest_charge');
122 }
123 }
124
125 if (!is_array($paymentIntent)) {
126 $paymentIntent = $api->getStripeObject('payment_intents/' . $paymentIntent, [
127 'expand' => ['latest_charge']
128 ]);
129 }
130
131
132 $charge = Arr::get($paymentIntent, 'latest_charge', []);
133
134 if ($charge) {
135 $billingInfo = $this->extractBillingInfoFromCharge($charge);
136 $this->processPaymentIntentConfirmation($paymentIntent, $transaction);
137 } else {
138 if ($paymentStatus === 'paid' || $transaction->total <= 0) {
139 // Try to get payment method from setup intent
140 $setupIntent = Arr::get($session, 'setup_intent');
141 if ($setupIntent) {
142 $setupIntentData = $api->getStripeObject('setup_intents/' . $setupIntent);
143 if (!is_wp_error($setupIntentData)) {
144 $paymentMethodId = Arr::get($setupIntentData, 'payment_method');
145 if ($paymentMethodId) {
146 $billingInfo = $this->getPaymentMethodDetails($paymentMethodId);
147 }
148 }
149 }
150
151 $transaction->status = Status::TRANSACTION_SUCCEEDED;
152 $transaction->save();
153 }
154 }
155
156 if ($subscription && !in_array($subscription->status, Status::getValidableSubscriptionStatuses())) {
157 (new SubscriptionsManager())->confirmSubscriptionAfterChargeSucceeded($subscription, $billingInfo);
158 }
159
160 (new StatusHelper($transaction->order))->syncOrderStatuses($transaction);
161
162 } else {
163 if ($paymentStatus === 'paid') {
164 $paymentIntent = Arr::get($session, 'payment_intent');
165 if (is_array($paymentIntent)) {
166 $paymentIntent = $api->getStripeObject('payment_intents/' . $paymentIntent['id'], [
167 'expand' => ['latest_charge']
168 ]);
169 $this->processPaymentIntentConfirmation($paymentIntent, $transaction);
170 } elseif ($paymentIntent) {
171 $intentData = $api->getStripeObject('payment_intents/' . $paymentIntent, [
172 'expand' => ['latest_charge']
173 ]);
174 if (!is_wp_error($intentData)) {
175 $this->processPaymentIntentConfirmation($intentData, $transaction);
176 }
177 }
178 }
179 }
180 }
181
182
183 /**
184 * Process payment intent confirmation
185 */
186 private function processPaymentIntentConfirmation($intent, $transaction)
187 {
188 $charge = Arr::get($intent, 'latest_charge', []);
189 $intentId = Arr::get($intent, 'id');
190
191 if ($charge && $intentId) {
192 $this->confirmPaymentSuccessByCharge($transaction, [
193 'charge' => $charge,
194 'intent_id' => $intentId
195 ]);
196 }
197 }
198
199 /**
200 * Extract billing info from charge for subscription confirmation
201 */
202 private function extractBillingInfoFromCharge($charge)
203 {
204 $billingDetails = Arr::get($charge, 'billing_details', []);
205 $paymentMethodDetails = Arr::get($charge, 'payment_method_details', []);
206
207 return [
208 'method' => 'stripe',
209 'vendor_method_id' => Arr::get($charge, 'payment_method', ''),
210 'payment_type' => Arr::get($paymentMethodDetails, 'type'),
211 'details' => array_filter([
212 'brand' => Arr::get($paymentMethodDetails, 'card.brand'),
213 'last_4' => Arr::get($paymentMethodDetails, 'card.last4'),
214 'exp_month' => Arr::get($paymentMethodDetails, 'card.exp_month'),
215 'exp_year' => Arr::get($paymentMethodDetails, 'card.exp_year'),
216 'country' => Arr::get($paymentMethodDetails, 'card.country'),
217 'postal_code' => Arr::get($billingDetails, 'address.postal_code', ''),
218 'name' => Arr::get($billingDetails, 'name', '')
219 ])
220 ];
221 }
222
223 /*
224 * Only for validating hosted checkout payment confirmation
225 */
226 public function confirmStripePayment()
227 {
228 $intentId = App::request()->get('intentId');
229 if (empty($intentId)) {
230 wp_send_json(
231 [
232 'message' => __('Intent ID is required to confirm the payment.', 'fluent-cart'),
233 ],
234 400
235 );
236 }
237
238 $intentId = sanitize_text_field($intentId);
239
240 // in case of plan change, and first payment is 0, then setup intent will be created
241 if (strpos($intentId, 'seti_') === 0) {
242 $trxHash = sanitize_text_field(App::request()->get('trx_hash'));
243 if (empty($trxHash)) {
244 wp_send_json(['message' => __('Invalid request.', 'fluent-cart')], 400);
245 }
246 $result = $this->confirmSetupIntent($intentId, $trxHash);
247 if (is_wp_error($result)) {
248 wp_send_json(
249 [
250 'message' => $result->get_error_message(),
251 ], 400
252 );
253 }
254 wp_send_json(
255 [
256 'message' => __('Setup intent confirmed successfully. Please check your subscriptions.', 'fluent-cart'),
257 ], 200
258 );
259 }
260
261 $api = new API();
262 $response = $api->getStripeObject('payment_intents/' . $intentId, [
263 'expand' => ['latest_charge']
264 ]);
265
266 if (is_wp_error($response)) {
267 wp_send_json(
268 [
269 'message' => $response->get_error_message(),
270 ],
271 500
272 );
273 }
274
275 $transaction = OrderTransaction::query()->where('vendor_charge_id', $intentId)->first();
276
277 if (!$transaction) {
278 wp_send_json(
279 [
280 'message' => __('Order not found for the provided intent ID.', 'fluent-cart'),
281 ],
282 404
283 );
284 }
285
286 $this->confirmPaymentSuccessByCharge($transaction, [
287 'charge' => Arr::get($response, 'latest_charge', []),
288 'intent_id' => $intentId
289 ]);
290
291 wp_send_json(
292 [
293 'redirect_url' => $transaction->getReceiptPageUrl(),
294 'order' => [
295 'uuid' => $transaction->order->uuid,
296 ],
297 'message' => __('Payment confirmed successfully. Redirecting...!', 'fluent-cart')
298 ], 200
299 );
300 }
301
302 // make sure customer given the acknowledgement for saving the payment methods
303 public function savePaymentMethodToCustomerMeta($vendorCustomer, $paymentMethodId, $order)
304 {
305 $fctCustomer = Customer::query()->where('id', $order->customer_id)->first();
306 $metaKey = 'saved_payment_method';
307
308 $stripeApiKey = (new StripeSettingsBase())->getApiKey();
309 $api = new API();
310
311 // Allow redisplay for the payment method
312 $api->makeRequest('payment_methods/' . $paymentMethodId, ['allow_redisplay' => 'always'], $stripeApiKey, 'POST');
313
314 // Fetch customer to get default payment method
315 $customer = $api->makeRequest('customers/' . $vendorCustomer, [], $stripeApiKey, 'GET');
316 $defaultPaymentMethodId = Arr::get($customer, 'invoice_settings.default_payment_method');
317
318 $paymentMethodsResponse = $api->makeRequest(
319 'customers/' . $vendorCustomer . '/payment_methods',
320 [],
321 $stripeApiKey,
322 'GET'
323 );
324
325 $stripeMeta = [
326 'customer_id' => $vendorCustomer,
327 'payment_methods' => []
328 ];
329
330 if ($paymentMethodsResponse && !is_wp_error($paymentMethodsResponse) && ($methods = Arr::get($paymentMethodsResponse, 'data', []))) {
331 $seenFingerprints = [];
332 foreach ($methods as $method) {
333
334 $type = Arr::get($method, 'type');
335 $pm = [
336 'id' => Arr::get($method, 'id'),
337 'type' => $type,
338 ];
339
340 $fingerprint = null;
341 switch ($type) {
342 case 'card':
343 $pm['last4'] = Arr::get($method, 'card.last4');
344 $pm['brand'] = Arr::get($method, 'card.brand');
345 $pm['exp_month'] = Arr::get($method, 'card.exp_month');
346 $pm['exp_year'] = Arr::get($method, 'card.exp_year');
347 $pm['fingerprint'] = Arr::get($method, 'card.fingerprint');
348 $fingerprint = $pm['fingerprint'];
349 break;
350 // case 'sepa_debit':
351 // $pm['last4'] = Arr::get($method, 'sepa_debit.last4');
352 // $fingerprint = Arr::get($method, 'sepa_debit.fingerprint');
353 // break;
354 // case 'ach_debit':
355 // $pm['last4'] = Arr::get($method, 'ach_debit.last4');
356 // $fingerprint = Arr::get($method, 'ach_debit.fingerprint');
357 // break;
358 // case 'ach_credit_transfer':
359 // $pm['account_number'] = Arr::get($method, 'ach_credit_transfer.account_number');
360 // $fingerprint = Arr::get($method, 'ach_credit_transfer.fingerprint');
361 // break;
362 // case 'us_bank_account':
363 // $pm['account_number'] = Arr::get($method, 'us_bank_account.account_number');
364 // $fingerprint = Arr::get($method, 'us_bank_account.fingerprint');
365 // break;
366 // case 'bacs_debit':
367 // $pm['account_number'] = Arr::get($method, 'bacs_debit.account_number');
368 // $fingerprint = Arr::get($method, 'bacs_debit.fingerprint');
369 // break;
370 default:
371 break;
372 }
373
374 if ($fingerprint && in_array($fingerprint, $seenFingerprints, true)) {
375 continue;
376 }
377 if ($fingerprint) {
378 $seenFingerprints[] = $fingerprint;
379 }
380
381 if ($defaultPaymentMethodId === Arr::get($method, 'id')) {
382 $stripeMeta['payment_methods']['default'] = $pm;
383 } else {
384 $stripeMeta['payment_methods'][] = $pm;
385 }
386 }
387 }
388
389 $meta = $fctCustomer->getMeta($metaKey);
390 $meta['stripe'] = $stripeMeta;
391
392 $fctCustomer->updateMeta($metaKey, [
393 'stripe' => $stripeMeta
394 ]);
395 }
396
397 public function confirmSetupIntent($setupIntent, $trxHash = null)
398 {
399 $api = new API();
400
401 $response = $api->getStripeObject('setup_intents/' . $setupIntent);
402
403 if (is_wp_error($response)) {
404 return $response;
405 }
406
407 $transaction = OrderTransaction::query()->where('vendor_charge_id', $setupIntent)->first();
408
409 if (!$transaction) {
410 return new \WP_Error(
411 'transaction_not_found',
412 __('Transaction not found for the provided setup intent.', 'fluent-cart')
413 );
414 }
415
416 if ($trxHash !== null && $transaction->uuid !== $trxHash) {
417 return new \WP_Error('invalid_request', __('Invalid request.', 'fluent-cart'));
418 }
419
420 if (Arr::get($response, 'status') !== 'succeeded') {
421 return new \WP_Error(
422 'setup_intent_not_succeeded',
423 __('Payment method setup is not complete. Please complete the payment method setup.', 'fluent-cart')
424 );
425 }
426
427 $transaction->status = Status::TRANSACTION_PENDING;
428
429 if ($transaction->total <= 0) {
430 $transaction->status = Status::TRANSACTION_SUCCEEDED;
431 }
432
433
434 $transaction->vendor_charge_id = ''; // removing vendor charge id , because setup intent id is not the charge id
435 $transaction->save();
436
437 $order = Order::query()->where('id', $transaction->order_id)->first();
438
439
440 $paymentMethod = Arr::get($response, 'payment_method');
441 $customer = Arr::get($response, 'customer');
442
443 $billingInfo = $this->getPaymentMethodDetails($paymentMethod);
444
445 // attach the payment method to the customer
446 if ($paymentMethod && $customer) {
447 $api->createStripeObject('payment_methods/' . $paymentMethod . '/attach', [
448 'customer' => $customer
449 ]);
450
451 $this->savePaymentMethodToCustomerMeta($customer, $paymentMethod, $order);
452 }
453
454
455 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
456
457 if ($subscription) {
458 (new SubscriptionsManager())->confirmSubscriptionAfterChargeSucceeded($subscription, $billingInfo);
459 }
460
461 (new StatusHelper($order))->syncOrderStatuses($transaction);
462
463 }
464
465 public function getPaymentMethodDetails($methodId)
466 {
467 $paymentMethodDetails = (new API())->makeRequest('payment_methods/' . $methodId, [], (new StripeSettingsBase())->getApiKey(), 'GET');
468
469 if (is_wp_error($paymentMethodDetails) || !$paymentMethodDetails) {
470 $billingInfo = PaymentHelper::parsePaymentMethodDetails('stripe', ['type' => 'card']);
471 } else {
472 $billingInfo = PaymentHelper::parsePaymentMethodDetails('stripe', $paymentMethodDetails);
473 }
474
475 return $billingInfo;
476 }
477
478
479 /**
480 * Confirm payment success by charge.
481 * Currently used by:
482 * - fluent_cart/payments/stripe/webhook_charge_succeeded
483 * -
484 *
485 * @param OrderTransaction $transaction
486 * @param array $args
487 * @param array $args ['charge'] - The charge details from Stripe.
488 * @param string $args ['intent_id'] - The intent ID from Stripe.
489 */
490 public function confirmPaymentSuccessByCharge(OrderTransaction $transaction, $args = [])
491 {
492 $charge = Arr::get($args, 'charge', []);
493 $intentId = Arr::get($args, 'intent_id', '');
494
495 if (!$intentId) {
496 $intentId = Arr::get($charge, 'payment_intent', '');
497 }
498
499 $order = Order::query()->where('id', $transaction->order_id)->first();
500
501 // in race conditions between webhook and AJAX confirmation
502 $transaction = OrderTransaction::query()->where('id', $transaction->id)->first();
503 if ($transaction->status === Status::TRANSACTION_SUCCEEDED) {
504 if ($transaction->subscription_id) {
505 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
506 if ($subscription) {
507 $subscription->reSyncFromRemote();
508 }
509 }
510
511 return (new StatusHelper($order))->syncOrderStatuses($transaction);
512 }
513
514 $chargeCurrency = Arr::get($charge, 'currency', $transaction->currency);
515 $status = Arr::get($charge, 'status') === 'succeeded' ? Status::TRANSACTION_SUCCEEDED : Status::TRANSACTION_PENDING;
516
517 if ($status === Status::TRANSACTION_PENDING) {
518 if (!$transaction->vendor_charge_id && !empty($intentId)) {
519 $transaction->update(['vendor_charge_id' => $intentId]);
520 }
521 return $order; // already pending,
522 }
523
524 $normalizedAmount = (int)Arr::get($charge, 'amount', 0);
525
526 if ($chargeCurrency && CurrenciesHelper::isZeroDecimal($chargeCurrency)) {
527 $normalizedAmount = $normalizedAmount * 100;
528 }
529
530 $transactionUpdateData = array_filter([
531 'order_id' => $order->id,
532 'total' => $normalizedAmount,
533 'currency' => $chargeCurrency,
534 'status' => $status,
535 'payment_method' => 'stripe',
536 'card_last_4' => Arr::get($charge, 'payment_method_details.card.last4', ''),
537 'card_brand' => Arr::get($charge, 'payment_method_details.card.brand', ''),
538 'payment_method_type' => Arr::get($charge, 'payment_method_details.type', ''),
539 'vendor_charge_id' => $intentId,
540 'payment_mode' => Arr::isTrue($charge, 'livemode') ? 'live' : 'test'
541 ]);
542
543 if (Arr::get($charge, 'disputed', false)) {
544 $transactionUpdateData['transaction_type'] = Status::TRANSACTION_TYPE_DISPUTE;
545 $disputeId = Arr::get($charge, 'dispute', '');
546 $reason = 'unknown';
547
548 $retreiveDispute = (new API())->getStripeObject('disputes/' . $disputeId);
549
550 if (!is_wp_error($retreiveDispute)) {
551 $reason = Arr::get($retreiveDispute, 'reason');
552 }
553
554 $transaction->meta = array_merge($transaction->meta, [
555 'dispute_id' => $disputeId,
556 'dispute_reason' => $reason,
557 'is_dispute_actionable' => in_array(Arr::get($retreiveDispute, 'status'), ['needs_response']),
558 'is_charge_refundable' => Arr::get($retreiveDispute, 'is_charge_refundable', false)
559 ]);
560
561 fluent_cart_warning_log('Stripe charge disputed', 'This payment was disputed (' . $charge['id'] . ')', [
562 'module_name' => 'order',
563 'module_id' => $order->id,
564 'log_type' => 'api'
565 ]);
566 if ($transaction->subscription_id) {
567 fluent_cart_warning_log('Stripe charge disputed', 'This payment was disputed (' . $charge['id'] . ')', [
568 'module_type' => 'FluentCart\App\Models\Subscription',
569 'module_id' => $transaction->subscription_id,
570 'module_name' => 'subscription',
571 'log_type' => 'api'
572 ]);
573 }
574 }
575
576 $transaction->fill($transactionUpdateData);
577 $transaction->save();
578
579 fluent_cart_add_log(__('Stripe Payment Confirmation', 'fluent-cart'), __('Payment confirmation received from Stripe. Transaction ID:', 'fluent-cart') . ' ' . $intentId, 'info', [
580 'module_name' => 'order',
581 'module_id' => $order->id,
582 ]);
583 if ($transaction->subscription_id) {
584 fluent_cart_add_log(__('Stripe Payment Confirmation', 'fluent-cart'), __('Payment confirmation received from Stripe. Transaction ID:', 'fluent-cart') . ' ' . $intentId, 'info', [
585 'module_type' => 'FluentCart\App\Models\Subscription',
586 'module_id' => $transaction->subscription_id,
587 'module_name' => 'subscription',
588 ]);
589 }
590
591 $billingDetails = Arr::get($charge, 'billing_details', []);
592 $paymentMethodDetails = Arr::get($charge, 'payment_method_details', []);
593 $billingInfo = [
594 'method' => 'stripe',
595 'vendor_method_id' => Arr::get($charge, 'payment_method', ''),
596 'payment_type' => Arr::get($paymentMethodDetails, 'type'),
597 'details' => array_filter([
598 'brand' => Arr::get($paymentMethodDetails, 'card.brand'),
599 'last_4' => Arr::get($paymentMethodDetails, 'card.last4'),
600 'exp_month' => Arr::get($paymentMethodDetails, 'card.exp_month'),
601 'exp_year' => Arr::get($paymentMethodDetails, 'card.exp_year'),
602 'country' => Arr::get($paymentMethodDetails, 'card.country'),
603 'postal_code' => Arr::get($billingDetails, 'address.postal_code', ''),
604 'name' => Arr::get($billingDetails, 'name', '')
605 ])
606 ];
607
608 if ($order->type === Status::ORDER_TYPE_RENEWAL) {
609
610 $parentOrderId = $transaction->order->parent_id;
611 if (!$parentOrderId) {
612 return;
613 }
614 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
615
616 if (!$subscription) {
617 return $order; // No subscription found for this renewal order. Something is wrong.
618 }
619
620 $api = new API();
621 $response = $api->getStripeObject('subscriptions/' . $subscription->vendor_subscription_id, [], $transaction->payment_mode);
622
623 $subscriptionArgs = [
624 'status' => Status::SUBSCRIPTION_ACTIVE,
625 'canceled_at' => null,
626 'current_payment_method' => 'stripe'
627 ];
628
629 if (!is_wp_error($response)) {
630 $nextBillingDate = Arr::get($response, 'current_period_end') ?? null;
631 if ($nextBillingDate) {
632 $subscriptionArgs['next_billing_date'] = gmdate('Y-m-d H:i:s', (int)$nextBillingDate);
633 }
634 }
635
636 SubscriptionService::recordManualRenewal($subscription, $transaction, [
637 'billing_info' => $billingInfo,
638 'subscription_args' => $subscriptionArgs
639 ]);
640
641 } else {
642 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
643
644 if ($subscription && !in_array($subscription->status, Status::getValidableSubscriptionStatuses())) {
645 (new SubscriptionsManager())->confirmSubscriptionAfterChargeSucceeded($subscription, $billingInfo);
646 }
647
648 (new StatusHelper($order))->syncOrderStatuses($transaction);
649 }
650
651 return $order;
652 }
653
654 }
655
656