简体   繁体   中英

Creating test payment gateway with react and stripe

Hey i am working on payment gateway on my website maded with react so i am using stripe js (for test) i have wraped my payment component in Elements in app.js

const promise = loadStripe("my stipe publishable key")

<Route path='/payment'>
          <Header/>
          <Elements stripe={promise}>
          <Payment/>
          </Elements>
        </Route>

and my code in payment component is

import React, { useEffect, useState } from 'react';
import CheckoutProduct from './CheckoutProduct';
import "./Payment.css";
import { useStateValue } from "./StateProvider";
import { Link, useHistory } from "react-router-dom";
import { CardElement, useStripe, useElements } from "@stripe/react-stripe-js";
import CurrencyFormat from "react-currency-format";
import { getBasketTotal } from './Reducer';
import axios from "./axios"

function Payment() {
    const [{ basket, user }, dispatch] = useStateValue();
    const history = useHistory();

    const stripe = useStripe();
    const elements = useElements();

    const [succeeded, setSucceeded] = useState(false);
    const [processing, setProcessing] = useState("");
    const [error, setError] = useState(null);
    const [disabled, setDisabled] = useState(true);
    const [clientSecret, setClientSecret] = useState(true);

    useEffect(() => {
        // generate the special stripe secret which allows us to charge a customer

        const getClientSecret = async () => {
            const response = await axios({
                method: 'post',
                url: `/payment/create?total=${getBasketTotal(basket) * 100}`
            });
            setClientSecret(response.data.clientSecret)
        }

        getClientSecret();
    }, [basket])

    console.log('THE SECRET IS >>>', clientSecret)

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

        const payload = await stripe.confirmCardPayment(clientSecret, {
            payment_method: {
                card: elements.getElement(CardElement)
            }
        }).then(({ paymentIntent }) => {
            //paymentIntent = Payment confirmation

            setSucceeded(true);
            setError(null)
            setProcessing(false)

            history.replace('/order')
        })

    };

    const handleChange = event => {
        // This will listen for changes in the CardElement
        // and then display any errors as the customer types their card details
        setDisabled(event.empty);
        setError(event.error ? event.error.message : "");
    }

    return (
        <div className="payment">
            <div className="payment__container">
                <h1>
                    Checkout (<Link to="/checkout">{basket?.length} items</Link>)
                </h1>
                {/* Payment section - delivery address*/}
                <div className="payment__section">
                    <div className="payment__title">
                        <h1>Delivery address</h1>
                    </div>
                    <div className="payment__address">
                        <p>{user?.email}</p>
                        <p>123 React Lane</p>
                        <p>Los Angeles, CA</p>
                    </div>
                </div>

                {/* Payment section - Review Items */}
                <div className="payment__section">
                    <div className="payment__title">
                        <h1>Review items and delivery</h1>
                    </div>
                    <div className="payment__items">
                        {basket.map(item => (
                            <CheckoutProduct
                                id={item.id}
                                title={item.title}
                                image={item.image}
                                price={item.price}
                                rating={item.rating}
                            />
                        ))}
                    </div>
                </div>

                {/* Payment section - Payment method */}
                <div className="payment__section">
                    <div className="payment__title">
                        <h3>Payment Method</h3>
                    </div>
                    <div className="payment__details">
                        <form onSubmit={handleSubmit}>
                            <CardElement onChange={handleChange} />

                            <div className="payment__priceContainer">
                                <CurrencyFormat
                                    renderText={(value) => (
                                        <>
                                            <h3>Order Total: {value}</h3>
                                        </>
                                    )}
                                    decimalScale={2}
                                    value={getBasketTotal(basket)}
                                    displayType={"text"}
                                    thousandSeparator={true}
                                    prefix={"$"}
                                />
                                <button disabled={processing || disabled ||succeeded}>
                                    <span>{processing ? <p>Processing</p> :"Buy Now" }</span>
                                </button>
                            </div>

                            {error && <div>{error}</div>}
                        </form>
                    </div>
                </div>

            </div>
        </div>
    )
}

export default Payment

and i am using firebase hosting as backend so my running command firebase init a folder is created named function ("because i selected function option after running firebase init")and for axios i have written this code in my axios.js file

import axios from "axios";

const instance = axios.create({
    baseURL: "http://localhost:5001/*****/******/api"
});

export default instance;

and baseURL used here i get by writing some code in index.js in function folder created by running firebase init the code i wrote in index.js is

const functions = require('firebase-functions');
const express = require("express");
const cors = require("cors");
const stripe = require("stripe")('my stripe secret key here')

//API

//App config
const app = express();

//Middlewares
app.use(cors({origin: true}));
app.use(express.json());

//API routes
app.get('/', (request, response) => response.status(200).send('hello world'))

app.post('/payment/create', async(request, response) => {
    const total = request.query.total;

    console.log('Payment Request Recieved BOOM!!! for this amount', total)

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

    //OK Created
    response.status(201).send({
        clientSecret: paymentIntent.client_secret,
    })
})

//Listen command
exports.api = functions.https.onRequest(app)

from here i got my api that is use as base url in terminal functions[api]: http function initialized (http://localhost:5001/abcdabcdabcd/abcabcd/api).

now all thing is setup i got on my website running on local host then when i go to /payment then is console it is comming

THE SECRET IS >>> true
THE SECRET IS >>> true
THE SECRET IS >>> true
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg

and whenever i enter 424242.... (used to test in stripe js) so whenever i type first 42 then my THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg is repeating 3 times i have no problem with this but when i click on buy now button yes it is redirecting me to /orders page which i have not maded so it redirecting me on home page but when i check my account on stripe js (test account)no test money is added there and in my console it is comming:-

THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg 
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg 
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg
**shared-a0382af03b7a06522c9bc0f5c75b552d.js:1 POST https://api.stripe.com/v1/payment_intents/pi_fgdhfdgsdfgfg/confirm 400***
(anonymous) @ shared-a0382af03b7a06522c9bc0f5c75b552d.js:1
f @ shared-a0382af03b7a06522c9bc0f5c75b552d.js:1
Q @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
$ @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
rt @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
(anonymous) @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
Promise.then (async)
confirmPaymentIntent @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
(anonymous) @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
(anonymous) @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
Gt._respondUsingPromise @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
Gt.handleAction @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
value @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
(anonymous) @ controller-207a0a1b39a190f1e22c08ab25c9f3ee.js:1
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg
THE SECRET IS >>> pi_fgdhfdgsdfgfg_secret_fgsdgfdgdfgdg

please tell me how to solve this

A few things to consider here:

  1. By putting your call to /payment/create in your useEffect hook, you are creating a new PaymentIntent every time your component updates. This is quite inefficient and will leave you with many unused PaymentIntents, cluttering up your Stripe account. Instead you should only create the PaymentIntent when your user intends to purchase something, like when they click the "buy" button.

  2. You are passing in the total amount to be charged from the client. This means that it is trivial for a malicious user to add many things to their basket and then edit that request to ensure that they are charged a much lower amount than you expect. All logic pertaining to calculating amount totals should be done on the server, not the client.

  3. Your server logs don't show any failure in actual payments. Since you are confirming on the client, it's possible that you are getting an error there but redirecting before you see the error. You should listen for the error object instead of immediately redirecting:

stripe.confirmCardPayment(clientSecret, {
  payment_method: {
    card: elements.getElement(CardElement)
  }
}).then((result) => {
  if (result.error) {
    // payment failed, do something with the error
    console.log(result.error.message);
  } else {
    setSucceeded(true);
    setError(null)
    setProcessing(false)
    history.replace('/order')
});

You can also inspect your Stripe logs by looking at your dashboard: https://dashboard.stripe.com/test/logs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM