Search code examples
reactjsnext.jspaypalpaypal-sandbox

Paypal React / Next JS buttons not updating after changing products quantities in cart


I'm encountering an issue with the React PayPal Buttons integration. When I change the quantity of products directly in the cart, the products are updating in the local storage and in the cart, but the changes are not reflected in the PaypalButtons component props createOrder and onApprove. As a result, the total amount in the PayPal standard checkout is incorrect, and can't put the right quantity of products in my database.

Here's the relevant code :

'use client'

import { PayPalButtons, PayPalScriptProvider } from '@paypal/react-paypal-js'
import { createPaypalOrder, handlePaypalCheckout } from '../../_actions/paypal'
import { useRouter } from 'next/navigation'
import { useContext } from 'react'
import { CartContext } from '@/app/_context/CartContext'
import { toast } from '../ui/use-toast'
import { useSession } from 'next-auth/react'
export default function PaypalButton({ total, uniqueProducts }) {
  console.log('🚀 ~ PaypalButton ~ uniqueProducts:', uniqueProducts)
  console.log('🚀 ~ total:', total)
  const { data: session } = useSession()
  console.log('🚀 ~ session:', session?.user.email)
  const { setShowCart } = useContext(CartContext)

  const router = useRouter()

  const captureOrder = async (orderId) => {
    try {
      const res = await fetch('/api/paypal/capture', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ orderId: orderId }),
      })

      const orderCapture = await res.json()

      if (orderCapture.status == 200) {
        const orderId = await createPaypalOrder(
          uniqueProducts,
          orderCapture.response.result,
          total,
          session?.user.email,
        )
        setShowCart(false)
        router.push(`/order/success?order_id=${orderId}`)
      }
    } catch (error) {
      toast({
        title: 'Invalid payment',
        variant: 'destructive',
      })
      console.log('🚀 ~ captureOrder ~ error:', error)
    }
  }

  const createOrderAction = async () => {
    try {
      console.log(total)
      console.log(uniqueProducts)

      const res = await fetch('/api/paypal', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ total: total }),
      })
      const order = await res.json()
      if (order.id) return order.id
      else {
        const errorDetail = order?.details?.[0]
        const errorMessage = errorDetail
          ? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
          : JSON.stringify(orderData)

        throw new Error(errorMessage)
      }
    } catch (error) {
      console.log('🚀 ~ createOrderAction ~ error:', error)
      console.error(error)
    }
  }

  return (
    <div>
      <PayPalScriptProvider
        options={{
          clientId:
            'test',
          disableFunding: 'card',
          currency: 'EUR',
        }}
      >
        <PayPalButtons
          style={{
            color: 'gold',
            disableMaxWidth: 'true',
          }}
          createOrder={async (data, actions) => {
            const orderId = createOrderAction()
            return orderId
          }}
          onApprove={async (data, actions) => {
            const orderCapture = await captureOrder(data.orderID)
            if (orderCapture) return true
          }}
          onCancel={(data) => {}}
          onError={(err) => {
            console.log('🚀 ~ err:', err)
            console.error('PayPal Checkout onError', err)
          }}
        />
      </PayPalScriptProvider>
    </div>
  )
}

The console log in createOrder and onApprove always display the old uniqueProducts and total before I changed the quantity

I've attempted to pass the updated product items in the request body when creating the order, but I couldn't retrieve them when capturing the order. I've also tried to retrieve the updated products directly in the local storage and the total using useRef just before fetching the backend, it was working in the createOrderAction but not the captureOrder which is the crucial one to send data to my database.


Solution

  • You can use forceReRender props in the PaypalButtons component and the cart will update accordingly

      <PayPalButtons
          forceReRender={[uniqueProducts]}
          style={{
            color: 'gold',
            disableMaxWidth: 'true',
          }}
          createOrder={async (data, actions) => {
            const orderId = createOrderAction()
            return orderId
          }}
          onApprove={async (data, actions) => {
            const orderCapture = await captureOrder(data.orderID)
            if (orderCapture) return true
          }}
          onCancel={(data) => {}}
          onError={(err) => {
            console.log('🚀 ~ err:', err)
            console.error('PayPal Checkout onError', err)
          }}
        />
    

    Here is an example with this props in the official documentation https://paypal.github.io/react-paypal-js/?path=/docs/example-paypalbuttons--default