diff --git a/src/components/EditListingPhotosPanel/EditListingPhotosPanel.js b/src/components/EditListingPhotosPanel/EditListingPhotosPanel.js index 1734f44e..d9a27248 100644 --- a/src/components/EditListingPhotosPanel/EditListingPhotosPanel.js +++ b/src/components/EditListingPhotosPanel/EditListingPhotosPanel.js @@ -65,11 +65,16 @@ class EditListingPhotosPanel extends Component { bankAccountToken, address: omitBy(address, isUndefined), }; - this.props.onPayoutDetailsSubmit(params).then(() => { - this.setState({ showPayoutDetails: false }); - this.props.onManageDisableScrolling('EditListingPhotosPanel.payoutModal', false); - this.props.onSubmit(this.state.submittedValues); - }); + this.props + .onPayoutDetailsSubmit(params) + .then(() => { + this.setState({ showPayoutDetails: false }); + this.props.onManageDisableScrolling('EditListingPhotosPanel.payoutModal', false); + this.props.onSubmit(this.state.submittedValues); + }) + .catch(() => { + // do nothing + }); } render() { diff --git a/src/containers/PayoutDetailsForm/PayoutDetailsForm.js b/src/containers/PayoutDetailsForm/PayoutDetailsForm.js index a10e6509..c1ad34e4 100644 --- a/src/containers/PayoutDetailsForm/PayoutDetailsForm.js +++ b/src/containers/PayoutDetailsForm/PayoutDetailsForm.js @@ -15,6 +15,7 @@ import { TextInputField, } from '../../components'; import * as validators from '../../util/validators'; +import { isStripeInvalidPostalCode } from '../../util/errors'; import css from './PayoutDetailsForm.css'; @@ -191,11 +192,22 @@ const PayoutDetailsFormComponent = props => { const submitReady = false; const submitInProgress = submitting || inProgress; const submitDisabled = pristine || invalid || disabled || submitInProgress; - const error = createStripeAccountError ? ( -
- -
- ) : null; + + let error = null; + + if (isStripeInvalidPostalCode(createStripeAccountError)) { + error = ( +
+ +
+ ); + } else if (createStripeAccountError) { + error = ( +
+ +
+ ); + } return (
diff --git a/src/ducks/user.duck.js b/src/ducks/user.duck.js index 6b2aa25a..a5996b85 100644 --- a/src/ducks/user.duck.js +++ b/src/ducks/user.duck.js @@ -380,9 +380,14 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => .then(() => { dispatch(stripeAccountCreateSuccess(accountResponse)); }) - .catch(e => { - dispatch(stripeAccountCreateError(storableError(e))); - log.error(e, 'create-stripe-account-failed'); + .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.stripe_message + : null; + log.error(err, 'create-stripe-account-failed', { stripeMessage }); throw e; }); }; diff --git a/src/translations/en.json b/src/translations/en.json index e313c8ed..16bb5881 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -398,6 +398,7 @@ "PayoutDetailsForm.countryPlaceholder": "Select your country…", "PayoutDetailsForm.countryRequired": "This field is required", "PayoutDetailsForm.createStripeAccountFailed": "Whoops, something went wrong. Please try again.", + "PayoutDetailsForm.createStripeAccountFailedInvalidPostalCode": "Whoops, check that the selected country and postal code are correct and try again.", "PayoutDetailsForm.firstNameLabel": "First name", "PayoutDetailsForm.firstNamePlaceholder": "John", "PayoutDetailsForm.firstNameRequired": "This field is required", diff --git a/src/util/errors.js b/src/util/errors.js index a17ce45a..a361f1cc 100644 --- a/src/util/errors.js +++ b/src/util/errors.js @@ -157,6 +157,21 @@ export const isChangeEmailWrongPassword = error => error && error.status === 403 */ export const isChangePasswordWrongPassword = error => error && error.status === 403; +/** + * Check if the given API error (from + * 'sdk.currentUser.createStripeAccount(payoutDetails)') is due to + * invalid postal code in the given country. + */ +export const isStripeInvalidPostalCode = error => { + const msgRe = /^Invalid [A-Z]{2} postal code$/; + return errorAPIErrors(error).some(apiError => { + // Stripe doesn't seem to give an error code for this specific + // case, so we have to recognize it from the message. + const msg = apiError.meta && apiError.meta.stripe_message ? apiError.meta.stripe_message : ''; + return msgRe.test(msg); + }); +}; + export const storableError = error => { const { name, message, status, statusText } = error; // Status, statusText, and data.errors are (possibly) added to the error object by SDK diff --git a/src/util/log.js b/src/util/log.js index 70e9595c..e3eeb7f1 100644 --- a/src/util/log.js +++ b/src/util/log.js @@ -56,5 +56,6 @@ export const error = (e, code, data) => { Raven.captureException(e, { tags: { code }, extra: data }); } else { console.error(e); // eslint-disable-line + console.error('Error code:', code, 'data:', data); } }; diff --git a/src/util/propTypes.js b/src/util/propTypes.js index fa233053..53a39c47 100644 --- a/src/util/propTypes.js +++ b/src/util/propTypes.js @@ -373,6 +373,7 @@ export const ERROR_CODE_EMAIL_NOT_FOUND = 'email-not-found'; export const ERROR_CODE_EMAIL_NOT_VERIFIED = 'email-unverified'; export const ERROR_CODE_TOO_MANY_VERIFICATION_REQUESTS = 'email-too-many-verification-requests'; export const ERROR_CODE_UPLOAD_OVER_LIMIT = 'request-upload-over-limit'; +export const ERROR_CODE_VALIDATION_INVALID_PARAMS = 'validation-invalid-params'; const ERROR_CODES = [ ERROR_CODE_TRANSACTION_LISTING_NOT_FOUND, ERROR_CODE_TRANSACTION_INVALID_TRANSITION, @@ -384,6 +385,7 @@ const ERROR_CODES = [ ERROR_CODE_EMAIL_NOT_VERIFIED, ERROR_CODE_TOO_MANY_VERIFICATION_REQUESTS, ERROR_CODE_UPLOAD_OVER_LIMIT, + ERROR_CODE_VALIDATION_INVALID_PARAMS, ]; // API error @@ -393,6 +395,7 @@ export const apiError = shape({ status: number.isRequired, code: oneOf(ERROR_CODES).isRequired, title: string.isRequired, + meta: object, }); // Storable error prop type. (Error object should not be stored as it is.) diff --git a/yarn.lock b/yarn.lock index 106b2e0a..28e0b679 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7214,9 +7214,9 @@ sharetribe-scripts@1.0.14: optionalDependencies: fsevents "1.1.2" -"sharetribe-sdk@git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#ff76ef91e1e3b1e26b035a04d643d328ea3d1d61": +"sharetribe-sdk@git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#267efc4ca52dd1ad54db6bf3c37ab8ebcd8a86ab": version "0.0.1" - resolved "git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#ff76ef91e1e3b1e26b035a04d643d328ea3d1d61" + resolved "git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#267efc4ca52dd1ad54db6bf3c37ab8ebcd8a86ab" dependencies: axios "^0.15.3" js-cookie "^2.1.3"