diff --git a/src/components/settings/PaymentSuccessForm.tsx b/src/components/settings/PaymentSuccessForm.tsx
index 29eba57..466bcdb 100644
--- a/src/components/settings/PaymentSuccessForm.tsx
+++ b/src/components/settings/PaymentSuccessForm.tsx
@@ -1,22 +1,59 @@
'use client';
import Image from 'next/image';
-import { useRouter } from 'next/navigation';
-
-interface TransactionDetail {
- label: string;
- value: string;
- bold?: boolean;
-}
-
-const TRANSACTION_DETAILS: TransactionDetail[] = [
- { label: 'Transaction ID', value: 'REQ-89234475', bold: true },
- { label: 'Amount Paid', value: '$499.00', bold: true },
- { label: 'Date', value: 'Oct 24, 2024', bold: true },
-];
+import { useRouter, useSearchParams } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { stripeService, AgentSubscription } from '@/services/stripe.service';
export function PaymentSuccessForm() {
const router = useRouter();
+ const searchParams = useSearchParams();
+ const sessionId = searchParams.get('session_id');
+
+ const [subscription, setSubscription] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ loadSubscription();
+ }, []);
+
+ const loadSubscription = async () => {
+ try {
+ const sub = await stripeService.getSubscription();
+ setSubscription(sub);
+ } catch (error) {
+ console.error('Failed to load subscription:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const formatDate = (dateStr: string | null | undefined) => {
+ if (!dateStr) return 'N/A';
+ return new Date(dateStr).toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ });
+ };
+
+ const formatAmount = (amount: number) => {
+ return `$${(amount / 100).toFixed(2)}`;
+ };
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ const transactionDetails = [
+ { label: 'Transaction ID', value: sessionId ? sessionId.slice(0, 16) + '...' : 'N/A', bold: true },
+ { label: 'Amount Paid', value: subscription?.plan ? formatAmount(subscription.plan.amount) : '$499.00', bold: true },
+ { label: 'Date', value: formatDate(subscription?.createdAt || new Date().toISOString()), bold: true },
+ ];
return (
<>
@@ -74,7 +111,7 @@ export function PaymentSuccessForm() {
{/* Detail Rows */}
- {TRANSACTION_DETAILS.map((detail) => (
+ {transactionDetails.map((detail) => (
{detail.label}
@@ -95,7 +132,7 @@ export function PaymentSuccessForm() {
Next Renewal Date
- Oct 24, 2025
+ {formatDate(subscription?.currentPeriodEnd)}
@@ -114,6 +151,7 @@ export function PaymentSuccessForm() {
Go To Dashboard
router.push('/agent/settings/billings')}
className="flex-1 bg-[#e58625] border border-[#e58625] text-white font-fractul font-bold text-[14px] py-4 rounded-[15px] hover:bg-[#d47920] transition-colors flex items-center justify-center gap-2"
>
- Download Receipt
+ View Subscription
diff --git a/src/components/settings/SubscriptionForm.tsx b/src/components/settings/SubscriptionForm.tsx
index 798a0f2..106f5a3 100644
--- a/src/components/settings/SubscriptionForm.tsx
+++ b/src/components/settings/SubscriptionForm.tsx
@@ -2,7 +2,8 @@
import Image from 'next/image';
import Link from 'next/link';
-import { useRouter } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { stripeService, AgentSubscription } from '@/services/stripe.service';
interface PlanFeature {
icon: string;
@@ -17,7 +18,81 @@ const PLAN_FEATURES: PlanFeature[] = [
];
export function SubscriptionForm() {
- const router = useRouter();
+ const [subscription, setSubscription] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [actionLoading, setActionLoading] = useState(false);
+
+ useEffect(() => {
+ loadSubscription();
+ }, []);
+
+ const loadSubscription = async () => {
+ try {
+ const subData = await stripeService.getSubscription();
+ setSubscription(subData);
+ } catch (error) {
+ console.error('Failed to load subscription data:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleGetPremium = async () => {
+ setActionLoading(true);
+ try {
+ // Fetch plan ID on-demand when user clicks the button
+ const plans = await stripeService.getPlans();
+ if (!plans.length) {
+ alert('No subscription plans available. Please try again later.');
+ return;
+ }
+ const { url } = await stripeService.createCheckoutSession(plans[0].id);
+ window.location.href = url;
+ } catch (error: any) {
+ console.error('Failed to create checkout session:', error);
+ alert(error.response?.data?.message || 'Failed to start checkout. Please try again.');
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ const handleManageSubscription = async () => {
+ setActionLoading(true);
+ try {
+ const { url } = await stripeService.createPortalSession();
+ window.location.href = url;
+ } catch (error) {
+ console.error('Failed to open billing portal:', error);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ const handleCancelSubscription = async () => {
+ if (!confirm('Are you sure you want to cancel your subscription? You will retain access until the end of your billing period.')) {
+ return;
+ }
+ setActionLoading(true);
+ try {
+ await stripeService.cancelSubscription();
+ await loadSubscription();
+ } catch (error) {
+ console.error('Failed to cancel subscription:', error);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ const formatDate = (dateStr: string | null) => {
+ if (!dateStr) return '';
+ return new Date(dateStr).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ });
+ };
+
+ const isActive = subscription?.status === 'ACTIVE';
return (
<>
@@ -31,6 +106,46 @@ export function SubscriptionForm() {
+ {/* Active Subscription Status */}
+ {isActive && (
+
+
+
+
+ Active Subscription
+
+
+
+ Plan: {subscription.plan?.name}
+
+ {subscription.currentPeriodEnd && (
+
+ {subscription.cancelAtPeriodEnd
+ ? `Cancels on: ${formatDate(subscription.currentPeriodEnd)}`
+ : `Renews on: ${formatDate(subscription.currentPeriodEnd)}`}
+
+ )}
+
+
+ {actionLoading ? 'Loading...' : 'Manage Subscription'}
+
+ {!subscription.cancelAtPeriodEnd && (
+
+ Cancel Subscription
+
+ )}
+
+
+ )}
+
{/* Annual Membership Plans */}
@@ -74,12 +189,19 @@ export function SubscriptionForm() {
{/* CTA Button */}
- router.push('/agent/settings/billings/checkout')}
- className="bg-[#e58625] border border-[#e58625] text-white font-fractul font-bold text-[14px] px-10 py-4 rounded-[15px] hover:bg-[#d47920] transition-colors"
- >
- Get Premium Access
-
+ {isActive ? (
+
+ Current Plan
+
+ ) : (
+
+ {actionLoading ? 'Processing...' : 'Get Premium Access'}
+
+ )}
{/* Right Side - Features + Illustration */}
diff --git a/src/services/api.ts b/src/services/api.ts
index 72e4d1c..bdaccdb 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -95,6 +95,7 @@ const publicEndpoints = [
'/auth/2fa/verify-backup',
'/agent-types',
'/cms',
+ '/stripe/plans',
];
// Check if URL is a public endpoint
diff --git a/src/services/index.ts b/src/services/index.ts
index 1132900..94f098e 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -63,3 +63,7 @@ export { cmsService } from './cms.service';
export { notificationService } from './notification.service';
export { notificationsApiService } from './notifications-api.service';
export type { AppNotification } from './notifications-api.service';
+
+// Stripe Service
+export { stripeService } from './stripe.service';
+export type { SubscriptionPlan, AgentSubscription, PaymentRecord } from './stripe.service';
diff --git a/src/services/stripe.service.ts b/src/services/stripe.service.ts
new file mode 100644
index 0000000..588ff7d
--- /dev/null
+++ b/src/services/stripe.service.ts
@@ -0,0 +1,76 @@
+import api from './api';
+
+export interface SubscriptionPlan {
+ id: string;
+ name: string;
+ description: string | null;
+ stripePriceId: string;
+ amount: number;
+ currency: string;
+ interval: string;
+ features: string | null;
+ isActive: boolean;
+ sortOrder: number;
+}
+
+export interface AgentSubscription {
+ id: string;
+ userId: string;
+ planId: string;
+ stripeCustomerId: string;
+ stripeSubscriptionId: string | null;
+ status: string;
+ currentPeriodStart: string | null;
+ currentPeriodEnd: string | null;
+ cancelAtPeriodEnd: boolean;
+ canceledAt: string | null;
+ plan: SubscriptionPlan;
+}
+
+export interface PaymentRecord {
+ id: string;
+ subscriptionId: string | null;
+ userId: string;
+ stripePaymentIntentId: string | null;
+ stripeInvoiceId: string | null;
+ amount: number;
+ currency: string;
+ status: string;
+ receiptUrl: string | null;
+ createdAt: string;
+ subscription: AgentSubscription | null;
+}
+
+class StripeApiService {
+ async getPlans(): Promise {
+ const response = await api.get('/stripe/plans');
+ return response.data.data;
+ }
+
+ async getSubscription(): Promise {
+ const response = await api.get('/stripe/subscription');
+ return response.data.data;
+ }
+
+ async createCheckoutSession(planId: string): Promise<{ url: string }> {
+ const response = await api.post('/stripe/create-checkout-session', { planId });
+ return response.data.data;
+ }
+
+ async createPortalSession(): Promise<{ url: string }> {
+ const response = await api.post('/stripe/create-portal-session', {});
+ return response.data.data;
+ }
+
+ async cancelSubscription(): Promise {
+ const response = await api.post('/stripe/cancel-subscription');
+ return response.data.data;
+ }
+
+ async getPaymentHistory(): Promise {
+ const response = await api.get('/stripe/payments');
+ return response.data.data;
+ }
+}
+
+export const stripeService = new StripeApiService();