stripe-archive / react-stripe-elements

Moved to stripe/react-stripe-js.
https://github.com/stripe/react-stripe-js
MIT License
3.03k stars 319 forks source link

PaymentRequestButtonElement: Payment Request "token" event triggering handler multiple times #526

Closed caretapps-developer closed 4 years ago

caretapps-developer commented 4 years ago

Summary

My requirement is, as the user updates amount, i would like to update paymentRequest and fire "token" created event only once after Apple Pay has been processed.

What I am experiencing is, when i change the amount I update the paymentRequest and the "token" event handler. As a result Multiple "token" event handlers are registered. All of them fire when Apple Pay has been processed.

import React, {useState, useEffect} from 'react';
import {PaymentRequestButtonElement} from '@stripe/react-stripe-js';

const StripePaymentRequestButton = (props) => {
  const {stripe, amount, label, handlePaymentMethodReceived} = props;
  const [paymentRequest, setPaymentRequest] = useState(null);
  const [counter, setCounter] = useState(1)

  useEffect(() => {
        if (stripe) {
          const pr = stripe.paymentRequest({
            country: 'US',
            currency: 'usd',
            total: {
              label: "Goodbricks",
              amount: 100,
            },
            requestPayerName: true,
            requestPayerEmail: true,
            requestShipping: true,
            requestPayerPhone: true,
            shippingOptions: [{
              id: 'basic',
              label: 'Receipt delivery by email',
              amount: 0,
            }]
          });

          // Check the availability of the Payment Request API.
          pr.canMakePayment().then(result => {
            if (result) {
              setPaymentRequest(pr);
            }
          });
        }
      }, [stripe]
  );

  useEffect(() => {
        if (paymentRequest) {
          paymentRequest.update({
            total: {
              label: label,
              amount: amount*100,
            }
          });
          paymentRequest.on('token', ( (event) => {
            handlePaymentMethodReceived(event, counter)
          }));
          console.log(counter)
          setCounter(counter + 1)
        }
      }, [amount]
  );

  if (paymentRequest) {
    return <PaymentRequestButtonElement options={{paymentRequest}}/>
  }
  return <div/>;
}

export default StripePaymentRequestButton;
....  PaymentForm.jsx
const handlePaymentMethodReceived = async (event, counter) => {
    await event.complete("success")
    console.log("Counter", counter)
}
....
<StripePaymentRequestButton stripe={stripe} amount={state.categoryAmount} label={organization.name} handlePaymentMethodReceived={handlePaymentMethodReceived} />
...

As state.categoryAmount changes from 10, 20, 30, 40, 50 there are 5 handlePaymentMethodReceived event handlers registered as a result paymentRequest.on('token', ...)

Is this expected behavior. I would like to have only the latest listener triggered. I am making a payment transaction call once the token is generated. Right the payment api is getting invoked 5 times.

petrogad commented 4 years ago

Looking at your code, my thought is that paymentRequest.on is triggering every time you change your amount. I'd move that outside of the scope of your useEffect for the amount and see if the behavior changes.

caretapps-developer commented 4 years ago

You are correct. I had it outside the scope of useEffect for the amount initially.


  useEffect(() => {
        if (stripe) {
          const pr = stripe.paymentRequest({
            country: 'US',
            currency: 'usd',
            total: {
              label: "Goodbricks",
              amount: 100,
            },
            requestPayerName: true,
            requestPayerEmail: true,
            requestShipping: true,
            requestPayerPhone: true,
            shippingOptions: [{
              id: 'basic',
              label: 'Receipt delivery by email',
              amount: 0,
            }]
          });

          // Check the availability of the Payment Request API.
          pr.canMakePayment().then(result => {
            if (result) {
              setPaymentRequest(pr);
              pr.on('token', ((event) => {
                handlePaymentMethodReceived(event)
              }));
            }
          });
        }
      }, [stripe]
  );

However, with that code, As the state/amount changes, the paymentRequest.on "token" only returns the amount from the initial render. For Ex. As the amount changes from 10, 20, 30, 40, 50, paymentRequest.on "token" when triggered has 10 as the amount and not 50.

I would like to know what was the amount when the token was generated and then use that amount to trigger a server side transaction.

caretapps-developer commented 4 years ago

Any updates? @petrogad

caretapps-developer commented 4 years ago

Is anyone looking into this? It's been 10 days now.

christopher-stripe commented 4 years ago

Hey @caretapps-developer, try unbinding the token event listener by returning a function to clean up the effect. Something like this:

useEffect(() => {
  if (paymentRequest) {
    paymentRequest.update({
      total: {
        label: label,
        amount: amount * 100,
      },
    });
    paymentRequest.on('token', (event) => {
      handlePaymentMethodReceived(event, counter);
    });
    console.log(counter);
    setCounter(counter + 1);
  }

  // Return a function to clean up the last effect
  return () => {
    if (paymentRequest) {
      paymentRequest.off('token');
    }
  };
}, [amount]);

In the future, Stripe support is a great place to get help with integration questions like these.

caretapps-developer commented 4 years ago

It worked. Thank you very much. I will keep Stripe Support in mind next time. Is paymentRequest.off('token'); new, i wasn't able to find it in the documentation. Is there an api reference that I not aware of ?

christopher-stripe commented 4 years ago

Is paymentRequest.off('token'); new, i wasn't able to find it in the documentation. Is there an api reference that I not aware of ?

Sadly no, we have an outstanding task to add it to our docs 😞 I'll fix that soon.

Glad to hear it's working now.