React-paypal advanced integration errors

Viewed 63

I have been trying to implement React-Paypal advanced integration for almost a week now but I keep getting an error. I did not use api from paypal sandbox, instead I'm calling our own api built with dotnet.

When I proceed with payment, order Id was created but the payment declined.

Working flow that I want to achieve is;

onClick Prepare for payment will generate all data that I need to process in the back end. Then, on render or onClick Pay, it will create an order including orderId and capture the payment.

Can anyone spot where I went wrong on my code below?

Error:

enter image description here

Checkout.jsx

import { useState } from "react"
import {
    PayPalScriptProvider
} from "@paypal/react-paypal-js";
import CheckoutButton from "./CheckoutButton";
import "../styles/Checkout.css";
import NewCheckoutBtn from "./Checkout2";

const PrepareForPayment = () => {

    const [clientId, setClientId] = useState(null)
    const [clientToken, setClientToken] = useState(null)
    const [merchantRef, setMerchantRef] = useState(null)
    const [sessionHash, setSessionHash] = useState(null)
    const [showData, setShowData] = useState(false);

    const baseUrl = "http://local.payment.web"

    const onClick = async () => {
        console.log("onClick ran");
        return await fetch(`${baseUrl}/api/Ppal/ReactApi/PrepareForPayment`, {
                mode: "cors",
                method: "post",
                headers: {
                    'content-type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
    
                },
                body: JSON.stringify({
                    "curCode": "USD",
                    "orderAmount": 500
                })
            }).then(res => {
                console.log("fetch Data : ", res);
                return res.json()
            }).then(data => {
                console.log("fetch Data : ", data);
                setShowData(true)
                setClientId(data.ClientId)
                setClientToken(data.ClientToken)
    
                if (data.prepareForPayment) {
                    setMerchantRef(data.merchantRef)
                    setSessionHash(data.sessionHash)
                }
            }).catch(err => console.log(err))
    }
    
    return (
        <>
            <button onClick={onClick} disabled={showData ? true : false}>Prepare for payment</button>
            {
                clientToken && (
                    <PayPalScriptProvider
                        options={{
                            "client-id": clientId,
                            components: "buttons,hosted-fields",
                            "data-client-token": clientToken,
                            intent: "capture",
                            vault: false,
                        }}
                    >
                        <CheckoutButton merchantRef={merchantRef} sessionHash={sessionHash} />
                    </PayPalScriptProvider>
                )
            }
        </>
    )
}

export default PrepareForPayment

CheckoutButton.jsx

import { useState, useRef, useEffect } from "react";
import { usePayPalHostedFields, usePayPalScriptReducer } from "@paypal/react-paypal-js";

import "../styles/CheckoutBtn.css";
import { useCallback } from "react";

const CheckoutButton = ({ merchantRef, sessionHash }) => {
    const [paid, hasPaid] = useState(false)
    const [orderId, setOrderId] = useState(null)
    const [supportsHostedFields, setSupportsHostedFields] = useState(null)
    const [renderInstance, setRenderInstance] = useState()

    const cardHolderName = useRef(null);
    const hostedField = usePayPalHostedFields();
    const [{ isResolved, options }] = usePayPalScriptReducer()

    const paypal = window.paypal
    const baseUrl = "http://local.payment.web"

    const initPaypal = useCallback(async () => {
        if (typeof supportsHostedFields === "boolean" && !!supportsHostedFields) {
            const instance = await paypal.HostedFields.render({
                createOrder: function() {
                    return fetch(`${baseUrl}/api/Ppal/ReactApi/CreateOrder2`, {
                        method: 'post',
                        headers: {
                            'content-type': 'application/json'
                        },
                        body: JSON.stringify({
                            merchantRef: merchantRef,
                            sessionHash: sessionHash,
                            orderId: orderId,
                            intent: "capture",
                        })
                    }).then(res => {
                        console.log("res from createOrder", res);
                        return res.json()
                    }).then(data => {
                        console.log("orderId from initialize : ", data?.orderId);
                        if (data?.createOrder) return setOrderId(data?.orderId)
                    }).catch(err => console.log(err))
                },
                styles: {
                    input: {
                      "font-size": "16pt",
                      color: "#3A3A3A"
                    },
                    ".number": {
                      "font-family": "monospace"
                    },
                    ".valid": {
                      color: "green"
                    }
                  },
                  fields: {
                    number: {
                      selector: "#card-number",
                      placeholder: "Credit Card Number"
                    },
                    cvv: {
                      selector: "#cvv",
                      placeholder: "CVV"
                    },
                    expirationDate: {
                      selector: "#expiration-date",
                      placeholder: "MM/YYYY"
                    }
                  }
            })
            setRenderInstance(instance)
        }
    }, [merchantRef, sessionHash, orderId, paypal, supportsHostedFields])

    useEffect(() => {
        if (isResolved) {
          setSupportsHostedFields(paypal.HostedFields.isEligible());
        }
    }, [setSupportsHostedFields, options, isResolved, paypal]);

    useEffect(() => {
        initPaypal()
    }, [initPaypal])

    
    const handleClick = (e) => {
        e.preventDefault()
        
        console.log("Order id onclick : ", orderId);
        renderInstance.submit().then((data) => {
            console.log("Reach here?");
            const captureOrder = async () => {
                return await fetch(`${baseUrl}/api/Ppal/ReactApi/CaptureOrder`, {
                    mode: "cors",
                    method: "post",
                    headers: {
                        'content-type': 'application/json'
                    },
                    body: JSON.stringify({
                        merchantRef: merchantRef,
                        sessionHash: sessionHash
                    })
                }).then((res) => {
                    console.log("res from pay button : ", res);
                    return res.json()
                }).then((data) => {
                    console.log("data from pay button : ", data);
                    if(data.isApproved) {
                        alert("Payment successful")
                        hasPaid(true)
                    }
                    if(!data.isApproved) alert("Payment declined")
                    if(data.isExpired) alert("Payment expired")
                })
            }
            captureOrder().catch((err) => {
                console.log(err)
            })
            return data
        })
        
        console.log("Card holder : ", cardHolderName.current.value);
        console.log("merchant ref : ", merchantRef);
        console.log("Order id onclick after : ", orderId);
    };

    return (
        <form onSubmit={handleClick}>
            <label htmlFor="card-number">Card Number</label>
            <div id="card-number"></div>
            <label htmlFor="expiration-date">Expiration Date</label>
            <div id="expiration-date"></div>
            <label htmlFor="cvv">CVV</label>
            <div id="cvv"></div>
            <button value="submit" id="submit" className="btn">
                Pay with Card
            </button>
        </form>
    )
};

export default CheckoutButton;
1 Answers

As others have pointed out, the error in your screenshot comes from this line inside of handleClick

        console.log("Card holder : ", cardHolderName.current.value);

You've initialized cardHolderName with null.

    const cardHolderName = useRef(null);

But then your code appears to do nothing with that ref. So that current value of the ref will always be null.

To fix the error, remove the reverence to cardHolderName.current.value, or set the value of the ref to an object with a value property.

Related