PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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
← All changes | api/Checkout/CheckoutApi.php +48 -51 1.5.4 → 1.6.5 View file →
@@ -21,8 +21,9 @@
21 21 use FluentCart\App\Models\Order;
22 22 use FluentCart\App\Models\OrderAddress;
23 23 use FluentCart\App\Models\ShippingMethod;
24 24 use FluentCart\App\Services\CheckoutService;
25 +use FluentCart\App\Services\CustomerIdentity\EmailVerificationService;
25 26 use FluentCart\App\Services\Localization\LocalizationManager;
26 27 use FluentCart\App\Services\OrderService;
27 28 use FluentCart\App\Services\Payments\PaymentHelper;
28 29 use FluentCart\App\Services\Payments\PaymentInstance;
@@ -163,9 +164,14 @@
163 164 }
164 165
165 166 $orderData = OrderService::groupSanitizedData($validatedData);
166 167
167 - $shippingMethodId = Arr::get($orderData, 'others.fc_shipping_method');
168 + // The form posts the method twice: the checked radio (fc_shipping_method) and its
169 + // hidden mirror (fc_selected_shipping_method). validateData() checks only the mirror
170 + // against the address's zones, so pricing from the radio let a request pass with one
171 + // method and be charged by another, from a zone the address is not in. Read from
172 + // $validatedData, not the sanitized copy in others: that is the exact integer checked.
173 + $shippingMethodId = (int) Arr::get($validatedData, 'fc_selected_shipping_method', 0);
168 174
169 175 $shippingMethod = null;
170 176 $shippingCharge = 0;
171 177 if (!$cartCheckoutService->isAllDigital()) {
@@ -291,14 +297,16 @@
291 297 }
292 298
293 299 private static function getOrCreateCustomer(CartCheckoutHelper $cartCheckoutHelper, $orderData)
294 300 {
295 - $customerEmail = static::getCustomerEmail($orderData['billing_address']);
296 - if (is_user_logged_in()) {
297 - $customerEmail = wp_get_current_user()->user_email;
298 - Arr::set($orderData, 'billing_address.email', $customerEmail);
301 + $customer = is_user_logged_in() ? ApiCustomerResource::getCurrentCustomer() : null;
302 + $email = static::getCustomerEmail($orderData['billing_address']);
303 + Arr::set($orderData, 'billing_address.email', $email);
304 + if (!$customer) {
305 + // Reuse the email's customer for the purchase without granting ownership.
306 + $customer = Customer::query()->where('email', $email)->orderBy('id')->first();
299 307 }
300 - $customer = $cartCheckoutHelper->getCustomer($customerEmail);
308 +
301 309 return static::createCustomerWithAddress(
302 310 $customer,
303 311 $orderData,
304 312 $orderData['billing_address'],
@@ -431,15 +439,12 @@
431 439
432 440 static::syncCustomerNames($order, $args);
433 441 $cart = CartHelper::getCart();
434 442
435 - $utmData = [];
436 - if (!empty($cart) && is_array($cart->utm_data) && count($cart->utm_data) > 0) {
437 - $utmData = $cart->utm_data;
438 - }
439 -
440 - $requestUtmData = UtmHelper::getUtmDataOfRequest();
441 - $utmData = wp_parse_args($requestUtmData, $utmData);
443 + $utmData = UtmHelper::resolveUtmData(
444 + UtmHelper::getUtmDataOfRequest(),
445 + !empty($cart) ? $cart->utm_data : []
446 + );
442 447 UtmHelper::addUtmToOrder($order->id, $utmData);
443 448
444 449 $prevOrder = Arr::get($args, 'prev_order', null);
445 450
@@ -459,8 +464,14 @@
459 464 }
460 465
461 466 $paymentInstance = new PaymentInstance($order);
462 467
468 + // Transition subscription from pending → intended before submitting to the gateway
469 + if ($paymentInstance->subscription && $paymentInstance->subscription->status === Status::SUBSCRIPTION_PENDING) {
470 + $paymentInstance->subscription->status = Status::SUBSCRIPTION_INTENDED;
471 + $paymentInstance->subscription->save();
472 + }
473 +
463 474 $data = $gateway->makePaymentFromPaymentInstance($paymentInstance);
464 475
465 476 if (is_wp_error($data)) {
466 477 // Server-observed create failure: mark the transaction FAILED so the next
@@ -520,9 +531,10 @@
520 531 private static function syncCustomerNames($order, $args)
521 532 {
522 533 $customer = $order->customer;
523 534
524 - if (empty($customer)) {
535 + if (empty($customer) || !is_user_logged_in() || (int) $customer->user_id !== get_current_user_id()
536 + || EmailVerificationService::isRequired(get_current_user_id())) {
525 537 return;
526 538 }
527 539
528 540 $firstName = Arr::get($args, 'billing_address.first_name');
@@ -532,18 +544,13 @@
532 544 'first_name' => $firstName,
533 545 'last_name' => $lastName,
534 546 ]);
535 547
536 - $user = get_user_by('email', $customer->email);
537 -
538 - if (empty($user)) {
539 - return;
548 + // Keep profile updates tied to the buyer's stored account link too.
549 + if (is_user_logged_in() && (int) $customer->user_id === get_current_user_id()) {
550 + update_user_meta(get_current_user_id(), 'first_name', $firstName);
551 + update_user_meta(get_current_user_id(), 'last_name', $lastName);
540 552 }
541 -
542 - if (is_user_logged_in() && $user->ID === get_current_user_id()) {
543 - update_user_meta($user->ID, 'first_name', $firstName);
544 - update_user_meta($user->ID, 'last_name', $lastName);
545 - }
546 553 }
547 554
548 555 public static function updateStock($order)
549 556 {
@@ -571,16 +578,18 @@
571 578 if ($current_user->ID) {
572 579 $billingAddress['email'] = $current_user->user_email;
573 580 $billingAddress['user_id'] = $current_user->ID;
574 581 } else {
575 - static::handleUserCreation($orderData, $billingAddress);
582 + unset($billingAddress['user_id']);
576 583 }
577 584
578 585 $customer = CustomerResource::create($billingAddress);
579 586 $customer = Arr::get($customer, 'data', null);
580 587 $customerId = Arr::get($customer, 'id', null);
581 - static::createCustomerAddress($billingAddress, $customerId);
582 - static::createCustomerAddress($shippingAddress, $customerId);
588 + if ($customer && $customer->wasRecentlyCreated) {
589 + static::createCustomerAddress($billingAddress, $customerId);
590 + static::createCustomerAddress($shippingAddress, $customerId);
591 + }
583 592
584 593 return $customer;
585 594 }
586 595
@@ -585,17 +594,13 @@
585 594 }
586 595
587 596 private static function updateExistingCustomer($customer, $orderData, $billingAddress, $shippingAddress)
588 597 {
589 - if (empty($customer->user_id)) {
590 - $currentLoggedInUser = wp_get_current_user();
591 - if ($currentLoggedInUser && $currentLoggedInUser->user_email === $customer->email) {
592 - $userId = get_current_user_id();
593 - $customer->update(['user_id' => $userId]);
594 - $billingAddress['user_id'] = $userId;
595 - }
598 + // Order addresses come from this checkout; saved profile data needs proof.
599 + if (!is_user_logged_in() || (int) $customer->user_id !== get_current_user_id()
600 + || EmailVerificationService::isRequired(get_current_user_id())) {
601 + return;
596 602 }
597 -
598 603 $customer->load(['billing_address', 'shipping_address']);
599 604
600 605 if ($customer->billing_address->count() < 1) {
601 606 static::createCustomerAddress($billingAddress, $customer->id);
@@ -602,25 +607,10 @@
602 607 }
603 608 if ($customer->shipping_address->count() < 1) {
604 609 static::createCustomerAddress($shippingAddress, $customer->id);
605 610 }
606 -
607 - static::handleUserCreation($orderData, $billingAddress, $customer);
608 611 }
609 612
610 - private static function handleUserCreation($orderData, &$billingAddress, $customer = null)
611 - {
612 - $userEmail = Arr::get($billingAddress, 'email');
613 - $user = get_user_by('email', $userEmail);
614 -
615 - if ($user) {
616 - $billingAddress['user_id'] = $user->ID;
617 - if ($customer) {
618 - $customer->update(['user_id' => $user->ID]);
619 - }
620 - }
621 - }
622 -
623 613 private static function getCustomerEmail($billingAddress)
624 614 {
625 615 return is_user_logged_in() ? wp_get_current_user()->user_email : $billingAddress['email'];
626 616 }
@@ -1000,9 +990,16 @@
1000 990
1001 991
1002 992 if ($cart->requireShipping()) {
1003 993 if (!empty($data['fc_selected_shipping_method'])) {
1004 - $selectedMethod = $data['fc_selected_shipping_method'];
994 + // One integer, decided here, is both what is checked and what placeOrder() prices.
995 + // A loose compare let PHP 7.4 match "1<b>2" to method 1, and sanitize_text_field()
996 + // then turned the same string into "12", so the order was priced by method 12.
997 + $rawMethod = $data['fc_selected_shipping_method'];
998 + $isPlainId = (is_string($rawMethod) || is_int($rawMethod)) && (string) absint($rawMethod) === (string) $rawMethod;
999 + $selectedMethod = $isPlainId ? absint($rawMethod) : 0;
1000 + $data['fc_selected_shipping_method'] = $selectedMethod;
1001 +
1005 1002 $shippingCountry = Arr::get($data, 'billing_country', '');
1006 1003 $shippingState = Arr::get($data, 'billing_state', '');
1007 1004 $shipToDifferent = Arr::get($data, 'ship_to_different', 'no') === 'yes';
1008 1005
@@ -1018,9 +1015,9 @@
1018 1015 $errors['shipping_method']['unavailable'] = __('We don\'t ship to this address. Please select a different address.', 'fluent-cart');
1019 1016 } else {
1020 1017 $found = false;
1021 1018 foreach ($availableShippingMethods as $shippingMethod) {
1022 - if ($shippingMethod->id == $selectedMethod) {
1019 + if ((int) $shippingMethod->id === $selectedMethod) {
1023 1020 $found = true;
1024 1021 break;
1025 1022 }
1026 1023 }