mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-10 01:15:45 +08:00
Refactor PurchaseUnitSanitizer to subtract 0.01 instead of rounding down
Refactor PurchaseUnitSanitizer to use singleton pattern
This commit is contained in:
parent
dea55e24c4
commit
bb3b8553b9
8 changed files with 70 additions and 61 deletions
33
lib/packages/Pattern/SingletonTrait.php
Normal file
33
lib/packages/Pattern/SingletonTrait.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Pattern;
|
||||
|
||||
trait SingletonTrait {
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var self
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Static method to get the instance of the Singleton class
|
||||
*
|
||||
* @return self|null
|
||||
*/
|
||||
public static function get_instance(): ?self {
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to get the instance of the Singleton class
|
||||
*
|
||||
* @param self $instance
|
||||
* @return self
|
||||
*/
|
||||
protected static function set_instance( self $instance ): self {
|
||||
self::$instance = $instance;
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
}
|
|
@ -824,11 +824,14 @@ return array(
|
|||
return new OrderTransient( $cache, $purchase_unit_sanitizer );
|
||||
},
|
||||
'api.helper.purchase-unit-sanitizer' => static function( ContainerInterface $container ): PurchaseUnitSanitizer {
|
||||
if ( $instance = PurchaseUnitSanitizer::get_instance() ) {
|
||||
return $instance;
|
||||
}
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$behavior = $settings->has( 'subtotal_mismatch_behavior' ) ? $settings->get( 'subtotal_mismatch_behavior' ) : null;
|
||||
$line_name = $settings->has( 'subtotal_mismatch_line_name' ) ? $settings->get( 'subtotal_mismatch_line_name' ) : null;
|
||||
return new PurchaseUnitSanitizer( $behavior, $line_name );
|
||||
return PurchaseUnitSanitizer::singleton( $behavior, $line_name );
|
||||
},
|
||||
);
|
||||
|
|
|
@ -201,13 +201,12 @@ class Item {
|
|||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @param bool $round_to_floor If value rounding should be floor.
|
||||
* @return array
|
||||
*/
|
||||
public function to_array( bool $round_to_floor = false ): array {
|
||||
public function to_array(): array {
|
||||
$item = array(
|
||||
'name' => $this->name(),
|
||||
'unit_amount' => $this->unit_amount()->to_array( $round_to_floor ),
|
||||
'unit_amount' => $this->unit_amount()->to_array(),
|
||||
'quantity' => $this->quantity(),
|
||||
'description' => $this->description(),
|
||||
'sku' => $this->sku(),
|
||||
|
|
|
@ -62,11 +62,10 @@ class Money {
|
|||
/**
|
||||
* The value formatted as string for API requests.
|
||||
*
|
||||
* @param bool $round_to_floor If value rounding should be floor.
|
||||
* @return string
|
||||
*/
|
||||
public function value_str( bool $round_to_floor = false ): string {
|
||||
return $this->money_formatter->format( $this->value, $this->currency_code, $round_to_floor );
|
||||
public function value_str(): string {
|
||||
return $this->money_formatter->format( $this->value, $this->currency_code );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,22 +80,12 @@ class Money {
|
|||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @param bool $round_to_floor If value rounding should be floor.
|
||||
* @return array
|
||||
*/
|
||||
public function to_array( bool $round_to_floor = false ): array {
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'currency_code' => $this->currency_code(),
|
||||
'value' => $this->value_str( $round_to_floor ),
|
||||
'value' => $this->value_str(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the default rounding for this value is rounding up.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_rounding_up(): bool {
|
||||
return (float) $this->value_str() > $this->value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,7 +336,7 @@ class PurchaseUnit {
|
|||
}
|
||||
|
||||
if ( $sanitize_output && isset( $this->sanitizer ) ) {
|
||||
$purchase_unit = ( $this->sanitizer->sanitize( $purchase_unit, $this->items(), $allow_ditch_items ) );
|
||||
$purchase_unit = ( $this->sanitizer->sanitize( $purchase_unit, $allow_ditch_items ) );
|
||||
}
|
||||
|
||||
return $purchase_unit;
|
||||
|
|
|
@ -25,31 +25,12 @@ class MoneyFormatter {
|
|||
*
|
||||
* @param float $value The value.
|
||||
* @param string $currency The 3-letter currency code.
|
||||
* @param bool $round_to_floor If value rounding should be floor.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format( float $value, string $currency, bool $round_to_floor = false ): string {
|
||||
if ( $round_to_floor ) {
|
||||
return in_array( $currency, $this->currencies_without_decimals, true )
|
||||
? (string) floor( $value )
|
||||
: number_format( $this->floor_with_decimals( $value, 2 ), 2, '.', '' );
|
||||
}
|
||||
|
||||
public function format( float $value, string $currency ): string {
|
||||
return in_array( $currency, $this->currencies_without_decimals, true )
|
||||
? (string) round( $value, 0 )
|
||||
: number_format( $value, 2, '.', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds to floor with decimal precision.
|
||||
*
|
||||
* @param float $value The value.
|
||||
* @param int $decimals The number of decimals.
|
||||
* @return float
|
||||
*/
|
||||
private function floor_with_decimals( float $value, int $decimals = 0 ): float {
|
||||
$adjustment = (float) pow( 10, $decimals );
|
||||
return floor( $value * $adjustment ) / $adjustment;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,14 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Pattern\SingletonTrait;
|
||||
|
||||
/**
|
||||
* Class PurchaseUnitSanitizer
|
||||
*/
|
||||
class PurchaseUnitSanitizer {
|
||||
use SingletonTrait;
|
||||
|
||||
const MODE_DITCH = 'ditch';
|
||||
const MODE_EXTRA_LINE = 'extra_line';
|
||||
const VALID_MODES = array(
|
||||
|
@ -40,13 +43,6 @@ class PurchaseUnitSanitizer {
|
|||
*/
|
||||
private $purchase_unit = array();
|
||||
|
||||
/**
|
||||
* The purchase unit Item objects
|
||||
*
|
||||
* @var array|Item[]
|
||||
*/
|
||||
private $item_objects = array();
|
||||
|
||||
/**
|
||||
* Whether to allow items to be ditched.
|
||||
*
|
||||
|
@ -95,6 +91,17 @@ class PurchaseUnitSanitizer {
|
|||
$this->extra_line_name = $extra_line_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* PurchaseUnitSanitizer singleton.
|
||||
*
|
||||
* @param string|null $mode The mismatch handling mode, ditch or extra_line.
|
||||
* @param string|null $extra_line_name The name of the extra line.
|
||||
* @return self
|
||||
*/
|
||||
public static function singleton( string $mode = null, string $extra_line_name = null ): self {
|
||||
return self::set_instance( new self( $mode, $extra_line_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if mode is ditch.
|
||||
*
|
||||
|
@ -166,13 +173,11 @@ class PurchaseUnitSanitizer {
|
|||
* The sanitizes the purchase_unit array.
|
||||
*
|
||||
* @param array $purchase_unit The purchase_unit array that should be sanitized.
|
||||
* @param array|Item[] $item_objects The purchase unit Item objects used for recalculations.
|
||||
* @param bool $allow_ditch_items Whether to allow items to be ditched.
|
||||
* @return array
|
||||
*/
|
||||
public function sanitize( array $purchase_unit, array $item_objects, bool $allow_ditch_items = true ): array {
|
||||
public function sanitize( array $purchase_unit, bool $allow_ditch_items = true ): array {
|
||||
$this->purchase_unit = $purchase_unit;
|
||||
$this->item_objects = $item_objects;
|
||||
$this->allow_ditch_items = $allow_ditch_items;
|
||||
|
||||
$this->sanitize_item_amount_mismatch();
|
||||
|
@ -193,10 +198,9 @@ class PurchaseUnitSanitizer {
|
|||
if ( $item_mismatch < 0 ) {
|
||||
|
||||
// Do floors on item amounts so item_mismatch is a positive value.
|
||||
foreach ( $this->item_objects as $index => $item ) {
|
||||
$this->purchase_unit['items'][ $index ] = $item->to_array(
|
||||
$item->unit_amount()->is_rounding_up()
|
||||
);
|
||||
foreach ( $this->purchase_unit['items'] as $index => $item ) {
|
||||
// get a more intelligent adjustment mechanism
|
||||
$this->purchase_unit['items'][ $index ]['unit_amount']['value'] = ( (float) $this->purchase_unit['items'][ $index ]['unit_amount']['value'] ) - 0.01;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -662,11 +662,11 @@ class PurchaseUnitTest extends TestCase
|
|||
'insurance' => null,
|
||||
],
|
||||
],
|
||||
'with_rounding_down_only_first_item' => [
|
||||
'message' => 'Extra line should be added with price 0.01 and lines amount 10.00 and 5.00.',
|
||||
'with_two_rounding_down' => [
|
||||
'message' => 'Extra line should be added with price 0.03 and lines amount 10.00 and 4.99.',
|
||||
'expected' => [
|
||||
'item_value' => [10.00, 5.00],
|
||||
'extra_line_value' => 0.01
|
||||
'item_value' => [10.00, 4.99],
|
||||
'extra_line_value' => 0.03
|
||||
],
|
||||
'items' => [
|
||||
[
|
||||
|
@ -693,11 +693,11 @@ class PurchaseUnitTest extends TestCase
|
|||
'insurance' => null,
|
||||
],
|
||||
],
|
||||
'with_multiple_roundings_down' => [
|
||||
'with_many_roundings_down' => [
|
||||
'message' => 'Extra line should be added with price 0.01 and lines amount 10.00, 5.00 and 6.66.',
|
||||
'expected' => [
|
||||
'item_value' => [10.00, 5.00, 6.66],
|
||||
'extra_line_value' => 0.01
|
||||
'item_value' => [10.00, 4.99, 6.66],
|
||||
'extra_line_value' => 0.02
|
||||
],
|
||||
'items' => [
|
||||
[
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue