Search code examples
javascriptreactjsreact-custom-hooks

CustomHook with multiple functions doesn't work


I am not sure why this customhook doesn't work , I am trying to make a payment Integration to my website but it doesn't return the frame to the other component so i can get the link , here is the code

import React , {useState} from 'react'
import { useEffect } from 'react';

export async function usePayment(data) {

const API = process.env.PAYMOB_API;
const integrationID = 2874212;

const [frame,setFrame] = useState('');

     
async function firstStep (datas) {
    let data = {
        "api_key": API
    }

    let request = await fetch('https://accept.paymob.com/api/auth/tokens' , {
        method : 'post',
        headers : {'Content-Type' : 'application/json'} ,
        body : JSON.stringify(data)
    })

    let response = await request.json()

    let token = response.token

    await secondStep(token , datas)
}

async function secondStep (token , datas) {
    let data = {
        "auth_token":  token,
        "delivery_needed": "false",
        "amount_cents": datas.amount * 100,
        "currency": "EGP",
        "items": [],
    }

    let request = await fetch('https://accept.paymob.com/api/ecommerce/orders' , {
        method : 'post',
        headers : {'Content-Type' : 'application/json'} ,
        body : JSON.stringify(data)
    })

    let response = await request.json()

    let id = response.id

    await thirdStep(datas , token , id)
}

async function thirdStep (datas , token , id) {
    let data = {
        "auth_token": token,
        "amount_cents": datas.amount * 100, 
        "expiration": 3600, 
        "order_id": id,
        "billing_data": {
            "apartment": "803", 
            "email": datas.email, 
            "floor": "42", 
            "first_name": datas.name, 
            "street": "Ethan Land", 
            "building": "8028", 
            "phone_number": "00000000000", 
            "shipping_method": "PKG", 
            "postal_code": "01898", 
            "city": "Jaskolskiburgh", 
            "country": "CR", 
            "last_name": "Nicolas", 
            "state": "Utah"
        }, 
        "currency": "EGP", 
        "integration_id": integrationID
    }

    let request = await fetch('https://accept.paymob.com/api/acceptance/payment_keys' , {
        method : 'post',
        headers : {'Content-Type' : 'application/json'} ,
        body : JSON.stringify(data)
    })

    let response = await request.json()

    let TheToken = response.token

    let iframURL = `https://accept.paymob.com/api/acceptance/iframes/377194?payment_token=${TheToken}`
    setFrame(iframURL)
    console.log(frame)
}

    useEffect(() =>{
    firstStep(data)
    },[])

    return { frame };
}

export default usePayment;`

I am not sure what is missing here , please need someone to guide me why multiple functions in customhook doesn't work , error message is

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

1 - You might have mismatching versions of React and the renderer (such as React DOM)

2 - You might be breaking the Rules of Hooks

3 - You might have more than one copy of React in the same app

and this is the code i use to call the function :

 const handleSubmit = async (event) => {
    event.preventDefault();

    try {
        if ( !name || !email || !amount ) {
            generateError("Please Fill The Form !")
        } else {
        const { frame } = await usePayment(datas);
        console.log(frame)
        // location.href = pay.iframURL;
        }
    } catch (err) {
        console.log(err);
    }
};

I expect to get a url to the frame state then pass it to another component

NOTE : that this functions works fine when i add it to the component but its not working as a customhook , i am not sure why


Solution

  • For this use case it would be better not to use a hook, rather just write this as a js function. There's no advantage I can see to using a hook, since you're just waiting for data in useEffect and invoking the 3 api calls after that. Instead just send the payload directly to the async function when needed.

    
    const API = process.env.PAYMOB_API;
    const integrationID = 2874212;
    
         
    async function firstStep (data) {
        let data = {
            "api_key": API
        }
    
        let request = await fetch('https://accept.paymob.com/api/auth/tokens' , {
            method : 'post',
            headers : {'Content-Type' : 'application/json'} ,
            body : JSON.stringify(data)
        })
        const { token }  = await request.json()
        return token
    }
    
    async function secondStep ({token , data}) {
        let data = {
            "auth_token":  token,
            "delivery_needed": "false",
            "amount_cents": data.amount * 100,
            "currency": "EGP",
            "items": [],
        }
    
        let request = await fetch('https://accept.paymob.com/api/ecommerce/orders' , {
            method : 'post',
            headers : {'Content-Type' : 'application/json'} ,
            body : JSON.stringify(data)
        })
        const  { id } = await request.json()
        return id
    }
    
    async function thirdStep ({data , token , id}) {
        let data = {
            "auth_token": token,
            "amount_cents": data.amount * 100, 
            "expiration": 3600, 
            "order_id": id,
            "billing_data": {
                "apartment": "803", 
                "email": data.email, 
                "floor": "42", 
                "first_name": data.name, 
                "street": "Ethan Land", 
                "building": "8028", 
                "phone_number": "00000000000", 
                "shipping_method": "PKG", 
                "postal_code": "01898", 
                "city": "Jaskolskiburgh", 
                "country": "CR", 
                "last_name": "Nicolas", 
                "state": "Utah"
            }, 
            "currency": "EGP", 
            "integration_id": integrationID
        }
    
        let request = await fetch('https://accept.paymob.com/api/acceptance/payment_keys' , {
            method : 'post',
            headers : {'Content-Type' : 'application/json'} ,
            body : JSON.stringify(data)
        })
    
        let response = await request.json()
    
        let TheToken = response.token
    
        let iframURL = `https://accept.paymob.com/api/acceptance/iframes/377194?payment_token=${TheToken}`
        return frame;
    }
    
    }
    
    
    export async function handlePayment(data) => {
     const token = await firstStep(data);
     const id = await secondStep({ data, token });
     return await thirdStep({ data, token, id })
    }
    
    export default handlePayment;`