Skip to content

Commit

Permalink
[13.x] Implement stripe_product column (#1185)
Browse files Browse the repository at this point in the history
* Implement stripe_product column

* Update CHANGELOG.md

* Update UPGRADE.md

* Add product methods

* Update UPGRADE.md

* Update UPGRADE.md

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
driesvints and taylorotwell authored Jun 7, 2021
1 parent a42036d commit 9661ead
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
- Multiple discounts on receipts ([#1147](https://github.com/laravel/cashier-stripe/pull/1147))
- Preview upcoming invoice ([#1146](https://github.com/laravel/cashier-stripe/pull/1146))
- Add new metered price methods ([#1177](https://github.com/laravel/cashier-stripe/pull/1177))
- Allow customers to be synced with Stripe ([#1178](https://github.com/laravel/cashier-stripe/pull/1178), [#1183](https://github.com/laravel/cashier-stripe/pull/1183))
- Add `stripe_product` column to `subscriptions_items` table ([#1185](https://github.com/laravel/cashier-stripe/pull/1185))

### Changed
- Rename plans to prices ([#1166](https://github.com/laravel/cashier-stripe/pull/1166))
Expand Down
14 changes: 14 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,20 @@ PR: https://github.com/laravel/cashier-stripe/pull/1120

The hosted payment page for handling payment method failures has been improved to provide support for additional payment methods. No changes to your application are required if you have not published the `payment.blade.php` template. However, all translation support has been removed. If you were relying on this functionality you should publish the view and re-add the appropriate calls to Laravel's translation services.

### Stripe Product Support

PR: https://github.com/laravel/cashier-stripe/pull/1185

Cashier Stripe v13 comes with support for checking Stripe Product identifiers. To provide support for this feature, a new `stripe_product` column should be added to the `stripe_subscriptions` table:

```php
Schema::table('subscription_items', function (Blueprint $table) {
$table->string('stripe_product')->nullable()->after('stripe_id');
});
```

If you'd like to make use of the new `onProduct` & `subscribedToProduct` methods on your billable model, you should ensure the records in the `subscription_items` have their `stripe_product` column filled with the correct Product ID from Stripe.

## Upgrading To 12.8 From 12.7

### Metered Billing
Expand Down
1 change: 1 addition & 0 deletions database/factories/SubscriptionItemFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function definition()
return [
'subscription_id' => Subscription::factory(),
'stripe_id' => 'si_'.Str::random(40),
'stripe_product' => 'prod_'.Str::random(40),
'stripe_price' => 'price_'.Str::random(40),
'quantity' => null,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function up()
$table->bigIncrements('id');
$table->unsignedBigInteger('subscription_id');
$table->string('stripe_id')->index();
$table->string('stripe_product');
$table->string('stripe_price');
$table->integer('quantity')->nullable();
$table->timestamps();
Expand Down
37 changes: 37 additions & 0 deletions src/Concerns/ManagesSubscriptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,30 @@ public function hasIncompletePayment($name = 'default')
return false;
}

/**
* Determine if the Stripe model is actively subscribed to one of the given products.
*
* @param string|string[] $products
* @param string $name
* @return bool
*/
public function subscribedToProduct($products, $name = 'default')
{
$subscription = $this->subscription($name);

if (! $subscription || ! $subscription->valid()) {
return false;
}

foreach ((array) $products as $product) {
if ($subscription->hasProduct($product)) {
return true;
}
}

return false;
}

/**
* Determine if the Stripe model is actively subscribed to one of the given prices.
*
Expand All @@ -149,6 +173,19 @@ public function subscribedToPrice($prices, $name = 'default')
return false;
}

/**
* Determine if the customer has a valid subscription on the given product.
*
* @param string $price
* @return bool
*/
public function onProduct($price)
{
return ! is_null($this->subscriptions->first(function (Subscription $subscription) use ($price) {
return $subscription->valid() && $subscription->hasProduct($price);
}));
}

/**
* Determine if the customer has a valid subscription on the given price.
*
Expand Down
2 changes: 2 additions & 0 deletions src/Http/Controllers/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ protected function handleCustomerSubscriptionCreated(array $payload)
foreach ($data['items']['data'] as $item) {
$subscription->items()->create([
'stripe_id' => $item['id'],
'stripe_product' => $item['price']['product'],
'stripe_price' => $item['price']['id'],
'quantity' => $item['quantity'] ?? null,
]);
Expand Down Expand Up @@ -183,6 +184,7 @@ protected function handleCustomerSubscriptionUpdated(array $payload)
$subscription->items()->updateOrCreate([
'stripe_id' => $item['id'],
], [
'stripe_product' => $item['price']['product'],
'stripe_price' => $item['price']['id'],
'quantity' => $item['quantity'] ?? null,
]);
Expand Down
21 changes: 18 additions & 3 deletions src/Subscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ public function hasSinglePrice()
return ! $this->hasMultiplePrices();
}

/**
* Determine if the subscription has a specific product.
*
* @param string $product
* @return bool
*/
public function hasProduct($product)
{
return $this->items->contains(function (SubscriptionItem $item) use ($product) {
return $item->stripe_product === $product;
});
}

/**
* Determine if the subscription has a specific price.
*
Expand Down Expand Up @@ -675,6 +688,7 @@ public function swap($prices, array $options = [])
$this->items()->updateOrCreate([
'stripe_id' => $item->id,
], [
'stripe_product' => $item->price->product,
'stripe_price' => $item->price->id,
'quantity' => $item->quantity,
]);
Expand Down Expand Up @@ -816,7 +830,7 @@ public function addPrice($price, $quantity = 1, array $options = [])
throw SubscriptionUpdateFailure::duplicatePrice($this, $price);
}

$item = $this->owner->stripe()->subscriptionItems->create(array_merge([
$stripeSubscriptionItem = $this->owner->stripe()->subscriptionItems->create(array_merge([
'subscription' => $this->stripe_id,
'price' => $price,
'quantity' => $quantity,
Expand All @@ -826,8 +840,9 @@ public function addPrice($price, $quantity = 1, array $options = [])
], $options));

$this->items()->create([
'stripe_id' => $item->id,
'stripe_price' => $price,
'stripe_id' => $stripeSubscriptionItem->id,
'stripe_product' => $stripeSubscriptionItem->price->product,
'stripe_price' => $stripeSubscriptionItem->price->id,
'quantity' => $quantity,
]);

Expand Down
1 change: 1 addition & 0 deletions src/SubscriptionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ protected function createSubscription(StripeSubscription $stripeSubscription)
foreach ($stripeSubscription->items as $item) {
$subscription->items()->create([
'stripe_id' => $item->id,
'stripe_product' => $item->price->product,
'stripe_price' => $item->price->id,
'quantity' => $item->quantity,
]);
Expand Down
3 changes: 2 additions & 1 deletion src/SubscriptionItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ public function swap($price, array $options = [])
], $options));

$this->fill([
'stripe_price' => $price,
'stripe_product' => $stripeSubscriptionItem->price->product,
'stripe_price' => $stripeSubscriptionItem->price->id,
'quantity' => $stripeSubscriptionItem->quantity,
])->save();

Expand Down
4 changes: 4 additions & 0 deletions tests/Feature/MultipriceSubscriptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public function test_customers_can_have_multiprice_subscriptions()
->create('pm_card_visa');

$this->assertTrue($user->subscribed('main', self::$priceId));
$this->assertTrue($user->onProduct(self::$productId));
$this->assertTrue($user->onPrice(self::$priceId));

$item = $subscription->findItemOrFail(self::$priceId);
Expand All @@ -122,6 +123,7 @@ public function test_customers_can_add_prices()

$subscription->addPrice(self::$otherPriceId, 5);

$this->assertTrue($user->onProduct(self::$productId));
$this->assertTrue($user->onPrice(self::$priceId));
$this->assertFalse($user->onPrice(self::$premiumPriceId));

Expand Down Expand Up @@ -331,6 +333,7 @@ protected function createSubscriptionWithSinglePrice(User $user)

$subscription->items()->create([
'stripe_id' => 'it_foo',
'stripe_product' => self::$productId,
'stripe_price' => self::$priceId,
'quantity' => 1,
]);
Expand All @@ -354,6 +357,7 @@ protected function createSubscriptionWithMultiplePrices(User $user)

$subscription->items()->create([
'stripe_id' => 'it_foo',
'stripe_product' => self::$productId,
'stripe_price' => self::$otherPriceId,
'quantity' => 1,
]);
Expand Down
1 change: 1 addition & 0 deletions tests/Feature/SubscriptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public function test_subscriptions_can_be_created()
$this->assertSame($metadata, $subscription->asStripeSubscription()->metadata->toArray());

$this->assertTrue($user->subscribed('main'));
$this->assertTrue($user->subscribedToProduct(static::$productId, 'main'));
$this->assertTrue($user->subscribedToPrice(static::$priceId, 'main'));
$this->assertFalse($user->subscribedToPrice(static::$priceId, 'something'));
$this->assertFalse($user->subscribedToPrice(static::$otherPriceId, 'main'));
Expand Down
13 changes: 9 additions & 4 deletions tests/Feature/WebhooksTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function test_subscriptions_are_created()
'items' => [
'data' => [[
'id' => 'bar',
'price' => ['id' => 'price_foo'],
'price' => ['id' => 'price_foo', 'product' => 'prod_bar'],
'quantity' => 10,
]],
],
Expand All @@ -76,6 +76,7 @@ public function test_subscriptions_are_created()

$this->assertDatabaseHas('subscription_items', [
'stripe_id' => 'bar',
'stripe_product' => 'prod_bar',
'stripe_price' => 'price_foo',
'quantity' => 10,
]);
Expand All @@ -94,6 +95,7 @@ public function test_subscriptions_are_updated()

$item = $subscription->items()->create([
'stripe_id' => 'it_foo',
'stripe_product' => 'prod_bar',
'stripe_price' => 'price_bar',
'quantity' => 1,
]);
Expand All @@ -109,7 +111,7 @@ public function test_subscriptions_are_updated()
'items' => [
'data' => [[
'id' => 'bar',
'price' => ['id' => 'price_foo'],
'price' => ['id' => 'price_foo', 'product' => 'prod_bar'],
'quantity' => 5,
]],
],
Expand All @@ -127,6 +129,7 @@ public function test_subscriptions_are_updated()
$this->assertDatabaseHas('subscription_items', [
'subscription_id' => $subscription->id,
'stripe_id' => 'bar',
'stripe_product' => 'prod_bar',
'stripe_price' => 'price_foo',
'quantity' => 5,
]);
Expand All @@ -150,6 +153,7 @@ public function test_subscriptions_on_update_cancel_at_date_is_correct()

$item = $subscription->items()->create([
'stripe_id' => 'it_foo',
'stripe_product' => 'prod_bar',
'stripe_price' => 'price_bar',
'quantity' => 1,
]);
Expand All @@ -166,7 +170,7 @@ public function test_subscriptions_on_update_cancel_at_date_is_correct()
'items' => [
'data' => [[
'id' => 'bar',
'price' => ['id' => 'price_foo'],
'price' => ['id' => 'price_foo', 'product' => 'prod_bar'],
'quantity' => 5,
]],
],
Expand All @@ -185,6 +189,7 @@ public function test_subscriptions_on_update_cancel_at_date_is_correct()
$this->assertDatabaseHas('subscription_items', [
'subscription_id' => $subscription->id,
'stripe_id' => 'bar',
'stripe_product' => 'prod_bar',
'stripe_price' => 'price_foo',
'quantity' => 5,
]);
Expand Down Expand Up @@ -212,7 +217,7 @@ public function test_cancelled_subscription_is_properly_reactivated()
'items' => [
'data' => [[
'id' => $subscription->items()->first()->stripe_id,
'price' => ['id' => static::$priceId],
'price' => ['id' => static::$priceId, 'product' => static::$productId],
'quantity' => 1,
]],
],
Expand Down

0 comments on commit 9661ead

Please sign in to comment.