react-paypal-js icon indicating copy to clipboard operation
react-paypal-js copied to clipboard

[BUG]: React skipped hostedField

Open tengkuzulfadli opened this issue 2 years ago β€’ 3 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues.

🐞 Describe the Bug

Referring to my other bug report on #304 , I tried another way using code example as a base. I did not get any error but it seems like React skipped hostedfield.cardFields.submit()

I was unable to find any solutions for this issue but I found a few recent posts on stackoverflow to address the same issue.

My code:

App.js

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

const CUSTOM_FIELD_STYLE = {"border":"1px solid #606060","boxShadow":"2px 2px 10px 2px rgba(0,0,0,0.1)"};
const INVALID_COLOR = {
	color: "#dc3545",
};

// Example of custom component to handle form submit
const SubmitPayment = ({ customStyle, mr, sh }) => {
	const [paying, setPaying] = useState(false);
	const cardHolderName = useRef(null);
	const hostedField = usePayPalHostedFields();

  	const baseUrl = "http://local.payment.web"
  
	const handleClick = () => {
	           if (!hostedField?.cardFields) {
                       const childErrorMessage = 'Unable to find any child components in the <PayPalHostedFieldsProvider />';
          
                       // eslint-disable-next-line no-undef
                       action(ERROR)(childErrorMessage);
                       throw new Error(childErrorMessage);
                  }
		  const isFormInvalid =
		 	Object.values(hostedField.cardFields.getState().fields).some(
		 		(field) => !field.isValid
		 	) || !cardHolderName?.current?.value;
		
		  if (isFormInvalid) {
		 	return alert(
		 		"The payment form is invalid"
		 	);
		}

		setPaying(true);
		
		hostedField.cardFields
			.submit({
				cardholderName: cardHolderName?.current?.value,
			})
			.then(async () => {
				return await fetch(`${baseUrl}/api/Ppal/ReactApi/CaptureOrder`, {
					mode: "cors",
					method: "post",
					headers: {
						'content-type': 'application/json'
					},
					body: JSON.stringify({
						merchantRef: mr,
						sessionHash: sh
					})
				})
				.then(res => res.json())
				.then((data) => {
					console.log("data from pay button : ", data);
					if(data.isApproved) alert("Payment successful")
					if(!data.isApproved) alert("Payment declined")
					if(data.isExpired) alert("Payment expired")
				})
				.catch(err => console.log("err : ", err))
				.finally(() => setPaying(false))
      		})
      		.catch(err => console.log("Error catch : ", err))
	};

	return (
		<>
      		<label title="This represents the full name as shown in the card">
				Card Holder Name
				<input
					id="card-holder"
					ref={cardHolderName}
					className="card-field"
					style={{ ...customStyle, outline: "none" }}
					type="text"
					placeholder="Full name"
				/>
			</label>
			<button
				className={`btn${paying ? "" : " btn-primary"}`}
				style={{ float: "right" }}
				onClick={handleClick}
			>
				{paying ? <div className="spinner tiny" /> : "Pay"}
			</button>
		</>
	);
};

export default function App() {
	const [clientId, setClientId] = useState(null)
  	const [clientToken, setClientToken] = useState(null)
	const [merchantRef, setMerchantRef] = useState(null)
	const [sessionHash, setSessionHash] = useState(null)
 	const [orderId, setOrderId] = useState(null)

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

	useEffect(() => {
		(async () => {
			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);
				setClientId(data.ClientId)
				setClientToken(data.ClientToken)
	
				if (data.prepareForPayment) {
					setMerchantRef(data.merchantRef)
					setSessionHash(data.sessionHash)
				}
			}).catch(err => console.log(err))
		})();
	}, []);

	return (
		<>
			{clientToken ? (
				<PayPalScriptProvider
					options={{
						"client-id": clientId,
						components: "buttons,hosted-fields",
						"data-client-token": clientToken,
						intent: "capture",
						vault: false,
					}}
				>
					<PayPalHostedFieldsProvider
						styles={{".valid":{"color":"#28a745"},".invalid":{"color":"#dc3545"},"input":{"font-family":"monospace","font-size":"16px"}}}
						createOrder={async() => {
							return await fetch(`${baseUrl}/api/Ppal/ReactApi/CreateOrder2`, {
								method: 'post',
								headers: {
									'content-type': 'application/json'
								},
								body: JSON.stringify({
									merchantRef: merchantRef,
									sessionHash: sessionHash
								})
							}).then(res => {
								console.log("res from createOrder", res);
								return res.json()
							}).then(data => {
								console.log("orderId from button : ", data?.orderId);
								if (data?.createOrder) return setOrderId(data?.orderId)
							})
					}}
					>
						<label htmlFor="card-number">
							Card Number
							<span style={INVALID_COLOR}>*</span>
						</label>
						<PayPalHostedField
							id="card-number"
							className="card-field"
							style={CUSTOM_FIELD_STYLE}
							hostedFieldType="number"
							options={{
								selector: "#card-number",
								placeholder: "4111 1111 1111 1111",
							}}
						/>
						<label htmlFor="cvv">
							CVV<span style={INVALID_COLOR}>*</span>
						</label>
						<PayPalHostedField
							id="cvv"
							className="card-field"
							style={CUSTOM_FIELD_STYLE}
							hostedFieldType="cvv"
							options={{
								selector: "#cvv",
								placeholder: "123",
								maskInput: true,
							}}
						/>
						<label htmlFor="expiration-date">
							Expiration Date
							<span style={INVALID_COLOR}>*</span>
						</label>
						<PayPalHostedField
							id="expiration-date"
							className="card-field"
							style={CUSTOM_FIELD_STYLE}
							hostedFieldType="expirationDate"
							options={{
								selector: "#expiration-date",
								placeholder: "MM/YYYY",
							}}
						/>
						<SubmitPayment customStyle={{"border":"1px solid #606060","boxShadow":"2px 2px 10px 2px rgba(0,0,0,0.1)"}} />
					</PayPalHostedFieldsProvider>
				</PayPalScriptProvider>
			) : (
				<h1>Loading token...</h1>
			)}
		</>
	);
}

πŸ˜• Current Behavior

No error and Alert tag render but on Network tab, it did not fetch capture api as screenshot below

Uploading Screenshot 2022-09-12 160040.png…

πŸ€” Expected Behavior

It should call capture api and render alert tag whether the payment was successful or declined or log any errors

πŸ”¬ Minimal Reproduction

No response

🌍 Environment

| Software         | Version(s) |
| ---------------- | ---------- |
| react-paypal-js  |    ^7.8.1  |
| Browser          |   Chrome   |
| Operating System |  Window10  |

Relevant log output

No log or output, React skipped `Hostedfield.cardfield.submit()`

Code of Conduct

  • [X] I agree to follow this project's Code of Conduct

βž• Anything else?

No response

tengkuzulfadli avatar Sep 12 '22 06:09 tengkuzulfadli

@westeezy Any update on this?

tengkuzulfadli avatar Sep 15 '22 01:09 tengkuzulfadli

Not yet we have to triage and schedule it. I'll post updates once we have someone assigned to it.

westeezy avatar Sep 15 '22 19:09 westeezy

Not yet we have to triage and schedule it. I'll post updates once we have someone assigned to it.

Ok well noted

tengkuzulfadli avatar Sep 18 '22 23:09 tengkuzulfadli

Hi @tengkuzulfadli is this still a problem for you? We have a working example of Hosted Fields on Storybook here: https://paypal.github.io/react-paypal-js/?path=/story/paypal-paypalhostedfields--default. That one does call submit() as expected.

If this is still a problem, can you try removing the loading state around paying/setPaying and see if that helps?

gregjopa avatar Oct 12 '22 19:10 gregjopa

Hi @gregjopa Yes it is. I did remove the loading state and avoid to use any additional state to my code. But I'm still having problem with Hostedfield. Everytime I run the code, it skipped hostedfield.cardFields.submit()

tengkuzulfadli avatar Oct 12 '22 22:10 tengkuzulfadli

Okay thanks for confirming.

Let’s verify your eligibility. With the Hosted Fields component merchants have to onboard to be eligible.

What does this command return when you run it in the console? paypal.HostedFields.isEligible()

https://developer.paypal.com/docs/checkout/advanced/integrate/#link-fullstackexamplenodejs

gregjopa avatar Oct 13 '22 01:10 gregjopa

@gregjopa sorry for the late response. I will get back to you soon today with my implementations using 2 ways; PaypalButton and Hostedfields.cardfield to emphasis the issue with hostedfields. I will place my code to sandbox

tengkuzulfadli avatar Oct 13 '22 22:10 tengkuzulfadli

For PaypalButtons , it seems like you have updated your docs and removed hostedfields of components object inside options of PaypalScriptProvider. This was one of issue I was facing before. The paypal buttons was working fine after I removed hostedfields and only define buttons. So, for this, it is resolved and I will not provide sample code in sandbox. However, we do not want to use PaypalButtons, instead we want to use PayPalHostedFieldsProvider.

For PayPalHostedFieldsProvider, the docs that you suggested, is not using React-Paypal-Js package, I did log paypal.HostedFields.isEligible() and it returns true. I implemented it before and it was working. However, again we want to use PayPalHostedFieldsProvider from React-Paypal-js package. When I run my code above, onClick event, browser skipped hostedfields.cardfields.submit()

tengkuzulfadli avatar Oct 14 '22 00:10 tengkuzulfadli

Just to clarify, you can use the components prop to load both buttons and hosted fields or just one component. Ex:

<PayPalScriptProvider options={{ "client-id": "test", components: "buttons,hosted-fields" }}>
...
</PayPalScriptProvider>

Please provide a url to the reproduction of the hosted fields issue you are experiencing related to submit() not firing and we can debug further.

gregjopa avatar Oct 14 '22 22:10 gregjopa

Please see my implementation below if any mistakes from our side.

  1. The form was finally rendered because I did not use hostedFields.cardfields method on this implementation
  2. When I click Submit button, it only logged Error, submit() and fetch did not fire. I replaced path with our own server path built with .net framework and you can test using yours
  3. If I remove PaypalScriptProvider that wrapping form tag, I've got an error that usePayPalScriptReducer needs to be wrapped inside PaypalScriptProvider
import React, { useCallback, useEffect, useState } from "react";
import { PayPalScriptProvider, usePayPalScriptReducer } from "@paypal/react-paypal-js";

const PaypalForm = () => {
	const paypal = window.paypal;

	const [{ isResolved, options }] = usePayPalScriptReducer();

	const [getHostedFields, setGetHostedFields] = useState(null);
	const [renderForm, setRenderForm] = useState();

	const initPaypal = useCallback(async () => {

		if (typeof getHostedFields === "boolean" && !!getHostedFields) {

			const renderFormFields = await paypal.HostedFields.render({

				createOrder: function (data, actions) {

					return ""
					// return fetch("create-order-server", {
					// 	method: "post"
					// }).then((res) => {
					// 	return res.json()
					// }).then((data) => {
					// 	return console.log(data.id);
					// })
				},
				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"
					}
				}
			});

			setRenderForm(renderFormFields);
		}
	}, [paypal, getHostedFields]);

	useEffect(() => {
		if(isResolved) {
			console.log("isResolved : ", isResolved)
			setGetHostedFields(paypal.HostedFields.isEligible());
			console.log("Hostedfields eligible : ", paypal.HostedFields.isEligible());
		}
	}, [setGetHostedFields, isResolved, paypal])

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

	const handleFormSubmit = (e) => {
		e.preventDefault();

		renderForm
			.submit()
			.then((payload) => {
				console.log("Payload : ", payload);
				// return fetch(`server-to-handle-capture-payment/${payload.orderId}`, {
				// 	method: "post"
				// }).then((res) => {
				// 	if(!res.ok) {
				// 		alert("Payment unable to capture")
				// 	}
				// });
				return payload;
			}).catch((err) => console.log("Error", err))
	}

	if (!getHostedFields) return null;

	return(
             <PayPalScriptProvider
			options={{
				"client-id": "use your own client id",
				"data-client-token": "use your own token",
				components: "hosted-fields",
				intent: "capture",
				vault: false
			}}
		>
	          <form id="my-sample-form" onSubmit={handleFormSubmit}>
		          <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>
               </PayPalScriptProvider>
	)
}

const App = () => {
	return(
		<PayPalScriptProvider
			options={{
				"client-id": "use your own client id",
				"data-client-token": "use your own token",
				components: "hosted-fields",
				intent: "capture",
				vault: false
			}}
		>
			<PaypalForm />
		</PayPalScriptProvider>
	)
}

export default App;

tengkuzulfadli avatar Oct 17 '22 01:10 tengkuzulfadli

I did create PaypalForm as a separate component and removed PaypalScriptProvider that wrapping form tag, the isResolved was false and I have 4 errors: -

  1. TypeError: Cannot read properties of undefined (reading 'HostedFields')
  2. react_devtools_backend.js:4026 Failed to load the PayPal JS SDK script. Error: Expected client-id to be passed (debug id: f212034907ac9)
  3. react-paypal-js.js:520 GET https://www.paypal.com/sdk/js?components=buttons,funding-eligibility,hosted-fields&intent=capture&vault=false net::ERR_ABORTED 400
  4. react-paypal-js.js:679 GET https://www.paypal.com/sdk/js?components=buttons,funding-eligibility,hosted-fields&intent=capture&vault=false 400

tengkuzulfadli avatar Oct 19 '22 01:10 tengkuzulfadli

Hi @tengkuzulfadli check the network tab to see why the JS SDK script is failing to load. From what I can tell is it's missing hte client-id which is a required param.

gregjopa avatar Oct 19 '22 14:10 gregjopa

@gregjopa Thank you for pointing it out. I managed to fix it by passing the client-id. However, I'm still getting the same errors for hostedfields as screenshot below:

Screenshot 2022-10-20 110101

Here is my implementation:

import React, { useCallback, useEffect, useState } from "react";
import { PayPalScriptProvider, usePayPalScriptReducer } from "@paypal/react-paypal-js";

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

const PaymentForm = ({ merchantRef, sessionHash }) => {

    const [orderId, setOrderId] = useState();

    const paypal = window.paypal;

	const [{ isResolved, options }] = usePayPalScriptReducer();

	const [getHostedFields, setGetHostedFields] = useState(null);
	const [renderForm, setRenderForm] = useState();

    const initPaypal = useCallback(async () => {

        const renderFormFields = await paypal.HostedFields?.render({
            createOrder: function (data, actions) {
                return fetch(`${baseUrl}/api/Ppal/ReactApi/CreateOrder2`, {
                    method: 'post',
                    headers: {
                        'content-type': 'application/json'
                    },
                    body: JSON.stringify({
                        merchantRef: merchantRef,
                        sessionHash: sessionHash
                    })
                }).then(res => {
                    console.log("res from createOrder", res);
                    return res.json()
                }).then(data => {
                    console.log("orderId from button : ", data?.orderId);
                    if (data?.createOrder) return setOrderId(data?.orderId)
                })
            },
            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"
                }
            }
        })
        setRenderForm(renderFormFields);

    }, [paypal, getHostedFields]);
    

    useEffect(() => {
		if(isResolved) {
			console.log("isResolved : ", isResolved)
			setGetHostedFields(paypal.HostedFields?.isEligible());
			console.log("Hostedfields eligible : ", paypal.HostedFields?.isEligible());
		}
	}, [setGetHostedFields, options, isResolved, paypal])

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

    const handleFormSubmit = (e) => {
		e.preventDefault();

		renderForm
			.submit()
			.then(async (payload) => {
				console.log("Payload : ", payload);
				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((response) => response.json()).then((data) => {
                    console.log("data from pay button : ", data);
                    if(data.isApproved) {
                        alert("Payment successful")
                    }
                    if(!data.isApproved) alert("Payment declined")
                    if(data.isExpired) alert("Payment expired")
                }).catch((err) => console.log("Error here : ", err));
	})}

	if (!getHostedFields) {
        console.log("Something wrong");
    }

	return(
		<>
            {
                isResolved && (
                    <form id="my-sample-form" onSubmit={handleFormSubmit}>
                        <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 PaymentForm

tengkuzulfadli avatar Oct 20 '22 00:10 tengkuzulfadli

@gregjopa I tried another implementation using an example from this package. The code is missing hostedFields?.cardFields?.submit({}) , not hostedFields.submit({}) to make the submit() event fire when it's triggered. However, I have an error of invalid token and require me to pass orderId in hostedfields and asking for access token. Why do you need an access token? Since our back end is preparing all required data such as clientId, clientToken, orderId, etc.. I couldn't able to find a good answer for that. Our back end is built with .net Framework. How can I pass the orderId on capture payment? Your docs is not really clear. We are using the same back end for Angular site and it's working well on Angular.

Here is the screenshot: Screenshot 2022-10-20 125808

This is the implementation:

import React, { useState, useEffect } from "react";
import {
    PayPalScriptProvider,
    PayPalHostedFieldsProvider,
    PayPalHostedField,
    usePayPalHostedFields,
} from "@paypal/react-paypal-js";

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

const SubmitPayment = ({ merchantRefData, sessionHashData, orderId }) => {
    const hostedFields = usePayPalHostedFields();

	console.log('Merchant Ref : ', merchantRefData);
	console.log('Session Hash : ', sessionHashData);
	console.log('order Id : ', orderId);
    const submitHandler = () => {
        if (typeof hostedFields?.cardFields?.submit !== "function") {
			console.log("Submit something wrong ");
		}
        hostedFields.cardFields?.submit({
                // The full name as shown in the card and billing address
                cardholderName: "John Wick",
            })
            .then(async (order) => {
                return await fetch(
                    `${baseUrl}/api/Ppal/ReactApi/CaptureOrder`, {
						mode: "cors",
						method: "post",
						headers: {
						  'content-type': 'application/json'
						},
						body: JSON.stringify({
						  merchantRef: merchantRefData,
						  sessionHash: sessionHashData
						})
					}
                )
                    .then((response) => response.json())
                    .then((data) => {
                        console.log("data from pay button : ", data);
						if(data.isApproved) {
						alert("Payment successful")
						}
						if(!data.isApproved) alert("Payment declined")
						if(data.isExpired) alert("Payment expired")
                    })
                    .catch((err) => {
                        console.log("Error here : ", err)
                    });
            });
    };

    return <button onClick={submitHandler}>Pay</button>;
};

export default function App() {

	const [clientId, setClientId] = useState(null);
	const [clientToken, setClientToken] = useState(null);
	const [merchantRef, setMerchantRef] = useState(null);
	const [sessionHash, setSessionHash] = useState(null);
	const [orderId, setOrderId] = useState(null);

	const prepareForPayment = async () => {

		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);
			setClientId(data.ClientId)
			setClientToken(data.ClientToken)
			if (data.prepareForPayment) {
			  setMerchantRef(data.merchantRef)
			  setSessionHash(data.sessionHash)
			}
		}).catch(err => console.log(err))
	}

	console.log("Client id after click : ", clientId);
	console.log("Client token after click : ", clientToken);

    return (
        <>
			<button onClick={prepareForPayment}>Prepare for payment</button>
			{
				clientToken ?
				<PayPalScriptProvider
					options={{
						"client-id": clientId,
						"data-client-token": clientToken,
						components: "hosted-fields",
						vault: false
					}}
				>
					<PayPalHostedFieldsProvider
						createOrder={async () => {
							return await fetch(`${baseUrl}/api/Ppal/ReactApi/CreateOrder2`, {
								method: "post",
								headers: {
								  'content-type': 'application/json'
								},
								body: JSON.stringify({
								  merchantRef: merchantRef,
								  sessionHash: sessionHash
								})
							  }).then(res => {
								console.log("res from createOrder", res);
								return res.json()
							  }).then(data => {
								console.log("orderId from button : ", data?.orderId);
								if (data?.createOrder) return setOrderId(data?.orderId)
								return data.orderId;
							  }).catch(err => console.log(err))
						}}
					>
						<span style={{ "backgroundColor": "red" }}><PayPalHostedField
							id="card-number"
							hostedFieldType="number"
							options={{ selector: "#card-number" }}
						/></span>
						<span style={{ "backgroundColor": "blue" }}><PayPalHostedField
							id="cvv"
							hostedFieldType="cvv"
							options={{ selector: "#cvv" }}
						/></span>
						<span style={{ "backgroundColor": "violet" }}><PayPalHostedField
							id="expiration-date"
							hostedFieldType="expirationDate"
							options={{
								selector: "#expiration-date",
								placeholder: "MM/YY",
							}}
						/></span>
						<SubmitPayment merchantRefData={merchantRef} sessionHashData={sessionHash} orderId={orderId} />
					</PayPalHostedFieldsProvider>
				</PayPalScriptProvider> :
				<div>Loading.........</div>
			}
		</>
    );
}

tengkuzulfadli avatar Oct 20 '22 02:10 tengkuzulfadli

Hi @tengkuzulfadli, one thing I realized is you didn't return the orderId. Could you please update the createOrder callback and make it return the orderId?

createOrder={async () => {
	return await fetch(`${baseUrl}/api/Ppal/ReactApi/CreateOrder2`, {
		method: "post",
		headers: {
				'content-type': 'application/json'
		},
		body: JSON.stringify({
				merchantRef: merchantRef,
				sessionHash: sessionHash
		})
			}).then(res => {
		console.log("res from createOrder", res);
		return res.json()
		.then((data) => {
                    if (data?.createOrder) {
                      setOrderId(data?. orderId)
                     //need to return orderId
                      return data.orderId;
                    }
                }).catch(err => console.log(err))
   }}

xuanwan05 avatar Oct 20 '22 16:10 xuanwan05

Hey @xuanwan05 , thank you for pointing out. Yeah I missed that one and just updated to my code. I'm still getting the same error with invalid access token . I already checked our access token, it's all good. Is there anything that I'm missing right now?

tengkuzulfadli avatar Oct 20 '22 22:10 tengkuzulfadli

Hey @tengkuzulfadli, Could you please follow the steps and send me the screenshot?

  1. open browser inspect and click on Network tab
  2. find "fetch-client-token" which throws "invalid access token" error
  3. Click on the name
  4. Send me the screen shot for Headers and Payload the screen shots should look like: Screen Shot 2022-10-20 at 5 42 24 PM Screen Shot 2022-10-20 at 5 42 40 PM

xuanwan05 avatar Oct 20 '22 22:10 xuanwan05

@xuanwan05 I don't have the fetch-client-token but I do have confirm-payment-source of hosted-fields-payments-sdk-tokenization

headers payload Screenshot 2022-10-21 095807

tengkuzulfadli avatar Oct 20 '22 22:10 tengkuzulfadli

Hey @tengkuzulfadli, It seems like you didn't get the orderId correctly. Could you please add console.log(data.orderId) before the added row return data.orderId; ? I want to check what the orderId value is.

xuanwan05 avatar Oct 20 '22 23:10 xuanwan05

@xuanwan05 You're right. It didn't get the orderId on createOrder event. However, on SubmitPayment component, it is returning an orderId.

See below screenshots: Screenshot 2022-10-21 101428 Screenshot 2022-10-21 101510 Screenshot 2022-10-21 101532

tengkuzulfadli avatar Oct 20 '22 23:10 tengkuzulfadli

@xuanwan05 update from above comment, I placed a log before if statement on createOrder event, it is returning the same orderId, but it did not return orderId after if statement. Screenshot below:

Screenshot 2022-10-21 101907

I removed if statement , now it is returning the orderId but still getting the same error both in console and network

Screenshot 2022-10-21 102132

tengkuzulfadli avatar Oct 20 '22 23:10 tengkuzulfadli

@xuanwan05 further updates:

I changed from return order?.id to return order.id, now it's returning orderId but still having the same invalid token issue.

@gregjopa It seems like it's not firing hostedFields.cardFields.submit()

Screenshots below:

Screenshot 2022-10-21 103500 Screenshot 2022-10-21 103531 Screenshot 2022-10-21 103551

tengkuzulfadli avatar Oct 20 '22 23:10 tengkuzulfadli

@tengkuzulfadli Could you please send a screenshot of Preview tab which is next to Payload?

xuanwan05 avatar Oct 21 '22 00:10 xuanwan05

@xuanwan05 Please see attached screenshot. I think my bad, it is firing hostedFields.cardFields.submit() because it captured the cardholder details but it didn't call the ${baseUrl}/api/Ppal/ReactApi/CaptureOrder

Screenshot 2022-10-21 112800

tengkuzulfadli avatar Oct 21 '22 00:10 tengkuzulfadli

This is our access token call in postman

Screenshot 2022-10-21 114036

tengkuzulfadli avatar Oct 21 '22 00:10 tengkuzulfadli

Could you please check if Authorization from Request Headers has the same value as the one from postman? Screen Shot 2022-10-20 at 7 49 39 PM

xuanwan05 avatar Oct 21 '22 00:10 xuanwan05

@xuanwan05 Yeah now I'm getting where it went wrong. I have totally different authorization token.

But why is this happening? The clientId that I've got when preparing an order (That I've got from our back end) is the same as clientId that I have in postman.

tengkuzulfadli avatar Oct 21 '22 01:10 tengkuzulfadli

I just want to double check, Could you please

  1. From Postman, make the call one more time
  2. From web browser, fresh the page and click Pay button after fill the card fields and capture the Authorization value
  3. Compare the access tokens

I want to see if the access tokens are still different.

xuanwan05 avatar Oct 21 '22 01:10 xuanwan05

Yes, still different

tengkuzulfadli avatar Oct 21 '22 01:10 tengkuzulfadli

@xuanwan05 But in the request header, the path is v2 but in Postman, I have v1 . Are these related to capture a payment and access-token?

Screenshot 2022-10-21 122324

tengkuzulfadli avatar Oct 21 '22 01:10 tengkuzulfadli