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: '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.

Sachin Karia
  • 547
  • 2
  • 8
  • 22
  • There's a missing `'` at the end of : `this.props.stripe.createToken({email: 'test@test.com` – Dyo Jan 30 '18 at 11:01
  • thanks - updated (same error) – Sachin Karia Jan 30 '18 at 11:02
  • 2
    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 props` – Dyo Jan 30 '18 at 11:14
  • 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. – Sachin Karia Jan 30 '18 at 11:16
  • 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 https://github.com/stripe/react-stripe-elements#getting-started. – Sia Mar 10 '18 at 19:32

4 Answers4

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;
Sachin Karia
  • 547
  • 2
  • 8
  • 22
  • @Sachin_Karia can you please share how you managed to get the card values from An Input and then payment Using the Info. – Eddy May 13 '20 at 10:38
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;
}
Uby
  • 41
  • 1
  • 3
  • I ended up doing something similar, but with React Context API (https://reactjs.org/docs/context.html) – asgeo1 Oct 10 '18 at 07:00
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
Surya K
  • 117
  • 2
  • 9
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;
manoj patel
  • 1,150
  • 12
  • 10