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

4

7

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: '[email protected]'}).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.

Aegis answered 30/1, 2018 at 10:50 Comment(5)
There's a missing ' at the end of : this.props.stripe.createToken({email: '[email protected]Accession
thanks - updated (same error)Aegis
Don't know much about react-stripe but it seems you need to use the HOC injectStripe(CheckoutForm) instead of passing stripe directly in ChekoutForm propsAccession
In this case I've passed the stripe object through the props manually, i think the result is the same (stripe has been loaded and the functions are present when printed in the console.Aegis
It works fine for me when I use the injectStripe method. I attempted to do something similar to you because I am using Redux, but I could not get it to work. I went back to using stripe only in the component below any connect() calls, but using the primary method in the readme github.com/stripe/react-stripe-elements#getting-started.Plemmons
A
4

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;
Aegis answered 30/1, 2018 at 16:13 Comment(1)
@Sachin_Karia can you please share how you managed to get the card values from An Input and then payment Using the Info.Alethiaaletta
S
4

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

The docs 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;
}
Struve answered 22/8, 2018 at 14:1 Comment(1)
I ended up doing something similar, but with React Context API (reactjs.org/docs/context.html)Sequent
D
2

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
Dispel answered 26/4, 2021 at 7:51 Comment(0)
C
1

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;
Capstone answered 24/3, 2021 at 17:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.