T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/public_html/src/components/payments/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/public_html/src/components/payments/PaymentForm.tsx
'use client';

import React, { useState, useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
  Elements,
  CardElement,
  useStripe,
  useElements
} from '@stripe/react-stripe-js';
import { useSession } from 'next-auth/react';

// Initialize Stripe with proper error handling
const getStripePromise = () => {
  const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
  
  // Check if the key is properly configured (not a placeholder)
  if (!publishableKey || 
      publishableKey === 'pk_test_your_stripe_publishable_key_here' ||
      publishableKey === 'pk_live_your_stripe_publishable_key_here') {
    console.warn('Stripe publishable key not properly configured. Payment functionality will be disabled.');
    return null;
  }
  
  return loadStripe(publishableKey);
};

const stripePromise = getStripePromise();

interface PaymentFormProps {
  caseId: string;
  lawyerId: string;
  amount: number;
  description?: string;
  onSuccess?: (result: any) => void;
  onError?: (error: string) => void;
}

// Main Payment Form Component
export default function PaymentForm({
  caseId,
  lawyerId,
  amount,
  description,
  onSuccess,
  onError
}: PaymentFormProps) {
  // If Stripe is not properly configured, show a message
  if (!stripePromise) {
    return (
      <div className="max-w-md mx-auto bg-white rounded-lg shadow-lg p-6">
        <div className="text-center">
          <div className="text-red-500 mb-4">
            <svg className="w-12 h-12 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
            </svg>
          </div>
          <h3 className="text-lg font-semibold text-gray-900 mb-2">
            Payment System Unavailable
          </h3>
          <p className="text-sm text-gray-600 mb-4">
            Stripe payment processing is not properly configured. Please contact support.
          </p>
          <div className="text-xs text-gray-500">
            Amount: ${amount.toFixed(2)} CAD
          </div>
        </div>
      </div>
    );
  }

  return (
    <Elements stripe={stripePromise}>
      <PaymentFormContent
        caseId={caseId}
        lawyerId={lawyerId}
        amount={amount}
        description={description}
        onSuccess={onSuccess}
        onError={onError}
      />
    </Elements>
  );
}

// Internal form component with Stripe hooks
function PaymentFormContent({
  caseId,
  lawyerId,
  amount,
  description,
  onSuccess,
  onError
}: PaymentFormProps) {
  const stripe = useStripe();
  const elements = useElements();
  const { data: session } = useSession();

  const [isLoading, setIsLoading] = useState(false);
  const [paymentIntent, setPaymentIntent] = useState<any>(null);
  const [societyDiscount, setSocietyDiscount] = useState(0);
  const [platformFee, setPlatformFee] = useState(0);
  const [lawyerPayout, setLawyerPayout] = useState(0);
  const [xpReward, setXpReward] = useState(0);
  const [clientSecret, setClientSecret] = useState('');

  // Create payment intent when component mounts
  useEffect(() => {
    if (caseId && lawyerId && amount > 0) {
      createPaymentIntent();
    }
  }, [caseId, lawyerId, amount]);

  const createPaymentIntent = async () => {
    try {
      setIsLoading(true);
      const response = await fetch('/api/payments/create-payment-intent', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          amount,
          caseId,
          lawyerId,
          description
        })
      });

      if (!response.ok) {
        throw new Error('Failed to create payment intent');
      }

      const data = await response.json();
      setClientSecret(data.clientSecret);
      setSocietyDiscount(data.societyDiscount);
      setPlatformFee(data.platformFeeAmount);
      setLawyerPayout(data.lawyerPayoutAmount);
      setXpReward(data.xpEarned);
      setPaymentIntent(data);

    } catch (error) {
      console.error('Error creating payment intent:', error);
      onError?.('Failed to initialize payment. Please try again.');
    } finally {
      setIsLoading(false);
    }
  };

  const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault();

    if (!stripe || !elements || !clientSecret) {
      onError?.('Payment system not ready. Please wait and try again.');
      return;
    }

    setIsLoading(true);

    const cardElement = elements.getElement(CardElement);
    if (!cardElement) {
      onError?.('Card element not found');
      setIsLoading(false);
      return;
    }

    try {
      // Confirm payment with Stripe
      const { error, paymentIntent: confirmedPayment } = await stripe.confirmCardPayment(
        clientSecret,
        {
          payment_method: {
            card: cardElement,
            billing_details: {
              name: session?.user?.name || 'Anonymous',
              email: session?.user?.email || undefined,
            },
          },
        }
      );

      if (error) {
        console.error('Payment confirmation error:', error);
        onError?.(error.message || 'Payment failed. Please try again.');
      } else if (confirmedPayment.status === 'succeeded') {
        console.log('Payment succeeded:', confirmedPayment);
        onSuccess?.({
          paymentIntent: confirmedPayment,
          ...paymentIntent
        });
      } else {
        onError?.('Payment was not completed. Please try again.');
      }
    } catch (error) {
      console.error('Payment error:', error);
      onError?.('An unexpected error occurred. Please try again.');
    } finally {
      setIsLoading(false);
    }
  };

  const cardElementOptions = {
    style: {
      base: {
        fontSize: '16px',
        color: '#424770',
        '::placeholder': {
          color: '#aab7c4',
        },
        fontFamily: 'Inter, system-ui, sans-serif',
      },
      invalid: {
        color: '#9e2146',
      },
    },
    hidePostalCode: false,
  };

  if (isLoading && !clientSecret) {
    return (
      <div className="flex items-center justify-center p-8">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
        <span className="ml-3 text-gray-600">Initializing payment...</span>
      </div>
    );
  }

  return (
    <div className="max-w-md mx-auto bg-white rounded-lg shadow-lg p-6">
      <div className="mb-6">
        <h3 className="text-lg font-semibold text-gray-900 mb-2">
          Complete Payment
        </h3>
        <div className="text-sm text-gray-600 space-y-1">
          <div className="flex justify-between">
            <span>Case Fee:</span>
            <span>${amount.toFixed(2)} CAD</span>
          </div>
          {societyDiscount > 0 && (
            <div className="flex justify-between text-green-600">
              <span>Society Discount:</span>
              <span>-${(amount * societyDiscount).toFixed(2)}</span>
            </div>
          )}
          <div className="flex justify-between text-gray-500">
            <span>Platform Fee:</span>
            <span>${platformFee.toFixed(2)}</span>
          </div>
          <div className="flex justify-between text-gray-500">
            <span>Lawyer Receives:</span>
            <span>${lawyerPayout.toFixed(2)}</span>
          </div>
          {xpReward > 0 && (
            <div className="flex justify-between text-blue-600">
              <span>XP Reward:</span>
              <span>+{xpReward} XP</span>
            </div>
          )}
          <hr className="my-2" />
          <div className="flex justify-between font-semibold">
            <span>Total:</span>
            <span>${amount.toFixed(2)} CAD</span>
          </div>
        </div>
      </div>

      <form onSubmit={handleSubmit} className="space-y-4">
        <div>
          <label className="block text-sm font-medium text-gray-700 mb-2">
            Card Information
          </label>
          <div className="p-3 border border-gray-300 rounded-md focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500">
            <CardElement options={cardElementOptions} />
          </div>
        </div>

        {societyDiscount > 0 && (
          <div className="bg-green-50 border border-green-200 rounded-md p-3">
            <div className="flex items-center">
              <svg className="h-5 w-5 text-green-400 mr-2" fill="currentColor" viewBox="0 0 20 20">
                <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
              </svg>
              <span className="text-sm text-green-800">
                Society Member Discount Applied!
                <br />
                <span className="text-xs">
                  You saved ${(amount * societyDiscount).toFixed(2)} with your membership.
                </span>
              </span>
            </div>
          </div>
        )}

        <div className="bg-blue-50 border border-blue-200 rounded-md p-3">
          <div className="flex items-start">
            <svg className="h-5 w-5 text-blue-400 mr-2 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
              <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
            </svg>
            <div className="text-sm text-blue-800">
              <strong>Escrow Protection:</strong>
              <br />
              Your payment will be held in escrow until case milestones are met, 
              ensuring your money is protected.
            </div>
          </div>
        </div>

        <button
          type="submit"
          disabled={!stripe || isLoading}
          className={`w-full py-3 px-4 rounded-md text-white font-medium ${
            isLoading || !stripe
              ? 'bg-gray-400 cursor-not-allowed'
              : 'bg-blue-600 hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2'
          } transition-colors`}
        >
          {isLoading ? (
            <div className="flex items-center justify-center">
              <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
              Processing...
            </div>
          ) : (
            `Pay $${amount.toFixed(2)} CAD`
          )}
        </button>
      </form>

      <div className="mt-4 text-xs text-gray-500 text-center">
        <div className="flex items-center justify-center space-x-2">
          <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
            <path fillRule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clipRule="evenodd" />
          </svg>
          <span>Secured by Stripe • Your payment information is encrypted</span>
        </div>
      </div>
    </div>
  );
} 

CasperSecurity Mini