I got this error message when I run this.props.stripe.createToken on redux form.
inject.js:53 Uncaught Error: You did not specify the type of Source or Token to create.
We could not infer which Element you want to use for this operation.
Hi there! How are you setting up your component tree? Is there a wrapping
Thank you for your help.
fetchCards pulls users credit cards list and create the select box users can select one of cards from existing cards in database.
There are radio button users can select "existing" or "add new".
If users click "add new", I render
I got this error when I click the submit button then when I click the button again, it succeeds.
deposit.js
return (
<StripeProvider apiKey="xxxxxxxxxxxx">
<Elements>
<DepositElements />
</Elements>
</StripeProvider>
);
deposit_element.js
import { fetchCards, submitPurchase } from '../actions/account';
class DepositElements extends Component {
componentWillMount(){
this.props.fetchCards();
}
onSubmit = (values) => {
createStripeToken(values){
this.props.stripe.createToken({type: 'card', name: values.card_name}).then(({token}) => {
this.props.submitPurchase(Object.assign(values, token), this.callBack);
}
}
render(){
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
......
<label><Field name="card_type" component="input" type="radio" value="existing" /> Use existing</label>
<label><Field name="card_type" component="input" type="radio" value="new" /> Add New</label>
......
<Field className="select-field" name="card_id" component={renderSelectField} label="Credit Card">
{this.props.cards.data.cards.map((card) =>
<MenuItem key={card.id} value={card.id} primaryText={`${card.card_name} (${card.card_brand} ending in ${card.card_last4})`} />
)}
</Field>
......
<Field
name="card_name"
component="input"
type="text"
placeholder="Name on card"
/>
<CardElement style={{base: {fontSize: '18px'}}} />
<button className="button button--primary" >Contine</button>
</form>
......
}
}
function mapStateToProps(state){
return {
cards: state.cards, // response from fetchCards()
form: state.form,
};
}
export default reduxForm({
form: 'DepositForm',
errors: []
})(
connect(mapStateToProps, { fetchCards, submitPurchase })(injectStripe(DepositElements))
);
I also get this error on the server side.
You cannot use a Stripe token more than once: tok_1An1keHVN1dQlS3mGxnaHdzw.
@hurano You cannot use a Stripe token more than once means that your backend code is using the token more than once, it's unrelated to this Redux/React issue. If you have more questions about that please contact Stripe Support and we can help you further: https://support.stripe.com/email/login
I'm not sure what's going on with the Redux issue but I am curious to see what turns up!
It seems like there are some issue with connect not Redux form. I think its because of too much rendering. If I remove connect, there is no error on frontend and backend. Thanks.
@hurano how do you actually map the elements states to props?
I was able to dig in further, and I finally have a page that reproduces the error! So—progress. What seems to be happening is that both the Redux HOC and the Stripe HOC want to configure things in the React context for the component, however, whichever component is the outermost "wins". For example, if you do this:
injectStripe(connect()(YourComponent))
The Stripe context (which keeps track of the elements) will be available in the wrapped version of YourComponent.
If instead you do this:
connect()(injectStripe(YourComponent))
The Redux context (which is used to pass the Redux store into connect()d components) will be used instead. If Redux's context is the "outer" HOC, then the Stripe-created HOC will lose track of the <Elements> components registered with it.
There doesn't seem to be any merging of the wrapped component's contextTypes, and React contexts only share the data that you explicitly ask for.[1]
I'm not sure what the solution is here, but now we know what's happening and how to reproduce. I'll continue research on this end, as I'm not sure if it's a Stripe bug, a Redux bug, or a bug with how React contexts + HOC work (there are some issues on the React repo that suggest this may be the case).
[1] https://facebook.github.io/react/docs/context.html#how-to-use-context
See the very last sentence here: "If contextTypes is not defined, then context will be an empty object."
Interesting, this could indeed be the issue I was having with redux-form as I found changing the order did casue Stripe to work, but then the form couldn't access the Redux state.
I added an explanation for this issue to the readme:
https://github.com/stripe/react-stripe-elements#troubleshooting
Please let me know if the suggested solutions do not solve your problems.
Hi @cweiss-stripe, @fred-stripe
I am still facing an issue with the Error: You did not specify the type of Source or Token to create.We could not infer which Element you want to use for this operation.
I have a CardAdd component that is a simple form with just one element:
import React, { Component } from 'react';
import { injectStripe } from 'react-stripe-elements';
class CardAdd extends Component {
constructor(props) {
super(props);
this.state = {input: ''};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleFormSubmit(event, values) {
event.preventDefault();
const promise = this.props.stripe.createToken({type: 'card', name: 'sample'});
promise.then((response) => {
console.log('response', response);
}).catch((err) => {
console.log('error', err);
});
}
handleInputChange(event) {
this.setState({
input: event.target.value
});
}
render() {
// const { handleSubmit } = this.props;
return (
<div>
<form className="add-card-form" onSubmit={this.handleFormSubmit}>
<a className="closeBtn" onClick={() => this.props.closeModal()}><span className="icon icon-close">×</span></a>
<div className="sbs-row">
<div className="sbs-col-12">
<input type="number" name="credit_card[number]" label="Card Number" placeholder="Your Card Number" onChange={this.handleInputChange} />
</div>
</div>
<button type="submit">Add</button>
</form>
</div>
);
}
}
export default injectStripe(CardAdd);
I have a CardList component that basically mounts the CardAdd component as a collapsible component.
import React, { Component } from 'react';
import { Elements, StripeProvider } from 'react-stripe-elements';
import AddCard from './add';
import SECRETS from '../../../constants/app-secrets';
class CardList extends Component {
constructor(props) {
super(props);
this.state = {
showAddModal: false
};
this.openAddModal = this.openAddModal.bind(this);
this.closeAddModal = this.closeAddModal.bind(this);
}
openAddModal() {
this.setState({
showAddModal: true
});
}
closeAddModal() {
this.setState({
showAddModal: false
});
}
render() {
return (
<div className="card-list">
<button className="add-card-btn" onClick={this.openAddModal}>Add Card</button>
{ this.state.showAddModal && <Elements><AddCard closeModal={this.closeAddModal} /></Elements>}
<div className="sbs-row">
<div className="sbs-col-12">
Here you can see the list of all cards.
</div>
</div>
</div>
);
}
}
export default CardList;
My App Component where i attach the provider:
class App extends Component {
render() {
return (
<StripeProvider apiKey={SECRETS.stripeApiKey}>
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<div>
{configRoutes()}
</div>
</BrowserRouter>
</Provider>
</StripeProvider>
);
}
}
On filling the values and submitting it (using the a sample value gives me the error mentioned above).
Initially, this was implemented with redux-form, i just tried to test it on a simple form ( thought redux form might be doing something to it) but it's still gives the same error.
Version : 1.4.1,
Browser: Chrome
OS: Mac OS Sierra
The possible solutions in the Troubleshooting section did not work for me. Instead, I instantiated my stripe provider inside my connected component and used all my stripe stuff outside of redux (so my onSubmit is defined in my React component though I dispatch things from there to update my store after getting my token).
Very disappointed with Stripe's implementation of their React library. I have to refactor a lot of code to make this work with my pre-existing app. Ideally I would have liked to import one line of JSX and be done with it. Why is there so much boilerplate code for no apparent reason?
@saurishkar it looks like you aren't using the <CardElement /> from react-stripe-elements in your CardAdd component. The <CardElement /> or the <CardNumberElement /> are the only supported way of accepting card numbers. You cannot use your own input.
That said, the error message should have been clearer here and tell you that you should be using an Element. We'll work on fixing that!
@siakaramalegos sorry to hear that! Do you have a reproduction or sample code to show why the troubleshooting section didn't work? It would also be helpful to know the versions of React, Redux, react-redux, and react-stripe-elements you're using.
@viktorbakari sorry that using the library has proven frustrating. Can you share what you hope your ideal API might look like ("one line of JSX" as you mention)?
The boilerplate might seem like too much, but in many ways is necessary.
<StripeProvider /> provides the Stripe object to your codebase. It needs to be separated from the actual form components since you might want to use the Stripe object in many places (and you will, when calling this.props.stripe.createToken, for example.)<Elements /> serves the purpose of grouping together multiple input components, for the purposes of autofill as well as common configuration (you probably only want to specify custom fonts in one place.)<CardElement /> or <IbanElement /> each serve the purpose of accepting their own kind of input.We're always open to feedback on simplifying the interface, so would love to hear about what is frustrating or confusing, and how we can improve things.
@atty-stripe See my comment for the solution that worked for me.
@siakaramalegos do you have an example of your code anywhere? I've been having the same trouble and have my redux form with my stripe injected form inside of it. I can't figure out how to connect the two.
@chagoy it has been a while, and the repo is private, but I think this is the basic gist - act like you're not using Redux:
Checkout Container
class CheckoutContainer extends Component {
render() {
const fonts = [{ cssSrc: "https://fonts.googleapis.com/css?family=Podkova:400" }]
return (
<StripeProvider apiKey={stripePubkey}>
<Elements fonts={fonts}>
<Checkout {...this.props} />
</Elements>
</StripeProvider>
)
}
}
const mapStateToProps = (state) => {
// state to props things not related to the stripe element itself
}
const mapDispatchToProps = (dispatch) => {
// dispatch to props things not related to the stripe element itself
}
export default connect(mapStateToProps, mapDispatchToProps)(CheckoutContainer)
And then checkout itself:
import {injectStripe} from 'react-stripe-elements';
class Checkout extends Component {
onSubmit = (e) => {
e.preventDefault()
// Delete all previous errors and invalid states
this.props.clearErrors()
const { stripe, hold } = this.props
const { gross, hold_id } = hold
const form = e.target
const data = validate.collectFormValues(form)
const { first_name, last_name } = data
const requestData = {
first_name,
last_name,
hold_id,
expecting_to_pay: gross,
}
// Disable payment button
this.props.requestBooking()
// Get stripe token if payment needed
if (gross > 0) {
stripe.createToken({ name: data.card_name })
.then(({ token, error }) => {
if (error) {
this.props.receiveErrors({card: error.message})
return
}
requestData.stripe_token = token.id
this.props.createBooking(requestData, form)
})
} else {
this.props.createBooking(requestData, form)
}
}
render() {
const {
errors,
hold,
isLoading,
requestingBooking,
} = this.props;
return (
<div>
<h2>Payment</h2>
<form id="payment-form" onSubmit={this.onSubmit}>
<Input
name="first_name"
placeholder="Your first name"
required
errors={errors.first_name} />
<Input
name="last_name"
placeholder="Your last name"
required
errors={errors.last_name} />
<CreditCardGroup
total={hold.gross}
errors={errors} />
<PayButton
isLoading={isLoading}
total={hold.gross}
requestingBooking={requestingBooking} />
</form>
</div>
)
}
}
export default injectStripe(BookingCheckout)
Has anyone actually managed to set this up with redux-form yet? I have struggled with this for the better part of a week.
An official example using redux-form would be very appreciated.
Hey
I got it to work. Let’s see if this makes sense. Here is my component structure
At the bottom you gotta define MainFormComponent = reduxForm({form: ‘name’, onSubmitSuccess: doSubmitSuccess}) and then export default connect(mapStateToProps)(MainFormComponent)
I hope this all makes sense. If not just message me and I’d be willing to walk through it with you.
On Dec 6, 2018, at 9:32 AM, Chris Wright notifications@github.com wrote:
Has anyone actually managed to set this up with redux-form yet? I have struggled with this for the better part of a week.
An official example using redux-form would be very appreciated.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/stripe/react-stripe-elements/issues/55#issuecomment-444959163, or mute the thread https://github.com/notifications/unsubscribe-auth/AQGzuArRKEshRorAf5BqA5qOT1wTR6m9ks5u2VSngaJpZM4OsFx7.
^^^ The 'onSubmitSuccess' callback approach worked well. If you need to call redux actions from the callback, they are in the props argument passed into the callback.
So basically, the redux form onSubmit handler is called first. If your validation passes, you will return the the submitted data in the return of the handler. Next, the 'onSubmitSuccess' callback is called. The submitted data is then sent as the first argument - the function signature is onSubmitSuccess(result, dispatch, props). Inside of of your callback you call the stripe createToken().
strip extra Billing Info fields to pass into createToken():
{
name: '',
address_city: ''
address_country: ''
address_line1: ''
address_line2: ''
address_state: ''
address_zip: ''
}
Callback example:
const submitSuccess = (result, dispatch, props) => {
// result has any of your custom fields from your form
// I am passing in the extra stripe fields
props.stripe.createToken({ ...result})
.then(({ token }) => {
props.setBillingInfo({ billingInfo: token })
})
}
let BillingInfoFormRedux = reduxForm({
form: 'billing-info-form',
onSubmitSuccess: submitSuccess
})(BillingInfoForm)
This is what my form container looks like:
// Stripped down for example purpose
onSubmit = (formData) => {
return formData
}
render () {
return (
<StripeProvider stripe={this.state.stripe}>
<div id='payment-billing-container'>
<Elements>
<BillingInfoForm onSubmit={this.onSubmit} setBillingInfo={this.props.setBillingInfo} />
</Elements>
</div>
</StripeProvider>
)
}
So what chagoy said, just a few different words 😄
Hey I got it to work. Let’s see if this makes sense. Here is my component structure
// in componentDidMount you’ll set the stripe key as it’s done on the docs. Also, write an onSubmit function that will be called on form submission, however form will submit on the InjectedCheckoutForm, not from here. At the bottom you gotta define MainFormComponent = reduxForm({form: ‘name’, onSubmitSuccess: doSubmitSuccess}) and then export default connect(mapStateToProps)(MainFormComponent) I hope this all makes sense. If not just message me and I’d be willing to walk through it with you.// pass it a prop called stripe which is equal to your stripe key.
…
On Dec 6, 2018, at 9:32 AM, Chris Wright @.*> wrote: Has anyone actually managed to set this up with redux-form yet? I have struggled with this for the better part of a week. An official example using redux-form would be very appreciated. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#55 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/AQGzuArRKEshRorAf5BqA5qOT1wTR6m9ks5u2VSngaJpZM4OsFx7.
@scalebig
Were are you injecting the stripe? I tried following your example but I can't get the values of redux form into the submitSuccess function
Ok so unfortunately I don’t have an answer here. I have an idea of what’s going on, but that’s call I’ve got.
When I first set this up it actually freaked me out because I had the same though. How do I connect the dispatch action in redux on form submit and how do I send the info to stripe?
What I’ve found is that the original form gets injected with stripe and there is an additional checkout component which has the card element inside. The additional checkout component should also have your send button. If you have a button on the original form take it out.
I’m not sure how or why but if I click the submit inside the checkout component it will actually submit both components. My first form makes the query to my back end using onSubmit(values) { return this.props.dispatch(register(values))} meanwhile checkout does the onSubmit fetch to the stripe endpoint.
I think ideally the checkout component would receive the info from the original form component and do one query, but I could not get that to work.
On Dec 18, 2018, at 9:18 AM, Agustina Chaer notifications@github.com wrote:
onSubmitSuccess: submitSuccess
Were are you injecting the stripe? I tried following your example but I can't get the values of redux form into the submitSuccess function
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/stripe/react-stripe-elements/issues/55#issuecomment-448299056, or mute the thread https://github.com/notifications/unsubscribe-auth/AQGzuPjMtmJMe0-lEYIkNIAMOHdIVkNzks5u6SNggaJpZM4OsFx7.
@scalebig
Were are you injecting the stripe? I tried following your example but I can't get the values of redux form into the
submitSuccessfunction
@aguscha333 sorry for delay - trying to get our Stripe integration out the door before next semester classes start!
I will provide more of my code here...
This first file is the parent component to the billing form component -
I register stripe here - I had some weirdness registering... so feel free to provide better alternatives.
See onSubmit = (formFieldValues) => { gets called when you submit the form. If you return a list of values from this callback then will be passed to the onSubmitSuccess callback next.
import BillingInfoForm from './billing-info-form'
import {
StripeProvider,
Elements
} from 'react-stripe-elements'
class BillingInfo extends React.Component {
constructor () {
super()
this.state = {stripe: null}
}
componentDidMount () {
const stripeJs = document.createElement('script')
stripeJs.src = 'https://js.stripe.com/v3/'
stripeJs.async = true
stripeJs.onload = () => {
setTimeout(() => {
this.setState({
stripe: window.Stripe('YOUR STRIPE PK HERE')
})
}, 500)
}
document.body && document.body.appendChild(stripeJs)
}
onSubmit = (formFieldValues) => {
// Do field level validations here
// return for success then to onSubmitSuccess
return formFieldValues
}
render () {
return (
<StripeProvider stripe={this.state.stripe}>
<div id='payment-billing-container'>
<Elements>
<BillingInfoForm
onSubmit={this.onSubmit}
setBillingInfo={this.props.setBillingInfo}
skuInfoList={
this.props.skuList
.filter((skuItem) => (skuItem.include))
.map((skuItem) => ({
price: skuItem.price,
taxCode: skuItem.product.taxCode
}))}
/>
</Elements>
</div>
</StripeProvider>
)
}
}
export default BillingInfo
Here is the ReduxForm class (with onSubmitSuccess registered with ReduxForm HOC)
class BillingInfoForm extends React.Component {
constructor () {
super()
this.state = {
stripeNumberElement: null,
stripeExpiryElement: null,
stripeCVCElement: null,
ccInfo: 'uknown'
}
}
render () {
const { handleSubmit } = this.props
// removed code not needed for example
let self = this
return (
<React.Fragment>
<form onSubmit={handleSubmit}>
<h2>Payment method</h2>
<div className="row">
<div className="field">
<Field
fullwidth
label='Name on card'
name='name'
component={ ReduxTextFieldInline }
test-id='billing-name-field'
/>
</div>
</div>
<div className="row">
<div className="field">
<Field
fullwidth
label='Address'
name='address_line1'
component={ ReduxTextFieldInline }
test-id='address-line1-field'
/>
</div>
</div>
// ... other fields here - trying to make this readable
<div className='stripe-mdc'>
<div className="row">
<div className="field">
<div id='card-number'>
<CardNumberElement
className='input'
style={elementStyles}
classes={elementClasses}
onReady={(el) => {
self.setState({
stripeNumberElement: el
})
}}
onChange={(evt) => {
self.setState({
ccIcon: evt.brand ? evt.brand : 'unknown'
})
}}
/>
<label htmlFor='card-number' data-tid="elements_examples.form.card_number_label"
onClick={
() => {
self.state.stripeNumberElement && self.state.stripeNumberElement.focus()
}
}
>Card number</label>
<div className="baseline"></div>
</div>
</div>
<div className="cc-icon"><img src={'/app/images/cc-' + self.state.ccIcon + '.svg'}/></div>
</div>
<div className="row">
<div className="field half-width">
<div id='card-expiry'>
<CardExpiryElement
className='input'
style={elementStyles}
classes={elementClasses}
onReady={(el) => {
self.setState({
stripeExpiryElement: el
})
}}
/>
<label htmlFor="example2-card-expiry" data-tid="elements_examples.form.card_expiry_label"
onClick={
() => {
self.state.stripeExpiryElement.focus()
}
}
>Expiration</label>
<div className="baseline"></div>
</div>
</div>
<div className="field half-width" style={{marginLeft: '10px'}} >
<div id="example2-card-cvc" className="input empty"></div>
<CardCVCElement
className='input'
style={elementStyles}
classes={elementClasses}
onReady={(el) => {
self.setState({
stripeCVCElement: el
})
}}
/>
<label htmlFor="example2-card-cvc" data-tid="elements_examples.form.card_cvc_label"
onClick={
() => {
self.state.stripeCVCElement.focus()
}
}
>CVC</label>
<div className="baseline"></div>
</div>
</div>
</div>
<div className='button-bar'>
<div><Button outlined testing-id='cofirm-payment-button'>Cancel</Button></div>
<div><Button unelevated testing-id='cofirm-payment-button'>Continue</Button></div>
</div>
</form>
</React.Fragment>
)
}
}
const submitSuccess = (result, dispatch, props) => {
props.stripe.createToken({ ...result, address_country: 'US' })
.then(({ token }) => {
console.log('token', token)
props.setBillingInfo({
token,
skuInfoList: props.skuInfoList,
addressInfo: {
country: 'US',
zip: token.card.address_zip,
state: token.card.address_state,
city: token.card.address_city,
street: token.card.address_line1
}
})
})
}
let BillingInfoFormRedux = reduxForm({
form: 'billing-info-form',
touchOnChange: true,
destroyOnUnmount: false,
onSubmitSuccess: submitSuccess
})(BillingInfoForm)
export default injectStripe(BillingInfoFormRedux)
Most helpful comment
Very disappointed with Stripe's implementation of their React library. I have to refactor a lot of code to make this work with my pre-existing app. Ideally I would have liked to import one line of JSX and be done with it. Why is there so much boilerplate code for no apparent reason?