Bug fixes where somehow payment_collection became an array and broke everything.

Somehow the order is not being marked as captured.
This commit is contained in:
Amritanshu Agrawal 2025-02-16 04:34:07 +00:00
parent 12d7d21d28
commit e7c40cb5f1
3 changed files with 73 additions and 40 deletions

View File

@ -0,0 +1,17 @@
// app/order-success/page.tsx
'use client';
import Link from 'next/link';
export default function OrderSuccessPage() {
return (
<div className='mx-auto max-w-lg py-10 text-center'>
<h1 className='text-2xl font-bold mb-4'>Thank you for your order!</h1>
<p className='mb-6'>Your order has been placed successfully.</p>
<Link href='/' className='text-blue-600 underline'>
Return to Homepage
</Link>
</div>
);
}

View File

@ -1,6 +1,7 @@
'use client'; // include with Next.js 13+
import { HttpTypes } from '@medusajs/types';
import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useState } from 'react';
import RazorpayPayment from './razorpay';
@ -8,7 +9,8 @@ import RazorpayPayment from './razorpay';
import { useCart } from '@/providers/cart';
export default function CheckoutPaymentStep() {
const { cart, setCart } = useCart();
const router = useRouter();
const { cart, setCart, refreshCart } = useCart();
const [paymentProvider, setPaymentProvider] = useState<HttpTypes.StorePaymentProvider | null>(null);
const [loading, setLoading] = useState(false);
@ -26,7 +28,6 @@ export default function CheckoutPaymentStep() {
if (!cart) {
return;
}
console.log('useEffect cart', cart);
fetch(`${process.env.NEXT_PUBLIC_STORE_URL}/store/payment-providers?region_id=${cart.region_id}`, {
credentials: 'include',
@ -42,7 +43,8 @@ export default function CheckoutPaymentStep() {
});
}, [cart]);
const fetchOrCreatePaymentCollection = async (cart: any) => {
const fetchOrCreatePaymentCollection = async (cartId: string) => {
const cart = await fetchUpdatedCart(cartId);
const paymentCollectionId = cart.payment_collection?.id;
if (paymentCollectionId) {
return paymentCollectionId;
@ -59,7 +61,6 @@ export default function CheckoutPaymentStep() {
cart_id: cart.id,
}),
}).then((res) => res.json());
return payment_collection.id;
};
const initializePaymentSession = async (paymentCollectionId: string, providerId: string) => {
@ -93,20 +94,19 @@ export default function CheckoutPaymentStep() {
};
useEffect(() => {
console.log('useEffect paymentProvider', cart, paymentProvider);
if (!cart || !paymentProvider) {
if (!paymentProvider || !cart) {
return;
}
const initializePayment = async () => {
try {
// Step 1: Fetch or create the Payment Collection
const paymentCollectionId = await fetchOrCreatePaymentCollection(cart);
const paymentCollectionId = await fetchOrCreatePaymentCollection(cart?.id);
// Step 2: Initialize Payment Session for the provider
const session = await initializePaymentSession(paymentCollectionId, paymentProvider.id);
// Step 3: Fetch and update the cart to reflect the changes
const updatedCart = await fetchUpdatedCart(cart.id);
const updatedCart = await fetchUpdatedCart(cart?.id);
if (JSON.stringify(cart) !== JSON.stringify(updatedCart)) {
setCart(updatedCart);
}
@ -120,10 +120,47 @@ export default function CheckoutPaymentStep() {
initializePayment();
}, [paymentProvider]);
/**
* CAPTURE PAYMENT:
* - Called once Razorpay payment succeeds in the child component
* - Tries to "complete" the cart in Medusa which effectively captures the payment
*/
const capturePayment = useCallback(
async (cartId: string) => {
setLoading(true);
try {
// Complete the cart in Medusa
const result = await fetch(`${process.env.NEXT_PUBLIC_STORE_URL}/store/carts/${cartId}/complete`, {
credentials: 'include',
headers: {
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || 'temp',
},
method: 'POST',
})
.then((res) => res.json())
.then(({ type, cart, order, error }) => {
if (type === 'cart' && cart) {
// an error occured
console.error('Error completing cart:', error);
} else if (type === 'order' && order) {
console.log(order);
// Clears/reloads cart in your client state
refreshCart();
router.push('/checkout/order-success');
}
});
} catch (err) {
console.error('Error completing cart:', err);
} finally {
setLoading(false);
}
},
[cart],
);
const getPaymentUi = useCallback(() => {
const activePaymentSession = cart?.payment_collection?.payment_sessions?.[0];
if (activePaymentSession) {
return <RazorpayPayment />;
return <RazorpayPayment cart={cart} capturePayment={capturePayment} />;
}
}, [cart]);

View File

@ -5,11 +5,16 @@ import React, { useState } from 'react';
import { useCart } from '@/providers/cart';
interface RazorpayPaymentProps {
cart: any;
capturePayment: (cartId: string) => Promise<void>;
}
/**
* This component injects the Razorpay checkout script
* and displays the `RazorpayForm` after the script has loaded.
*/
export default function RazorpayPayment() {
export default function RazorpayPayment({ cart, capturePayment }: RazorpayPaymentProps) {
const [scriptLoaded, setScriptLoaded] = useState(false);
return (
@ -21,7 +26,7 @@ export default function RazorpayPayment() {
<Script src='https://checkout.razorpay.com/v1/checkout.js' onLoad={() => setScriptLoaded(true)} />
{/* Only render the payment form if the script has loaded */}
{scriptLoaded && <RazorpayForm />}
{scriptLoaded && <RazorpayForm cart={cart} capturePayment={capturePayment} />}
</>
);
}
@ -30,8 +35,8 @@ export default function RazorpayPayment() {
* A simple form that triggers the Razorpay checkout
* using data from the cart.
*/
function RazorpayForm() {
const { cart, refreshCart } = useCart();
function RazorpayForm({ cart, capturePayment }: RazorpayPaymentProps) {
const { refreshCart } = useCart();
const [loading, setLoading] = useState(false);
// Typically the order_id is generated on your server (Medusa backend)
@ -67,33 +72,7 @@ function RazorpayForm() {
// `response.razorpay_order_id` will have the Order ID
// `response.razorpay_signature` will have the signature
try {
// Complete the cart in Medusa
const result = await fetch(`${process.env.NEXT_PUBLIC_STORE_URL}/store/carts/${cart.id}/complete`, {
credentials: 'include',
headers: {
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || 'temp',
},
method: 'POST',
});
const data = await result.json();
// Example response shape:
// { type: "order" | "cart", order?: any, cart?: any, error?: any }
if (data.type === 'cart' && data.cart) {
// Some error or failure state
console.error('Error completing cart:', data.error);
} else if (data.type === 'order' && data.order) {
// Payment and order creation successful
alert('Order placed.');
// Clears/reloads cart in your client state
refreshCart();
}
} catch (err) {
console.error('Error completing cart:', err);
} finally {
setLoading(false);
}
await capturePayment(cart.id);
},
prefill: {
// Prefill customer data in the Razorpay form