From 884ccd924cdf4304854c11cc119087df7e068510 Mon Sep 17 00:00:00 2001 From: Josias Montag Date: Fri, 13 Aug 2021 15:24:18 +0200 Subject: [PATCH] Add support for inline price data (#1235) * Add support for inline price data * Apply style fixes * Cleanup * Update ManagesInvoices.php Co-authored-by: Dries Vints Co-authored-by: Taylor Otwell --- src/Concerns/ManagesInvoices.php | 11 ++++-- src/Subscription.php | 5 ++- src/SubscriptionBuilder.php | 12 ++++--- tests/Feature/InvoicesTest.php | 23 ++++++++++++ tests/Feature/SubscriptionsTest.php | 54 +++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/Concerns/ManagesInvoices.php b/src/Concerns/ManagesInvoices.php index f3a2609f..d9ab99f2 100644 --- a/src/Concerns/ManagesInvoices.php +++ b/src/Concerns/ManagesInvoices.php @@ -25,8 +25,8 @@ trait ManagesInvoices */ public function tab($description, $amount, array $options = []) { - if ($this->isAutomaticTaxEnabled()) { - throw new LogicException('For now, you cannot add invoice items in combination automatic tax calculation.'); + if ($this->isAutomaticTaxEnabled() && ! array_key_exists('price_data', $options)) { + throw new LogicException('When using automatic tax calculation, you need to define the "price_data" in the options.'); } $this->assertCustomerExists(); @@ -37,7 +37,12 @@ public function tab($description, $amount, array $options = []) 'description' => $description, ], $options); - if (array_key_exists('quantity', $options)) { + if (array_key_exists('price_data', $options)) { + $options['price_data'] = array_merge([ + 'unit_amount' => $amount, + 'currency' => $this->preferredCurrency(), + ], $options['price_data']); + } elseif (array_key_exists('quantity', $options)) { $options['unit_amount'] = $options['unit_amount'] ?? $amount; } else { $options['amount'] = $amount; diff --git a/src/Subscription.php b/src/Subscription.php index 4596b477..e1d0b6ab 100644 --- a/src/Subscription.php +++ b/src/Subscription.php @@ -743,10 +743,13 @@ protected function parseSwapPrices(array $prices) $options = is_string($options) ? [] : $options; $payload = [ - 'price' => $price, 'tax_rates' => $this->getPriceTaxRatesForPayload($price), ]; + if (! isset($options['price_data'])) { + $payload['price'] = $price; + } + if ($isSinglePriceSwap && ! is_null($this->quantity)) { $payload['quantity'] = $this->quantity; } diff --git a/src/SubscriptionBuilder.php b/src/SubscriptionBuilder.php index 8699d3ea..3bf7ba7f 100644 --- a/src/SubscriptionBuilder.php +++ b/src/SubscriptionBuilder.php @@ -75,7 +75,7 @@ class SubscriptionBuilder * * @param mixed $owner * @param string $name - * @param string|string[] $prices + * @param string|string[]|array[] $prices * @return void */ public function __construct($owner, $name, $prices = []) @@ -91,13 +91,13 @@ public function __construct($owner, $name, $prices = []) /** * Set a price on the subscription builder. * - * @param string $price + * @param string|array $price * @param int|null $quantity * @return $this */ public function price($price, $quantity = 1) { - $options = ['price' => $price]; + $options = is_array($price) ? $price : ['price' => $price]; if (! is_null($quantity)) { $options['quantity'] = $quantity; @@ -107,7 +107,11 @@ public function price($price, $quantity = 1) $options['tax_rates'] = $taxRates; } - $this->items[$price] = $options; + if (is_array($price)) { + $this->items[] = $options; + } else { + $this->items[$price] = $options; + } return $this; } diff --git a/tests/Feature/InvoicesTest.php b/tests/Feature/InvoicesTest.php index f9cb1c22..ce2c54e3 100644 --- a/tests/Feature/InvoicesTest.php +++ b/tests/Feature/InvoicesTest.php @@ -62,6 +62,29 @@ public function test_customer_can_be_invoiced_with_a_price() $this->assertEquals(998, $response->total); } + public function test_customer_can_be_invoiced_with_inline_price_data() + { + $user = $this->createCustomer('customer_can_be_invoiced_with_inline_price_data'); + $user->createAsStripeCustomer(); + $user->updateDefaultPaymentMethod('pm_card_visa'); + + $productId = self::stripe()->products->create([ + 'name' => 'Laravel Cashier Test Product', + 'type' => 'service', + ])->id; + + $response = $user->invoiceFor('Laravel T-shirt', 599, [ + 'price_data' => [ + 'product' => $productId, + 'tax_behavior' => 'exclusive', + ], + ]); + + $this->assertInstanceOf(Invoice::class, $response); + $this->assertEquals(599, $response->total); + $this->assertEquals('exclusive', $response->invoiceLineItems()[0]->price->tax_behavior); + } + public function test_find_invoice_by_id() { $user = $this->createCustomer('find_invoice_by_id'); diff --git a/tests/Feature/SubscriptionsTest.php b/tests/Feature/SubscriptionsTest.php index b74acf52..b1fcda88 100644 --- a/tests/Feature/SubscriptionsTest.php +++ b/tests/Feature/SubscriptionsTest.php @@ -222,6 +222,28 @@ public function test_swapping_subscription_and_adopting_new_quantity() $this->assertSame(3, $subscription->asStripeSubscription()->quantity); } + public function test_swapping_subscription_with_inline_price_data() + { + $user = $this->createCustomer('swapping_subscription_with_inline_price_data'); + $user->newSubscription('main', static::$priceId)->create('pm_card_visa'); + $subscription = $user->subscription('main'); + + $subscription->swap([[ + 'price_data' => [ + 'product' => static::$productId, + 'tax_behavior' => 'exclusive', + 'currency' => 'USD', + 'recurring' => [ + 'interval' => 'month', + ], + 'unit_amount' => 1100, + ], + ]]); + + $this->assertEquals(1100, $subscription->asStripeSubscription()->items->data[0]->price->unit_amount); + $this->assertEquals('exclusive', $subscription->asStripeSubscription()->items->data[0]->price->tax_behavior); + } + public function test_declined_card_during_new_quantity() { $user = $this->createCustomer('declined_card_during_new_quantity'); @@ -439,6 +461,38 @@ public function test_creating_subscription_with_coupons() $this->assertFalse($coupon->isPercentage()); } + public function test_creating_subscription_with_inline_price_data() + { + $user = $this->createCustomer('creating_subscription_with_inline_price_data'); + + $user->newSubscription('main')->price([ + 'price_data' => [ + 'product' => static::$productId, + 'tax_behavior' => 'exclusive', + 'currency' => 'USD', + 'recurring' => [ + 'interval' => 'month', + ], + 'unit_amount' => 1100, + ], + ])->create('pm_card_visa'); + + $subscription = $user->subscription('main'); + + $this->assertTrue($user->subscribed('main')); + $this->assertNotNull($user->subscribed('main', static::$otherPriceId)); + $this->assertTrue($subscription->active()); + $this->assertFalse($subscription->cancelled()); + $this->assertFalse($subscription->onGracePeriod()); + $this->assertTrue($subscription->recurring()); + $this->assertFalse($subscription->ended()); + + $invoice = $user->invoices()[0]; + + $this->assertEquals('$11.00', $invoice->total()); + $this->assertEquals('exclusive', $invoice->invoiceLineItems()[0]->price->tax_behavior); + } + public function test_creating_subscription_with_an_anchored_billing_cycle() { $user = $this->createCustomer('creating_subscription_with_an_anchored_billing_cycle');