mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Do not send negative price items, add to discount
This commit is contained in:
parent
c3887bec7a
commit
88220aa3c6
3 changed files with 141 additions and 35 deletions
|
@ -124,6 +124,27 @@ class AmountFactory {
|
|||
$currency = $order->get_currency();
|
||||
$items = $this->item_factory->from_wc_order( $order );
|
||||
|
||||
$discount_value = array_sum(
|
||||
array(
|
||||
(float) $order->get_total_discount( false ), // Only coupons.
|
||||
$this->discounts_from_items( $items ),
|
||||
)
|
||||
);
|
||||
$discount = null;
|
||||
if ( $discount_value ) {
|
||||
$discount = new Money(
|
||||
(float) $discount_value,
|
||||
$currency
|
||||
);
|
||||
}
|
||||
|
||||
$items = array_filter(
|
||||
$items,
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() > 0;
|
||||
}
|
||||
);
|
||||
|
||||
$total_value = (float) $order->get_total();
|
||||
if ( (
|
||||
CreditCardGateway::ID === $order->get_payment_method()
|
||||
|
@ -160,14 +181,6 @@ class AmountFactory {
|
|||
$currency
|
||||
);
|
||||
|
||||
$discount = null;
|
||||
if ( (float) $order->get_total_discount( false ) ) {
|
||||
$discount = new Money(
|
||||
(float) $order->get_total_discount( false ),
|
||||
$currency
|
||||
);
|
||||
}
|
||||
|
||||
$breakdown = new AmountBreakdown(
|
||||
$item_total,
|
||||
$shipping,
|
||||
|
@ -251,4 +264,29 @@ class AmountFactory {
|
|||
|
||||
return new AmountBreakdown( ...$money );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of items with negative amount;
|
||||
*
|
||||
* @param Item[] $items PayPal order items.
|
||||
* @return float
|
||||
*/
|
||||
private function discounts_from_items( array $items ): float {
|
||||
$discounts = array_filter(
|
||||
$items,
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() < 0;
|
||||
}
|
||||
);
|
||||
return abs(
|
||||
array_sum(
|
||||
array_map(
|
||||
function ( Item $item ): float {
|
||||
return (float) $item->quantity() * $item->unit_amount()->value();
|
||||
},
|
||||
$discounts
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,12 @@ class PurchaseUnitFactory {
|
|||
*/
|
||||
public function from_wc_order( \WC_Order $order ): PurchaseUnit {
|
||||
$amount = $this->amount_factory->from_wc_order( $order );
|
||||
$items = $this->item_factory->from_wc_order( $order );
|
||||
$items = array_filter(
|
||||
$this->item_factory->from_wc_order( $order ),
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() > 0;
|
||||
}
|
||||
);
|
||||
$shipping = $this->shipping_factory->from_wc_order( $order );
|
||||
if (
|
||||
! $this->shipping_needed( ... array_values( $items ) ) ||
|
||||
|
@ -153,7 +158,12 @@ class PurchaseUnitFactory {
|
|||
*/
|
||||
public function from_wc_cart( \WC_Cart $cart ): PurchaseUnit {
|
||||
$amount = $this->amount_factory->from_wc_cart( $cart );
|
||||
$items = $this->item_factory->from_wc_cart( $cart );
|
||||
$items = array_filter(
|
||||
$this->item_factory->from_wc_cart( $cart ),
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() > 0;
|
||||
}
|
||||
);
|
||||
|
||||
$shipping = null;
|
||||
$customer = \WC()->customer;
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Address;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payee;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
|
@ -21,7 +22,19 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
private $wcOrderId = 1;
|
||||
private $wcOrderNumber = '100000';
|
||||
|
||||
public function testWcOrderDefault()
|
||||
private $item;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->item = Mockery::mock(Item::class, [
|
||||
'category' => Item::PHYSICAL_GOODS,
|
||||
'unit_amount' => new Money(42.5, 'USD'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function testWcOrderDefault()
|
||||
{
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder->expects('get_order_number')->andReturn($this->wcOrderNumber);
|
||||
|
@ -37,13 +50,11 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payee = Mockery::mock(Payee::class);
|
||||
$payeeRepository
|
||||
->shouldReceive('payee')->andReturn($payee);
|
||||
$item = Mockery::mock(Item::class);
|
||||
$item->shouldReceive('category')->andReturn(Item::PHYSICAL_GOODS);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->shouldReceive('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn([$item]);
|
||||
->andReturn([$this->item]);
|
||||
|
||||
$address = Mockery::mock(Address::class);
|
||||
$address
|
||||
|
@ -79,11 +90,67 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$this->assertEquals($this->wcOrderId, $unit->custom_id());
|
||||
$this->assertEquals('', $unit->soft_descriptor());
|
||||
$this->assertEquals('WC-' . $this->wcOrderNumber, $unit->invoice_id());
|
||||
$this->assertEquals([$item], $unit->items());
|
||||
$this->assertEquals([$this->item], $unit->items());
|
||||
$this->assertEquals($amount, $unit->amount());
|
||||
$this->assertEquals($shipping, $unit->shipping());
|
||||
}
|
||||
|
||||
public function testWcOrderWithNegativeFees()
|
||||
{
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder->expects('get_order_number')->andReturn($this->wcOrderNumber);
|
||||
$wcOrder->expects('get_id')->andReturn($this->wcOrderId);
|
||||
$amount = Mockery::mock(Amount::class);
|
||||
$amountFactory = Mockery::mock(AmountFactory::class);
|
||||
$amountFactory
|
||||
->shouldReceive('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn($amount);
|
||||
$payeeFactory = Mockery::mock(PayeeFactory::class);
|
||||
$payeeRepository = Mockery::mock(PayeeRepository::class);
|
||||
$payee = Mockery::mock(Payee::class);
|
||||
$payeeRepository
|
||||
->shouldReceive('payee')->andReturn($payee);
|
||||
|
||||
$fee = Mockery::mock(Item::class, [
|
||||
'category' => Item::DIGITAL_GOODS,
|
||||
'unit_amount' => new Money(10.0, 'USD'),
|
||||
]);
|
||||
$discount = Mockery::mock(Item::class, [
|
||||
'unit_amount' => new Money(-5, 'USD'),
|
||||
]);
|
||||
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->shouldReceive('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn([$this->item, $fee, $discount]);
|
||||
|
||||
$address = Mockery::mock(Address::class);
|
||||
$address->shouldReceive('country_code')->andReturn('DE');
|
||||
$address->shouldReceive('postal_code')->andReturn('12345');
|
||||
$shipping = Mockery::mock(Shipping::class);
|
||||
$shipping->shouldReceive('address')->andReturn($address);
|
||||
$shippingFactory = Mockery::mock(ShippingFactory::class);
|
||||
$shippingFactory
|
||||
->shouldReceive('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn($shipping);
|
||||
$paymentsFacory = Mockery::mock(PaymentsFactory::class);
|
||||
$testee = new PurchaseUnitFactory(
|
||||
$amountFactory,
|
||||
$payeeRepository,
|
||||
$payeeFactory,
|
||||
$itemFactory,
|
||||
$shippingFactory,
|
||||
$paymentsFacory
|
||||
);
|
||||
|
||||
$unit = $testee->from_wc_order($wcOrder);
|
||||
$this->assertTrue(is_a($unit, PurchaseUnit::class));
|
||||
$this->assertEquals([$this->item, $fee], $unit->items());
|
||||
}
|
||||
|
||||
public function testWcOrderShippingGetsDroppedWhenNoPostalCode()
|
||||
{
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
|
@ -100,12 +167,11 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payee = Mockery::mock(Payee::class);
|
||||
$payeeRepository
|
||||
->expects('payee')->andReturn($payee);
|
||||
$item = Mockery::mock(Item::class, ['category' => Item::PHYSICAL_GOODS]);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->expects('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn([$item]);
|
||||
->andReturn([$this->item]);
|
||||
|
||||
$address = Mockery::mock(Address::class);
|
||||
$address
|
||||
|
@ -155,12 +221,11 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payee = Mockery::mock(Payee::class);
|
||||
$payeeRepository
|
||||
->expects('payee')->andReturn($payee);
|
||||
$item = Mockery::mock(Item::class, ['category' => Item::PHYSICAL_GOODS]);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->expects('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn([$item]);
|
||||
->andReturn([$this->item]);
|
||||
|
||||
$address = Mockery::mock(Address::class);
|
||||
$address
|
||||
|
@ -208,13 +273,11 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payeeRepository
|
||||
->expects('payee')->andReturn($payee);
|
||||
|
||||
$item = Mockery::mock(Item::class);
|
||||
$item->shouldReceive('category')->andReturn(Item::PHYSICAL_GOODS);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->expects('from_wc_cart')
|
||||
->with($wcCart)
|
||||
->andReturn([$item]);
|
||||
->andReturn([$this->item]);
|
||||
|
||||
$address = Mockery::mock(Address::class);
|
||||
$address
|
||||
|
@ -251,7 +314,7 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$this->assertEquals('', $unit->custom_id());
|
||||
$this->assertEquals('', $unit->soft_descriptor());
|
||||
$this->assertEquals('', $unit->invoice_id());
|
||||
$this->assertEquals([$item], $unit->items());
|
||||
$this->assertEquals([$this->item], $unit->items());
|
||||
$this->assertEquals($amount, $unit->amount());
|
||||
$this->assertEquals($shipping, $unit->shipping());
|
||||
}
|
||||
|
@ -273,13 +336,12 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payee = Mockery::mock(Payee::class);
|
||||
$payeeRepository
|
||||
->expects('payee')->andReturn($payee);
|
||||
$item = Mockery::mock(Item::class);
|
||||
$item->shouldReceive('category')->andReturn(Item::PHYSICAL_GOODS);
|
||||
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->expects('from_wc_cart')
|
||||
->with($wcCart)
|
||||
->andReturn([$item]);
|
||||
->andReturn([$this->item]);
|
||||
$shippingFactory = Mockery::mock(ShippingFactory::class);
|
||||
$paymentsFacory = Mockery::mock(PaymentsFactory::class);
|
||||
$testee = new PurchaseUnitFactory(
|
||||
|
@ -312,12 +374,11 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payee = Mockery::mock(Payee::class);
|
||||
$payeeRepository
|
||||
->expects('payee')->andReturn($payee);
|
||||
$item = Mockery::mock(Item::class, ['category' => Item::PHYSICAL_GOODS]);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$itemFactory
|
||||
->expects('from_wc_cart')
|
||||
->with($wcCart)
|
||||
->andReturn([$item]);
|
||||
->andReturn([$this->item]);
|
||||
|
||||
$address = Mockery::mock(Address::class);
|
||||
$address
|
||||
|
@ -359,8 +420,7 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payeeFactory->expects('from_paypal_response')->with($rawPayee)->andReturn($payee);
|
||||
$payeeRepository = Mockery::mock(PayeeRepository::class);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$item = Mockery::mock(Item::class, ['category' => Item::PHYSICAL_GOODS]);
|
||||
$itemFactory->expects('from_paypal_response')->with($rawItem)->andReturn($item);
|
||||
$itemFactory->expects('from_paypal_response')->with($rawItem)->andReturn($this->item);
|
||||
$shippingFactory = Mockery::mock(ShippingFactory::class);
|
||||
$shipping = Mockery::mock(Shipping::class);
|
||||
$shippingFactory->expects('from_paypal_response')->with($rawShipping)->andReturn($shipping);
|
||||
|
@ -394,7 +454,7 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$this->assertEquals('customId', $unit->custom_id());
|
||||
$this->assertEquals('softDescriptor', $unit->soft_descriptor());
|
||||
$this->assertEquals('invoiceId', $unit->invoice_id());
|
||||
$this->assertEquals([$item], $unit->items());
|
||||
$this->assertEquals([$this->item], $unit->items());
|
||||
$this->assertEquals($amount, $unit->amount());
|
||||
$this->assertEquals($shipping, $unit->shipping());
|
||||
}
|
||||
|
@ -411,8 +471,7 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payeeFactory = Mockery::mock(PayeeFactory::class);
|
||||
$payeeRepository = Mockery::mock(PayeeRepository::class);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$item = Mockery::mock(Item::class, ['category' => Item::PHYSICAL_GOODS]);
|
||||
$itemFactory->expects('from_paypal_response')->with($rawItem)->andReturn($item);
|
||||
$itemFactory->expects('from_paypal_response')->with($rawItem)->andReturn($this->item);
|
||||
$shippingFactory = Mockery::mock(ShippingFactory::class);
|
||||
$shipping = Mockery::mock(Shipping::class);
|
||||
$shippingFactory->expects('from_paypal_response')->with($rawShipping)->andReturn($shipping);
|
||||
|
@ -454,8 +513,7 @@ class PurchaseUnitFactoryTest extends TestCase
|
|||
$payeeFactory->expects('from_paypal_response')->with($rawPayee)->andReturn($payee);
|
||||
$payeeRepository = Mockery::mock(PayeeRepository::class);
|
||||
$itemFactory = Mockery::mock(ItemFactory::class);
|
||||
$item = Mockery::mock(Item::class, ['category' => Item::PHYSICAL_GOODS]);
|
||||
$itemFactory->expects('from_paypal_response')->with($rawItem)->andReturn($item);
|
||||
$itemFactory->expects('from_paypal_response')->with($rawItem)->andReturn($this->item);
|
||||
$shippingFactory = Mockery::mock(ShippingFactory::class);
|
||||
$paymentsFacory = Mockery::mock(PaymentsFactory::class);
|
||||
$testee = new PurchaseUnitFactory(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue