diff --git a/src/app/(storefront)/checkout/order-success/page.tsx b/src/app/(storefront)/checkout/order-success/page.tsx
new file mode 100644
index 0000000..62560e6
--- /dev/null
+++ b/src/app/(storefront)/checkout/order-success/page.tsx
@@ -0,0 +1,17 @@
+// app/order-success/page.tsx
+
+'use client';
+
+import Link from 'next/link';
+
+export default function OrderSuccessPage() {
+ return (
+
+
Thank you for your order!
+
Your order has been placed successfully.
+
+ Return to Homepage
+
+
+ );
+}
diff --git a/src/app/(storefront)/checkout/payment/page.tsx b/src/app/(storefront)/checkout/payment/page.tsx
index 45958d1..99c7208 100644
--- a/src/app/(storefront)/checkout/payment/page.tsx
+++ b/src/app/(storefront)/checkout/payment/page.tsx
@@ -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(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 ;
+ return ;
}
}, [cart]);
diff --git a/src/app/(storefront)/checkout/payment/razorpay.tsx b/src/app/(storefront)/checkout/payment/razorpay.tsx
index 19bb5f6..5aeccb3 100644
--- a/src/app/(storefront)/checkout/payment/razorpay.tsx
+++ b/src/app/(storefront)/checkout/payment/razorpay.tsx
@@ -5,11 +5,16 @@ import React, { useState } from 'react';
import { useCart } from '@/providers/cart';
+interface RazorpayPaymentProps {
+ cart: any;
+ capturePayment: (cartId: string) => Promise;
+}
+
/**
* 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() {