feat(billing): apply Stripe tax rates to subscriptions (#325)
## Summary
Every subscription now gets Stripe tax rates attached automatically via
Cashier.
## Changes
- `config/subscriptions.php`: new `tax_rates` array, env
`STRIPE_TAX_RATES` (comma-separated), default
`txr_1TPfzrLRCmKA3oWMNWmkQeq2`
- `app/Models/User.php`: `taxRates()` reads from config — Cashier picks
it up automatically on `newSubscription()` checkout + subscription
creation
- `tests/Feature/SubscriptionTest.php`: 2 tests
## Applies to
- New checkout sessions (`SubscriptionController::checkout`)
- New subscriptions created via Cashier
## Existing subscriptions
Not updated automatically. To sync:
```php
$user->subscription('default')->syncTaxRates();
```
## Notes
Using hard-coded tax rate IDs (not Stripe Tax auto-calc). Switch to
`Cashier::calculateTaxes()` later if desired.
This commit is contained in:
parent
b399aaaa0d
commit
74cbdd42ef
|
|
@ -165,6 +165,16 @@ class User extends Authenticatable implements HasLocalePreference, MustVerifyEma
|
|||
return $this->subscribed('default');
|
||||
}
|
||||
|
||||
/**
|
||||
* The tax rates that should apply to the customer's subscriptions.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function taxRates(): array
|
||||
{
|
||||
return config('subscriptions.tax_rates', []);
|
||||
}
|
||||
|
||||
public function isDemoAccount(): bool
|
||||
{
|
||||
return $this->email === config('app.demo.email');
|
||||
|
|
|
|||
|
|
@ -131,6 +131,18 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Tax Rates
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Stripe tax rate IDs applied to every subscription created via Cashier.
|
||||
| Configure tax rates in your Stripe dashboard and reference them here.
|
||||
|
|
||||
*/
|
||||
|
||||
'tax_rates' => array_values(array_filter(explode(',', (string) env('STRIPE_TAX_RATES', 'txr_1TPfzrLRCmKA3oWMNWmkQeq2')))),
|
||||
|
||||
'promo' => [
|
||||
'enabled' => env('PROMO_ENABLED', true),
|
||||
'code' => 'FOUNDER',
|
||||
|
|
|
|||
|
|
@ -272,6 +272,18 @@ test('paywall shows canUseFreePlan false when user has a bank connection', funct
|
|||
);
|
||||
});
|
||||
|
||||
test('taxRates returns configured stripe tax rate ids', function () {
|
||||
config(['subscriptions.tax_rates' => ['txr_test_1', 'txr_test_2']]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect($user->taxRates())->toBe(['txr_test_1', 'txr_test_2']);
|
||||
});
|
||||
|
||||
test('taxRates config defaults to the production tax rate id', function () {
|
||||
expect(config('subscriptions.tax_rates'))->toContain('txr_1TPfzrLRCmKA3oWMNWmkQeq2');
|
||||
});
|
||||
|
||||
test('billing portal creates stripe customer when user has no stripe id', function () {
|
||||
$user = Mockery::mock(User::class)->shouldIgnoreMissing();
|
||||
$user->shouldReceive('isDemoAccount')->andReturn(false);
|
||||
|
|
|
|||
Loading…
Reference in New Issue