feat: update checkout and API resources for cycle-aware pricing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,7 +69,7 @@ class CheckoutController extends Controller
|
||||
}
|
||||
|
||||
return Inertia::render('Checkout/Show', [
|
||||
'plan' => $plan,
|
||||
'plan' => $plan->load('prices'),
|
||||
'paymentMethods' => $stripeService->getPaymentMethods($user),
|
||||
'intent' => $user->hasStripeId() ? $user->createSetupIntent() : null,
|
||||
'stripeKey' => config('cashier.key'),
|
||||
|
||||
@@ -28,6 +28,9 @@ class ServiceResource extends JsonResource
|
||||
'name' => $this->plan->name,
|
||||
'price' => $this->plan->price,
|
||||
'billing_cycle' => $this->plan->billing_cycle,
|
||||
'prices' => $this->plan->prices?->mapWithKeys(fn ($p) => [
|
||||
$p->billing_cycle => $p->price,
|
||||
]) ?? [],
|
||||
]),
|
||||
'provisioned_at' => $this->provisioned_at?->toIso8601String(),
|
||||
'created_at' => $this->created_at?->toIso8601String(),
|
||||
|
||||
@@ -22,6 +22,9 @@ class SubscriptionResource extends JsonResource
|
||||
'plan' => $this->when($this->plan_name !== null, fn () => [
|
||||
'name' => $this->plan_name,
|
||||
'price' => $this->plan_price,
|
||||
'prices' => $this->plan?->prices?->mapWithKeys(fn ($p) => [
|
||||
$p->billing_cycle => $p->price,
|
||||
]) ?? [],
|
||||
]),
|
||||
'billing_cycle' => $this->plan_billing_cycle ?? null,
|
||||
'current_period_end' => $this->current_period_end?->toIso8601String(),
|
||||
|
||||
@@ -41,8 +41,10 @@ const couponApplied = ref(false)
|
||||
const couponDiscount = ref(0)
|
||||
const couponError = ref('')
|
||||
|
||||
// Billing cycle selection
|
||||
const billingCycle = ref<'monthly' | 'quarterly' | 'semi_annual' | 'annual'>('monthly')
|
||||
// Billing cycle selection — pre-select from URL query param if present
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const initialCycle = (urlParams.get('cycle') || 'monthly') as 'monthly' | 'quarterly' | 'semi_annual' | 'annual'
|
||||
const billingCycle = ref<'monthly' | 'quarterly' | 'semi_annual' | 'annual'>(initialCycle)
|
||||
|
||||
const billingCycles = [
|
||||
{ value: 'monthly', label: 'Monthly', months: 1, discount: 0, popular: false },
|
||||
@@ -66,8 +68,21 @@ const selectedCycle = computed(() =>
|
||||
|
||||
const monthlyPrice = computed(() => parseFloat(props.plan.price))
|
||||
|
||||
// Calculate price with billing cycle discount
|
||||
// Get price for the selected billing cycle from server-provided plan.prices
|
||||
function getSelectedPrice(): number {
|
||||
const planPrice = props.plan.prices?.find(
|
||||
(p: { billing_cycle: string }) => p.billing_cycle === billingCycle.value
|
||||
)
|
||||
return planPrice ? parseFloat(planPrice.price) : monthlyPrice.value
|
||||
}
|
||||
|
||||
// Calculate price with billing cycle — use server prices if available, fall back to client-side discount
|
||||
const basePriceWithCycle = computed(() => {
|
||||
if (props.plan.prices && props.plan.prices.length > 0) {
|
||||
const cyclePrice = getSelectedPrice()
|
||||
return cyclePrice * selectedCycle.value.months
|
||||
}
|
||||
// Fallback: client-side discount calculation
|
||||
const monthly = monthlyPrice.value
|
||||
const cycle = selectedCycle.value
|
||||
const discountedMonthly = monthly * (1 - cycle.discount)
|
||||
@@ -115,7 +130,7 @@ const form = useForm({
|
||||
gateway: 'stripe',
|
||||
payment_method_id: props.paymentMethods?.[0]?.id || '',
|
||||
coupon_code: '',
|
||||
billing_cycle: 'monthly',
|
||||
billing_cycle: initialCycle,
|
||||
configuration: {
|
||||
os_template_id: props.osTemplates[0]?.id || null,
|
||||
auth_method: 'password' as 'password' | 'ssh',
|
||||
|
||||
Reference in New Issue
Block a user