Skip to content

Commit 9d09208

Browse files
committed
🔀 Merge ranch 'dev/PCP-4891-agentic-commerce'
2 parents 0a224e7 + afec75b commit 9d09208

16 files changed

Lines changed: 1271 additions & 542 deletions

‎modules/ppcp-store-sync/services.php‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use WooCommerce\PayPalCommerce\StoreSync\Inspector\InspectionFormHandler;
3838
use WooCommerce\PayPalCommerce\StoreSync\Inspector\InspectionStatusPage;
3939
use WooCommerce\PayPalCommerce\StoreSync\Helper\AgenticCheckoutProcessor;
40+
use WooCommerce\PayPalCommerce\StoreSync\Helper\ShippingOptionsBuilder;
4041
use WooCommerce\PayPalCommerce\StoreSync\Helper\PayPalOrderManager;
4142
use WooCommerce\PayPalCommerce\StoreSync\Helper\AgenticCartBuilder;
4243
use WooCommerce\PayPalCommerce\StoreSync\Helper\ProductManager;
@@ -142,7 +143,9 @@
142143
$c->get( 'agentic.logger' )
143144
);
144145
},
145-
146+
'agentic.helper.shipping-options-builder' => static function (): ShippingOptionsBuilder {
147+
return new ShippingOptionsBuilder();
148+
},
146149
'agentic.helper.checkout-processor' => static function ( ContainerInterface $c ): AgenticCheckoutProcessor {
147150
return new AgenticCheckoutProcessor(
148151
$c->get( 'agentic.helper.paypal-order-manager' ),
@@ -222,7 +225,8 @@
222225
'agentic.response.factory' => static function ( ContainerInterface $c ): ResponseFactory {
223226
return new ResponseFactory(
224227
$c->get( 'agentic.helper.cart-builder' ),
225-
$c->get( 'agentic.response.applied-coupons-builder' )
228+
$c->get( 'agentic.response.applied-coupons-builder' ),
229+
$c->get( 'agentic.helper.shipping-options-builder' )
226230
);
227231
},
228232

‎modules/ppcp-store-sync/src/CartValidation/CouponValidator/CouponContextBuilder.php‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ private function build_minimum_spend( string $code, PayPalCart $cart, ?WC_Coupon
193193
$currency = CartHelper::currency( $cart, get_woocommerce_currency() );
194194

195195
return array(
196-
'minimum_required' => number_format( $minimum, 2, '.', '' ),
197-
'current_subtotal' => number_format( $subtotal, 2, '.', '' ),
198-
'shortage_amount' => number_format( $shortage, 2, '.', '' ),
196+
'minimum_required' => CartHelper::format_decimal( $minimum ),
197+
'current_subtotal' => CartHelper::format_decimal( $subtotal ),
198+
'shortage_amount' => CartHelper::format_decimal( $shortage ),
199199
'currency_code' => $currency,
200200
);
201201
}
@@ -219,8 +219,8 @@ private function build_maximum_spend( string $code, PayPalCart $cart, ?WC_Coupon
219219
$currency = CartHelper::currency( $cart, get_woocommerce_currency() );
220220

221221
return array(
222-
'maximum_allowed' => number_format( $maximum, 2, '.', '' ),
223-
'current_subtotal' => number_format( $subtotal, 2, '.', '' ),
222+
'maximum_allowed' => CartHelper::format_decimal( $maximum ),
223+
'current_subtotal' => CartHelper::format_decimal( $subtotal ),
224224
'currency_code' => $currency,
225225
);
226226
}

‎modules/ppcp-store-sync/src/CartValidation/CouponValidator/DiscountCalculator.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use WC_Discounts;
1616
use WooCommerce\PayPalCommerce\StoreSync\Helper\ProductManager;
1717
use WooCommerce\PayPalCommerce\StoreSync\Schema\PayPalCart;
18+
use WooCommerce\PayPalCommerce\StoreSync\Helper\CartHelper;
1819

1920
/**
2021
* Calculates discount amounts for coupons using WooCommerce's native discount calculation.
@@ -70,8 +71,7 @@ public function calculate_discount_amount( WC_Coupon $wc_coupon, PayPalCart $car
7071
? array_sum( $totals[ $code ] )
7172
: $totals[ $code ];
7273

73-
// The value from get_discounts_by_coupon() is already in regular decimal format.
74-
return number_format( $discount_value, 2, '.', '' );
74+
return CartHelper::format_decimal( $discount_value );
7575
}
7676

7777
return '0.00';

‎modules/ppcp-store-sync/src/Helper/CartHelper.php‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,15 @@ static function ( float $cart_total, CartItem $item ): float {
7979
}
8080

8181
/**
82-
* Formats a price value to two decimal places.
82+
* Formats a price value for an API response.
8383
*
84-
* @param float $value The price value to format.
84+
* PayPal expects monetary values to be strings with two decimal places.
85+
*
86+
* @param int|float|string $value The price value to format.
8587
* @return string The formatted price (e.g., "123.45").
8688
*/
87-
public static function format_decimal( float $value ): string {
88-
return number_format( $value, 2, '.', '' );
89+
public static function format_decimal( $value ): string {
90+
return number_format( (float) $value, 2, '.', '' );
8991
}
9092

9193
public static function full_customer_name( PayPalCart $cart, string $default = '' ): string {
@@ -195,7 +197,7 @@ public static function calculate_totals( WC_Cart $wc_cart, string $currency_code
195197
public static function money( string $currency_code, float $value ): array {
196198
return array(
197199
'currency_code' => $currency_code,
198-
'value' => number_format( $value, 2 ),
200+
'value' => self::format_decimal( $value ),
199201
);
200202
}
201203
}

‎modules/ppcp-store-sync/src/Helper/PayPalOrderManager.php‎

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ private function build_items_for_patch( PayPalCart $cart ): array {
257257
'quantity' => (string) $item->quantity(),
258258
'unit_amount' => array(
259259
'currency_code' => $currency,
260-
'value' => $this->format_money( (float) $price->value() ),
260+
'value' => CartHelper::format_decimal( $price->value() ),
261261
),
262262
);
263263
}
@@ -406,16 +406,4 @@ public function capture_order( string $order_id ): ?array {
406406
return null;
407407
}
408408
}
409-
410-
/**
411-
* Format a money value for PayPal API.
412-
*
413-
* PayPal requires money values as strings with 2 decimal places.
414-
*
415-
* @param float $value The money value.
416-
* @return string Formatted money value.
417-
*/
418-
private function format_money( float $value ): string {
419-
return number_format( $value, 2, '.', '' );
420-
}
421409
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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+
}

‎modules/ppcp-store-sync/src/Ingestion/ProductsPayload.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use WC_Product;
66
use WC_Product_Variation;
7+
use WooCommerce\PayPalCommerce\StoreSync\Helper\CartHelper;
78

89
class ProductsPayload {
910
private string $merchant_store_url;
@@ -293,6 +294,6 @@ private function format_price( $price ): string {
293294
return '';
294295
}
295296

296-
return number_format( (float) $price, 2, '.', '' ) . ' ' . get_woocommerce_currency();
297+
return CartHelper::format_decimal( $price ) . ' ' . get_woocommerce_currency();
297298
}
298299
}

0 commit comments

Comments
 (0)