Skip to content

Commit 540a81f

Browse files
simonhampclaude
andcommitted
Fix upgrade price calculation for EAP customers selecting monthly billing
stripePriceId() checked EAP status before billing interval, so EAP customers always got the yearly EAP price regardless of monthly/yearly selection. Move the monthly interval check first since no monthly EAP prices exist. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 21699ae commit 540a81f

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

app/Enums/Subscription.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ public function name(): string
8383

8484
public function stripePriceId(bool $forceEap = false, bool $discounted = false, string $interval = 'year'): string
8585
{
86+
// Monthly billing uses the regular monthly price (no EAP/discounted monthly prices exist)
87+
if ($interval === 'month') {
88+
return config("subscriptions.plans.{$this->value}.stripe_price_id_monthly")
89+
?? config("subscriptions.plans.{$this->value}.stripe_price_id");
90+
}
91+
8692
// EAP ends June 1st at midnight UTC
8793
if (now()->isBefore('2025-06-01 00:00:00') || $forceEap) {
8894
return config("subscriptions.plans.{$this->value}.stripe_price_id_eap");
@@ -92,10 +98,6 @@ public function stripePriceId(bool $forceEap = false, bool $discounted = false,
9298
return config("subscriptions.plans.{$this->value}.stripe_price_id_discounted");
9399
}
94100

95-
if ($interval === 'month') {
96-
return config("subscriptions.plans.{$this->value}.stripe_price_id_monthly");
97-
}
98-
99101
return config("subscriptions.plans.{$this->value}.stripe_price_id");
100102
}
101103

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Tests\Unit;
4+
5+
use App\Enums\Subscription;
6+
use Illuminate\Support\Facades\Config;
7+
use PHPUnit\Framework\Attributes\Test;
8+
use Tests\TestCase;
9+
10+
class SubscriptionStripePriceIdTest extends TestCase
11+
{
12+
private const YEARLY_PRICE = 'price_max_yearly';
13+
14+
private const MONTHLY_PRICE = 'price_max_monthly';
15+
16+
private const EAP_PRICE = 'price_max_eap';
17+
18+
private const DISCOUNTED_PRICE = 'price_max_discounted';
19+
20+
protected function setUp(): void
21+
{
22+
parent::setUp();
23+
24+
Config::set('subscriptions.plans.max.stripe_price_id', self::YEARLY_PRICE);
25+
Config::set('subscriptions.plans.max.stripe_price_id_monthly', self::MONTHLY_PRICE);
26+
Config::set('subscriptions.plans.max.stripe_price_id_eap', self::EAP_PRICE);
27+
Config::set('subscriptions.plans.max.stripe_price_id_discounted', self::DISCOUNTED_PRICE);
28+
}
29+
30+
#[Test]
31+
public function yearly_returns_default_price(): void
32+
{
33+
$this->assertEquals(self::YEARLY_PRICE, Subscription::Max->stripePriceId());
34+
}
35+
36+
#[Test]
37+
public function monthly_returns_monthly_price(): void
38+
{
39+
$this->assertEquals(self::MONTHLY_PRICE, Subscription::Max->stripePriceId(interval: 'month'));
40+
}
41+
42+
#[Test]
43+
public function eap_yearly_returns_eap_price(): void
44+
{
45+
$this->assertEquals(self::EAP_PRICE, Subscription::Max->stripePriceId(forceEap: true));
46+
}
47+
48+
#[Test]
49+
public function eap_monthly_returns_monthly_price_not_eap(): void
50+
{
51+
$this->assertEquals(
52+
self::MONTHLY_PRICE,
53+
Subscription::Max->stripePriceId(forceEap: true, interval: 'month')
54+
);
55+
}
56+
57+
#[Test]
58+
public function discounted_returns_discounted_price(): void
59+
{
60+
$this->assertEquals(self::DISCOUNTED_PRICE, Subscription::Max->stripePriceId(discounted: true));
61+
}
62+
63+
#[Test]
64+
public function monthly_falls_back_to_default_when_no_monthly_price(): void
65+
{
66+
Config::set('subscriptions.plans.max.stripe_price_id_monthly', null);
67+
68+
$this->assertEquals(self::YEARLY_PRICE, Subscription::Max->stripePriceId(interval: 'month'));
69+
}
70+
}

0 commit comments

Comments
 (0)