Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
use WooCommerce\PayPalCommerce\StoreSync\Helper\CartHelper;
use WooCommerce\PayPalCommerce\StoreSync\Helper\ProductManager;
use WooCommerce\PayPalCommerce\StoreSync\Schema\PayPalCart;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\IssueContext;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\Specific\PricingDiscountCustomerIneligibleContext;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\Specific\PricingDiscountExpiredContext;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\Specific\PricingDiscountMinimumNotMetContext;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\Specific\PricingDiscountUsageLimitExceededContext;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\Specific\PricingPromotionalConflictContext;

/**
* Builds context data for coupon validation issues.
Expand Down Expand Up @@ -44,11 +50,11 @@ public function __construct( ProductManager $product_manager, DiscountCalculator
* Builds context by calling declared context builders.
*
* @param string $issue_type The issue type.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $builders Array of builder names to call.
* @param array $extra Extra context data.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $builders Array of builder names to call.
* @param array $extra Extra context data.
* @return array The built context.
*/
public function build_coupon_context(
Expand All @@ -75,11 +81,11 @@ public function build_coupon_context(
/**
* Dispatches to the appropriate builder method.
*
* @param string $builder The builder name.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $builder The builder name.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data from the builder.
*/
private function call_builder( string $builder, string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand Down Expand Up @@ -108,10 +114,10 @@ private function call_builder( string $builder, string $code, PayPalCart $cart,
/**
* Builds alternative coupons context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_alternatives( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -130,10 +136,10 @@ private function build_alternatives( string $code, PayPalCart $cart, ?WC_Coupon
/**
* Builds expiration context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_expiration( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -147,17 +153,17 @@ private function build_expiration( string $code, PayPalCart $cart, ?WC_Coupon $w
}

return array(
'expiration_date' => $expiration_date->format( 'c' ),
'expiration_date' => $expiration_date->getTimestamp(),
);
}

/**
* Builds usage limits context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_usage_limits( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -174,10 +180,10 @@ private function build_usage_limits( string $code, PayPalCart $cart, ?WC_Coupon
/**
* Builds minimum spend context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_minimum_spend( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -201,10 +207,10 @@ private function build_minimum_spend( string $code, PayPalCart $cart, ?WC_Coupon
/**
* Builds maximum spend context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_maximum_spend( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -226,10 +232,10 @@ private function build_maximum_spend( string $code, PayPalCart $cart, ?WC_Coupon
/**
* Builds eligible items context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_eligible_items( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -249,10 +255,10 @@ private function build_eligible_items( string $code, PayPalCart $cart, ?WC_Coupo
/**
* Builds stacking conflict context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_stacking( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand Down Expand Up @@ -285,10 +291,10 @@ private function build_stacking( string $code, PayPalCart $cart, ?WC_Coupon $wc_
/**
* Builds email restriction context.
*
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param string $code The coupon code.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $extra Extra context data.
* @param array $extra Extra context data.
* @return array The context data.
*/
private function build_email_restriction( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
Expand All @@ -309,8 +315,8 @@ private function build_email_restriction( string $code, PayPalCart $cart, ?WC_Co
* Gets alternative coupon suggestions via filter.
*
* @param string $failed_code The coupon code that failed.
* @param string $reason The failure reason.
* @param PayPalCart $cart The cart context.
* @param string $reason The failure reason.
* @param PayPalCart $cart The cart context.
* @return array Array of alternative coupon codes.
*/
private function get_alternative_coupons( string $failed_code, string $reason, PayPalCart $cart ): array {
Expand All @@ -323,6 +329,68 @@ private function get_alternative_coupons( string $failed_code, string $reason, P
);
}

/**
* Builds a typed IssueContext instance from a resolved context array.
*
* Returns null for issue types that have no matching context class.
*
* @param string $issue_type The coupon issue type (e.g. 'COUPON_EXPIRED').
* @param string $coupon_code The coupon code that triggered the issue.
* @param array $args The flat context array built by build_coupon_context().
* @return IssueContext|null
*/
public function build_coupon_issue_context( string $issue_type, string $coupon_code, array $args ): ?IssueContext {
$context = null;
switch ( $issue_type ) {
case 'COUPON_EXPIRED':
$context = PricingDiscountExpiredContext::create();
break;

case 'USAGE_LIMIT_EXCEEDED':
$context = PricingDiscountUsageLimitExceededContext::create();
break;

case 'MINIMUM_ORDER_NOT_MET':
$context = PricingDiscountMinimumNotMetContext::create();
break;

case 'COUPON_NOT_APPLICABLE':
case 'COUPON_EMAIL_RESTRICTED':
$context = PricingDiscountCustomerIneligibleContext::create();
break;

case 'COUPON_STACKING_NOT_ALLOWED':
$context = PricingPromotionalConflictContext::create();
break;
}

if ( ! $context ) {
return null;
}

$context->coupon_code( $coupon_code );
if ( isset( $args['expiration_date'] ) ) {
$context->expiration_date( (int) $args['expiration_date'] );
}
if ( isset( $args['usage_limit'] ) && is_numeric( $args['usage_limit'] ) ) {
$context->usage_limit( (int) $args['usage_limit'] );
}
if ( isset( $args['minimum_required'] ) ) {
$context->minimum_order_amount( (string) $args['minimum_required'] );
}
if ( isset( $args['current_usage'] ) ) {
$context->current_usage( (int) $args['current_usage'] );
}
if ( isset( $args['currency_code'] ) ) {
$context->currency_code( (string) $args['currency_code'] );
}
if ( isset( $args['current_discount'] ) ) {
$context->discount_amount( (string) $args['current_discount'] );
}

return $context;
}

/**
* Gets eligible items for a coupon via filter.
*
Expand All @@ -331,7 +399,7 @@ private function get_alternative_coupons( string $failed_code, string $reason, P
* excluded items, sale items, and third-party plugin logic.
*
* @param WC_Coupon $wc_coupon The WC coupon.
* @param PayPalCart $cart The cart context.
* @param PayPalCart $cart The cart context.
* @return array Array of eligible variant IDs.
*/
private function get_eligible_items( WC_Coupon $wc_coupon, PayPalCart $cart ): array {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
use WooCommerce\PayPalCommerce\StoreSync\Enums\Priority;
use WooCommerce\PayPalCommerce\StoreSync\Helper\CartHelper;
use WooCommerce\PayPalCommerce\StoreSync\Schema\PayPalCart;
use WooCommerce\PayPalCommerce\StoreSync\Schema\ResolutionOption;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\Action\ApplyDifferentCoupon;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\Action\KeepCurrentCoupon;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\Action\ModifyCart;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\Action\RedirectToMerchant;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\Action\RemoveCoupon;
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\ResolutionOption;

/**
* Builds resolution options for coupon validation issues.
Expand All @@ -26,11 +31,11 @@ class CouponResolutionBuilder {
* Builds resolutions from config keys, with special handling for stacking.
*
* @param string $issue_type The issue type.
* @param array $keys Resolution keys from config.
* @param string $code The coupon code.
* @param array $context The context data.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @param array $keys Resolution keys from config.
* @param string $code The coupon code.
* @param array $context The context data.
* @param PayPalCart $cart The cart context.
* @param WC_Coupon|null $wc_coupon The WC coupon object.
* @return ResolutionOption[] The resolution options.
*/
public function build_resolution_options(
Expand Down Expand Up @@ -61,50 +66,52 @@ public function build_resolution_options(
/**
* Dispatches to the appropriate resolution factory method.
*
* @param string $key The resolution key.
* @param string $key The resolution key.
* @param array $context The context data.
* @param PayPalCart $cart The cart context.
* @param PayPalCart $cart The cart context.
* @return ResolutionOption|null The resolution option or null if key not recognized.
*/
private function build_resolution_by_key( string $key, array $context, PayPalCart $cart ): ?ResolutionOption {
switch ( $key ) {
case 'try_different':
return ResolutionOption::apply_different_coupon( 'Try a different coupon code', Priority::HIGH );
return ApplyDifferentCoupon::create()
->label( 'Try a different coupon code' )
->priority( Priority::HIGH );

case 'remove':
return ResolutionOption::remove_coupon( 'Continue without coupon', Priority::MEDIUM );
return RemoveCoupon::create()
->label( 'Continue without coupon' )
->priority( Priority::MEDIUM );

case 'modify_cart':
return ResolutionOption::modify_cart(
'Add eligible items to use this coupon',
array( 'priority' => Priority::HIGH )
);
return ModifyCart::create()
->label( 'Add eligible items to use this coupon' )
->priority( Priority::HIGH );

case 'view_available':
return ResolutionOption::redirect_to_merchant(
'View available offers',
'',
array( 'priority' => Priority::LOW )
);
return RedirectToMerchant::create()
->label( 'View available offers' )
->priority( Priority::LOW );

case 'suggest_alternative':
return ResolutionOption::apply_different_coupon( 'Try a different coupon', Priority::MEDIUM );
return ApplyDifferentCoupon::create()
->label( 'Try a different coupon' )
->priority( Priority::MEDIUM );

case 'add_items_to_minimum':
$formatted_amount = isset( $context['shortage_amount'] )
? CartHelper::format_price( $context['shortage_amount'], $cart )
: '';

return ResolutionOption::modify_cart(
sprintf( 'Add %s more to qualify', $formatted_amount ),
array(
'priority' => Priority::HIGH,
'amount_needed' => $formatted_amount,
)
);
return ModifyCart::create()
->label( sprintf( 'Add %s more to qualify', $formatted_amount ) )
->priority( Priority::HIGH )
->set_meta( 'amount_needed', $formatted_amount );

case 'continue_without':
return ResolutionOption::remove_coupon( 'Continue without coupon', Priority::LOW );
return RemoveCoupon::create()
->label( 'Continue without coupon' )
->priority( Priority::LOW );

default:
return null;
Expand All @@ -114,9 +121,9 @@ private function build_resolution_by_key( string $key, array $context, PayPalCar
/**
* Builds stacking-specific resolutions with savings comparison.
*
* @param string $code The coupon code.
* @param string $code The coupon code.
* @param array $context The context data.
* @param PayPalCart $cart The cart context.
* @param PayPalCart $cart The cart context.
* @return ResolutionOption[] The resolution options.
*/
private function build_stacking_resolutions( string $code, array $context, PayPalCart $cart ): array {
Expand All @@ -128,16 +135,14 @@ private function build_stacking_resolutions( string $code, array $context, PayPa
$formatted_attempted = CartHelper::format_price( $attempted_discount, $cart );

return array(
ResolutionOption::keep_current_coupon(
sprintf( 'Keep %s (saves %s)', $code, $formatted_current ),
Priority::HIGH,
array( 'savings' => $formatted_current )
),
ResolutionOption::apply_different_coupon(
sprintf( 'Switch to %s (saves %s)', $attempted_coupon, $formatted_attempted ),
Priority::LOW,
array( 'savings' => $formatted_attempted )
),
KeepCurrentCoupon::create()
->label( sprintf( 'Keep %s (saves %s)', $code, $formatted_current ) )
->priority( Priority::HIGH )
->set_meta( 'savings', $formatted_current ),
ApplyDifferentCoupon::create()
->label( sprintf( 'Switch to %s (saves %s)', $attempted_coupon, $formatted_attempted ) )
->priority( Priority::LOW )
->set_meta( 'savings', $formatted_attempted ),
);
}
}
Loading
Loading