mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-29 05:00:43 +10:00
Merge pull request #1086 from sharetribe/create-payment-to-stripe-duck
Move creating payment token to stripe.duck.js
This commit is contained in:
commit
07cd93a649
7 changed files with 120 additions and 38 deletions
|
|
@ -14,6 +14,9 @@ way to update this template, but currently, we follow a pattern:
|
|||
|
||||
## Upcoming version 2019-XX-XX
|
||||
|
||||
- [change] Move Stripe SDK call from `StripePaymentForm` to `stripe.duck.js` for consistency.
|
||||
[#1086](https://github.com/sharetribe/flex-template-web/pull/1086)
|
||||
|
||||
## [v2.16.0] 2019-05-08
|
||||
|
||||
This release makes 2 big updates to `sharetribe-scripts` package (which is our fork from Create
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
setInitialValues,
|
||||
speculateTransaction,
|
||||
} from './CheckoutPage.duck';
|
||||
import { createStripePaymentToken } from '../../ducks/stripe.duck.js';
|
||||
import config from '../../config';
|
||||
|
||||
import { storeData, storedData, clearData } from './CheckoutPageSessionHelpers';
|
||||
|
|
@ -206,6 +207,10 @@ export class CheckoutPageComponent extends Component {
|
|||
intl,
|
||||
params,
|
||||
currentUser,
|
||||
onCreateStripePaymentToken,
|
||||
stripePaymentTokenInProgress,
|
||||
stripePaymentTokenError,
|
||||
stripePaymentToken,
|
||||
} = this.props;
|
||||
|
||||
// Since the listing data is already given from the ListingPage
|
||||
|
|
@ -464,6 +469,10 @@ export class CheckoutPageComponent extends Component {
|
|||
paymentInfo={intl.formatMessage({ id: 'CheckoutPage.paymentInfo' })}
|
||||
authorDisplayName={currentAuthor.attributes.profile.displayName}
|
||||
showInitialMessageInput={showInitialMessageInput}
|
||||
onCreateStripePaymentToken={onCreateStripePaymentToken}
|
||||
stripePaymentTokenInProgress={stripePaymentTokenInProgress}
|
||||
stripePaymentTokenError={stripePaymentTokenError}
|
||||
stripePaymentToken={stripePaymentToken}
|
||||
/>
|
||||
) : null}
|
||||
</section>
|
||||
|
|
@ -506,6 +515,7 @@ CheckoutPageComponent.defaultProps = {
|
|||
speculatedTransaction: null,
|
||||
enquiredTransaction: null,
|
||||
currentUser: null,
|
||||
stripePaymentToken: null,
|
||||
};
|
||||
|
||||
CheckoutPageComponent.propTypes = {
|
||||
|
|
@ -528,6 +538,10 @@ CheckoutPageComponent.propTypes = {
|
|||
slug: string,
|
||||
}).isRequired,
|
||||
sendOrderRequest: func.isRequired,
|
||||
onCreateStripePaymentToken: func.isRequired,
|
||||
stripePaymentTokenInProgress: bool.isRequired,
|
||||
stripePaymentTokenError: bool.isRequired,
|
||||
stripePaymentToken: object,
|
||||
|
||||
// from connect
|
||||
dispatch: func.isRequired,
|
||||
|
|
@ -553,6 +567,11 @@ const mapStateToProps = state => {
|
|||
initiateOrderError,
|
||||
} = state.CheckoutPage;
|
||||
const { currentUser } = state.user;
|
||||
const {
|
||||
stripePaymentTokenInProgress,
|
||||
stripePaymentTokenError,
|
||||
stripePaymentToken,
|
||||
} = state.stripe;
|
||||
return {
|
||||
scrollingDisabled: isScrollingDisabled(state),
|
||||
currentUser,
|
||||
|
|
@ -564,6 +583,9 @@ const mapStateToProps = state => {
|
|||
enquiredTransaction,
|
||||
listing,
|
||||
initiateOrderError,
|
||||
stripePaymentTokenInProgress,
|
||||
stripePaymentTokenError,
|
||||
stripePaymentToken,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -573,6 +595,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
sendOrderRequestAfterEnquiry: (transactionId, params) =>
|
||||
dispatch(initiateOrderAfterEnquiry(transactionId, params)),
|
||||
fetchSpeculatedTransaction: params => dispatch(speculateTransaction(params)),
|
||||
onCreateStripePaymentToken: params => dispatch(createStripePaymentToken(params)),
|
||||
});
|
||||
|
||||
const CheckoutPage = compose(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ describe('CheckoutPage', () => {
|
|||
fetchSpeculatedTransaction: noop,
|
||||
speculateTransactionInProgress: false,
|
||||
scrollingDisabled: false,
|
||||
onCreateStripePaymentToken: noop,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: false,
|
||||
};
|
||||
const tree = renderShallow(<CheckoutPageComponent {...props} />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -90,9 +90,13 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
authorDisplayName="author display name"
|
||||
formId="CheckoutPagePaymentForm"
|
||||
inProgress={false}
|
||||
onCreateStripePaymentToken={[Function]}
|
||||
onSubmit={[Function]}
|
||||
paymentInfo="CheckoutPage.paymentInfo"
|
||||
showInitialMessageInput={true}
|
||||
stripePaymentToken={null}
|
||||
stripePaymentTokenError={false}
|
||||
stripePaymentTokenInProgress={false}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ 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';
|
||||
|
||||
// ================ Reducer ================ //
|
||||
|
||||
const initialState = {
|
||||
|
|
@ -29,6 +33,9 @@ const initialState = {
|
|||
persons: [],
|
||||
stripeAccount: null,
|
||||
stripeAccountFetched: false,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: false,
|
||||
stripePaymentToken: null,
|
||||
};
|
||||
|
||||
export default function reducer(state = initialState, action = {}) {
|
||||
|
|
@ -94,6 +101,18 @@ export default function reducer(state = initialState, action = {}) {
|
|||
}),
|
||||
};
|
||||
|
||||
case STRIPE_PAYMENT_TOKEN_CREATE_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
stripePaymentTokenError: null,
|
||||
stripePaymentTokenInProgress: true,
|
||||
};
|
||||
case STRIPE_PAYMENT_TOKEN_CREATE_SUCCESS:
|
||||
return { ...state, stripePaymentTokenInProgress: false, stripePaymentToken: payload };
|
||||
case STRIPE_PAYMENT_TOKEN_CREATE_ERROR:
|
||||
console.error(payload);
|
||||
return { ...state, stripePaymentTokenError: payload, stripePaymentTokenInProgress: false };
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -150,6 +169,21 @@ export const personCreateError = payload => ({
|
|||
error: true,
|
||||
});
|
||||
|
||||
export const stripePaymentTokenCreateRequest = () => ({
|
||||
type: STRIPE_PAYMENT_TOKEN_CREATE_REQUEST,
|
||||
});
|
||||
|
||||
export const stripePaymentTokenCreateSuccess = payload => ({
|
||||
type: STRIPE_PAYMENT_TOKEN_CREATE_SUCCESS,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const stripePaymentTokenCreateError = payload => ({
|
||||
type: STRIPE_PAYMENT_TOKEN_CREATE_ERROR,
|
||||
payload,
|
||||
error: true,
|
||||
});
|
||||
|
||||
// ================ Thunks ================ //
|
||||
|
||||
// Util: rename address fields to match Stripe API specifications
|
||||
|
|
@ -459,3 +493,28 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) =>
|
|||
return dispatch(createStripeCompanyAccount(payoutDetails, stripe));
|
||||
}
|
||||
};
|
||||
|
||||
export const createStripePaymentToken = params => dispatch => {
|
||||
// It's required to use the same instance of Stripe as where the card has been created
|
||||
// so that's why Stripe needs to be passed here and we can't create a new instance.
|
||||
const { stripe, card } = params;
|
||||
|
||||
dispatch(stripePaymentTokenCreateRequest());
|
||||
|
||||
return stripe
|
||||
.createToken(card)
|
||||
.then(response => {
|
||||
dispatch(stripePaymentTokenCreateSuccess(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;
|
||||
log.error(err, 'create-stripe-payment-token-failed', { stripeMessage });
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { fakeIntl } from '../../util/test-data';
|
||||
import StripePaymentForm from './StripePaymentForm';
|
||||
|
||||
const noop = () => null;
|
||||
|
||||
export const Empty = {
|
||||
component: StripePaymentForm,
|
||||
props: {
|
||||
|
|
@ -14,6 +16,9 @@ export const Empty = {
|
|||
console.log('form onSubmit:', values);
|
||||
},
|
||||
intl: fakeIntl,
|
||||
onCreateStripePaymentToken: noop,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: false,
|
||||
},
|
||||
group: 'forms',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
|||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { Form, PrimaryButton, ExpandingTextarea } from '../../components';
|
||||
import * as log from '../../util/log';
|
||||
import config from '../../config';
|
||||
|
||||
import css from './StripePaymentForm.css';
|
||||
|
|
@ -142,48 +141,27 @@ class StripePaymentForm extends Component {
|
|||
}
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
const { onSubmit, stripePaymentTokenInProgress, stripePaymentToken } = this.props;
|
||||
|
||||
if (this.state.submitting || !this.state.cardValueValid) {
|
||||
if (stripePaymentTokenInProgress || !this.state.cardValueValid) {
|
||||
// Already submitting or card value incomplete/invalid
|
||||
return;
|
||||
}
|
||||
|
||||
const { intl, onSubmit } = this.props;
|
||||
|
||||
if (this.state.token) {
|
||||
if (stripePaymentToken) {
|
||||
// Token already fetched for the current card value
|
||||
onSubmit({ token: this.state.token, message: this.state.message.trim() });
|
||||
onSubmit({ token: stripePaymentToken, message: this.state.message.trim() });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ submitting: true });
|
||||
const params = {
|
||||
stripe: this.stripe,
|
||||
card: this.card,
|
||||
};
|
||||
|
||||
this.stripe
|
||||
.createToken(this.card)
|
||||
.then(result => {
|
||||
const { error, token } = result;
|
||||
if (error) {
|
||||
this.setState({
|
||||
submitting: false,
|
||||
error: stripeErrorTranslation(intl, error),
|
||||
token: null,
|
||||
});
|
||||
} else {
|
||||
this.setState({ submitting: false, token: token.id });
|
||||
onSubmit({ token: token.id, message: this.state.message.trim() });
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
log.error(e, 'stripe-payment-form-submit-failed', {
|
||||
stripeErrorType: e.type,
|
||||
stripeErrorCode: e.code,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
submitting: false,
|
||||
error: stripeErrorTranslation(intl, e),
|
||||
});
|
||||
});
|
||||
this.props.onCreateStripePaymentToken(params).then(() => {
|
||||
onSubmit({ token: this.props.stripePaymentToken.id, message: this.state.message.trim() });
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
|
|
@ -196,13 +174,15 @@ class StripePaymentForm extends Component {
|
|||
authorDisplayName,
|
||||
showInitialMessageInput,
|
||||
intl,
|
||||
stripePaymentTokenInProgress,
|
||||
stripePaymentTokenError,
|
||||
} = this.props;
|
||||
const submitInProgress = this.state.submitting || inProgress;
|
||||
const submitInProgress = stripePaymentTokenInProgress || inProgress;
|
||||
const submitDisabled = !this.state.cardValueValid || submitInProgress;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const cardClasses = classNames(css.card, {
|
||||
[css.cardSuccess]: this.state.cardValueValid,
|
||||
[css.cardError]: this.state.error && !submitInProgress,
|
||||
[css.cardError]: stripePaymentTokenError && !submitInProgress,
|
||||
});
|
||||
|
||||
const messagePlaceholder = intl.formatMessage(
|
||||
|
|
@ -261,8 +241,8 @@ class StripePaymentForm extends Component {
|
|||
this.cardContainer = el;
|
||||
}}
|
||||
/>
|
||||
{this.state.error && !submitInProgress ? (
|
||||
<span style={{ color: 'red' }}>{this.state.error}</span>
|
||||
{stripePaymentTokenError && !submitInProgress ? (
|
||||
<span style={{ color: 'red' }}>{stripePaymentTokenError}</span>
|
||||
) : null}
|
||||
{initialMessage}
|
||||
<div className={css.submitContainer}>
|
||||
|
|
@ -291,9 +271,10 @@ StripePaymentForm.defaultProps = {
|
|||
inProgress: false,
|
||||
onChange: () => null,
|
||||
showInitialMessageInput: true,
|
||||
stripePaymentToken: null,
|
||||
};
|
||||
|
||||
const { bool, func, string } = PropTypes;
|
||||
const { bool, func, string, object } = PropTypes;
|
||||
|
||||
StripePaymentForm.propTypes = {
|
||||
className: string,
|
||||
|
|
@ -306,6 +287,10 @@ StripePaymentForm.propTypes = {
|
|||
paymentInfo: string.isRequired,
|
||||
authorDisplayName: string.isRequired,
|
||||
showInitialMessageInput: bool,
|
||||
onCreateStripePaymentToken: func.isRequired,
|
||||
stripePaymentTokenInProgress: bool.isRequired,
|
||||
stripePaymentTokenError: bool.isRequired,
|
||||
stripePaymentToken: object,
|
||||
};
|
||||
|
||||
export default injectIntl(StripePaymentForm);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue