diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js index 88c7fcb6..61219f86 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js @@ -5,26 +5,13 @@ import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; import { Form as FinalForm } from 'react-final-form'; import classNames from 'classnames'; import config from '../../config'; -import { - Button, - ExternalLink, - StripeBankAccountTokenInputField, - FieldSelect, - FieldBirthdayInput, - FieldRadioButton, - FieldTextInput, - Form, -} from '../../components'; -import * as validators from '../../util/validators'; +import { Button, ExternalLink, FieldRadioButton, Form } from '../../components'; import { isStripeInvalidPostalCode } from '../../util/errors'; -import PayoutDetailsAddress from './PayoutDetailsAddress'; +import PayoutDetailsFormCompany from './PayoutDetailsFormCompany'; +import PayoutDetailsFormIndividual from './PayoutDetailsFormIndividual'; import css from './PayoutDetailsForm.css'; -const MIN_STRIPE_ACCOUNT_AGE = 18; - -const supportedCountries = config.stripe.supportedCountries.map(c => c.code); - export const stripeCountryConfigs = countryCode => { const country = config.stripe.supportedCountries.find(c => c.code === countryCode); @@ -34,11 +21,6 @@ export const stripeCountryConfigs = countryCode => { return country; }; -const countryCurrency = countryCode => { - const country = stripeCountryConfigs(countryCode); - return country.currency; -}; - const PayoutDetailsFormComponent = props => ( ( className, createStripeAccountError, disabled, - form, handleSubmit, inProgress, intl, @@ -64,130 +45,12 @@ const PayoutDetailsFormComponent = props => ( id: 'PayoutDetailsForm.individualAccount', }); - const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); - const firstNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.firstNamePlaceholder', - }); - const firstNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.firstNameRequired', - }) - ); - - const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); - const lastNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.lastNamePlaceholder', - }); - const lastNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.lastNameRequired', - }) - ); - - const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); - const birthdayLabelMonth = intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayLabelMonth', - }); - const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); - const birthdayRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayRequired', - }) - ); - const birthdayMinAge = validators.ageAtLeast( - intl.formatMessage( - { - id: 'PayoutDetailsForm.birthdayMinAge', - }, - { - minAge: MIN_STRIPE_ACCOUNT_AGE, - } - ), - MIN_STRIPE_ACCOUNT_AGE - ); - const companyAccountLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyAccount' }); - const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); - const companyNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.companyNamePlaceholder', - }); - const companyNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.companyNameRequired', - }) - ); - - const companyTaxIdLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyTaxIdLabel' }); - const companyTaxIdPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.companyTaxIdPlaceholder', - }); - const companyTaxIdRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.companyTaxIdRequired', - }) - ); - const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); - const countryPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.countryPlaceholder', - }); - const countryRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.countryRequired', - }) - ); - - // StripeBankAccountTokenInputField handles the error messages - // internally, we just have to make sure we require a valid token - // out of the field. Therefore the empty validation message. - const bankAccountRequired = validators.required(' '); - - const showPersonalIdNumber = - (country && stripeCountryConfigs(country).personalIdNumberRequired) || - (country && stripeCountryConfigs(country).ssnLast4Required); - - const personalIdNumberRequired = validators.required( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberRequired`, - }) - ); - - let personalIdNumberLabel = null; - let personalIdNumberPlaceholder = null; - let personalIdNumberValid = personalIdNumberRequired; - - if (country === 'US') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.US`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, - }); - - const validSSN = validators.validSsnLast4( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); - } else if (country === 'HK') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.HK`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, - }); - const validHKID = validators.validHKID( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); - } - const classes = classNames(css.root, className, { [css.disabled]: disabled, }); + const submitInProgress = inProgress; const submitDisabled = pristine || invalid || disabled || submitInProgress; const showAsRequired = pristine; @@ -242,257 +105,32 @@ const PayoutDetailsFormComponent = props => ( {showIndividual ? ( - -
-

- -

-
- - -
- -
- -
-

- -

- - - {supportedCountries.map(c => ( - - ))} - - - -
- {country ? ( -
-

- -

- -
- ) : null} - - {showPersonalIdNumber ? ( -
-

- -

- -
- ) : null} - - {error} - -

- -

- -
+ + ) : showCompany ? ( + ) : null} - {showCompany === 'company' ? ( - -
-

- -

-
- - -
-
-
-

- -

+ {error} - - - {supportedCountries.map(c => ( - - ))} - - - -
- {country ? ( - -
-

- -

- -
-
- ) : null} - - {showPersonalIdNumber ? ( -
-

- -

- -
- ) : null} - - {error} - -

- -

- -
- ) : null} +

+ +

+ ); }} diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js new file mode 100644 index 00000000..55ee0846 --- /dev/null +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -0,0 +1,319 @@ +import React from 'react'; +import { bool, object, string } from 'prop-types'; +import { compose } from 'redux'; +import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; +import config from '../../config'; +import { + StripeBankAccountTokenInputField, + FieldSelect, + FieldBirthdayInput, + FieldTextInput, +} from '../../components'; +import * as validators from '../../util/validators'; + +import PayoutDetailsAddress from './PayoutDetailsAddress'; +import css from './PayoutDetailsForm.css'; + +const MIN_STRIPE_ACCOUNT_AGE = 18; + +const supportedCountries = config.stripe.supportedCountries.map(c => c.code); + +export const stripeCountryConfigs = countryCode => { + const country = config.stripe.supportedCountries.find(c => c.code === countryCode); + + if (!country) { + throw new Error(`Country code not found in Stripe config ${countryCode}`); + } + return country; +}; + +const countryCurrency = countryCode => { + const country = stripeCountryConfigs(countryCode); + return country.currency; +}; + +const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { + const { disabled, form, intl, values } = fieldRenderProps; + const { country } = values; + + const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); + const firstNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.firstNamePlaceholder', + }); + const firstNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.firstNameRequired', + }) + ); + + const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); + const lastNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.lastNamePlaceholder', + }); + const lastNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.lastNameRequired', + }) + ); + + const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); + const birthdayLabelMonth = intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayLabelMonth', + }); + const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); + const birthdayRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayRequired', + }) + ); + const birthdayMinAge = validators.ageAtLeast( + intl.formatMessage( + { + id: 'PayoutDetailsForm.birthdayMinAge', + }, + { + minAge: MIN_STRIPE_ACCOUNT_AGE, + } + ), + MIN_STRIPE_ACCOUNT_AGE + ); + + const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); + const companyNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.companyNamePlaceholder', + }); + const companyNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.companyNameRequired', + }) + ); + + const companyTaxIdLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyTaxIdLabel' }); + const companyTaxIdPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.companyTaxIdPlaceholder', + }); + const companyTaxIdRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.companyTaxIdRequired', + }) + ); + const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); + const countryPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.countryPlaceholder', + }); + const countryRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.countryRequired', + }) + ); + + // StripeBankAccountTokenInputField handles the error messages + // internally, we just have to make sure we require a valid token + // out of the field. Therefore the empty validation message. + const bankAccountRequired = validators.required(' '); + + const showPersonalIdNumber = + (country && stripeCountryConfigs(country).personalIdNumberRequired) || + (country && stripeCountryConfigs(country).ssnLast4Required); + + const personalIdNumberRequired = validators.required( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberRequired`, + }) + ); + + let personalIdNumberLabel = null; + let personalIdNumberPlaceholder = null; + let personalIdNumberValid = personalIdNumberRequired; + + if (country === 'US') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.US`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, + }); + + const validSSN = validators.validSsnLast4( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); + } else if (country === 'HK') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.HK`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, + }); + const validHKID = validators.validHKID( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); + } + + return ( + +
+

+ +

+
+ + +
+
+
+

+ +

+ + + + {supportedCountries.map(c => ( + + ))} + + + +
+ {country ? ( + +
+

+ +

+ +
+
+ ) : null} + +
+

+ +

+
+ + +
+ +
+ + {showPersonalIdNumber ? ( +
+

+ +

+ +
+ ) : null} +
+ ); +}; + +PayoutDetailsFormCompanyComponent.defaultProps = { + className: null, + country: null, + createStripeAccountError: null, + disabled: false, + inProgress: false, + ready: false, + submitButtonText: null, +}; + +PayoutDetailsFormCompanyComponent.propTypes = { + className: string, + createStripeAccountError: object, + disabled: bool, + inProgress: bool, + ready: bool, + submitButtonText: string, + + // from injectIntl + intl: intlShape.isRequired, +}; + +const PayoutDetailsFormCompany = compose(injectIntl)(PayoutDetailsFormCompanyComponent); + +export default PayoutDetailsFormCompany; diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js new file mode 100644 index 00000000..47c5b05f --- /dev/null +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js @@ -0,0 +1,259 @@ +import React from 'react'; +import { bool, object } from 'prop-types'; +import { compose } from 'redux'; +import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; +import config from '../../config'; +import { + StripeBankAccountTokenInputField, + FieldSelect, + FieldBirthdayInput, + FieldTextInput, +} from '../../components'; +import * as validators from '../../util/validators'; + +import PayoutDetailsAddress from './PayoutDetailsAddress'; +import css from './PayoutDetailsForm.css'; + +const MIN_STRIPE_ACCOUNT_AGE = 18; + +const supportedCountries = config.stripe.supportedCountries.map(c => c.code); + +export const stripeCountryConfigs = countryCode => { + const country = config.stripe.supportedCountries.find(c => c.code === countryCode); + + if (!country) { + throw new Error(`Country code not found in Stripe config ${countryCode}`); + } + return country; +}; + +const countryCurrency = countryCode => { + const country = stripeCountryConfigs(countryCode); + return country.currency; +}; + +const PayoutDetailsFormIndividualComponent = ({ fieldRenderProps }) => { + const { disabled, form, intl, values } = fieldRenderProps; + const { country } = values; + + const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); + const firstNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.firstNamePlaceholder', + }); + const firstNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.firstNameRequired', + }) + ); + + const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); + const lastNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.lastNamePlaceholder', + }); + const lastNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.lastNameRequired', + }) + ); + + const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); + const birthdayLabelMonth = intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayLabelMonth', + }); + const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); + const birthdayRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayRequired', + }) + ); + const birthdayMinAge = validators.ageAtLeast( + intl.formatMessage( + { + id: 'PayoutDetailsForm.birthdayMinAge', + }, + { + minAge: MIN_STRIPE_ACCOUNT_AGE, + } + ), + MIN_STRIPE_ACCOUNT_AGE + ); + + const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); + const countryPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.countryPlaceholder', + }); + const countryRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.countryRequired', + }) + ); + + // StripeBankAccountTokenInputField handles the error messages + // internally, we just have to make sure we require a valid token + // out of the field. Therefore the empty validation message. + const bankAccountRequired = validators.required(' '); + + const showPersonalIdNumber = + (country && stripeCountryConfigs(country).personalIdNumberRequired) || + (country && stripeCountryConfigs(country).ssnLast4Required); + + const personalIdNumberRequired = validators.required( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberRequired`, + }) + ); + + let personalIdNumberLabel = null; + let personalIdNumberPlaceholder = null; + let personalIdNumberValid = personalIdNumberRequired; + + if (country === 'US') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.US`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, + }); + + const validSSN = validators.validSsnLast4( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); + } else if (country === 'HK') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.HK`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, + }); + const validHKID = validators.validHKID( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); + } + + return ( + +
+

+ +

+
+ + +
+ +
+ +
+

+ +

+ + + {supportedCountries.map(c => ( + + ))} + + + +
+ {country ? ( +
+

+ +

+ +
+ ) : null} + + {showPersonalIdNumber ? ( +
+

+ +

+ +
+ ) : null} +
+ ); +}; + +PayoutDetailsFormIndividualComponent.defaultProps = { + disabled: false, +}; + +PayoutDetailsFormIndividualComponent.propTypes = { + disabled: bool, + form: object.isRequired, + + // from injectIntl + intl: intlShape.isRequired, +}; + +const PayoutDetailsFormIndividual = compose(injectIntl)(PayoutDetailsFormIndividualComponent); + +export default PayoutDetailsFormIndividual;