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:
parent
12d7d21d28
commit
e7c40cb5f1
17
src/app/(storefront)/checkout/order-success/page.tsx
Normal file
17
src/app/(storefront)/checkout/order-success/page.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
'use client'; // include with Next.js 13+
|
'use client'; // include with Next.js 13+
|
||||||
|
|
||||||
import { HttpTypes } from '@medusajs/types';
|
import { HttpTypes } from '@medusajs/types';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import RazorpayPayment from './razorpay';
|
import RazorpayPayment from './razorpay';
|
||||||
@ -8,7 +9,8 @@ import RazorpayPayment from './razorpay';
|
|||||||
import { useCart } from '@/providers/cart';
|
import { useCart } from '@/providers/cart';
|
||||||
|
|
||||||
export default function CheckoutPaymentStep() {
|
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 [paymentProvider, setPaymentProvider] = useState<HttpTypes.StorePaymentProvider | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@ -26,7 +28,6 @@ export default function CheckoutPaymentStep() {
|
|||||||
if (!cart) {
|
if (!cart) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('useEffect cart', cart);
|
|
||||||
|
|
||||||
fetch(`${process.env.NEXT_PUBLIC_STORE_URL}/store/payment-providers?region_id=${cart.region_id}`, {
|
fetch(`${process.env.NEXT_PUBLIC_STORE_URL}/store/payment-providers?region_id=${cart.region_id}`, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@ -42,7 +43,8 @@ export default function CheckoutPaymentStep() {
|
|||||||
});
|
});
|
||||||
}, [cart]);
|
}, [cart]);
|
||||||
|
|
||||||
const fetchOrCreatePaymentCollection = async (cart: any) => {
|
const fetchOrCreatePaymentCollection = async (cartId: string) => {
|
||||||
|
const cart = await fetchUpdatedCart(cartId);
|
||||||
const paymentCollectionId = cart.payment_collection?.id;
|
const paymentCollectionId = cart.payment_collection?.id;
|
||||||
if (paymentCollectionId) {
|
if (paymentCollectionId) {
|
||||||
return paymentCollectionId;
|
return paymentCollectionId;
|
||||||
@ -59,7 +61,6 @@ export default function CheckoutPaymentStep() {
|
|||||||
cart_id: cart.id,
|
cart_id: cart.id,
|
||||||
}),
|
}),
|
||||||
}).then((res) => res.json());
|
}).then((res) => res.json());
|
||||||
|
|
||||||
return payment_collection.id;
|
return payment_collection.id;
|
||||||
};
|
};
|
||||||
const initializePaymentSession = async (paymentCollectionId: string, providerId: string) => {
|
const initializePaymentSession = async (paymentCollectionId: string, providerId: string) => {
|
||||||
@ -93,20 +94,19 @@ export default function CheckoutPaymentStep() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('useEffect paymentProvider', cart, paymentProvider);
|
if (!paymentProvider || !cart) {
|
||||||
if (!cart || !paymentProvider) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const initializePayment = async () => {
|
const initializePayment = async () => {
|
||||||
try {
|
try {
|
||||||
// Step 1: Fetch or create the Payment Collection
|
// 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
|
// Step 2: Initialize Payment Session for the provider
|
||||||
const session = await initializePaymentSession(paymentCollectionId, paymentProvider.id);
|
const session = await initializePaymentSession(paymentCollectionId, paymentProvider.id);
|
||||||
|
|
||||||
// Step 3: Fetch and update the cart to reflect the changes
|
// 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)) {
|
if (JSON.stringify(cart) !== JSON.stringify(updatedCart)) {
|
||||||
setCart(updatedCart);
|
setCart(updatedCart);
|
||||||
}
|
}
|
||||||
@ -120,10 +120,47 @@ export default function CheckoutPaymentStep() {
|
|||||||
initializePayment();
|
initializePayment();
|
||||||
}, [paymentProvider]);
|
}, [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 getPaymentUi = useCallback(() => {
|
||||||
const activePaymentSession = cart?.payment_collection?.payment_sessions?.[0];
|
const activePaymentSession = cart?.payment_collection?.payment_sessions?.[0];
|
||||||
if (activePaymentSession) {
|
if (activePaymentSession) {
|
||||||
return <RazorpayPayment />;
|
return <RazorpayPayment cart={cart} capturePayment={capturePayment} />;
|
||||||
}
|
}
|
||||||
}, [cart]);
|
}, [cart]);
|
||||||
|
|
||||||
|
@ -5,11 +5,16 @@ import React, { useState } from 'react';
|
|||||||
|
|
||||||
import { useCart } from '@/providers/cart';
|
import { useCart } from '@/providers/cart';
|
||||||
|
|
||||||
|
interface RazorpayPaymentProps {
|
||||||
|
cart: any;
|
||||||
|
capturePayment: (cartId: string) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component injects the Razorpay checkout script
|
* This component injects the Razorpay checkout script
|
||||||
* and displays the `RazorpayForm` after the script has loaded.
|
* 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);
|
const [scriptLoaded, setScriptLoaded] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -21,7 +26,7 @@ export default function RazorpayPayment() {
|
|||||||
<Script src='https://checkout.razorpay.com/v1/checkout.js' onLoad={() => setScriptLoaded(true)} />
|
<Script src='https://checkout.razorpay.com/v1/checkout.js' onLoad={() => setScriptLoaded(true)} />
|
||||||
|
|
||||||
{/* Only render the payment form if the script has loaded */}
|
{/* 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
|
* A simple form that triggers the Razorpay checkout
|
||||||
* using data from the cart.
|
* using data from the cart.
|
||||||
*/
|
*/
|
||||||
function RazorpayForm() {
|
function RazorpayForm({ cart, capturePayment }: RazorpayPaymentProps) {
|
||||||
const { cart, refreshCart } = useCart();
|
const { refreshCart } = useCart();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
// Typically the order_id is generated on your server (Medusa backend)
|
// 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_order_id` will have the Order ID
|
||||||
// `response.razorpay_signature` will have the signature
|
// `response.razorpay_signature` will have the signature
|
||||||
|
|
||||||
try {
|
await capturePayment(cart.id);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
prefill: {
|
prefill: {
|
||||||
// Prefill customer data in the Razorpay form
|
// Prefill customer data in the Razorpay form
|
||||||
|
Loading…
x
Reference in New Issue
Block a user