Stripe PaymentElement 的集成。警告 不支持的属性更改:options.clientSecret 不是可变属性?

Dra*_*era 1 javascript stripe-payments typescript reactjs

我有 PaymentMethodPage,如下所示:

import React, { FC, useState } from 'react';
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import {
  getClientSecret,
} from '../../api/PaymentCalls/PaymentCalls';
import PaymentMethodSetupForm from './PaymentMethodSetupForm/PaymentMethodSetupForm';
import AddPaymentMethodContainer from './AddPaymentMethodContainer/AddPaymentMethodContainer';
import {
  ClientSecretResponse,
} from '../../interfaces/paymentMethod.interface';


const stripePromise = loadStripe(
  process.env.REACT_APP_STRIPE_PUBLIC_KEY as string
);

const PaymentMethodPage: FC = () => {
  const [enableAddPaymentMethod, setEnableAddPaymentMethod] =
    useState<boolean>(false);

  const {
    data: clientSecretKeyData,
  }: UseQueryResult<ClientSecretResponse, ExtendedError> = useQuery<
    ClientSecretResponse,
    ExtendedError
  >('getClientSecret', getClientSecret, {
    retry: false,
    refetchOnWindowFocus: false,
    enabled: enableAddPaymentMethod,
    onError: (error: ExtendedError) => {
      if (error) {
        console.log(error)
      }
    },
  });

  const options = {
    clientSecret: clientSecretKeyData?.client_secret,
  };

  const addPaymentMethodHandler = (): void => {
    setEnableAddPaymentMethod(!enableAddPaymentMethod);
  };

  
  return (
    <AppDrawer onLogOut={logoutHandler}>

          {paymentMethodData.default_payment_method !== null ? (
            <Box>
              <Typography>
                Payment Method
              </Typography>
            </Box>
          ) : (
            <Box>
              <AddPaymentMethodContainer
                addButtonHandler={addPaymentMethodHandler}
              />
            </Box>
            {clientSecretKeyData && (
              <Elements stripe={stripePromise} options={options}>
                <PaymentMethodSetupForm
                  isOpenPaymentDialog={enableAddPaymentMethod}
                  handleClose={addPaymentMethodHandler}
                  setPaymentDialogOpen={addPaymentMethodHandler}
                />
              </Elements>
            )}
    </AppDrawer>
  );
};

export default PaymentMethodPage;

Run Code Online (Sandbox Code Playgroud)

如果没有付款方式,我将呈现 AddPaymentMethodContainer。看起来像这样:

import React, { FC } from 'react';
import { Box, Typography, Button } from '@mui/material';

interface AddPaymentMethodContainerProps {
  addButtonHandler: () => void;
}

const AddPaymentMethodContainer: FC<AddPaymentMethodContainerProps> = ({
  addButtonHandler,
}) => {
  return (
    <Box >
      <Box >
        <img
          src={someImage}
          alt="no-payment-method"
        />
      </Box>
      <Typography>
        No payment method found. Click the button below to provide one.
      </Typography>
      <Box sx={styles.addButtonContainer}>
        <Button
          sx={styles.addButton}
          variant="contained"
          onClick={addButtonHandler}
        >
          Add Payment
        </Button>
      </Box>
    </Box>
  );
};

export default AddPaymentMethodContainer;

Run Code Online (Sandbox Code Playgroud)

单击添加按钮后,我将呈现付款表单,其中包含 Stipe PaymentElement...该组件如下所示:

import React, { FC, useState } from 'react';
import {
  PaymentElement,
  useStripe,
  useElements,
} from '@stripe/react-stripe-js';
import { useMutation, UseMutationResult, useQueryClient } from 'react-query';
import { Dialog, DialogTitle, DialogContent } from '@mui/material';

interface PaymentMethodSetupFormProps {
  isOpenPaymentDialog: boolean;
  handleClose: () => void;
  setPaymentDialogOpen: (e: boolean) => void;
}

const PaymentMethodSetupForm: FC<PaymentMethodSetupFormProps> = ({
  isOpenPaymentDialog,
  setPaymentDialogOpen,
  handleClose,
}) => {
  const stripe = useStripe();
  const elements = useElements();
  const [isLoading, setIsLoading] = useState<boolean>(false);


  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
  const handleSubmit = async (event: React.SyntheticEvent) => {
    event.preventDefault();

    if (!stripe || !elements) {
      // eslint-disable-next-line no-useless-return
      return;
    }
    setIsLoading(true);

    const result = await stripe.confirmSetup({
      elements,
      confirmParams: {
        return_url: 'http://localhost:3000/payment-status/',
      },
      redirect: 'if_required',
    });

    if (result.error) {
      console.log(`${result.error.message}`)
    } else {
      console.log('All okay!')
      setPaymentDialogOpen(false);
    }

    setIsLoading(false);
  };

  return (
    <Dialog
      open={isOpenPaymentDialog}
      onClose={handleClose}
    >
      <DialogTitle>
        Payment Methods:
      </DialogTitle>
      <DialogContent>
        <form onSubmit={handleSubmit}>
          <PaymentElement />
          <CustomSubmitButton
            cypressId="credit-card-submit-button"
            isLoading={isLoading}
            text="Save"
            style={{ margin: '2rem auto', fontWeight: 'bolder' }}
            disabled={isLoading}
          />
        </form>
      </DialogContent>
    </Dialog>
  );
};

export default PaymentMethodSetupForm;

Run Code Online (Sandbox Code Playgroud)

这里我禁用了handleSubmit的函数返回类型,因为我不知道应该是什么类型......它是一个Promise<>,所以如果有人有任何想法(请帮助)。一般来说,一切工作正常,我不喜欢的是当我单击“ADD”按钮打开表单并关闭它时,在按下按钮的第二次尝试时,控制台中出现警告:

Unsupported prop change: options.clientSecret is not a mutable property.
Run Code Online (Sandbox Code Playgroud)

此外,还有关于 PaymentElement 内图像的警告(用于卡号):

Each dictionary in the list "icons" should contain a non-empty UTF8 string field "type".
Run Code Online (Sandbox Code Playgroud)

最后一件事,非常烦人。根据他们的文档,我可以禁用表单中的提交/保存/付款按钮,如下所示:

return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button disabled={!stripe}>Submit</button>
      {/* Show error message to your customers */}
      {errorMessage && <div>{errorMessage}</div>}
    </form>
  )

Run Code Online (Sandbox Code Playgroud)

但看起来这个物体总是真实的。我试着把它做成这样:

return (
    <form onSubmit={handleSubmit}>
     {!stripe && (
      <PaymentElement />
      <button>Submit</button>
      {/* Show error message to your customers */}
      {errorMessage && <div>{errorMessage}</div>}
      )}
    </form>
  )
Run Code Online (Sandbox Code Playgroud)

因为当我可以在条纹可用时渲染它时,为什么我应该禁用它。但看起来它不是那样工作的。提交按钮始终存在,突然出现付款表格......这不太好。作为用户,我有一个按钮,有时会显示表单。好吧,不需要那么多时间,但在我看来,这并不酷。

Vla*_*nin 9

添加key={clientSecret}<Elements>组件。
也可以使用更新 PaymentIntent确认 PaymentIntent


Stripe 团队对此是这么说的:

我们目前不支持更新 clientSecret 属性,任何尝试的更新都将被忽略。这是尝试更新 clientSecret 时记录到控制台的警告:

Unsupported prop change: options.clientSecret is not a mutable property. 
Run Code Online (Sandbox Code Playgroud)

不过,我们确实有一些正在进行的工作来解决您的具体用例(创建后更新 PaymentIntent 上的金额)。同时,一个可能的解决方法是,当客户端密钥发生变化时,通过更新提供程序的密钥来重新安装提供程序并进行相应的操作:

-<Elements options={options} stripe={stripePromise}>
+<Elements options={options} stripe={stripePromise} key={clientSecret}>
Run Code Online (Sandbox Code Playgroud)

https://githubhot.com/repo/stripe/react-stripe-js/issues/246