mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Merge pull request #1088 from sharetribe/use-final-form-in-stripe-payment-form
Use final form in StripePaymentForm
This commit is contained in:
commit
f4a487fb3c
7 changed files with 69 additions and 60 deletions
|
|
@ -14,6 +14,9 @@ way to update this template, but currently, we follow a pattern:
|
|||
|
||||
## Upcoming version 2019-XX-XX
|
||||
|
||||
- [cghange] Use Final Form on `StripePaymentForm` for consistency. Note that card form Stripe
|
||||
Elements in `StripePaymentForm` is not a Final Form field so it's not available trough Final Form
|
||||
but handled separately. [#1088](https://github.com/sharetribe/flex-template-web/pull/1088)
|
||||
- [change] Move Stripe SDK call from `StripePaymentForm` to `stripe.duck.js` for consistency.
|
||||
[#1086](https://github.com/sharetribe/flex-template-web/pull/1086)
|
||||
|
||||
|
|
|
|||
|
|
@ -516,6 +516,8 @@ CheckoutPageComponent.defaultProps = {
|
|||
enquiredTransaction: null,
|
||||
currentUser: null,
|
||||
stripePaymentToken: null,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: null,
|
||||
};
|
||||
|
||||
CheckoutPageComponent.propTypes = {
|
||||
|
|
@ -539,8 +541,8 @@ CheckoutPageComponent.propTypes = {
|
|||
}).isRequired,
|
||||
sendOrderRequest: func.isRequired,
|
||||
onCreateStripePaymentToken: func.isRequired,
|
||||
stripePaymentTokenInProgress: bool.isRequired,
|
||||
stripePaymentTokenError: bool.isRequired,
|
||||
stripePaymentTokenInProgress: bool,
|
||||
stripePaymentTokenError: propTypes.error,
|
||||
stripePaymentToken: object,
|
||||
|
||||
// from connect
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe('CheckoutPage', () => {
|
|||
scrollingDisabled: false,
|
||||
onCreateStripePaymentToken: noop,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: false,
|
||||
stripePaymentTokenError: null,
|
||||
};
|
||||
const tree = renderShallow(<CheckoutPageComponent {...props} />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
paymentInfo="CheckoutPage.paymentInfo"
|
||||
showInitialMessageInput={true}
|
||||
stripePaymentToken={null}
|
||||
stripePaymentTokenError={false}
|
||||
stripePaymentTokenError={null}
|
||||
stripePaymentTokenInProgress={false}
|
||||
/>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ export const PERSON_CREATE_REQUEST = 'app/stripe/PERSON_CREATE_REQUEST';
|
|||
export const PERSON_CREATE_SUCCESS = 'app/stripe/PERSON_CREATE_SUCCESS';
|
||||
export const PERSON_CREATE_ERROR = 'app/stripe/PERSON_CREATE_ERROR';
|
||||
|
||||
export const STRIPE_PAYMENT_TOKEN_CREATE_REQUEST = 'app/stripe/STRIPE_PAYMENT_TOKEN_CREATE_REQUEST';
|
||||
export const STRIPE_PAYMENT_TOKEN_CREATE_SUCCESS = 'app/stripe/STRIPE_PAYMENT_TOKEN_CREATE_SUCCESS';
|
||||
export const STRIPE_PAYMENT_TOKEN_CREATE_ERROR = 'app/stripe/STRIPE_PAYMENT_TOKEN_CREATE_ERROR';
|
||||
export const CREATE_PAYMENT_TOKEN_REQUEST = 'app/stripe/CREATE_PAYMENT_TOKEN_REQUEST';
|
||||
export const CREATE_PAYMENT_TOKEN_SUCCESS = 'app/stripe/CREATE_PAYMENT_TOKEN_SUCCESS';
|
||||
export const CREATE_PAYMENT_TOKEN_ERROR = 'app/stripe/CREATE_PAYMENT_TOKEN_ERROR';
|
||||
|
||||
// ================ Reducer ================ //
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ const initialState = {
|
|||
stripeAccount: null,
|
||||
stripeAccountFetched: false,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: false,
|
||||
stripePaymentTokenError: null,
|
||||
stripePaymentToken: null,
|
||||
};
|
||||
|
||||
|
|
@ -101,15 +101,15 @@ export default function reducer(state = initialState, action = {}) {
|
|||
}),
|
||||
};
|
||||
|
||||
case STRIPE_PAYMENT_TOKEN_CREATE_REQUEST:
|
||||
case CREATE_PAYMENT_TOKEN_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
stripePaymentTokenError: null,
|
||||
stripePaymentTokenInProgress: true,
|
||||
};
|
||||
case STRIPE_PAYMENT_TOKEN_CREATE_SUCCESS:
|
||||
case CREATE_PAYMENT_TOKEN_SUCCESS:
|
||||
return { ...state, stripePaymentTokenInProgress: false, stripePaymentToken: payload };
|
||||
case STRIPE_PAYMENT_TOKEN_CREATE_ERROR:
|
||||
case CREATE_PAYMENT_TOKEN_ERROR:
|
||||
console.error(payload);
|
||||
return { ...state, stripePaymentTokenError: payload, stripePaymentTokenInProgress: false };
|
||||
|
||||
|
|
@ -169,17 +169,17 @@ export const personCreateError = payload => ({
|
|||
error: true,
|
||||
});
|
||||
|
||||
export const stripePaymentTokenCreateRequest = () => ({
|
||||
type: STRIPE_PAYMENT_TOKEN_CREATE_REQUEST,
|
||||
export const createPaymentTokenRequest = () => ({
|
||||
type: CREATE_PAYMENT_TOKEN_REQUEST,
|
||||
});
|
||||
|
||||
export const stripePaymentTokenCreateSuccess = payload => ({
|
||||
type: STRIPE_PAYMENT_TOKEN_CREATE_SUCCESS,
|
||||
export const createPaymentTokenSuccess = payload => ({
|
||||
type: CREATE_PAYMENT_TOKEN_SUCCESS,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const stripePaymentTokenCreateError = payload => ({
|
||||
type: STRIPE_PAYMENT_TOKEN_CREATE_ERROR,
|
||||
export const createPaymentTokenError = payload => ({
|
||||
type: CREATE_PAYMENT_TOKEN_ERROR,
|
||||
payload,
|
||||
error: true,
|
||||
});
|
||||
|
|
@ -499,21 +499,18 @@ export const createStripePaymentToken = params => dispatch => {
|
|||
// so that's why Stripe needs to be passed here and we can't create a new instance.
|
||||
const { stripe, card } = params;
|
||||
|
||||
dispatch(stripePaymentTokenCreateRequest());
|
||||
dispatch(createPaymentTokenRequest());
|
||||
|
||||
return stripe
|
||||
.createToken(card)
|
||||
.then(response => {
|
||||
dispatch(stripePaymentTokenCreateSuccess(response.token));
|
||||
dispatch(createPaymentTokenSuccess(response.token));
|
||||
return response;
|
||||
})
|
||||
.catch(err => {
|
||||
const e = storableError(err);
|
||||
dispatch(stripeAccountCreateError(e));
|
||||
const stripeMessage =
|
||||
e.apiErrors && e.apiErrors.length > 0 && e.apiErrors[0].meta
|
||||
? e.apiErrors[0].meta.stripeMessage
|
||||
: null;
|
||||
dispatch(createPaymentTokenError(e));
|
||||
const stripeMessage = e.message;
|
||||
log.error(err, 'create-stripe-payment-token-failed', { stripeMessage });
|
||||
throw e;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const Empty = {
|
|||
intl: fakeIntl,
|
||||
onCreateStripePaymentToken: noop,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: false,
|
||||
stripePaymentTokenError: null,
|
||||
},
|
||||
group: 'forms',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
/**
|
||||
* Note: This form is using card from Stripe Elements https://stripe.com/docs/stripe-js#elements
|
||||
* Card is not a Final Form field so it's not available trough Final Form.
|
||||
* It's also handled separately in handleSubmit function.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { Form as FinalForm } from 'react-final-form';
|
||||
import classNames from 'classnames';
|
||||
import { Form, PrimaryButton, ExpandingTextarea } from '../../components';
|
||||
import config from '../../config';
|
||||
import { propTypes } from '../../util/types';
|
||||
import { Form, PrimaryButton, FieldTextInput } from '../../components';
|
||||
|
||||
import css from './StripePaymentForm.css';
|
||||
|
||||
|
|
@ -74,7 +81,6 @@ const initialState = {
|
|||
submitting: false,
|
||||
cardValueValid: false,
|
||||
token: null,
|
||||
message: '',
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -92,6 +98,7 @@ class StripePaymentForm extends Component {
|
|||
this.state = initialState;
|
||||
this.handleCardValueChange = this.handleCardValueChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.paymentForm = this.paymentForm.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
if (!window.Stripe) {
|
||||
|
|
@ -139,9 +146,9 @@ class StripePaymentForm extends Component {
|
|||
};
|
||||
});
|
||||
}
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
handleSubmit(values) {
|
||||
const { onSubmit, stripePaymentTokenInProgress, stripePaymentToken } = this.props;
|
||||
const initialMessage = values.initialMessage ? values.initialMessage.trim() : null;
|
||||
|
||||
if (stripePaymentTokenInProgress || !this.state.cardValueValid) {
|
||||
// Already submitting or card value incomplete/invalid
|
||||
|
|
@ -150,7 +157,7 @@ class StripePaymentForm extends Component {
|
|||
|
||||
if (stripePaymentToken) {
|
||||
// Token already fetched for the current card value
|
||||
onSubmit({ token: stripePaymentToken, message: this.state.message.trim() });
|
||||
onSubmit({ token: stripePaymentToken.id, message: initialMessage });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -160,25 +167,28 @@ class StripePaymentForm extends Component {
|
|||
};
|
||||
|
||||
this.props.onCreateStripePaymentToken(params).then(() => {
|
||||
onSubmit({ token: this.props.stripePaymentToken.id, message: this.state.message.trim() });
|
||||
onSubmit({ token: this.props.stripePaymentToken.id, message: initialMessage });
|
||||
});
|
||||
}
|
||||
render() {
|
||||
|
||||
paymentForm(formRenderProps) {
|
||||
const {
|
||||
className,
|
||||
rootClassName,
|
||||
inProgress,
|
||||
formId,
|
||||
paymentInfo,
|
||||
onChange,
|
||||
authorDisplayName,
|
||||
showInitialMessageInput,
|
||||
intl,
|
||||
stripePaymentTokenInProgress,
|
||||
stripePaymentTokenError,
|
||||
} = this.props;
|
||||
invalid,
|
||||
handleSubmit,
|
||||
} = formRenderProps;
|
||||
|
||||
const submitInProgress = stripePaymentTokenInProgress || inProgress;
|
||||
const submitDisabled = !this.state.cardValueValid || submitInProgress;
|
||||
const submitDisabled = invalid || !this.state.cardValueValid || submitInProgress;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const cardClasses = classNames(css.card, {
|
||||
[css.cardSuccess]: this.state.cardValueValid,
|
||||
|
|
@ -190,22 +200,13 @@ class StripePaymentForm extends Component {
|
|||
{ name: authorDisplayName }
|
||||
);
|
||||
|
||||
const handleMessageChange = e => {
|
||||
// A change in the message should call the onChange prop with
|
||||
// the current token and the new message.
|
||||
const message = e.target.value;
|
||||
this.setState(prevState => {
|
||||
const { token } = prevState;
|
||||
const newState = { token, message };
|
||||
onChange(newState);
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
const messageOptionalText = intl.formatMessage({
|
||||
id: 'StripePaymentForm.messageOptionalText',
|
||||
});
|
||||
|
||||
const messageOptionalText = (
|
||||
<span className={css.messageOptional}>
|
||||
<FormattedMessage id="StripePaymentForm.messageOptionalText" />
|
||||
</span>
|
||||
const initialMessageLabel = intl.formatMessage(
|
||||
{ id: 'StripePaymentForm.messageLabel' },
|
||||
{ messageOptionalText: messageOptionalText }
|
||||
);
|
||||
|
||||
const initialMessage = showInitialMessageInput ? (
|
||||
|
|
@ -213,21 +214,20 @@ class StripePaymentForm extends Component {
|
|||
<h3 className={css.messageHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.messageHeading" />
|
||||
</h3>
|
||||
<label className={css.messageLabel} htmlFor={`${formId}-message`}>
|
||||
<FormattedMessage id="StripePaymentForm.messageLabel" values={{ messageOptionalText }} />
|
||||
</label>
|
||||
<ExpandingTextarea
|
||||
|
||||
<FieldTextInput
|
||||
type="textarea"
|
||||
id={`${formId}-message`}
|
||||
className={css.message}
|
||||
name="initialMessage"
|
||||
label={initialMessageLabel}
|
||||
placeholder={messagePlaceholder}
|
||||
value={this.state.message}
|
||||
onChange={handleMessageChange}
|
||||
className={css.message}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return config.stripe.publishableKey ? (
|
||||
<Form className={classes} onSubmit={this.handleSubmit}>
|
||||
<Form className={classes} onSubmit={handleSubmit}>
|
||||
<h3 className={css.paymentHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.paymentHeading" />
|
||||
</h3>
|
||||
|
|
@ -263,6 +263,11 @@ class StripePaymentForm extends Component {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onSubmit, ...rest } = this.props;
|
||||
return <FinalForm onSubmit={this.handleSubmit} {...rest} render={this.paymentForm} />;
|
||||
}
|
||||
}
|
||||
|
||||
StripePaymentForm.defaultProps = {
|
||||
|
|
@ -272,6 +277,8 @@ StripePaymentForm.defaultProps = {
|
|||
onChange: () => null,
|
||||
showInitialMessageInput: true,
|
||||
stripePaymentToken: null,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: null,
|
||||
};
|
||||
|
||||
const { bool, func, string, object } = PropTypes;
|
||||
|
|
@ -288,8 +295,8 @@ StripePaymentForm.propTypes = {
|
|||
authorDisplayName: string.isRequired,
|
||||
showInitialMessageInput: bool,
|
||||
onCreateStripePaymentToken: func.isRequired,
|
||||
stripePaymentTokenInProgress: bool.isRequired,
|
||||
stripePaymentTokenError: bool.isRequired,
|
||||
stripePaymentTokenInProgress: bool,
|
||||
stripePaymentTokenError: propTypes.error,
|
||||
stripePaymentToken: object,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue