paypal = $paypal; } /** * Get current subscription status */ public function status(Request $request): JsonResponse { $user = $request->user(); $subscription = $user->subscriptions()->active()->with('plan')->first(); $currentPlan = $user->currentPlan(); // Get current usage $usage = [ 'accounts' => $user->accounts()->count(), 'categories' => $user->categories()->count(), 'budgets' => $user->budgets()->count(), 'transactions' => $user->transactions()->count(), ]; // Calculate usage percentages if plan has limits $limits = $currentPlan?->limits ?? []; $usagePercentages = []; foreach ($usage as $resource => $count) { $limit = $limits[$resource] ?? null; if ($limit !== null && $limit > 0) { $usagePercentages[$resource] = round(($count / $limit) * 100, 1); } else { $usagePercentages[$resource] = null; // unlimited } } // Calculate guarantee period info (7 days from subscription creation) $withinGuaranteePeriod = false; $guaranteeDaysRemaining = 0; $guaranteeEndsAt = null; if ($subscription && $subscription->created_at) { $guaranteeEndsAt = $subscription->created_at->copy()->addDays(7); $withinGuaranteePeriod = now()->lt($guaranteeEndsAt); $guaranteeDaysRemaining = $withinGuaranteePeriod ? (int) ceil(now()->diffInHours($guaranteeEndsAt) / 24) : 0; } return response()->json([ 'success' => true, 'data' => [ 'has_subscription' => $subscription !== null, 'subscription' => $subscription ? [ 'id' => $subscription->id, 'status' => $subscription->status, 'trial_ends_at' => $subscription->trial_ends_at, 'current_period_start' => $subscription->current_period_start, 'current_period_end' => $subscription->current_period_end, 'canceled_at' => $subscription->canceled_at, 'ends_at' => $subscription->ends_at, 'on_trial' => $subscription->isOnTrial(), 'on_grace_period' => $subscription->onGracePeriod(), ] : null, 'on_trial' => $subscription?->isOnTrial() ?? false, 'trial_ends_at' => $subscription?->trial_ends_at, 'days_until_trial_ends' => $subscription?->days_until_trial_ends, 'current_period_start' => $subscription?->current_period_start, 'current_period_end' => $subscription?->current_period_end, 'status' => $subscription?->status, 'status_label' => $subscription?->status_label, 'canceled_at' => $subscription?->canceled_at, 'on_grace_period' => $subscription?->onGracePeriod() ?? false, 'within_guarantee_period' => $withinGuaranteePeriod, 'guarantee_days_remaining' => $guaranteeDaysRemaining, 'guarantee_ends_at' => $guaranteeEndsAt?->toIso8601String(), 'plan' => $currentPlan ? [ 'id' => $currentPlan->id, 'slug' => $currentPlan->slug, 'name' => $currentPlan->name, 'price' => $currentPlan->price, 'formatted_price' => $currentPlan->formatted_price, 'billing_period' => $currentPlan->billing_period, 'is_free' => $currentPlan->is_free, 'features' => $currentPlan->features, 'limits' => $currentPlan->limits, ] : null, 'usage' => $usage, 'usage_percentages' => $usagePercentages, ], ]); } /** * Create a subscription checkout */ public function subscribe(Request $request): JsonResponse { $request->validate([ 'plan_slug' => 'required|string|exists:plans,slug', ]); $user = $request->user(); $plan = Plan::where('slug', $request->plan_slug)->where('is_active', true)->first(); if (!$plan) { return response()->json([ 'success' => false, 'message' => 'Plan not found or inactive', ], 404); } // Check if plan is free if ($plan->is_free) { return $this->subscribeFree($user, $plan); } // Check if PayPal is configured if (!$this->paypal->isConfigured()) { return response()->json([ 'success' => false, 'message' => 'Payment gateway not configured', ], 500); } // Check if plan has PayPal plan ID if (!$plan->paypal_plan_id) { return response()->json([ 'success' => false, 'message' => 'Plan not configured for payments yet', ], 500); } // Create PayPal subscription $frontendUrl = config('app.frontend_url', 'https://webmoney.cnxifly.com'); $returnUrl = "{$frontendUrl}/billing?success=true&plan={$plan->slug}"; $cancelUrl = "{$frontendUrl}/pricing?canceled=true"; $paypalSubscription = $this->paypal->createSubscription($plan, $returnUrl, $cancelUrl); if (!$paypalSubscription) { return response()->json([ 'success' => false, 'message' => 'Failed to create subscription', ], 500); } // Find approve link $approveUrl = collect($paypalSubscription['links'] ?? []) ->firstWhere('rel', 'approve')['href'] ?? null; if (!$approveUrl) { return response()->json([ 'success' => false, 'message' => 'No approval URL received', ], 500); } // Create pending subscription in our DB $subscription = Subscription::create([ 'user_id' => $user->id, 'plan_id' => $plan->id, 'status' => Subscription::STATUS_TRIALING, 'paypal_subscription_id' => $paypalSubscription['id'], 'paypal_status' => $paypalSubscription['status'], 'paypal_data' => $paypalSubscription, 'price_paid' => $plan->price, 'currency' => $plan->currency, ]); return response()->json([ 'success' => true, 'data' => [ 'subscription_id' => $subscription->id, 'paypal_subscription_id' => $paypalSubscription['id'], 'approve_url' => $approveUrl, ], ]); } /** * Start subscription for newly registered user (public - no auth required) * Used immediately after registration, before user is logged in */ public function startSubscription(Request $request): JsonResponse { $request->validate([ 'plan_id' => 'required|integer|exists:plans,id', 'user_email' => 'required|email|exists:users,email', ]); $user = \App\Models\User::where('email', $request->user_email)->first(); if (!$user) { return response()->json([ 'success' => false, 'message' => 'User not found', ], 404); } // Verify user hasn't already verified email (prevent abuse) if ($user->email_verified_at) { return response()->json([ 'success' => false, 'message' => 'User already activated. Please login.', ], 400); } $plan = Plan::where('id', $request->plan_id)->where('is_active', true)->first(); if (!$plan) { return response()->json([ 'success' => false, 'message' => 'Plan not found or inactive', ], 404); } // All plans are paid now - no free subscriptions during registration if ($plan->is_free || $plan->price <= 0) { return response()->json([ 'success' => false, 'message' => 'All plans require payment', ], 400); } // Check if PayPal is configured if (!$this->paypal->isConfigured()) { return response()->json([ 'success' => false, 'message' => 'Payment gateway not configured', ], 500); } // Check if plan has PayPal plan ID if (!$plan->paypal_plan_id) { return response()->json([ 'success' => false, 'message' => 'Plan not configured for payments yet', ], 500); } // Create PayPal subscription $frontendUrl = config('app.frontend_url', 'https://webmoney.cnxifly.com'); $returnUrl = "{$frontendUrl}/payment-success?user_email={$user->email}&plan={$plan->slug}"; $cancelUrl = "{$frontendUrl}/register?payment_canceled=true"; $paypalSubscription = $this->paypal->createSubscription($plan, $returnUrl, $cancelUrl); if (!$paypalSubscription) { return response()->json([ 'success' => false, 'message' => 'Failed to create subscription', ], 500); } // Find approve link $approveUrl = collect($paypalSubscription['links'] ?? []) ->firstWhere('rel', 'approve')['href'] ?? null; if (!$approveUrl) { return response()->json([ 'success' => false, 'message' => 'No approval URL received', ], 500); } // Create pending subscription in our DB $subscription = Subscription::create([ 'user_id' => $user->id, 'plan_id' => $plan->id, 'status' => Subscription::STATUS_PENDING, 'paypal_subscription_id' => $paypalSubscription['id'], 'paypal_status' => $paypalSubscription['status'], 'paypal_data' => $paypalSubscription, 'price_paid' => $plan->price, 'currency' => $plan->currency, ]); \Illuminate\Support\Facades\Log::info("Started subscription for user {$user->email}, PayPal ID: {$paypalSubscription['id']}"); return response()->json([ 'success' => true, 'data' => [ 'subscription_id' => $subscription->id, 'paypal_subscription_id' => $paypalSubscription['id'], 'approve_url' => $approveUrl, ], ]); } /** * Subscribe to free plan */ private function subscribeFree($user, Plan $plan): JsonResponse { // Cancel any existing subscriptions $user->subscriptions()->active()->update([ 'status' => Subscription::STATUS_CANCELED, 'canceled_at' => now(), 'ends_at' => now(), 'cancel_reason' => 'Downgraded to free plan', ]); // Create free subscription $subscription = Subscription::createForUser($user, $plan); return response()->json([ 'success' => true, 'message' => 'Subscribed to free plan', 'data' => [ 'subscription_id' => $subscription->id, 'status' => $subscription->status, ], ]); } /** * Confirm subscription after PayPal approval */ public function confirm(Request $request): JsonResponse { $request->validate([ 'subscription_id' => 'required|string', ]); $user = $request->user(); $subscription = Subscription::where('paypal_subscription_id', $request->subscription_id) ->where('user_id', $user->id) ->first(); if (!$subscription) { return response()->json([ 'success' => false, 'message' => 'Subscription not found', ], 404); } // Get subscription details from PayPal $paypalData = $this->paypal->getSubscription($request->subscription_id); if (!$paypalData) { return response()->json([ 'success' => false, 'message' => 'Failed to verify subscription', ], 500); } // Update subscription based on PayPal status $this->updateSubscriptionFromPayPal($subscription, $paypalData); // Cancel other active subscriptions $user->subscriptions() ->where('id', '!=', $subscription->id) ->active() ->update([ 'status' => Subscription::STATUS_CANCELED, 'canceled_at' => now(), 'ends_at' => now(), 'cancel_reason' => 'Replaced by new subscription', ]); // Create invoice for the subscription if ($subscription->isActive() && !$subscription->plan->is_free) { Invoice::createForSubscription( $subscription, Invoice::REASON_SUBSCRIPTION_CREATE, "{$subscription->plan->name} - Nueva suscripción" )->markAsPaid($paypalData['id'] ?? null); } // Send activation email if subscription is active and user not verified yet $activationSent = false; if ($subscription->isActive() && !$user->email_verified_at) { try { $verificationToken = EmailVerificationToken::createForUser($user); $frontendUrl = config('app.frontend_url', 'https://webmoney.cnxifly.com'); $activationUrl = "{$frontendUrl}/activate?token={$verificationToken->token}"; Mail::to($user->email)->send(new AccountActivationMail( $user, $activationUrl, $subscription->plan->name )); $activationSent = true; Log::info("Activation email sent to {$user->email}"); } catch (\Exception $e) { Log::error("Failed to send activation email: " . $e->getMessage()); } } return response()->json([ 'success' => true, 'message' => $activationSent ? 'Suscripción confirmada. Revisa tu email para activar tu cuenta.' : 'Subscription confirmed', 'data' => [ 'status' => $subscription->status, 'status_label' => $subscription->status_label, 'plan' => $subscription->plan->name, 'activation_email_sent' => $activationSent, 'email_verified' => $user->email_verified_at !== null, ], ]); } /** * Confirm subscription after PayPal approval (public - no auth required) * Used for new user registration flow */ public function confirmPublic(Request $request): JsonResponse { $request->validate([ 'subscription_id' => 'required|string', 'user_email' => 'required|email', ]); $user = \App\Models\User::where('email', $request->user_email)->first(); if (!$user) { return response()->json([ 'success' => false, 'message' => 'User not found', ], 404); } $subscription = Subscription::where('paypal_subscription_id', $request->subscription_id) ->where('user_id', $user->id) ->first(); if (!$subscription) { return response()->json([ 'success' => false, 'message' => 'Subscription not found', ], 404); } // Get subscription details from PayPal $paypalData = $this->paypal->getSubscription($request->subscription_id); if (!$paypalData) { return response()->json([ 'success' => false, 'message' => 'Failed to verify subscription with PayPal', ], 500); } \Illuminate\Support\Facades\Log::info("PayPal confirmation for {$user->email}: " . json_encode($paypalData)); // Update subscription based on PayPal status $this->updateSubscriptionFromPayPal($subscription, $paypalData); // Cancel other active subscriptions $user->subscriptions() ->where('id', '!=', $subscription->id) ->active() ->update([ 'status' => Subscription::STATUS_CANCELED, 'canceled_at' => now(), 'ends_at' => now(), 'cancel_reason' => 'Replaced by new subscription', ]); // Create invoice for the subscription if ($subscription->isActive() && !$subscription->plan->is_free) { Invoice::createForSubscription( $subscription, Invoice::REASON_SUBSCRIPTION_CREATE, "{$subscription->plan->name} - Nueva suscripción" )->markAsPaid($paypalData['id'] ?? null); } // Send activation email if subscription is active and user not verified yet $activationSent = false; if ($subscription->isActive() && !$user->email_verified_at) { try { $verificationToken = EmailVerificationToken::createForUser($user); $frontendUrl = config('app.frontend_url', 'https://webmoney.cnxifly.com'); $activationUrl = "{$frontendUrl}/activate?token={$verificationToken->token}"; Mail::to($user->email)->send(new AccountActivationMail( $user, $activationUrl, $subscription->plan->name )); $activationSent = true; \Illuminate\Support\Facades\Log::info("Activation email sent to {$user->email}"); } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error("Failed to send activation email: " . $e->getMessage()); } } return response()->json([ 'success' => true, 'message' => $activationSent ? 'Pagamento confirmado! Verifique seu email para ativar sua conta.' : 'Payment confirmed', 'data' => [ 'status' => $subscription->status, 'status_label' => $subscription->status_label, 'plan' => $subscription->plan->name, 'activation_email_sent' => $activationSent, ], ]); } /** * Cancel subscription */ public function cancel(Request $request): JsonResponse { $request->validate([ 'reason' => 'nullable|string|max:500', 'immediately' => 'nullable|boolean', 'request_refund' => 'nullable|boolean', ]); $user = $request->user(); $subscription = $user->subscriptions()->active()->first(); if (!$subscription) { return response()->json([ 'success' => false, 'message' => 'No active subscription found', ], 404); } $refundResult = null; $isWithinGuaranteePeriod = false; // Check if within 7-day guarantee period (from subscription creation date) if ($subscription->created_at) { $daysSinceCreation = now()->diffInDays($subscription->created_at); $isWithinGuaranteePeriod = $daysSinceCreation <= 7; } // If it's a paid plan with PayPal subscription if ($subscription->paypal_subscription_id && !$subscription->plan->is_free) { // If user requests refund and is within guarantee period, cancel and refund if ($request->boolean('request_refund') && $isWithinGuaranteePeriod) { $refundResult = $this->paypal->cancelAndRefund( $subscription->paypal_subscription_id, $request->reason ?? 'Refund within 7-day guarantee period' ); if (!$refundResult['canceled']) { return response()->json([ 'success' => false, 'message' => 'Failed to cancel subscription on PayPal', ], 500); } Log::info('Subscription canceled with refund', [ 'user_id' => $user->id, 'subscription_id' => $subscription->id, 'refund_result' => $refundResult, ]); } else { // Just cancel without refund $canceled = $this->paypal->cancelSubscription( $subscription->paypal_subscription_id, $request->reason ?? 'User requested cancellation' ); if (!$canceled) { return response()->json([ 'success' => false, 'message' => 'Failed to cancel subscription on PayPal', ], 500); } } } // Cancel in our DB - always immediately when refunded $cancelImmediately = $request->boolean('request_refund') || $request->boolean('immediately', false); $subscription->cancel( $request->reason, $cancelImmediately ); // Send cancellation confirmation email try { $wasRefunded = $refundResult && $refundResult['refunded']; $refundAmount = $wasRefunded && isset($refundResult['refund_amount']) ? number_format($refundResult['refund_amount'], 2) . ' ' . ($subscription->plan->currency ?? 'EUR') : null; Mail::to($user->email)->send(new SubscriptionCancelledMail( $user, $subscription->plan->name, $wasRefunded, $refundAmount )); Log::info('Cancellation email sent', [ 'user_id' => $user->id, 'email' => $user->email, 'refunded' => $wasRefunded, ]); } catch (\Exception $e) { Log::error('Failed to send cancellation email', [ 'user_id' => $user->id, 'error' => $e->getMessage(), ]); // Don't fail the cancellation just because email failed } // Build response message $message = 'Subscription canceled'; if ($refundResult && $refundResult['refunded']) { $message = 'Subscription canceled and refund processed'; } elseif ($refundResult && !$refundResult['refunded']) { $message = 'Subscription canceled. Refund could not be processed automatically - please contact support.'; } elseif ($cancelImmediately) { $message = 'Subscription canceled immediately'; } else { $message = 'Subscription will be canceled at period end'; } return response()->json([ 'success' => true, 'message' => $message, 'data' => [ 'status' => $subscription->status, 'ends_at' => $subscription->ends_at, 'refunded' => $refundResult['refunded'] ?? false, 'refund_id' => $refundResult['refund_id'] ?? null, 'within_guarantee_period' => $isWithinGuaranteePeriod, ], ]); } /** * Get invoices */ public function invoices(Request $request): JsonResponse { $user = $request->user(); $invoices = $user->invoices() ->with('subscription.plan') ->recent() ->paginate(20); return response()->json([ 'success' => true, 'data' => [ 'invoices' => $invoices->map(function ($invoice) { return [ 'id' => $invoice->id, 'number' => $invoice->number, 'status' => $invoice->status, 'status_label' => $invoice->status_label, 'status_color' => $invoice->status_color, 'description' => $invoice->description, 'subtotal' => $invoice->subtotal, 'tax' => $invoice->tax, 'tax_percent' => $invoice->tax_percent, 'total' => $invoice->total, 'formatted_total' => $invoice->formatted_total, 'currency' => $invoice->currency, 'billing_reason' => $invoice->billing_reason, 'billing_reason_label' => $invoice->billing_reason_label, 'paid_at' => $invoice->paid_at, 'created_at' => $invoice->created_at, 'plan_name' => $invoice->subscription?->plan?->name, ]; }), 'pagination' => [ 'current_page' => $invoices->currentPage(), 'last_page' => $invoices->lastPage(), 'per_page' => $invoices->perPage(), 'total' => $invoices->total(), ], ], ]); } /** * Get PayPal client ID for frontend */ public function paypalConfig(): JsonResponse { return response()->json([ 'success' => true, 'data' => [ 'client_id' => $this->paypal->getClientId(), 'sandbox' => $this->paypal->isSandbox(), ], ]); } /** * PayPal webhook handler */ public function webhook(Request $request): JsonResponse { // Log all incoming webhooks for debugging Log::channel('single')->info('=== PAYPAL WEBHOOK RECEIVED ===', [ 'event_type' => $request->input('event_type'), 'content' => $request->all(), ]); $webhookId = config('services.paypal.webhook_id'); // Verify webhook signature (skip in sandbox for testing) if (!$this->paypal->isSandbox() && $webhookId) { $verified = $this->paypal->verifyWebhookSignature( $request->headers->all(), $request->getContent(), $webhookId ); if (!$verified) { Log::warning('PayPal webhook signature verification failed'); return response()->json(['status' => 'signature_invalid'], 400); } } $event = $request->all(); $eventType = $event['event_type'] ?? ''; $resource = $event['resource'] ?? []; Log::info('PayPal webhook received', ['event_type' => $eventType]); switch ($eventType) { case 'BILLING.SUBSCRIPTION.ACTIVATED': $this->handleSubscriptionActivated($resource); break; case 'BILLING.SUBSCRIPTION.CANCELLED': $this->handleSubscriptionCancelled($resource); break; case 'BILLING.SUBSCRIPTION.EXPIRED': $this->handleSubscriptionExpired($resource); break; case 'BILLING.SUBSCRIPTION.SUSPENDED': $this->handleSubscriptionSuspended($resource); break; case 'PAYMENT.SALE.COMPLETED': $this->handlePaymentCompleted($resource); break; case 'PAYMENT.SALE.DENIED': case 'PAYMENT.SALE.REFUNDED': $this->handlePaymentFailed($resource); break; default: Log::info('Unhandled PayPal webhook event', ['event_type' => $eventType]); } return response()->json(['status' => 'ok']); } // ==================== WEBHOOK HANDLERS ==================== private function handleSubscriptionActivated(array $resource): void { $subscription = Subscription::where('paypal_subscription_id', $resource['id'])->first(); if (!$subscription) return; $this->updateSubscriptionFromPayPal($subscription, $resource); Log::info('Subscription activated', ['subscription_id' => $subscription->id]); } private function handleSubscriptionCancelled(array $resource): void { $subscription = Subscription::where('paypal_subscription_id', $resource['id'])->first(); if (!$subscription) return; $subscription->update([ 'status' => Subscription::STATUS_CANCELED, 'canceled_at' => now(), 'paypal_status' => $resource['status'] ?? 'CANCELLED', 'paypal_data' => $resource, ]); Log::info('Subscription cancelled via webhook', ['subscription_id' => $subscription->id]); } private function handleSubscriptionExpired(array $resource): void { $subscription = Subscription::where('paypal_subscription_id', $resource['id'])->first(); if (!$subscription) return; $subscription->markAsExpired(); $subscription->update([ 'paypal_status' => $resource['status'] ?? 'EXPIRED', 'paypal_data' => $resource, ]); Log::info('Subscription expired via webhook', ['subscription_id' => $subscription->id]); } private function handleSubscriptionSuspended(array $resource): void { $subscription = Subscription::where('paypal_subscription_id', $resource['id'])->first(); if (!$subscription) return; $subscription->update([ 'status' => Subscription::STATUS_PAST_DUE, 'paypal_status' => $resource['status'] ?? 'SUSPENDED', 'paypal_data' => $resource, ]); Log::info('Subscription suspended via webhook', ['subscription_id' => $subscription->id]); } private function handlePaymentCompleted(array $resource): void { $billingAgreementId = $resource['billing_agreement_id'] ?? null; if (!$billingAgreementId) return; $subscription = Subscription::where('paypal_subscription_id', $billingAgreementId)->first(); if (!$subscription) return; // Create invoice for recurring payment $invoice = Invoice::createForSubscription( $subscription, Invoice::REASON_SUBSCRIPTION_CYCLE, "{$subscription->plan->name} - " . now()->format('F Y') ); $invoice->update([ 'paypal_payment_id' => $resource['id'] ?? null, 'paypal_data' => $resource, ]); $invoice->markAsPaid($resource['id'] ?? null); // Renew subscription period $subscription->renew(); Log::info('Payment completed, subscription renewed', [ 'subscription_id' => $subscription->id, 'invoice_id' => $invoice->id, ]); } private function handlePaymentFailed(array $resource): void { $billingAgreementId = $resource['billing_agreement_id'] ?? null; if (!$billingAgreementId) return; $subscription = Subscription::where('paypal_subscription_id', $billingAgreementId)->first(); if (!$subscription) return; $subscription->update([ 'status' => Subscription::STATUS_PAST_DUE, ]); Log::warning('Payment failed', ['subscription_id' => $subscription->id]); } // ==================== HELPERS ==================== private function updateSubscriptionFromPayPal(Subscription $subscription, array $paypalData): void { $status = $paypalData['status'] ?? ''; $subscription->paypal_status = $status; $subscription->paypal_data = $paypalData; switch ($status) { case 'ACTIVE': $subscription->status = Subscription::STATUS_ACTIVE; // Always set current_period_start to now() on activation if not set if (!$subscription->current_period_start) { $subscription->current_period_start = now(); } // Calculate period end based on plan interval $plan = $subscription->plan; if ($plan) { $periodEnd = now(); if ($plan->interval === 'year') { $periodEnd = now()->addYear(); } else { $periodEnd = now()->addMonth(); } $subscription->current_period_end = $periodEnd; } // Only use PayPal dates if they make sense (within reasonable range) if (isset($paypalData['billing_info']['last_payment']['time'])) { $lastPayment = Carbon::parse($paypalData['billing_info']['last_payment']['time']); // Accept if within last 30 days if ($lastPayment->gte(now()->subDays(30)) && $lastPayment->lte(now()->addDay())) { $subscription->current_period_start = $lastPayment; } } if (isset($paypalData['billing_info']['next_billing_time'])) { $nextBilling = Carbon::parse($paypalData['billing_info']['next_billing_time']); // Accept if within next 13 months (reasonable for monthly/yearly plans) if ($nextBilling->gt(now()) && $nextBilling->lt(now()->addMonths(13))) { $subscription->current_period_end = $nextBilling; } } break; case 'APPROVAL_PENDING': // Keep as trialing until approved break; case 'APPROVED': // Subscription approved but not yet active if ($subscription->plan->has_trial) { $subscription->startTrial($subscription->plan->trial_days); } else { $subscription->activate(); } break; case 'SUSPENDED': $subscription->status = Subscription::STATUS_PAST_DUE; break; case 'CANCELLED': $subscription->status = Subscription::STATUS_CANCELED; $subscription->canceled_at = now(); break; case 'EXPIRED': $subscription->status = Subscription::STATUS_EXPIRED; break; } $subscription->save(); } }