* Make Stripe Checkout configurable via billing env knobs Replace the hard-required $1 first-month coupon with env-driven trial days, optional coupon, and allow_promotion_codes so SaaS can switch checkout modes without a code change. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix no-effect ReflectionClass import in checkout test CI treats bare global use statements as ErrorException and aborts loading the suite before any assertions run. Co-authored-by: Cursor <cursoragent@cursor.com> * Document checkout env knobs in AGENTS.md instead of .ai/ Remove the Boost record-rule .ai/rules folder and keep durable billing checkout guidance in AGENTS.md / CLAUDE.md project-specific rules. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden checkout env knobs from review findings Default allow_promotion_codes to false, grant Stripe trial only to first-time subscribers, clarify the coupon/promo XOR error, and cover negative XOR cases plus StartSubscriptionCheckout wiring. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
57 lines
2 KiB
PHP
57 lines
2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Actions\Billing\StartSubscriptionCheckout;
|
|
use App\Models\Account;
|
|
use App\Models\Workspace;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Laravel\Cashier\SubscriptionBuilder;
|
|
use Mockery\MockInterface;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
test('redirect applies checkout configuration before opening the stripe session', function () {
|
|
config([
|
|
'trypost.billing.require_card_for_trial' => true,
|
|
'cashier.trial_days' => 8,
|
|
'cashier.first_month_coupon_id' => '',
|
|
'cashier.allow_promotion_codes' => false,
|
|
]);
|
|
|
|
$account = Account::factory()->create();
|
|
Workspace::factory()->create(['account_id' => $account->id]);
|
|
|
|
$cancelUrl = route('app.welcome');
|
|
|
|
$builder = Mockery::mock(SubscriptionBuilder::class);
|
|
$builder->shouldReceive('quantity')->once()->with(1)->andReturnSelf();
|
|
$builder->shouldReceive('trialDays')->once()->with(8)->andReturnSelf();
|
|
$builder->shouldReceive('withCoupon')->never();
|
|
$builder->shouldReceive('allowPromotionCodes')->never();
|
|
$builder->shouldReceive('checkout')
|
|
->once()
|
|
->with([
|
|
'success_url' => route('app.billing.processing').'?session_id={CHECKOUT_SESSION_ID}',
|
|
'cancel_url' => $cancelUrl,
|
|
])
|
|
->andReturn((object) ['url' => 'https://checkout.stripe.test/session']);
|
|
|
|
/** @var Account&MockInterface $accountMock */
|
|
$accountMock = Mockery::mock($account)->makePartial();
|
|
$accountMock->shouldReceive('createOrGetStripeCustomer')->once()->andReturnNull();
|
|
$accountMock->shouldReceive('newSubscription')
|
|
->once()
|
|
->with(Account::SUBSCRIPTION_NAME, 'price_monthly_test')
|
|
->andReturn($builder);
|
|
|
|
$response = app(StartSubscriptionCheckout::class)->redirect(
|
|
$accountMock,
|
|
'price_monthly_test',
|
|
$cancelUrl,
|
|
);
|
|
|
|
expect($response->headers->get('Location'))->toBe('https://checkout.stripe.test/session')
|
|
->and($response->getStatusCode())->toBe(Response::HTTP_FOUND);
|
|
});
|