Skip to content

Latest commit

 

History

History
431 lines (363 loc) · 11 KB

File metadata and controls

431 lines (363 loc) · 11 KB

Detailed Changes Made - Stripe Payment Integration

Files Modified

1. frontend/src/pages/VendorAcceptedEvents.jsx

Change 1: Updated handlePaymentSubmit() function (Lines ~133-166)

Before:

const handlePaymentSubmit = async (e) => {
  e.preventDefault();
  if (!selectedEvent) return;

  // Basic validation
  if (!paymentData.cardNumber || !paymentData.cvv || !paymentData.expirationDate) {
    alert('Please fill in all payment fields');
    return;
  }

  try {
    setPaymentLoading(true);
    const token = localStorage.getItem('token');
    
    // Here you would typically send payment to your backend
    // For now, we'll simulate a successful payment
    const res = await axios.post(
      `http://localhost:5000/api/vendor-requests/${selectedEvent.requestId}/payment`,
      { ...paymentData, paymentMethod: 'card' },
      // ... sends card data to backend
    );

    if (res.status === 200) {
      alert('Payment successful!');
      // Updates local state
      setEvents(prev => prev.map(ev => 
        ev.requestId === selectedEvent.requestId 
          ? { ...ev, paymentStatus: 'paid', paidAt: new Date().toISOString() }
          : ev
      ));
      handleClosePaymentModal();
    }
  } catch (err) { ... }
};

After:

const handlePaymentSubmit = async (e) => {
  e.preventDefault();
  if (!selectedEvent || !selectedEvent.requestId) return;

  try {
    setPaymentLoading(true);
    const token = localStorage.getItem('token');
    
    // Request Stripe checkout session from backend
    const res = await axios.post(
      `http://localhost:5000/api/vendor-requests/${selectedEvent.requestId}/payment`,
      { paymentMethod: 'card' },  // No card data!
      {
        headers: {
          ...(token ? { Authorization: `Bearer ${token}` } : {}),
          'Content-Type': 'application/json'
        }
      }
    );

    if (res.status === 200 && res.data.checkoutUrl) {
      // Redirect to Stripe checkout
      window.location.href = res.data.checkoutUrl;
      handleClosePaymentModal();
    } else {
      alert(res.data?.message || 'Failed to initiate payment');
    }
  } catch (err) {
    console.error('Error initiating payment:', err);
    const msg = err.response?.data?.message || err.message || 'Error initiating payment';
    alert(msg);
  } finally {
    setPaymentLoading(false);
  }
};

Key Changes:

  • ✅ Removed card validation
  • ✅ Only sends { paymentMethod: 'card' } to backend
  • ✅ Backend returns Stripe checkout URL
  • ✅ Redirects to Stripe instead of processing locally
  • ✅ No card data sent to backend

Change 2: Updated Payment Modal Form (Lines ~978-1020)

Before:

<form onSubmit={handlePaymentSubmit}>
  <div style={{ marginBottom: '1.25rem' }}>
    <label htmlFor="cardNumber">Card Number</label>
    <input
      type="text"
      id="cardNumber"
      value={paymentData.cardNumber}
      onChange={(e) => setPaymentData({ ...paymentData, cardNumber: e.target.value })}
      placeholder="1234 5678 9012 3456"
      required
      // ... input styling
    />
  </div>

  <div style={{ display: 'flex', gap: '1rem', marginBottom: '1.25rem' }}>
    <div style={{ flex: 1 }}>
      <label htmlFor="expirationDate">Expiration Date</label>
      <input
        type="text"
        id="expirationDate"
        value={paymentData.expirationDate}
        onChange={(e) => setPaymentData({ ...paymentData, expirationDate: e.target.value })}
        placeholder="MM/YY"
        required
        // ... input styling
      />
    </div>

    <div style={{ flex: 1 }}>
      <label htmlFor="cvv">CVV</label>
      <input
        type="text"
        id="cvv"
        value={paymentData.cvv}
        onChange={(e) => setPaymentData({ ...paymentData, cvv: e.target.value })}
        placeholder="123"
        maxLength="4"
        required
        // ... input styling
      />
    </div>
  </div>

  <button type="submit" disabled={paymentLoading}>
    {paymentLoading ? 'Processing...' : 'Pay Now'}
  </button>
</form>

After:

<form onSubmit={handlePaymentSubmit}>
  {selectedEvent && (
    <div style={{
      backgroundColor: '#f3f4f6',
      padding: '1rem',
      borderRadius: '0.5rem',
      marginBottom: '1.5rem'
    }}>
      <div style={{ marginBottom: '0.75rem' }}>
        <span style={{ fontSize: '0.875rem', color: '#6b7280' }}>Amount to Pay:</span>
        <div style={{ fontSize: '1.75rem', fontWeight: '700', color: '#1D3557' }}>
          {selectedEvent.participationFee} EGP
        </div>
      </div>
      {selectedEvent.paymentDeadline && (
        <div>
          <span style={{ fontSize: '0.875rem', color: '#6b7280' }}>Payment Deadline:</span>
          <div style={{ fontSize: '0.875rem', fontWeight: '600', color: '#dc2626' }}>
            {new Date(selectedEvent.paymentDeadline).toLocaleDateString('en-US', { 
              year: 'numeric', 
              month: 'short', 
              day: 'numeric',
              hour: '2-digit',
              minute: '2-digit'
            })}
          </div>
        </div>
      )}
    </div>
  )}
  
  <div style={{
    backgroundColor: '#fef3c7',
    border: '1px solid #fcd34d',
    borderRadius: '0.5rem',
    padding: '0.75rem',
    marginBottom: '1.5rem',
    fontSize: '0.875rem',
    color: '#92400e'
  }}>
    ✓ Secure payment via Stripe. You will be redirected to complete your payment.
  </div>

  <button type="submit" disabled={paymentLoading}>
    {paymentLoading ? 'Processing...' : 'Pay Now'}
  </button>
</form>

Key Changes:

  • ✅ Removed all card input fields
  • ✅ Added payment amount display
  • ✅ Added payment deadline display
  • ✅ Added Stripe security message
  • ✅ Much simpler form (just a button)

2. frontend/src/pages/PaymentCancel.jsx (NEW FILE)

Created: Entire new component (69 lines)

import React from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

const PaymentCancel = () => {
  const navigate = useNavigate();
  const [searchParams] = useSearchParams();
  const { user } = useAuth();
  const type = searchParams.get('type');

  return (
    <div style={{ /* centered container */ }}>
      <div style={{ /* card styling */ }}>
        {/* Error icon */}
        <div style={{ /* ✕ in circle */ }}>✕</div>

        <h1>Payment Cancelled</h1>
        <p>Your payment was not completed.</p>

        {/* Info box showing what happens next */}
        <div style={{ /* info styling */ }}>
          <strong>What happens next:</strong>
          <ul>
            <li>Your request remains in "Accepted" status</li>
            <li>You can try paying again anytime before the deadline</li>
            {type === 'vendor-request' && <li>The payment deadline is still 3 days from acceptance</li>}
          </ul>
        </div>

        {/* Retry and Return buttons */}
        <button onClick={handleRetry}>Retry Payment</button>
        <button onClick={handleGoHome}>Go to Dashboard</button>

        <p>Need help? Contact events@guc.edu.eg</p>
      </div>
    </div>
  );
};

export default PaymentCancel;

Purpose:

  • Displayed when vendor cancels Stripe payment
  • Shows that no charge occurred
  • Allows retry or return to dashboard
  • Clear messaging about deadline still being valid

3. frontend/src/App.jsx

Change 1: Added Import (Line 50)

Before:

import PaymentSuccess from './pages/PaymentSuccess';
import MyWallet from './pages/MyWallet';

After:

import PaymentSuccess from './pages/PaymentSuccess';
import PaymentCancel from './pages/PaymentCancel';
import MyWallet from './pages/MyWallet';

Change 2: Added Route (Lines ~589-596)

Before:

<Route
  path="/payment-success"
  element={
    <ProtectedRoute>
      <PaymentSuccess />
    </ProtectedRoute>
  }
/>
<Route
  path="/events/payment-success"
  element={
    <ProtectedRoute>
      <PaymentSuccess />
    </ProtectedRoute>
  }
/>

After:

<Route
  path="/payment-success"
  element={
    <ProtectedRoute>
      <PaymentSuccess />
    </ProtectedRoute>
  }
/>
<Route
  path="/payment-cancel"
  element={
    <ProtectedRoute>
      <PaymentCancel />
    </ProtectedRoute>
  }
/>
<Route
  path="/events/payment-success"
  element={
    <ProtectedRoute>
      <PaymentSuccess />
    </ProtectedRoute>
  }
/>

Key Changes:

  • ✅ Added new /payment-cancel route
  • ✅ Wrapped in ProtectedRoute (requires login)
  • ✅ Routes to PaymentCancel component

Documentation Files Created

1. backend/STRIPE_PAYMENT_SETUP.md

  • Comprehensive 250+ line setup guide
  • Fee calculations
  • API endpoint documentation
  • Testing instructions
  • Troubleshooting guide
  • Production deployment steps

2. STRIPE_QUICK_START.md

  • 5-minute quick setup
  • Test card numbers
  • Basic flow description
  • Common issues
  • Future production setup

3. STRIPE_PAYMENT_SUMMARY.md

  • Executive summary of changes
  • Before/after comparison
  • Setup requirements
  • Testing steps
  • Key features list

4. IMPLEMENTATION_CHECKLIST.md

  • Comprehensive checklist
  • Verification steps
  • Manual testing checklist
  • Configuration checklist
  • Troubleshooting guide

Backend (No Changes Needed)

The backend already has complete Stripe integration:

✅ controllers/vendorRequestController.js - Payment endpoint ready ✅ controllers/stripeWebhookController.js - Webhook handler ready ✅ models/paymentModel.js - Payment schema ready ✅ models/vendorRequest.js - Payment fields ready

Backend just needs:

  • STRIPE_SECRET_KEY in .env
  • STRIPE_WEBHOOK_SECRET in .env

Summary of Changes

File Type Change Lines
VendorAcceptedEvents.jsx Modified Updated payment handler and form ~40 lines changed
PaymentCancel.jsx New Created cancellation page 69 lines
App.jsx Modified Added import and route 3 lines
STRIPE_PAYMENT_SETUP.md New Comprehensive guide 250+ lines
STRIPE_QUICK_START.md New Quick setup guide 100+ lines
STRIPE_PAYMENT_SUMMARY.md New Summary document 130+ lines
IMPLEMENTATION_CHECKLIST.md New Verification checklist 200+ lines

Total Changes: ~8 files, ~800 lines of code/documentation


Testing the Changes

  1. Start Servers

    cd backend && npm start
    cd frontend && npm start
  2. Test Payment Flow

    • Login as Admin → Accept vendor request
    • Login as Vendor → Click Payment
    • Should see amount and deadline (not card form)
    • Should redirect to Stripe on "Pay Now"
    • Use card: 4242 4242 4242 4242
  3. Verify Database

    • Payment record created with session ID
    • Vendor request has payment deadline
    • After payment: paymentStatus = "paid"

Key Improvements

✨ Security: No card data stored locally ✨ UX: Simpler, clearer payment flow
✨ Reliability: Stripe handles payment processing ✨ Compliance: PCI compliant ✨ Documentation: Comprehensive guides included


Implementation Status: ✅ Complete and Ready for Testing