From 45e00d2241e82208b8455d08772855baafcffdd2 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Wed, 5 Dec 2018 13:59:17 +0200 Subject: [PATCH] Add Stripe support for Canada --- ...tripeBankAccountTokenInputField.example.js | 17 +++++++ .../StripeBankAccountTokenInputField.util.js | 23 ++++++++- src/config.js | 16 ++++++ src/ducks/user.duck.js | 4 ++ .../PayoutDetailsForm/PayoutDetailsAddress.js | 51 ++++++++++++++++++- src/translations/en.json | 28 ++++++++++ 6 files changed, 136 insertions(+), 3 deletions(-) diff --git a/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.example.js b/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.example.js index f0b95162..c791f073 100644 --- a/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.example.js +++ b/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.example.js @@ -104,3 +104,20 @@ export const AU_AUD = { }, group: 'custom inputs', }; + +// CA +export const CA_CAD = { + component: formComponent('CA'), + props: { + formName: 'CA_CAD', + onChange: formState => { + if (formState.dirty) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('values submitted:', values); + }, + }, + group: 'custom inputs', +}; diff --git a/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.util.js b/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.util.js index 83277d0d..60b6d645 100644 --- a/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.util.js +++ b/src/components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.util.js @@ -6,6 +6,10 @@ import config from '../../config'; export const ACCOUNT_NUMBER = 'accountNumber'; // Australian equivalent for routing number export const BSB = 'bsb'; +// Needed for creating full routing number in Canada +export const INSTITUTION_NUMBER = 'institutionNumber'; +// Needed for creating full routing number in Canada +export const TRANSIT_NUMBER = 'transitNumber'; // International bank account number (e.g. EU countries use this) export const IBAN = 'iban'; // Routing number to separate bank account in different areas @@ -15,7 +19,15 @@ export const SORT_CODE = 'sortCode'; // Currently supported bank account inputs // the order here matters: account number input is asked after routing number and its equivalents -export const BANK_ACCOUNT_INPUTS = [BSB, SORT_CODE, ROUTING_NUMBER, ACCOUNT_NUMBER, IBAN]; +export const BANK_ACCOUNT_INPUTS = [ + BSB, + TRANSIT_NUMBER, + INSTITUTION_NUMBER, + SORT_CODE, + ROUTING_NUMBER, + ACCOUNT_NUMBER, + IBAN, +]; export const supportedCountries = config.stripe.supportedCountries.map(c => c.code); @@ -117,7 +129,7 @@ export const translateStripeError = (country, intl, stripeError) => { * * @return {Object} key - value in Object literal. */ -export const mapInputsToStripeAccountKeys = (country, inputsNeeded, values) => { +export const mapInputsToStripeAccountKeys = (country, values) => { // Stripe documentation speaks about actual bank account terms of different countries // (like BSB, sort code, routing number), but all of those get mapped to one of // the two different request keys: routing_number or account_number @@ -149,6 +161,13 @@ export const mapInputsToStripeAccountKeys = (country, inputsNeeded, values) => { routing_number: cleanedString(values[BSB]), account_number: cleanedString(values[ACCOUNT_NUMBER]), }; + case 'CA': + return { + routing_number: cleanedString(values[TRANSIT_NUMBER]).concat( + cleanedString(values[INSTITUTION_NUMBER]) + ), + account_number: cleanedString(values[ACCOUNT_NUMBER]), + }; case 'GB': return { routing_number: cleanedString(values[SORT_CODE]), diff --git a/src/config.js b/src/config.js index e224fdc4..133971d5 100644 --- a/src/config.js +++ b/src/config.js @@ -127,6 +127,22 @@ const stripeSupportedCountries = [ iban: true, }, }, + { + // Canada + code: 'CA', + currency: 'CAD', + addressConfig: { + addressLine: true, + city: true, + postalCode: true, + province: true, + }, + accountConfig: { + transitNumber: true, + institutionNumber: true, + accountNumber: true, + }, + }, { // Denmark code: 'DK', diff --git a/src/ducks/user.duck.js b/src/ducks/user.duck.js index 48ecc506..b33b9eeb 100644 --- a/src/ducks/user.duck.js +++ b/src/ducks/user.duck.js @@ -398,14 +398,18 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => postalCode, city, state, + province, bankAccountToken, personalIdNumber, } = payoutDetails; + const hasProvince = province && !state; + const address = { city, line1: streetAddress, postal_code: postalCode, + state: hasProvince ? province : state, }; // Params for Stripe SDK diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js index b0990d08..0e9ed0e1 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js @@ -1,11 +1,27 @@ import React from 'react'; import { bool, object, string } from 'prop-types'; -import { FieldTextInput } from '../../components'; +import { FieldSelect, FieldTextInput } from '../../components'; import * as validators from '../../util/validators'; import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; +const CANADIAN_PROVINCES = [ + 'AB', + 'BC', + 'MB', + 'NB', + 'NL', + 'NS', + 'NT', + 'NU', + 'ON', + 'PE', + 'QC', + 'SK', + 'YT', +]; + const PayoutDetailsAddress = props => { const { country, intl, disabled, form } = props; const countryConfig = country ? stripeCountryConfigs(country).addressConfig : null; @@ -60,6 +76,18 @@ const PayoutDetailsAddress = props => { }) ); + const showProvince = country && isRequired(countryConfig, 'province'); + + const provinceLabel = intl.formatMessage({ id: 'PayoutDetailsForm.canadianProvinceLabel' }); + const provincePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.canadianProvincePlaceholder', + }); + const provinceRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.canadianProvinceRequired', + }) + ); + return (
{showAddressLine ? ( @@ -120,6 +148,27 @@ const PayoutDetailsAddress = props => { onUnmount={() => form.change('state', undefined)} /> ) : null} + + {showProvince ? ( + + + {CANADIAN_PROVINCES.map(p => ( + + ))} + + ) : null}
); }; diff --git a/src/translations/en.json b/src/translations/en.json index 7de039a6..9de33dc9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -445,6 +445,22 @@ "PayoutDetailsForm.birthdayMonthPlaceholder": "mm", "PayoutDetailsForm.birthdayRequired": "Birthday is required and must be a valid date.", "PayoutDetailsForm.birthdayYearPlaceholder": "yyyy", + "PayoutDetailsForm.canadianProvinceLabel": "Province", + "PayoutDetailsForm.canadianProvinceNames.AB": "Alberta", + "PayoutDetailsForm.canadianProvinceNames.BC": "British Columbia", + "PayoutDetailsForm.canadianProvinceNames.MB": "Manitoba", + "PayoutDetailsForm.canadianProvinceNames.NB": "New Brunswick", + "PayoutDetailsForm.canadianProvinceNames.NL": "Newfoundland and Labrador", + "PayoutDetailsForm.canadianProvinceNames.NS": "Nova Scotia", + "PayoutDetailsForm.canadianProvinceNames.NT": "Northwest Territories", + "PayoutDetailsForm.canadianProvinceNames.NU": "Nunavut", + "PayoutDetailsForm.canadianProvinceNames.ON": "Ontario", + "PayoutDetailsForm.canadianProvinceNames.PE": "Prince Edward Island", + "PayoutDetailsForm.canadianProvinceNames.QC": "Quebec", + "PayoutDetailsForm.canadianProvinceNames.SK": "Saskatchewan", + "PayoutDetailsForm.canadianProvinceNames.YT": "Yukon", + "PayoutDetailsForm.canadianProvincePlaceholder": "Province", + "PayoutDetailsForm.canadianProvinceRequired": "This field is required", "PayoutDetailsForm.cityLabel": "City", "PayoutDetailsForm.cityPlaceholder": "Helsinki", "PayoutDetailsForm.cityRequired": "This field is required", @@ -452,6 +468,7 @@ "PayoutDetailsForm.countryNames.AT": "Austria", "PayoutDetailsForm.countryNames.AU": "Australia", "PayoutDetailsForm.countryNames.BE": "Belgium", + "PayoutDetailsForm.countryNames.CA": "Canada", "PayoutDetailsForm.countryNames.DE": "Germany", "PayoutDetailsForm.countryNames.DK": "Denmark", "PayoutDetailsForm.countryNames.ES": "Spain", @@ -478,6 +495,9 @@ "PayoutDetailsForm.lastNameRequired": "This field is required", "PayoutDetailsForm.personalDetailsTitle": "Personal details", "PayoutDetailsForm.personalIdNumberTitle": "Personal id number", + "PayoutDetailsForm.personalIdNumberLabel.HK": "Hong Kong Identity Card Number (HKID)", + "PayoutDetailsForm.personalIdNumberLabel.US": "Last 4 digits of social security number (SSN)", + "PayoutDetailsForm.personalIdNumberPlaceholder.HK": "XY123456", "PayoutDetailsForm.personalIdNumberRequired": "This field is required", "PayoutDetailsForm.postalCodeLabel": "Postal code", "PayoutDetailsForm.postalCodePlaceholder": "00100", @@ -644,6 +664,10 @@ "StripeBankAccountTokenInputField.iban.label": "Bank account number (IBAN)", "StripeBankAccountTokenInputField.iban.placeholder": "DE89 3704 0044 0532 0130 00", "StripeBankAccountTokenInputField.iban.required": "Bank account number (IBAN) is required", + "StripeBankAccountTokenInputField.institutionNumber.inline": "institution number", + "StripeBankAccountTokenInputField.institutionNumber.label": "Institution number", + "StripeBankAccountTokenInputField.institutionNumber.placeholder": "Type in institution number", + "StripeBankAccountTokenInputField.institutionNumber.required": "Institution number is required", "StripeBankAccountTokenInputField.routingNumber.inline": "routing number", "StripeBankAccountTokenInputField.routingNumber.label": "Routing number", "StripeBankAccountTokenInputField.routingNumber.placeholder": "Type in routing number…", @@ -653,6 +677,10 @@ "StripeBankAccountTokenInputField.sortCode.placeholder": "Type in sort code…", "StripeBankAccountTokenInputField.sortCode.required": "Sort code is required", "StripeBankAccountTokenInputField.unsupportedCountry": "Country not supported: {country}", + "StripeBankAccountTokenInputField.transitNumber.inline": "transit number", + "StripeBankAccountTokenInputField.transitNumber.label": "Transit number", + "StripeBankAccountTokenInputField.transitNumber.placeholder": "Type in transit number", + "StripeBankAccountTokenInputField.transitNumber.required": "Transit number is required", "StripePaymentForm.creditCardDetails": "Credit card details", "StripePaymentForm.genericError": "Could not handle payment data. Please try again.", "StripePaymentForm.messageHeading": "Message",