Add ID number field with validators to US and HK

This commit is contained in:
Jenni Nurmi 2018-12-10 11:27:43 +02:00
parent a137773b82
commit 0cc6cf9663
4 changed files with 108 additions and 18 deletions

View file

@ -412,6 +412,9 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) =>
state: hasProvince ? province : state,
};
const idNumber =
country === 'US' ? { ssn_last_4: personalIdNumber } : { personal_id_number: personalIdNumber };
// Params for Stripe SDK
const params = {
legal_entity: {
@ -420,7 +423,7 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) =>
address: omitBy(address, isUndefined),
dob: birthDate,
type: 'individual',
personal_id_number: personalIdNumber,
...idNumber,
},
tos_shown_and_accepted: true,
};

View file

@ -116,25 +116,47 @@ const PayoutDetailsFormComponent = props => (
const bankAccountRequired = validators.required(' ');
const showPersonalIdNumber =
country && stripeCountryConfigs(country).personalIdNumberRequired;
(country && stripeCountryConfigs(country).personalIdNumberRequired) ||
(country && stripeCountryConfigs(country).ssnLast4Required);
const personalIdNumberLabel = showPersonalIdNumber
? intl.formatMessage({
id: `PayoutDetailsForm.personalIdNumberLabel.${country}`,
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`,
})
: null;
const personalIdNumberPlaceholder = showPersonalIdNumber
? intl.formatMessage({
id: `PayoutDetailsForm.personalIdNumberPlaceholder.${country}`,
);
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`,
})
: null;
const personalIdNumberRequired = showPersonalIdNumber
? validators.required(
intl.formatMessage({
id: `PayoutDetailsForm.personalIdNumberRequired`,
})
)
: null;
);
personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID);
}
const classes = classNames(css.root, className, {
[css.disabled]: disabled,
@ -261,7 +283,7 @@ const PayoutDetailsFormComponent = props => (
type="text"
label={personalIdNumberLabel}
placeholder={personalIdNumberPlaceholder}
validate={personalIdNumberRequired}
validate={personalIdNumberValid}
/>
</div>
) : null}

View file

@ -503,6 +503,7 @@
"PayoutDetailsForm.personalIdNumberLabel.US": "Last 4 digits of social security number (SSN)",
"PayoutDetailsForm.personalIdNumberPlaceholder.HK": "XY123456",
"PayoutDetailsForm.personalIdNumberRequired": "This field is required",
"PayoutDetailsForm.personalIdNumberValid": "Invalid value",
"PayoutDetailsForm.postalCodeLabel": "Postal code",
"PayoutDetailsForm.postalCodePlaceholder": "00100",
"PayoutDetailsForm.postalCodeRequired": "This field is required",

View file

@ -136,5 +136,69 @@ export const ageAtLeast = (message, minYears) => value => {
return message;
};
export const validSsnLast4 = message => value => {
return value.length === 4 ? VALID : message;
};
export const validHKID = message => value => {
// Accept value 000000000 for testing Stripe
if (value.length === 9 && value.match(/([0]{9})/)) {
return VALID;
}
// HKID format example: AB364912(5)
// ID can start with one or two letters and the check digit in the end can be in brackets or not
if (value.length < 8) {
return message;
}
// Handle possible brackets in value
if (value.charAt(value.length - 3) === '(' && value.charAt(value.length - 1) === ')') {
value = value.substring(0, value.length - 3) + value.charAt(value.length - 2);
}
value = value.toUpperCase();
// Check that pattern is correct and split value to array
const hkidPattern = /^([A-Z]{1,2})([0-9]{6})([A0-9])$/;
const matchArray = value.match(hkidPattern);
if (!matchArray) {
return message;
}
const charPart = matchArray[1];
const numPart = matchArray[2];
const checkDigit = matchArray[3];
// Calculate the checksum for character part.
// Transfer letters to numbers so that A=10, B=11, C=12 etc.
// If there is only one letter in the ID use 36 as the first value
// Total calculation is weighted so that 1st digit is x9, 2nd digit x8, 3rd digit x7 etc.
const strValidChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let checkSum = 0;
if (charPart.length === 2) {
checkSum += 9 * (10 + strValidChars.indexOf(charPart.charAt(0)));
checkSum += 8 * (10 + strValidChars.indexOf(charPart.charAt(1)));
} else {
checkSum += 9 * 36;
checkSum += 8 * (10 + strValidChars.indexOf(charPart));
}
// Calculate the checksum for numeric part
for (let i = 0, j = 7; i < numPart.length; i++, j--) {
checkSum += j * numPart.charAt(i);
}
// Verify the check digit
const remaining = checkSum % 11;
const verify = remaining === 0 ? 0 : 11 - remaining;
const isValid = verify === checkDigit || (verify === 10 && checkDigit === 'A');
return isValid ? VALID : message;
};
export const composeValidators = (...validators) => value =>
validators.reduce((error, validator) => error || validator(value), VALID);