我为 Ecomm 创建了这个小型 React 项目。当我点击“货到付款”,然后点击“付款”时,一切都很顺利——订单完成,发票创建,购物车清空。但是,到了网上支付的时候,就回到首页了,没有清购物车,也没有创建发票
查出页面:
import React, { useState, useEffect } from 'react';
import { useMutation, useLazyQuery } from '@apollo/client';
import { CREATE_ORDER, CREATE_ADDRESS, GET_USER_ADDRESSES, GET_DISCOUNT, CREATE_ORDER_DETAIL, } from './queries';
import { useNavigate } from 'react-router-dom';
import { useCart } from './CartContext';
import './client.css';
import AddressList from './AddressList';
import OrderCompleteModal from './OrderCompleteModal';
import Modal from './Modal';
import { loadStripe } from '@stripe/stripe-js';
function CheckoutPage() {
const { cart, clearCart } = useCart();
const [createOrder] = useMutation(CREATE_ORDER);
const [createAddress] = useMutation(CREATE_ADDRESS);
const [getDiscount] = useLazyQuery(GET_DISCOUNT);
const [getUserAddresses, { data: addressesData, loading, error }] = useLazyQuery(GET_USER_ADDRESSES);
const [createOrderDetail] = useMutation(CREATE_ORDER_DETAIL);
const navigate = useNavigate();
const [selectedAddress, setSelectedAddress] = useState(null);
const [address, setAddress] = useState({
AddressName: '',
AddressPhone: '',
AddressCountry: '',
AddressCity: '',
AddressArea: '',
AddressStreet: '',
AddressHouseNum: ''
});
const [discountCode, setDiscountCode] = useState('');
const [discountAmount, setDiscountAmount] = useState(0);
const [isAddingAddress, setIsAddingAddress] = useState(false);
const [showModal, setShowModal] = useState(false);
const [orderId, setOrderId] = useState(null);
const [addressesList, setAddressesList] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const [paymentMethod, setPaymentMethod] = useState('');
const stripePromise = loadStripe('pk_test_51P5UiCRtvqzaEbEbROHSyEASzLd3qsZlhZrTvnQNBLErArOalM7iu20FkoSJqDL1Jet579YHk4c0jksIMFqTJC7n00aTNDFkNG');
useEffect(() => {
const userId = localStorage.getItem('userId');
if (userId) {
getUserAddresses({ variables: { userId: parseFloat(userId) } });
}
}, [getUserAddresses]);
useEffect(() => {
if (addressesData) {
setAddressesList(addressesData.getUserAddresses);
}
}, [addressesData]);
const handleInputChange = (e) => {
const { name, value } = e.target;
setAddress({ ...address, [name]: value });
};
const handleDiscountCodeChange = (e) => {
setDiscountCode(e.target.value);
};
const applyDiscount = async () => {
try {
const { data } = await getDiscount({ variables: { Dcode: discountCode } });
if (data && data.getDiscountByCode && data.getDiscountByCode.Dactive) {
setDiscountAmount(data.getDiscountByCode.Damount);
} else {
setDiscountAmount(0);
}
} catch (error) {
console.error('Discount application error:', error);
}
};
const subtotal = cart.reduce((acc, item) => acc + (item.price * (item.quantity || 1)), 0);
const taxRate = 0.05;
const tax = subtotal * taxRate;
const total = subtotal + tax - discountAmount;
const handleCheckout = async () => {
console.log("Checkout initiated");
const userId = localStorage.getItem('userId');
// Basic validation
if (!userId || total <= 0) {
setErrorMessage('Error: Invalid user ID or total amount');
console.error('Error: Invalid user ID or total amount');
return;
}
if (!selectedAddress) {
setErrorMessage('You must select or add an address to continue the order.');
return;
}
setErrorMessage('');
try {
const totalInCents = Math.round(total * 100); // Convert total to cents
console.log("Creating order with total:", totalInCents);
const { data: orderData } = await createOrder({
variables: {
userId: parseInt(userId, 10),
total: totalInCents,
},
});
const newOrderId = orderData.createOrder.id;
console.log("Order created with ID:", newOrderId);
// Create order details
for (const item of cart) {
await createOrderDetail({
variables: {
order_id: newOrderId,
product_id: item.id,
quantity: item.quantity || 1,
price: Math.round(item.price * 1.100),
},
});
}
setOrderId(newOrderId);
console.log("Order details created");
if (paymentMethod === 'cash') {
setShowModal(true);
clearCart();
return;
}
// Handle online payment
if (paymentMethod === 'online') {
const stripe = await stripePromise;
const response = await fetch('http://localhost:3001/api/create-checkout-session', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount: totalInCents }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { id } = await response.json();
// Redirect to Stripe checkout
const { error } = await stripe.redirectToCheckout({ sessionId: id });
if (error) {
console.error('Stripe redirect error:', error);
setErrorMessage('Failed to redirect to payment. Please try again.');
} else {
clearCart(); // Clear cart immediately if you want to do that
}
}
} catch (error) {
console.error('Checkout error:', error);
setErrorMessage('Failed to create the order or proceed with payment. Please try again.');
}
};
const handleBackToHomepage = () => {
navigate('/');
setShowModal(false);
};
const handleViewInvoice = () => {
if (orderId) {
navigate(`/invoice/${orderId}`);
} else {
console.error('Order ID is not set');
}
};
const handleCloseAddressForm = () => {
setIsAddingAddress(false);
setAddress({
AddressName: '',
AddressPhone: '',
AddressCountry: '',
AddressCity: '',
AddressArea: '',
AddressStreet: '',
AddressHouseNum: ''
});
};
const handleSaveAddress = async () => {
const userId = localStorage.getItem('userId');
if (!userId) {
console.error('User ID is not available');
return;
}
try {
await createAddress({
variables: { user_id: parseInt(userId, 10), ...address },
});
setAddress({
AddressName: '',
AddressPhone: '',
AddressCountry: '',
AddressCity: '',
AddressArea: '',
AddressStreet: '',
AddressHouseNum: ''
});
setIsAddingAddress(false);
getUserAddresses({ variables: { userId: parseFloat(userId) } });
} catch (error) {
console.error('Address creation error:', error);
}
};
return (
<div className="checkout-page">
{showModal && (
<OrderCompleteModal
onClose={handleBackToHomepage}
onViewInvoice={handleViewInvoice}
selectedAddress={selectedAddress}
cart={cart}
orderId={orderId}
/>
)}
{isAddingAddress && (
<Modal isOpen={isAddingAddress} onClose={handleCloseAddressForm}>
<div className="modal-content">
<div className="modal-header">
<h3>Add New Address</h3>
<button className="modal-close" onClick={handleCloseAddressForm}>×</button>
</div>
<div className="modal-body">
<div className="address-form">
{['AddressName', 'AddressPhone', 'AddressCountry', 'AddressCity', 'AddressArea', 'AddressStreet', 'AddressHouseNum'].map(field => (
<input
key={field}
type="text"
name={field}
placeholder={field.replace(/Address/, '')}
value={address[field]}
onChange={handleInputChange}
/>
))}
</div>
<div className="form-actions">
<button className="button save-address" onClick={handleSaveAddress}>Save Address</button>
<button className="button cancel" onClick={handleCloseAddressForm}>Cancel</button>
</div>
</div>
</div>
</Modal>
)}
<div className="checkout-container">
<div className="left-panel">
<div className="panel address-panel">
<h2>Select Address</h2>
{loading && <p>Loading addresses...</p>}
{error && <p className="error-message">Error loading addresses: {error.message}</p>}
{addressesList && (
<AddressList
addresses={addressesList}
onSelectAddress={(id) => setSelectedAddress(id)}
/>
)}
<button className="button add-address" onClick={() => setIsAddingAddress(true)}>Add Address</button>
{errorMessage && <p className="error-message">{errorMessage}</p>}
</div>
<div className="panel payment-panel">
<h2>Select Payment Method</h2>
<div className="payment-methods">
<label>
<input
type="radio"
name="payment"
value="cash"
onChange={() => setPaymentMethod('cash')}
checked={paymentMethod === 'cash'}
/>
Cash on Delivery (+$10)
</label>
<label>
<input
type="radio"
name="payment"
value="online"
onChange={() => setPaymentMethod('online')}
checked={paymentMethod === 'online'}
/>
Online Payment
</label>
</div>
</div>
</div>
<div className="right-panel">
<div className="panel cart-panel">
<h2>Shopping Cart</h2>
<ul className="cart-items">
{cart.map(item => (
<li key={item.id} className="cart-item">
<span className="item-name">{item.Pname}</span>
<span className="item-price">${item.price.toFixed(2)}</span>
<span className="item-quantity">x {item.quantity || 1}</span>
</li>
))}
</ul>
<div className="order-summary">
<h3>Order Summary</h3>
<div className="summary-row"><span>Subtotal:</span><span>${subtotal.toFixed(2)}</span></div>
<div className="summary-row"><span>Tax:</span><span>${tax.toFixed(2)}</span></div>
<div className="summary-row"><span>Discount:</span><span>-${discountAmount.toFixed(2)}</span></div>
<div className="summary-row total"><span>Total:</span><span>${total.toFixed(2)}</span></div>
</div>
<div className="discount-code">
<input
type="text"
placeholder="Discount Code"
value={discountCode}
onChange={handleDiscountCodeChange}
/>
<button className="button apply-discount" onClick={applyDiscount}>Apply</button>
</div>
<button className="button checkout-button" onClick={handleCheckout}>Continue to Payment</button>
</div>
</div>
</div>
</div>
);
}
export default CheckoutPage;
这里弹出窗口:
import React from 'react';
import { useMutation } from '@apollo/client';
import { CREATE_INVOICE } from './queries'; // Adjust import as necessary
import './client.css'; // Import your CSS for styling
import { useNavigate } from 'react-router-dom'; // Import useNavigate
const OrderCompleteModal = ({ onClose, selectedAddress, orderId }) => {
const [createInvoice] = useMutation(CREATE_INVOICE);
const navigate = useNavigate(); // Initialize navigate
const handleCreateInvoice = async () => {
try {
const userId = localStorage.getItem('userId');
const addressId = selectedAddress; // Ensure this is set
// Debugging logs
console.log("User ID:", userId);
console.log("Address ID:", addressId);
console.log("Order ID:", orderId);
if (!userId || !addressId || !orderId ) {
throw new Error("Missing required parameters");
}
const { data } = await createInvoice({
variables: {
userId: parseInt(userId, 10),
addressId: parseInt(addressId, 10),
orderId: parseInt(orderId, 10),
},
});
if (data) {
console.log('Invoice created successfully');
// Navigate to the invoice page
navigate(`/invoice/${orderId}`);
}
} catch (error) {
console.error('Error creating invoice:', error);
}
};
return (
<div className="modal-overlay">
<div className="modal-content">
<h2>Order Complete</h2>
<p>Your order has been successfully placed!</p>
<div className="modal-buttons">
<button onClick={() => {
handleCreateInvoice(); // Create the invoice
}}>
View Invoice
</button>
</div>
</div>
</div>
);
};
export default OrderCompleteModal;
我尝试了不止一种解决方案,所有的解决方案在线支付后都不显示弹窗
您所描述的问题可能会出现,因为在线支付流程依赖于通过 Stripe 的 redirectToCheckout 进行重定向,一旦用户重定向到 Stripe 结帐页面,处理购物车清算和发票创建的代码可能无法正确触发。 对于在线支付,一旦用户被重定向到 Stripe 的结帐页面,后端通常需要确认付款并处理付款后操作,例如创建发票和清理购物车。这个逻辑应该放在你的服务器上,而不是放在前端代码中,因为重定向后,前端不会自动知道支付是否成功。 可能会有帮助。