如何使用 Stripe 进行付款,并在 React 中使用 MateriaL UI 中内置的自定义付款表单

问题描述 投票:0回答:1

环境:我正在使用材料 ui 结帐模板参考:链接到 UI 和源代码:源代码 对于我的电子商务网站,其中在步骤 2:用户必须输入付款详细信息,在步骤 3:用户将能够查看购物车列表、送货地址、联系方式和付款详细信息。

条件:在第 3 步,当用户

Place Order
时,我想使用 Stripe 进行付款,并且为此付款详细信息使用 UseState 通过第 2 步。

问题:我不确定应该使用哪种 Stripe 付款方式?我尝试了confirmCardPayment,但它要求我没有使用CardElement

条件:我不想使用任何 Stripe 元素,例如支付元素/卡元素或任何按 stripe 预构建的结帐页面。

到目前为止,根据 Stripe 文档,我们可以使用

stripe.confirmCardPayment(paymentClientSecret,  {payment_method: {card: elements.getElement(`NOT SURE WHAT TO ADD HERE?`), billing_details: {`....`}},})
。但对于这个方法,我们必须通过 Stripe UI 添加 CardElement。

理解代码:

index.js

const stripe = require("stripe")([SECRET KEY GOES HERE]);
app.post("/payments/create", async (request, response) => {
  const total = request.query.total;
  console.log("payment request recieved", total);

  const paymentIntent = await stripe.paymentIntents.create({
    amount: total,
    currency: "usd",
  });

  response.status(201).send({
    clientSecret: paymentIntent.client_secret,
  });
});

应用程序.js

import { loadStripe } from "@stripe/stripe-js";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const promise = loadStripe(PUBLISH KEY GOES HERE)
function App(){

return (
<Router>
      <div className="app">
        <Switch>
          {... ALL OTHER ROUTE PATH GOES HERE....}
          <Route path="/checkout">
            <Elements stripe={promise}>
              <Checkout promise={promise} />
            </Elements>
          </Route>
         {... ALL OTHER ROUTE PATH GOES HERE....}
        </Switch>
      </div>
    </Router>)
}
export default App

结帐.js

import {
  Elements,
  PaymentElement,
  useElements,
  useStripe,
} from "@stripe/react-stripe-js";


import axios from "../../API/axios";
import { useHistory } from "react-router-dom";
const Checkout = ({
  submitShippingAddress,
  userUid,
  deleteDataFromDB,
  promise,
}) => {
const INITIAL_PAYMENT_FORM_STATE = {
    cardName: {
      value: "",
      error: false,
      errorMessage: "Please enter the full name on your card",
    },
    cardNumber: {
      value: "",
      error: false,
      errorMessage: "Please enter your card number",
    },
    expDate: {
      value: "",
      error: false,
      errorMessage: "Please enter the expiry date of your card",
    },
    cvv: {
      value: "",
      error: false,
      errorMessage: "Please enter your cvv",
    },
  };

const [paymentFormValues, setPaymentFormValues] = React.useState(
    INITIAL_PAYMENT_FORM_STATE
  );

const [productList, setProductList] = React.useState([]);
  const [totalAmount, setTotalAmount] = React.useState(0);
  const [cardType, setCardtype] = React.useState("");
  const [orderPlacing, setOrderPlacing] = React.useState(false);
  const [paymentClientSecret, setPaymentClientSecret] = React.useState("");

const [activeStep, setActiveStep] = React.useState(0);
const stripe = useStripe();
  const elements = useElements();
  const history = useHistory();

const handlePaymentFormChange = (e) => {
    let { name, value } = e.target;
    let error = true;
    value !== "" ? (error = false) : (error = true);
    if (name === "expDate") {
      value = value
        .replace(
          /[^0-9]/g,
          "" // To allow only numbers
        )
        .replace(
          /^([2-9])$/g,
          "0$1" // To handle 3 > 03
        )
        .replace(
          /^(1{1})([3-9]{1})$/g,
          "0$1/$2" // 13 > 01/3
        )
        .replace(
          /^0{1,}/g,
          "0" // To handle 00 > 0
        )
        .replace(
          /^([0-1]{1}[0-9]{1})([0-9]{1,2}).*/g,
          "$1/$2" // To handle 113 > 11/3
        );
    }
    setPaymentFormValues({
      ...paymentFormValues,
      [name]: {
        ...paymentFormValues[name],
        value,
        error,
      },
    });
  };

const handlePaymentFormSubmit = () => {
    const formFields = Object.keys(paymentFormValues);
    let newFormValues = { ...paymentFormValues }; // new State Values

    formFields.map((formField) => {
      const currentField = formField;
      const currentValue = paymentFormValues[currentField].value;

      if (
        currentValue === "" ||
        (currentField === "cardNumber" &&
          !handleCardNumberValidations(currentValue)) ||
        (currentField === "expDate" &&
          !handleExpDateValidation(currentValue)) ||
        (currentField === "cvv" && !handleCvvValidation(currentValue))
      ) {
        newFormValues = {
          ...newFormValues,
          [currentField]: {
            ...newFormValues[currentField],
            error: true,
          },
        };
      }
    });

    setPaymentFormValues(newFormValues);

    let formValidate = Object.values(newFormValues).every(
      (element) => element.error === false
    );
    
    if (formValidate) {
      setActiveStep(activeStep + 1);
    }
  };


const placeOrder = async () => {
    setOrderPlacing(true);
    if (paymentClientSecret !== undefined) {
      await axios({
        method: "post",
        url: `/payments/create?total=${Math.round(totalAmount * 100, 2)}`,
      }).then((res) => {
        // Which Payment METHOD SHALL I USE?
        stripe
          .confirmCardPayment(res.data.clientSecret, {
            payment_method: {
              card: "WHAT SHOULD GO HERE INSTEAD ANY CARD/PAYMENT ELEMENT",
              billing_details: {.... PAYMENT DETAILS....},
            },
          })
          .then(() => {
            //payment confirmation
            setOrderPlacing(false);
            deleteDataFromDB();
            history.replace("/home");
          })
          .catch((err) => console.log(err));
        setPaymentClientSecret(res.data.clientSecret); // its asynchronous
      });
      console.log("payment Client Secret in Place Order", paymentClientSecret);
    }
  };

function getStepContent(step) {
    switch (step) {
      case 0:
        return (
          <AddressForm
            shippingFormValues={shippingFormValues}
            handleShippingFormChange={handleShippingFormChange}
          />
        );
      case 1:
        return (
            <PaymentForm
              paymentFormValues={paymentFormValues}
              handlePaymentFormChange={handlePaymentFormChange}
            />
        );
      case 2:
        return (
          <Review
            totalAmount={totalAmount}
            productList={productList}
            paymentFormValues={paymentFormValues}
            shippingFormValues={shippingFormValues}
            cardType={cardType}
          />
        );
      default:
        throw new Error("Unknown step");
    }
  }

const handleNext = async (e) => {
    if (activeStep === 0) {
      //validate shipping Address and Submit
      e.preventDefault();
      await handleShippingFormSubmit();
    } else if (activeStep === 1) {
      handlePaymentFormSubmit();
    } else if (activeStep === 2) {
      await placeOrder();
    }
  };

  const handleBack = () => {
    setActiveStep(activeStep - 1);
  };


return (
<Container component="main" maxWidth="md" sx={{ mb: 4 }}>
        <Paper
          variant="outlined"
          sx={{ my: { xs: 3, md: 6 }, p: { xs: 2, md: 3 } }}
        >
          <Typography component="h1" variant="h4" align="center">
            Checkout
          </Typography>
          <Stepper activeStep={activeStep} sx={{ pt: 3, pb: 5 }}>
            {steps.map((label) => (
              <Step key={label}>
                <StepLabel>{label}</StepLabel>
              </Step>
            ))}
          </Stepper>
            <React.Fragment>
              {getStepContent(activeStep)}
              <Box sx={{ display: "flex", justifyContent: "flex-end" }}>
                {activeStep !== 0 && (
                  <Button onClick={handleBack} sx={{ mt: 3, ml: 1 }}>
                    Back
                  </Button>
                )}

                <Button
                  disabled={orderPlacing}
                  variant="contained"
                  onClick={(e) => handleNext(e)}
                  sx={{ mt: 3, ml: 1 }}
                >
                  {activeStep === steps.length - 1 ? "Place order" : "Next"}
                </Button>
              </Box>
            </React.Fragment>
        </Paper>
        <Copyright />
      </Container>
)
}

export default Checkout;

PaymentForm.js

export default function PaymentForm({
  paymentFormValues,
  handlePaymentFormChange,
}) {
  return (
    <React.Fragment>
      <Typography variant="h6" gutterBottom>
        Payment method
      </Typography>
      <form onChange={(e) => handlePaymentFormChange(e)}>
        <Grid container spacing={3}>
          <Grid item xs={12} md={6}>
            <TextField
              required
              id="cardName"
              name="cardName"
              label="Name on card"
              fullWidth
              variant="standard"
              error={paymentFormValues.cardName.error}
              value={
                paymentFormValues.cardName.value
                  ? paymentFormValues.cardName.value
                  : ""
              }
            />
          </Grid>
          <Grid item xs={12} md={6}>
            <TextField
              required
              type="number"
              id="cardNumber"
              name="cardNumber"
              label="Card number"
              fullWidth
              helperText="ex: 4242424242424242 for Testing"
              error={paymentFormValues.cardNumber.error}
              value={
                paymentFormValues.cardNumber.value
                  ? paymentFormValues.cardNumber.value
                  : ""
              }
              sx={{
                "input[type='number']::-webkit-outer-spin-button": {
                  WebkitAppearance: "none",
                },
                "input[type=number]::-webkit-inner-spin-button": {
                  WebkitAppearance: "none",
                },
                "input[type='number']": { MozAppearance: "textfield" },
              }}
              variant="standard"
            />
          </Grid>
          <Grid item xs={12} md={6}>
            <TextField
              required
              id="expDate"
              name="expDate"
              label="Expiry date"
              fullWidth
              helperText="MM/YY"
              //onChange={(e) => handleExpDateValidation(e.target.value)}
              error={paymentFormValues.expDate.error}
              value={
                paymentFormValues.expDate.value
                  ? paymentFormValues.expDate.value
                  : ""
              }
              variant="standard"
            />
          </Grid>
          <Grid item xs={12} md={6}>
            <TextField
              required
              id="cvv"
              name="cvv"
              label="CVV"
              sx={{
                "input[type='number']::-webkit-outer-spin-button": {
                  WebkitAppearance: "none",
                },
                "input[type=number]::-webkit-inner-spin-button": {
                  WebkitAppearance: "none",
                },
                "input[type='number']": { MozAppearance: "textfield" },
              }}
              helperText="Last three digits on signature strip"
              fullWidth
              error={paymentFormValues.cvv.error}
              value={
                paymentFormValues.cvv.value ? paymentFormValues.cvv.value : ""
              }
              variant="standard"
              type="password"
            />
          </Grid>
        </Grid>
      </form>
    </React.Fragment>
  );
}

Review.js

Note: This Component only shows the ProductList, Shipping Details, Payment Details in Exact form shown in template, Where as the Place Order button is in checkout.js and it handles the functionality in PlaceOrder function

Note: Since these files are pretty big therefore I have shown only the relevant code for Step 2 and Step 3, I have not mention the Validation functions in this code as there sole purpose was to check if input is validate or not!

对此的任何建议或解决方案都非常棒!谢谢你。

reactjs material-ui stripe-payments payment-gateway stripe-payment-intent
1个回答
0
投票

最短的方法:使用 stripe sdk v2,您需要将脚本包含在组件中并从窗口范围内使用它。

Stripe sdk v3 不支持没有其元素的令牌生成。

© www.soinside.com 2019 - 2024. All rights reserved.