PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
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 / Modules / PaymentMethods / StripeGateway / Confirmations.php

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

634 lines 25.3 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 $this->confirmSetupIntent($intentId);
243 wp_send_json(
244 [
245 'message' => __('Setup intent confirmed successfully. Please check your subscriptions.', 'fluent-cart'),
246 ], 200
247 );
248 }
249
250 $api = new API();
251 $response = $api->getStripeObject('payment_intents/' . $intentId, [
252 'expand' => ['latest_charge']
253 ]);
254
255 if (is_wp_error($response)) {
256 wp_send_json(
257 [
258 'message' => $response->get_error_message(),
259 ],
260 500
261 );
262 }
263
264 $transaction = OrderTransaction::query()->where('vendor_charge_id', $intentId)->first();
265
266 if (!$transaction) {
267 wp_send_json(
268 [
269 'message' => __('Order not found for the provided intent ID.', 'fluent-cart'),
270 ],
271 404
272 );
273 }
274
275 $this->confirmPaymentSuccessByCharge($transaction, [
276 'charge' => Arr::get($response, 'latest_charge', []),
277 'intent_id' => $intentId
278 ]);
279
280 wp_send_json(
281 [
282 'redirect_url' => $transaction->getReceiptPageUrl(),
283 'order' => [
284 'uuid' => $transaction->order->uuid,
285 ],
286 'message' => __('Payment confirmed successfully. Redirecting...!', 'fluent-cart')
287 ], 200
288 );
289 }
290
291 // make sure customer given the acknowledgement for saving the payment methods
292 public function savePaymentMethodToCustomerMeta($vendorCustomer, $paymentMethodId, $order)
293 {
294 $fctCustomer = Customer::query()->where('id', $order->customer_id)->first();
295 $metaKey = 'saved_payment_method';
296
297 $stripeApiKey = (new StripeSettingsBase())->getApiKey();
298 $api = new API();
299
300 // Allow redisplay for the payment method
301 $api->makeRequest('payment_methods/' . $paymentMethodId, ['allow_redisplay' => 'always'], $stripeApiKey, 'POST');
302
303 // Fetch customer to get default payment method
304 $customer = $api->makeRequest('customers/' . $vendorCustomer, [], $stripeApiKey, 'GET');
305 $defaultPaymentMethodId = Arr::get($customer, 'invoice_settings.default_payment_method');
306
307 $paymentMethodsResponse = $api->makeRequest(
308 'customers/' . $vendorCustomer . '/payment_methods',
309 [],
310 $stripeApiKey,
311 'GET'
312 );
313
314 $stripeMeta = [
315 'customer_id' => $vendorCustomer,
316 'payment_methods' => []
317 ];
318
319 if ($paymentMethodsResponse && !is_wp_error($paymentMethodsResponse) && ($methods = Arr::get($paymentMethodsResponse, 'data', []))) {
320 $seenFingerprints = [];
321 foreach ($methods as $method) {
322
323 $type = Arr::get($method, 'type');
324 $pm = [
325 'id' => Arr::get($method, 'id'),
326 'type' => $type,
327 ];
328
329 $fingerprint = null;
330 switch ($type) {
331 case 'card':
332 $pm['last4'] = Arr::get($method, 'card.last4');
333 $pm['brand'] = Arr::get($method, 'card.brand');
334 $pm['exp_month'] = Arr::get($method, 'card.exp_month');
335 $pm['exp_year'] = Arr::get($method, 'card.exp_year');
336 $pm['fingerprint'] = Arr::get($method, 'card.fingerprint');
337 $fingerprint = $pm['fingerprint'];
338 break;
339 // case 'sepa_debit':
340 // $pm['last4'] = Arr::get($method, 'sepa_debit.last4');
341 // $fingerprint = Arr::get($method, 'sepa_debit.fingerprint');
342 // break;
343 // case 'ach_debit':
344 // $pm['last4'] = Arr::get($method, 'ach_debit.last4');
345 // $fingerprint = Arr::get($method, 'ach_debit.fingerprint');
346 // break;
347 // case 'ach_credit_transfer':
348 // $pm['account_number'] = Arr::get($method, 'ach_credit_transfer.account_number');
349 // $fingerprint = Arr::get($method, 'ach_credit_transfer.fingerprint');
350 // break;
351 // case 'us_bank_account':
352 // $pm['account_number'] = Arr::get($method, 'us_bank_account.account_number');
353 // $fingerprint = Arr::get($method, 'us_bank_account.fingerprint');
354 // break;
355 // case 'bacs_debit':
356 // $pm['account_number'] = Arr::get($method, 'bacs_debit.account_number');
357 // $fingerprint = Arr::get($method, 'bacs_debit.fingerprint');
358 // break;
359 default:
360 break;
361 }
362
363 if ($fingerprint && in_array($fingerprint, $seenFingerprints, true)) {
364 continue;
365 }
366 if ($fingerprint) {
367 $seenFingerprints[] = $fingerprint;
368 }
369
370 if ($defaultPaymentMethodId === Arr::get($method, 'id')) {
371 $stripeMeta['payment_methods']['default'] = $pm;
372 } else {
373 $stripeMeta['payment_methods'][] = $pm;
374 }
375 }
376 }
377
378 $meta = $fctCustomer->getMeta($metaKey);
379 $meta['stripe'] = $stripeMeta;
380
381 $fctCustomer->updateMeta($metaKey, [
382 'stripe' => $stripeMeta
383 ]);
384 }
385
386 public function confirmSetupIntent($setupIntent)
387 {
388 $api = new API();
389
390 $response = $api->getStripeObject('setup_intents/' . $setupIntent);
391
392 if (is_wp_error($response)) {
393 return $response;
394 }
395
396 $transaction = OrderTransaction::query()->where('vendor_charge_id', $setupIntent)->first();
397
398 if (!$transaction) {
399 return new \WP_Error(
400 'transaction_not_found',
401 __('Transaction not found for the provided setup intent.', 'fluent-cart')
402 );
403 }
404
405 $transaction->status = Status::TRANSACTION_PENDING;
406
407 if ($transaction->total <= 0) {
408 $transaction->status = Status::TRANSACTION_SUCCEEDED;
409 }
410
411
412 $transaction->vendor_charge_id = ''; // removing vendor charge id , because setup intent id is not the charge id
413 $transaction->save();
414
415 $order = Order::query()->where('id', $transaction->order_id)->first();
416
417
418 $paymentMethod = Arr::get($response, 'payment_method');
419 $customer = Arr::get($response, 'customer');
420
421 $billingInfo = $this->getPaymentMethodDetails($paymentMethod);
422
423 // attach the payment method to the customer
424 if ($paymentMethod && $customer) {
425 $api->createStripeObject('payment_methods/' . $paymentMethod . '/attach', [
426 'customer' => $customer
427 ]);
428
429 $this->savePaymentMethodToCustomerMeta($customer, $paymentMethod, $order);
430 }
431
432
433 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
434
435 if ($subscription) {
436 (new SubscriptionsManager())->confirmSubscriptionAfterChargeSucceeded($subscription, $billingInfo);
437 }
438
439 (new StatusHelper($order))->syncOrderStatuses($transaction);
440
441 }
442
443 public function getPaymentMethodDetails($methodId)
444 {
445 $paymentMethodDetails = (new API())->makeRequest('payment_methods/' . $methodId, [], (new StripeSettingsBase())->getApiKey(), 'GET');
446
447 if (is_wp_error($paymentMethodDetails) || !$paymentMethodDetails) {
448 $billingInfo = PaymentHelper::parsePaymentMethodDetails('stripe', ['type' => 'card']);
449 } else {
450 $billingInfo = PaymentHelper::parsePaymentMethodDetails('stripe', $paymentMethodDetails);
451 }
452
453 return $billingInfo;
454 }
455
456
457 /**
458 * Confirm payment success by charge.
459 * Currently used by:
460 * - fluent_cart/payments/stripe/webhook_charge_succeeded
461 * -
462 *
463 * @param OrderTransaction $transaction
464 * @param array $args
465 * @param array $args ['charge'] - The charge details from Stripe.
466 * @param string $args ['intent_id'] - The intent ID from Stripe.
467 */
468 public function confirmPaymentSuccessByCharge(OrderTransaction $transaction, $args = [])
469 {
470 $charge = Arr::get($args, 'charge', []);
471 $intentId = Arr::get($args, 'intent_id', '');
472
473 if (!$intentId) {
474 $intentId = Arr::get($charge, 'payment_intent', '');
475 }
476
477 $order = Order::query()->where('id', $transaction->order_id)->first();
478
479 // in race conditions between webhook and AJAX confirmation
480 $transaction = OrderTransaction::query()->where('id', $transaction->id)->first();
481 if ($transaction->status === Status::TRANSACTION_SUCCEEDED) {
482 if ($transaction->subscription_id) {
483 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
484 if ($subscription) {
485 $subscription->reSyncFromRemote();
486 }
487 }
488
489 return (new StatusHelper($order))->syncOrderStatuses($transaction);
490 }
491
492 $chargeCurrency = Arr::get($charge, 'currency', $transaction->currency);
493 $status = Arr::get($charge, 'status') === 'succeeded' ? Status::TRANSACTION_SUCCEEDED : Status::TRANSACTION_PENDING;
494
495 if ($status === Status::TRANSACTION_PENDING) {
496 if (!$transaction->vendor_charge_id && !empty($intentId)) {
497 $transaction->update(['vendor_charge_id' => $intentId]);
498 }
499 return $order; // already pending,
500 }
501
502 $normalizedAmount = (int)Arr::get($charge, 'amount', 0);
503
504 if ($chargeCurrency && CurrenciesHelper::isZeroDecimal($chargeCurrency)) {
505 $normalizedAmount = $normalizedAmount * 100;
506 }
507
508 $transactionUpdateData = array_filter([
509 'order_id' => $order->id,
510 'total' => $normalizedAmount,
511 'currency' => $chargeCurrency,
512 'status' => $status,
513 'payment_method' => 'stripe',
514 'card_last_4' => Arr::get($charge, 'payment_method_details.card.last4', ''),
515 'card_brand' => Arr::get($charge, 'payment_method_details.card.brand', ''),
516 'payment_method_type' => Arr::get($charge, 'payment_method_details.type', ''),
517 'vendor_charge_id' => $intentId,
518 'payment_mode' => Arr::isTrue($charge, 'livemode') ? 'live' : 'test'
519 ]);
520
521 if (Arr::get($charge, 'disputed', false)) {
522 $transactionUpdateData['transaction_type'] = Status::TRANSACTION_TYPE_DISPUTE;
523 $disputeId = Arr::get($charge, 'dispute', '');
524 $reason = 'unknown';
525
526 $retreiveDispute = (new API())->getStripeObject('disputes/' . $disputeId);
527
528 if (!is_wp_error($retreiveDispute)) {
529 $reason = Arr::get($retreiveDispute, 'reason');
530 }
531
532 $transaction->meta = array_merge($transaction->meta, [
533 'dispute_id' => $disputeId,
534 'dispute_reason' => $reason,
535 'is_dispute_actionable' => in_array(Arr::get($retreiveDispute, 'status'), ['needs_response']),
536 'is_charge_refundable' => Arr::get($retreiveDispute, 'is_charge_refundable', false)
537 ]);
538
539 fluent_cart_warning_log('Stripe charge disputed', 'This payment was disputed (' . $charge['id'] . ')', [
540 'module_name' => 'order',
541 'module_id' => $order->id,
542 'log_type' => 'api'
543 ]);
544 if ($transaction->subscription_id) {
545 fluent_cart_warning_log('Stripe charge disputed', 'This payment was disputed (' . $charge['id'] . ')', [
546 'module_type' => 'FluentCart\App\Models\Subscription',
547 'module_id' => $transaction->subscription_id,
548 'module_name' => 'subscription',
549 'log_type' => 'api'
550 ]);
551 }
552 }
553
554 $transaction->fill($transactionUpdateData);
555 $transaction->save();
556
557 fluent_cart_add_log(__('Stripe Payment Confirmation', 'fluent-cart'), __('Payment confirmation received from Stripe. Transaction ID:', 'fluent-cart') . ' ' . $intentId, 'info', [
558 'module_name' => 'order',
559 'module_id' => $order->id,
560 ]);
561 if ($transaction->subscription_id) {
562 fluent_cart_add_log(__('Stripe Payment Confirmation', 'fluent-cart'), __('Payment confirmation received from Stripe. Transaction ID:', 'fluent-cart') . ' ' . $intentId, 'info', [
563 'module_type' => 'FluentCart\App\Models\Subscription',
564 'module_id' => $transaction->subscription_id,
565 'module_name' => 'subscription',
566 ]);
567 }
568
569 $billingDetails = Arr::get($charge, 'billing_details', []);
570 $paymentMethodDetails = Arr::get($charge, 'payment_method_details', []);
571 $billingInfo = [
572 'method' => 'stripe',
573 'vendor_method_id' => Arr::get($charge, 'payment_method', ''),
574 'payment_type' => Arr::get($paymentMethodDetails, 'type'),
575 'details' => array_filter([
576 'brand' => Arr::get($paymentMethodDetails, 'card.brand'),
577 'last_4' => Arr::get($paymentMethodDetails, 'card.last4'),
578 'exp_month' => Arr::get($paymentMethodDetails, 'card.exp_month'),
579 'exp_year' => Arr::get($paymentMethodDetails, 'card.exp_year'),
580 'country' => Arr::get($paymentMethodDetails, 'card.country'),
581 'postal_code' => Arr::get($billingDetails, 'address.postal_code', ''),
582 'name' => Arr::get($billingDetails, 'name', '')
583 ])
584 ];
585
586 if ($order->type === Status::ORDER_TYPE_RENEWAL) {
587
588 $parentOrderId = $transaction->order->parent_id;
589 if (!$parentOrderId) {
590 return;
591 }
592 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
593
594 if (!$subscription) {
595 return $order; // No subscription found for this renewal order. Something is wrong.
596 }
597
598 $api = new API();
599 $response = $api->getStripeObject('subscriptions/' . $subscription->vendor_subscription_id, [], $transaction->payment_mode);
600
601 $subscriptionArgs = [
602 'status' => Status::SUBSCRIPTION_ACTIVE,
603 'canceled_at' => null,
604 'current_payment_method' => 'stripe'
605 ];
606
607 if (!is_wp_error($response)) {
608 $nextBillingDate = Arr::get($response, 'current_period_end') ?? null;
609 if ($nextBillingDate) {
610 $subscriptionArgs['next_billing_date'] = gmdate('Y-m-d H:i:s', (int)$nextBillingDate);
611 }
612 }
613
614 SubscriptionService::recordManualRenewal($subscription, $transaction, [
615 'billing_info' => $billingInfo,
616 'subscription_args' => $subscriptionArgs
617 ]);
618
619 } else {
620 $subscription = Subscription::query()->where('id', $transaction->subscription_id)->first();
621
622 if ($subscription && !in_array($subscription->status, Status::getValidableSubscriptionStatuses())) {
623 (new SubscriptionsManager())->confirmSubscriptionAfterChargeSucceeded($subscription, $billingInfo);
624 }
625
626 (new StatusHelper($order))->syncOrderStatuses($transaction);
627 }
628
629 return $order;
630 }
631
632 }
633
634