简体   繁体   中英

react-stripe-elements Error: You must provide a Stripe Element or a valid token type to create a Token

I am using react-stripe-elements to create a token for payments. However, according to the documentation when the card form is wrapped in the Elements component it should automatically pickup which stripe elements to tokenize.

However, in this case we are presented with the error

You must provide a Stripe Element or a valid token type to create a Token.

Here is the code:

import React from 'react';
import {CardCVCElement, CardExpiryElement, CardNumberElement, PostalCodeElement, StripeProvider, Elements} from 'react-stripe-elements';

class CheckoutForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(ev) {
    ev.preventDefault();

    this.props.stripe.createToken({email: 'test@test.com'}).then(({token }) => {console.log('Received Stripe token:', token)});
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Card details
          <CardNumberElement />
          <CardExpiryElement />
          <CardCVCElement />
          <PostalCodeElement />
        </label>
        <button>Confirm order</button>
      </form>
    );
  }
}


class App extends React.Component {
  constructor() {
    super();
    this.state = { stripe: null };
  }

  componentDidMount() {
    this.setState({ stripe: window.Stripe('test_key') });
  }

  render() {
    return (
      <StripeProvider stripe={this.state.stripe}>
        <Elements>
          <CheckoutForm stripe={this.state.stripe} />
        </Elements>
      </StripeProvider>
    );
  }
}

export default App;

According to the documentation the following should be true:

'Within the context of Elements , this call to createToken knows which Element to tokenize, since there's only one in this group.'

However, this doesn't seem to be the case. I have also tried using the single 'Card Element' and have not found any success in doing so.

It turns out I never managed to solve the issue using react-stripe-elements. I ended using the standard JS version (from the stripe documentation). Here is my current working solution:

import React from 'react';

class CheckoutForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);

    this.state = {
      elements: null,
      card: null
    };
  }

  componentWillReceiveProps() {
    this.setState({ elements: this.props.stripe.elements() }, () => {
      this.setState({ card: this.state.elements.create('card') }, () => {
        this.state.card.mount('#card-element');
      });
    });
  }

  handleSubmit(ev) {
    ev.preventDefault();
    this.props.stripe.createToken(this.state.card).then((token) => {
      console.log('Received Stripe token:', token);
    });
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <div className="row">
          <label >
            Credit or debit card
          </label>
          <div id="card-element"/>
          <div id="card-errors" role="alert"/>
        </div>
        <button>Submit Payment</button>
      </form>
    );
  }
}

class App extends React.Component {
  constructor() {
    super();
    this.state = {stripe: window.Stripe('test_key')};
  }

  render() {
    return (
      <CheckoutForm stripe={this.state.stripe}/>
    );
  }
}

export default App;

In the comments they rightly say you need to use the HOC injectStripe .

Thedocs for stripe.createToken mention that you need to pass the element you wish to tokenize data from.

Also from the github repo README :

⚠️ NOTE injectStripe cannot be used on the same element that renders the Elements component; it must be used on the child component of Elements. injectStripe returns a wrapped component that needs to sit under but above any code where you'd like to access this.props.stripe.

In my specif case I was using a Mobx store and I needed to handle createToken and my form submission in the same place. Even though I had a reference to stripe since initialisation it didn't work. The createToken call needs to come from a component child of Elements and with stripe injected.

I ended up having:

@inject('signupStore')
@observer
class CardInput extends React.Component {
componentDidMount() {
    const { signupStore } = this.props;
    const handleCard = async name => {
        return await this.props.stripe.createToken({ name: name });
    };
    signupStore.assignHandleCard(handleCard);
}

render() {
    return (
        <label>
            Card details
            <CardElement style={{ base: { fontSize: '18px' } }} />
        </label>
    );
 }
}

export default injectStripe(CardInput);

Passing the handler back to the store, and then using it from there.

Part of signupStore :

@action
async submitForm(formValues) {
    if (this.stripe && this.handleCard) {
        const tokenResponse = await this.handleCard(
            `${formValues.firstName} ${formValues.lastName}`
        );

        runInAction(() => {
            console.log('Card token received ', tokenResponse);
            if (tokenResponse) {
                this.cardToken = tokenResponse.token.id;
                formValues.cardToken = this.cardToken;
            }
        });

        const response = await request.signup.submit(formValues);

        return response;
    }
    return null;
}

React js it's working for me Card component , Get error , Card Detail and Generate Token

import React, { useState, useEffect } from "react";
import {loadStripe} from '@stripe/stripe-js';
import {CardElement,Elements,useStripe,useElements} from '@stripe/react-stripe-js';
const stripePromise = loadStripe('pk_test_YOUR_STRIPE_KYE');
const CheckoutForm = () => {
const stripe = useStripe();
const elements = useElements();

const handleSubmit = async (event) => {
  event.preventDefault();
  const {error, paymentMethod} = await stripe.createPaymentMethod({
    type: 'card',
    card: elements.getElement(CardElement),
  });
    console.log("paymentMethod",paymentMethod);  
    console.log("error", error);
    if (paymentMethod) {
            const cardElement = elements.getElement(CardElement);
            let token  = await stripe.createToken(cardElement);
            console.log(token);
    }
};

return (
     
    <div>
    <form onSubmit={ handleSubmit }>
     <div className="login-box" id="step2"  >

              
             <div className="form-row">
                  <label for="card-element" style={ { color:" #76bbdf" } }>
                      Credit or debit card
                  </label>
              </div>
              
                  <div  >
                      <CardElement
                          className="StripeElement"
                            options={{
                                style: {
                                base: {
                                    fontSize: '16px',
                                    color: '#424770',
                                    '::placeholder': {
                                    color: '#aab7c4',
                                    },
                                },
                                invalid: {
                                    color: '#9e2146',
                                },
                                },
                            }}
                      />
                    </div>
                    <button name="submintbtn2" className="btn btn-primary"  > SUBSCRIBE </button>
                </div>
                </form>
    </div>
)};
const Registration = () => (
<div>
<Elements stripe={stripePromise}>
  <CheckoutForm />
    </Elements>
    </div>
); 
export default Registration;

With the new @stripe/react-stripe-js library it's a bit different. We need to use ElementsConsumer component. Load stripe using loadStripe method and use Elements component to use your form with Stripe.

Here is a basic example.

import { Elements, loadStripe } from "@stripe/react-stripe-js"

const stripePromise = loadStripe(STRIPEKEY)

<Elements stripe={stripePromise}>
   <CardForm />
</Elements>

CardForm.js

import {
  CardNumberElement,
  CardExpiryElement,
  CardCvcElement,
  ElementsConsumer,
} from "@stripe/react-stripe-js"

const StripeForm = ({ stripe, elements }) => {

  const handleSubmit = async () => {
    if (!stripe || !elements) {
      return
    }
    const cardNumberElement = elements.getElement(CardNumberElement)
    const res = await stripe.createToken(cardNumberElement)
  }

  return (
      <form>
        <div>
          <label htmlFor="cardNumber">Card Number</label>
          <div>
            <CardNumberElement />
          </div>
        </div>
        <div>
          <label htmlFor="cardName">Card Name</label>
          <input
            type="text"
            name="cardName"
            required
            placeholder="Please Enter"
            pattern="[A-Za-z]"
          />
        </div>
        <div>
          <label htmlFor="expDate">Exp. Date</label>
          <div>
            <CardExpiryElement />
          </div>
        </div>
        <div>
          <label htmlFor="CVC">CVC</label>
          <div>
            <CardCvcElement />
          </div>
        </div>
      </form>
  )
}

const CardForm = () => {
  return (
    <ElementsConsumer>
      {({ stripe, elements }) => (
        <StripeForm stripe={stripe} elements={elements} />
      )}
    </ElementsConsumer>
  )
}

export default CardForm

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