|
| 1 | +<?php |
| 2 | +/** |
| 3 | + * Builds the available_shipping_options array for cart API responses. |
| 4 | + * |
| 5 | + * @package WooCommerce\PayPalCommerce\StoreSync\Helper |
| 6 | + */ |
| 7 | + |
| 8 | +declare( strict_types = 1 ); |
| 9 | + |
| 10 | +namespace WooCommerce\PayPalCommerce\StoreSync\Helper; |
| 11 | + |
| 12 | +use WC_Cart; |
| 13 | + |
| 14 | +class ShippingOptionsBuilder { |
| 15 | + |
| 16 | + /** |
| 17 | + * Build shipping options from the WC cart state post calculate_totals(). |
| 18 | + * |
| 19 | + * Returns an empty array when no cart is available or no packages exist. |
| 20 | + * Exactly one option in a non-empty result has is_selected = true. |
| 21 | + * |
| 22 | + * @param WC_Cart|null $wc_cart The WooCommerce cart. |
| 23 | + * @return array |
| 24 | + */ |
| 25 | + public function build( ?WC_Cart $wc_cart ): array { |
| 26 | + if ( null === $wc_cart ) { |
| 27 | + return array(); |
| 28 | + } |
| 29 | + |
| 30 | + $packages = WC()->shipping()->get_packages(); |
| 31 | + |
| 32 | + // Note: This plugin only supports a single package. |
| 33 | + // Key constraint is the PayPal API that can receive one shipping address per order. |
| 34 | + $package = $packages[0] ?? null; |
| 35 | + |
| 36 | + if ( empty( $package ) ) { |
| 37 | + return array(); |
| 38 | + } |
| 39 | + |
| 40 | + $chosen_methods = WC()->session->get( 'chosen_shipping_methods' ); |
| 41 | + |
| 42 | + $chosen_id = null; |
| 43 | + |
| 44 | + // The CartResponse schema expects only one selected shipping option. |
| 45 | + // If we have _any_ options, we pluck the first value as it's always present, |
| 46 | + // even when the session incorrectly contains multiple chosen methods. |
| 47 | + // We silently ignore cases where more than one option is chosen in the session. |
| 48 | + if ( is_array( $chosen_methods ) && ! empty( $chosen_methods ) ) { |
| 49 | + $chosen_id = $chosen_methods[0]; |
| 50 | + } |
| 51 | + |
| 52 | + $currency = get_woocommerce_currency(); |
| 53 | + $options = array(); |
| 54 | + $all_rates = $package['rates'] ?? array(); |
| 55 | + $first_rate_id = null; |
| 56 | + |
| 57 | + foreach ( $all_rates as $rate ) { |
| 58 | + // Note: Rate IDs can repeat in different packages, e.g. "flat_rate:1" can apply |
| 59 | + // to all packages. We do not care about this potential repeat ID, for 2 reasons: |
| 60 | + // 1. The PayPal API only supports single-package orders. |
| 61 | + // 2. The PayPalCart only supports a single shipping method. |
| 62 | + $rate_id = $rate->get_id(); |
| 63 | + |
| 64 | + if ( null === $first_rate_id ) { |
| 65 | + $first_rate_id = $rate_id; |
| 66 | + } |
| 67 | + |
| 68 | + $options[] = array( |
| 69 | + 'id' => $rate_id, |
| 70 | + 'label' => $rate->get_label(), |
| 71 | + 'amount' => CartHelper::format_decimal( $rate->get_cost() ), |
| 72 | + 'currency' => $currency, |
| 73 | + 'is_selected' => false, |
| 74 | + ); |
| 75 | + } |
| 76 | + |
| 77 | + if ( empty( $options ) ) { |
| 78 | + return array(); |
| 79 | + } |
| 80 | + |
| 81 | + $selected_id = $chosen_id ?? $first_rate_id; |
| 82 | + foreach ( $options as &$option ) { |
| 83 | + $option['is_selected'] = ( $option['id'] === $selected_id ); |
| 84 | + } |
| 85 | + unset( $option ); |
| 86 | + |
| 87 | + return $options; |
| 88 | + } |
| 89 | +} |
0 commit comments