mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Update StripePaymentForm: Add billing address fields.
Form is not visible after handleCardPayment is called. Show errors on various steps of PaymentIntent (SCA) process.
This commit is contained in:
parent
5854ff0ecc
commit
9343ab9f30
5 changed files with 412 additions and 93 deletions
186
src/forms/StripePaymentForm/StripePaymentAddress.js
Normal file
186
src/forms/StripePaymentForm/StripePaymentAddress.js
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
import React from 'react';
|
||||
import { intlShape } from 'react-intl';
|
||||
import { bool, object, string } from 'prop-types';
|
||||
import config from '../../config';
|
||||
import * as validators from '../../util/validators';
|
||||
import getCountryCodes from '../../translations/countryCodes';
|
||||
import { FieldTextInput, FieldSelect } from '../../components';
|
||||
|
||||
import css from './StripePaymentForm.css';
|
||||
|
||||
const StripePaymentAddress = props => {
|
||||
const { className, intl, disabled, form, fieldId, card } = props;
|
||||
|
||||
const optionalText = intl.formatMessage({
|
||||
id: 'StripePaymentAddress.optionalText',
|
||||
});
|
||||
|
||||
const addressLine1Label = intl.formatMessage({
|
||||
id: 'StripePaymentAddress.addressLine1Label',
|
||||
});
|
||||
const addressLine1Placeholder = intl.formatMessage({
|
||||
id: 'StripePaymentAddress.addressLine1Placeholder',
|
||||
});
|
||||
const addressLine1Required = validators.required(
|
||||
intl.formatMessage({
|
||||
id: 'StripePaymentAddress.addressLine1Required',
|
||||
})
|
||||
);
|
||||
|
||||
const addressLine2Label = intl.formatMessage(
|
||||
{ id: 'StripePaymentAddress.addressLine2Label' },
|
||||
{ optionalText: optionalText }
|
||||
);
|
||||
|
||||
const addressLine2Placeholder = intl.formatMessage({
|
||||
id: 'StripePaymentAddress.addressLine2Placeholder',
|
||||
});
|
||||
|
||||
const postalCodeLabel = intl.formatMessage({ id: 'StripePaymentAddress.postalCodeLabel' });
|
||||
const postalCodePlaceholder = intl.formatMessage({
|
||||
id: 'StripePaymentAddress.postalCodePlaceholder',
|
||||
});
|
||||
const postalCodeRequired = validators.required(
|
||||
intl.formatMessage({
|
||||
id: 'StripePaymentAddress.postalCodeRequired',
|
||||
})
|
||||
);
|
||||
|
||||
const cityLabel = intl.formatMessage({ id: 'StripePaymentAddress.cityLabel' });
|
||||
const cityPlaceholder = intl.formatMessage({ id: 'StripePaymentAddress.cityPlaceholder' });
|
||||
const cityRequired = validators.required(
|
||||
intl.formatMessage({
|
||||
id: 'StripePaymentAddress.cityRequired',
|
||||
})
|
||||
);
|
||||
|
||||
const stateLabel = intl.formatMessage(
|
||||
{ id: 'StripePaymentAddress.stateLabel' },
|
||||
{ optionalText: optionalText }
|
||||
);
|
||||
const statePlaceholder = intl.formatMessage({ id: 'StripePaymentAddress.statePlaceholder' });
|
||||
|
||||
const countryLabel = intl.formatMessage({ id: 'StripePaymentAddress.countryLabel' });
|
||||
const countryPlaceholder = intl.formatMessage({ id: 'StripePaymentAddress.countryPlaceholder' });
|
||||
const countryRequired = validators.required(
|
||||
intl.formatMessage({
|
||||
id: 'StripePaymentAddress.countryRequired',
|
||||
})
|
||||
);
|
||||
|
||||
const handleOnChange = event => {
|
||||
const value = event.target.value;
|
||||
form.change('postal', value);
|
||||
card.update({ value: { postalCode: value } });
|
||||
};
|
||||
|
||||
// Use tha language set in config.locale to get the correct translations of the country names
|
||||
const countryCodes = getCountryCodes(config.locale);
|
||||
|
||||
return (
|
||||
<div className={className ? className : css.sectionContainer}>
|
||||
<FieldTextInput
|
||||
id={`${fieldId}.addressLine1`}
|
||||
name="addressLine1"
|
||||
disabled={disabled}
|
||||
className={css.field}
|
||||
type="text"
|
||||
autoComplete="billing address-line1"
|
||||
label={addressLine1Label}
|
||||
placeholder={addressLine1Placeholder}
|
||||
validate={addressLine1Required}
|
||||
onUnmount={() => form.change('addressLine1', undefined)}
|
||||
/>
|
||||
|
||||
<FieldTextInput
|
||||
id={`${fieldId}.addressLine2`}
|
||||
name="addressLine2"
|
||||
disabled={disabled}
|
||||
className={css.field}
|
||||
type="text"
|
||||
autoComplete="billing address-line2"
|
||||
label={addressLine2Label}
|
||||
placeholder={addressLine2Placeholder}
|
||||
onUnmount={() => form.change('addressLine2', undefined)}
|
||||
/>
|
||||
|
||||
<div className={css.formRow}>
|
||||
<FieldTextInput
|
||||
id={`${fieldId}.postalCode`}
|
||||
name="postal"
|
||||
disabled={disabled}
|
||||
className={css.postalCode}
|
||||
type="text"
|
||||
autoComplete="billing postal-code"
|
||||
label={postalCodeLabel}
|
||||
placeholder={postalCodePlaceholder}
|
||||
validate={postalCodeRequired}
|
||||
onUnmount={() => form.change('postal', undefined)}
|
||||
onChange={event => handleOnChange(event)}
|
||||
/>
|
||||
|
||||
<FieldTextInput
|
||||
id={`${fieldId}.city`}
|
||||
name="city"
|
||||
disabled={disabled}
|
||||
className={css.city}
|
||||
type="text"
|
||||
autoComplete="billing address-level2"
|
||||
label={cityLabel}
|
||||
placeholder={cityPlaceholder}
|
||||
validate={cityRequired}
|
||||
onUnmount={() => form.change('city', undefined)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FieldTextInput
|
||||
id={`${fieldId}.state`}
|
||||
name="state"
|
||||
disabled={disabled}
|
||||
className={css.field}
|
||||
type="text"
|
||||
autoComplete="billing address-level1"
|
||||
label={stateLabel}
|
||||
placeholder={statePlaceholder}
|
||||
onUnmount={() => form.change('state', undefined)}
|
||||
/>
|
||||
|
||||
<FieldSelect
|
||||
id={`${fieldId}.country`}
|
||||
name="country"
|
||||
disabled={disabled}
|
||||
className={css.field}
|
||||
label={countryLabel}
|
||||
validate={countryRequired}
|
||||
>
|
||||
<option disabled value="">
|
||||
{countryPlaceholder}
|
||||
</option>
|
||||
{countryCodes.map(country => {
|
||||
return (
|
||||
<option key={country.code} value={country.code}>
|
||||
{country.name}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</FieldSelect>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
StripePaymentAddress.defaultProps = {
|
||||
country: null,
|
||||
disabled: false,
|
||||
fieldId: null,
|
||||
};
|
||||
|
||||
StripePaymentAddress.propTypes = {
|
||||
country: string,
|
||||
disabled: bool,
|
||||
form: object.isRequired,
|
||||
fieldId: string,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default StripePaymentAddress;
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
height: 35px;
|
||||
}
|
||||
@media (--viewportLarge) {
|
||||
height: 42px;
|
||||
height: 38px;
|
||||
padding: 6px 0 14px 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -32,10 +32,34 @@
|
|||
border-bottom-color: var(--failColor);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--failColor);
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
margin-top: 24px;
|
||||
color: var(--failColor);
|
||||
}
|
||||
|
||||
.paymentHeading {
|
||||
margin: 0 0 14px 0;
|
||||
color: var(--matterColorAnti);
|
||||
|
||||
padding-top: 8px;
|
||||
padding-bottom: 0px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 0 0 26px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.billingHeading {
|
||||
margin: 0 0 14px 0;
|
||||
color: var(--matterColorAnti);
|
||||
|
||||
padding-top: 7px;
|
||||
padding-bottom: 1px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 0 0 26px 0;
|
||||
}
|
||||
|
|
@ -56,6 +80,9 @@
|
|||
color: var(--matterColorAnti);
|
||||
margin: 40px 0 14px 0;
|
||||
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 41px 0 26px 0;
|
||||
}
|
||||
|
|
@ -116,3 +143,29 @@
|
|||
.missingStripeKey {
|
||||
color: var(--failColor);
|
||||
}
|
||||
|
||||
.paymentAddressField {
|
||||
padding-top: 38px;
|
||||
}
|
||||
|
||||
.formRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.postalCode {
|
||||
margin-top: 24px;
|
||||
width: calc(40% - 9px);
|
||||
}
|
||||
|
||||
.city {
|
||||
margin-top: 24px;
|
||||
width: calc(60% - 9px);
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const Empty = {
|
|||
},
|
||||
intl: fakeIntl,
|
||||
onCreateStripePaymentToken: noop,
|
||||
onStripeInitialized: noop,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: null,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@
|
|||
* It's also handled separately in handleSubmit function.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bool, func, object, string } from 'prop-types';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { Form as FinalForm } from 'react-final-form';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import { propTypes } from '../../util/types';
|
||||
import { Form, PrimaryButton, FieldTextInput } from '../../components';
|
||||
|
||||
import StripePaymentAddress from './StripePaymentAddress';
|
||||
import css from './StripePaymentForm.css';
|
||||
|
||||
/**
|
||||
|
|
@ -78,19 +77,18 @@ const cardStyles = {
|
|||
|
||||
const initialState = {
|
||||
error: null,
|
||||
submitting: false,
|
||||
cardValueValid: false,
|
||||
token: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Payment form that asks for credit card info using Stripe Elements.
|
||||
*
|
||||
* When the card is valid and the user submits the form, a request is
|
||||
* sent to the Stripe API to fetch a token that is passed to the
|
||||
* onSubmit prop of this form.
|
||||
* sent to the Stripe API to handle payment. `stripe.handleCardPayment`
|
||||
* may ask more details from cardholder if 3D security steps are needed.
|
||||
*
|
||||
* See: https://stripe.com/docs/elements
|
||||
* See: https://stripe.com/docs/payments/payment-intents
|
||||
* https://stripe.com/docs/elements
|
||||
*/
|
||||
class StripePaymentForm extends Component {
|
||||
constructor(props) {
|
||||
|
|
@ -99,6 +97,7 @@ class StripePaymentForm extends Component {
|
|||
this.handleCardValueChange = this.handleCardValueChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.paymentForm = this.paymentForm.bind(this);
|
||||
this.finalFormAPI = null;
|
||||
}
|
||||
componentDidMount() {
|
||||
if (!window.Stripe) {
|
||||
|
|
@ -107,18 +106,22 @@ class StripePaymentForm extends Component {
|
|||
|
||||
if (config.stripe.publishableKey) {
|
||||
this.stripe = window.Stripe(config.stripe.publishableKey);
|
||||
const elements = this.stripe.elements(stripeElementsOptions);
|
||||
this.card = elements.create('card', { style: cardStyles });
|
||||
this.card.mount(this.cardContainer);
|
||||
this.card.addEventListener('change', this.handleCardValueChange);
|
||||
// EventListener is the only way to simulate breakpoints with Stripe.
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth < 1024) {
|
||||
this.card.update({ style: { base: { fontSize: '18px', lineHeight: '24px' } } });
|
||||
} else {
|
||||
this.card.update({ style: { base: { fontSize: '20px', lineHeight: '32px' } } });
|
||||
}
|
||||
});
|
||||
this.props.onStripeInitialized(this.stripe);
|
||||
|
||||
if (!this.props.hasHandledCardPayment) {
|
||||
const elements = this.stripe.elements(stripeElementsOptions);
|
||||
this.card = elements.create('card', { style: cardStyles });
|
||||
this.card.mount(this.cardContainer);
|
||||
this.card.addEventListener('change', this.handleCardValueChange);
|
||||
// EventListener is the only way to simulate breakpoints with Stripe.
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth < 1024) {
|
||||
this.card.update({ style: { base: { fontSize: '18px', lineHeight: '24px' } } });
|
||||
} else {
|
||||
this.card.update({ style: { base: { fontSize: '20px', lineHeight: '32px' } } });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
|
|
@ -128,71 +131,90 @@ class StripePaymentForm extends Component {
|
|||
}
|
||||
}
|
||||
handleCardValueChange(event) {
|
||||
const { intl, onChange } = this.props;
|
||||
const { intl } = this.props;
|
||||
const { error, complete } = event;
|
||||
|
||||
// A change in the card should clear the token and trigger a call
|
||||
// to the onChange prop with the cleared token and the current
|
||||
// message.
|
||||
const postalCode = event.value.postalCode;
|
||||
if (this.finalFormAPI) {
|
||||
this.finalFormAPI.change('postal', postalCode);
|
||||
}
|
||||
|
||||
this.setState(prevState => {
|
||||
const { message } = prevState;
|
||||
const token = null;
|
||||
onChange({ token, message });
|
||||
return {
|
||||
error: error ? stripeErrorTranslation(intl, error) : null,
|
||||
cardValueValid: complete,
|
||||
token,
|
||||
};
|
||||
});
|
||||
}
|
||||
handleSubmit(values) {
|
||||
const { onSubmit, stripePaymentTokenInProgress, stripePaymentToken } = this.props;
|
||||
const initialMessage = values.initialMessage ? values.initialMessage.trim() : null;
|
||||
const { onSubmit, inProgress, formId, hasHandledCardPayment } = this.props;
|
||||
const { initialMessage } = values;
|
||||
const cardInputNeedsAttention = !(hasHandledCardPayment || this.state.cardValueValid);
|
||||
|
||||
if (stripePaymentTokenInProgress || !this.state.cardValueValid) {
|
||||
if (inProgress || cardInputNeedsAttention) {
|
||||
// Already submitting or card value incomplete/invalid
|
||||
return;
|
||||
}
|
||||
|
||||
if (stripePaymentToken) {
|
||||
// Token already fetched for the current card value
|
||||
onSubmit({ token: stripePaymentToken.id, message: initialMessage });
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
stripe: this.stripe,
|
||||
message: initialMessage ? initialMessage.trim() : null,
|
||||
card: this.card,
|
||||
formId,
|
||||
formValues: values,
|
||||
};
|
||||
|
||||
this.props.onCreateStripePaymentToken(params).then(() => {
|
||||
onSubmit({ token: this.props.stripePaymentToken.id, message: initialMessage });
|
||||
});
|
||||
onSubmit(params);
|
||||
}
|
||||
|
||||
paymentForm(formRenderProps) {
|
||||
const {
|
||||
className,
|
||||
rootClassName,
|
||||
inProgress,
|
||||
inProgress: submitInProgress,
|
||||
formId,
|
||||
paymentInfo,
|
||||
authorDisplayName,
|
||||
showInitialMessageInput,
|
||||
intl,
|
||||
stripePaymentTokenInProgress,
|
||||
stripePaymentTokenError,
|
||||
initiateOrderError,
|
||||
handleCardPaymentError,
|
||||
confirmPaymentError,
|
||||
invalid,
|
||||
handleSubmit,
|
||||
form,
|
||||
hasHandledCardPayment,
|
||||
} = formRenderProps;
|
||||
|
||||
const submitInProgress = stripePaymentTokenInProgress || inProgress;
|
||||
const submitDisabled = invalid || !this.state.cardValueValid || submitInProgress;
|
||||
this.finalFormAPI = form;
|
||||
const billingDetailsNeeded = !(confirmPaymentError || hasHandledCardPayment);
|
||||
const cardInputNeedsAttention = !(hasHandledCardPayment || this.state.cardValueValid);
|
||||
const submitDisabled = invalid || cardInputNeedsAttention || submitInProgress;
|
||||
const hasCardError = this.state.error && !submitInProgress;
|
||||
const hasPaymentErrors = handleCardPaymentError || confirmPaymentError;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const cardClasses = classNames(css.card, {
|
||||
[css.cardSuccess]: this.state.cardValueValid,
|
||||
[css.cardError]: stripePaymentTokenError && !submitInProgress,
|
||||
[css.cardError]: hasCardError,
|
||||
});
|
||||
|
||||
// TODO: handleCardPayment can create all kinds of errors.
|
||||
// Currently, we provide translation support for one:
|
||||
// https://stripe.com/docs/error-codes
|
||||
const piAuthenticationFailure = 'payment_intent_authentication_failure';
|
||||
const paymentErrorMessage =
|
||||
handleCardPaymentError && handleCardPaymentError.code === piAuthenticationFailure
|
||||
? intl.formatMessage({ id: 'StripePaymentForm.handleCardPaymentError' })
|
||||
: handleCardPaymentError
|
||||
? handleCardPaymentError.message
|
||||
: confirmPaymentError
|
||||
? intl.formatMessage({ id: 'StripePaymentForm.confirmPaymentError' })
|
||||
: intl.formatMessage({ id: 'StripePaymentForm.genericError' });
|
||||
|
||||
const billingDetailsNameLabel = intl.formatMessage({
|
||||
id: 'StripePaymentForm.billingDetailsNameLabel',
|
||||
});
|
||||
|
||||
const billingDetailsNamePlaceholder = intl.formatMessage({
|
||||
id: 'StripePaymentForm.billingDetailsNamePlaceholder',
|
||||
});
|
||||
|
||||
const messagePlaceholder = intl.formatMessage(
|
||||
|
|
@ -209,43 +231,76 @@ class StripePaymentForm extends Component {
|
|||
{ messageOptionalText: messageOptionalText }
|
||||
);
|
||||
|
||||
const initialMessage = showInitialMessageInput ? (
|
||||
<div>
|
||||
<h3 className={css.messageHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.messageHeading" />
|
||||
</h3>
|
||||
// Asking billing address is recommended in PaymentIntent flow.
|
||||
// In CheckoutPage, we send name and email as billing details, but address only if it exists.
|
||||
const billingAddress = (
|
||||
<StripePaymentAddress intl={intl} form={form} fieldId={formId} card={this.card} />
|
||||
);
|
||||
|
||||
<FieldTextInput
|
||||
type="textarea"
|
||||
id={`${formId}-message`}
|
||||
name="initialMessage"
|
||||
label={initialMessageLabel}
|
||||
placeholder={messagePlaceholder}
|
||||
className={css.message}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
const hasStripeKey = config.stripe.publishableKey;
|
||||
|
||||
return config.stripe.publishableKey ? (
|
||||
return hasStripeKey ? (
|
||||
<Form className={classes} onSubmit={handleSubmit}>
|
||||
<h3 className={css.paymentHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.paymentHeading" />
|
||||
</h3>
|
||||
<label className={css.paymentLabel} htmlFor={`${formId}-card`}>
|
||||
<FormattedMessage id="StripePaymentForm.creditCardDetails" />
|
||||
</label>
|
||||
<div
|
||||
className={cardClasses}
|
||||
id={`${formId}-card`}
|
||||
ref={el => {
|
||||
this.cardContainer = el;
|
||||
}}
|
||||
/>
|
||||
{this.state.error && !submitInProgress ? (
|
||||
<span style={{ color: 'red' }}>{this.state.error}</span>
|
||||
{billingDetailsNeeded ? (
|
||||
<React.Fragment>
|
||||
<h3 className={css.paymentHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.paymentHeading" />
|
||||
</h3>
|
||||
<label className={css.paymentLabel} htmlFor={`${formId}-card`}>
|
||||
<FormattedMessage id="StripePaymentForm.creditCardDetails" />
|
||||
</label>
|
||||
|
||||
<div
|
||||
className={cardClasses}
|
||||
id={`${formId}-card`}
|
||||
ref={el => {
|
||||
this.cardContainer = el;
|
||||
}}
|
||||
/>
|
||||
{hasCardError ? <span className={css.error}>{this.state.error}</span> : null}
|
||||
<div className={css.paymentAddressField}>
|
||||
<h3 className={css.billingHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.billingDetails" />
|
||||
</h3>
|
||||
|
||||
<FieldTextInput
|
||||
className={css.field}
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
autoComplete="cc-name"
|
||||
label={billingDetailsNameLabel}
|
||||
placeholder={billingDetailsNamePlaceholder}
|
||||
/>
|
||||
|
||||
{billingAddress}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
|
||||
{initiateOrderError ? (
|
||||
<span className={css.errorMessage}>{initiateOrderError.message}</span>
|
||||
) : null}
|
||||
{showInitialMessageInput ? (
|
||||
<div>
|
||||
<h3 className={css.messageHeading}>
|
||||
<FormattedMessage id="StripePaymentForm.messageHeading" />
|
||||
</h3>
|
||||
|
||||
<FieldTextInput
|
||||
type="textarea"
|
||||
id={`${formId}-message`}
|
||||
name="initialMessage"
|
||||
label={initialMessageLabel}
|
||||
placeholder={messagePlaceholder}
|
||||
className={css.message}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{initialMessage}
|
||||
<div className={css.submitContainer}>
|
||||
{hasPaymentErrors ? (
|
||||
<span className={css.errorMessage}>{paymentErrorMessage}</span>
|
||||
) : null}
|
||||
<p className={css.paymentInfo}>{paymentInfo}</p>
|
||||
<PrimaryButton
|
||||
className={css.submitButton}
|
||||
|
|
@ -253,7 +308,11 @@ class StripePaymentForm extends Component {
|
|||
inProgress={submitInProgress}
|
||||
disabled={submitDisabled}
|
||||
>
|
||||
<FormattedMessage id="StripePaymentForm.submitPaymentInfo" />
|
||||
{billingDetailsNeeded ? (
|
||||
<FormattedMessage id="StripePaymentForm.submitPaymentInfo" />
|
||||
) : (
|
||||
<FormattedMessage id="StripePaymentForm.submitConfirmPaymentInfo" />
|
||||
)}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
@ -274,30 +333,27 @@ StripePaymentForm.defaultProps = {
|
|||
className: null,
|
||||
rootClassName: null,
|
||||
inProgress: false,
|
||||
onChange: () => null,
|
||||
showInitialMessageInput: true,
|
||||
stripePaymentToken: null,
|
||||
stripePaymentTokenInProgress: false,
|
||||
stripePaymentTokenError: null,
|
||||
hasHandledCardPayment: false,
|
||||
initiateOrderError: null,
|
||||
handleCardPaymentError: null,
|
||||
confirmPaymentError: null,
|
||||
};
|
||||
|
||||
const { bool, func, string, object } = PropTypes;
|
||||
|
||||
StripePaymentForm.propTypes = {
|
||||
className: string,
|
||||
rootClassName: string,
|
||||
inProgress: bool,
|
||||
initiateOrderError: object,
|
||||
handleCardPaymentError: object,
|
||||
confirmPaymentError: object,
|
||||
formId: string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
onSubmit: func.isRequired,
|
||||
onChange: func,
|
||||
paymentInfo: string.isRequired,
|
||||
authorDisplayName: string.isRequired,
|
||||
showInitialMessageInput: bool,
|
||||
onCreateStripePaymentToken: func.isRequired,
|
||||
stripePaymentTokenInProgress: bool,
|
||||
stripePaymentTokenError: propTypes.error,
|
||||
stripePaymentToken: object,
|
||||
hasHandledCardPayment: bool,
|
||||
};
|
||||
|
||||
export default injectIntl(StripePaymentForm);
|
||||
|
|
|
|||
|
|
@ -778,8 +778,30 @@
|
|||
"StripeBankAccountTokenInputField.transitNumber.placeholder": "Type in transit number…",
|
||||
"StripeBankAccountTokenInputField.transitNumber.required": "Transit number is required",
|
||||
"StripeBankAccountTokenInputField.unsupportedCountry": "Country not supported: {country}",
|
||||
"StripePaymentAddress.addressLine1Label": "Street address",
|
||||
"StripePaymentAddress.addressLine1Placeholder": "123 Example Street",
|
||||
"StripePaymentAddress.addressLine1Required": "Street address is required",
|
||||
"StripePaymentAddress.addressLine2Label": "Apt, suite, building # {optionalText}",
|
||||
"StripePaymentAddress.addressLine2Placeholder": "A 42",
|
||||
"StripePaymentAddress.cityLabel": "City",
|
||||
"StripePaymentAddress.cityPlaceholder": "Helsinki",
|
||||
"StripePaymentAddress.cityRequired": "City is required",
|
||||
"StripePaymentAddress.countryLabel": "Country",
|
||||
"StripePaymentAddress.countryPlaceholder": "Select a country…",
|
||||
"StripePaymentAddress.countryRequired": "Country is required",
|
||||
"StripePaymentAddress.optionalText": "• optional",
|
||||
"StripePaymentAddress.postalCodeLabel": "Postal code",
|
||||
"StripePaymentAddress.postalCodePlaceholder": "00100",
|
||||
"StripePaymentAddress.postalCodeRequired": "Postal code is required",
|
||||
"StripePaymentAddress.stateLabel": "State {optionalText}",
|
||||
"StripePaymentAddress.statePlaceholder": "Enter your state",
|
||||
"StripePaymentForm.billingDetails": "Billing details",
|
||||
"StripePaymentForm.billingDetailsNameLabel": "Card holder's name",
|
||||
"StripePaymentForm.billingDetailsNamePlaceholder": "Enter your name…",
|
||||
"StripePaymentForm.confirmPaymentError": "Payment has been made but we were unable to confirm the booking. Please try to confirm the booking request again! If the booking is not confirmed in time, the payment will be fully refunded.",
|
||||
"StripePaymentForm.creditCardDetails": "Credit card details",
|
||||
"StripePaymentForm.genericError": "Could not handle payment data. Please try again.",
|
||||
"StripePaymentForm.handleCardPaymentError": "We are unable to authenticate your payment method. Please check your authentication details and try again.",
|
||||
"StripePaymentForm.messageHeading": "Message",
|
||||
"StripePaymentForm.messageLabel": "Say hello to your host {messageOptionalText}",
|
||||
"StripePaymentForm.messageOptionalText": "• optional",
|
||||
|
|
@ -806,6 +828,7 @@
|
|||
"StripePaymentForm.stripe.validation_error.invalid_swipe_data": "The card's swipe data is invalid.",
|
||||
"StripePaymentForm.stripe.validation_error.missing": "There is no card on a customer that is being charged.",
|
||||
"StripePaymentForm.stripe.validation_error.processing_error": "An error occurred while processing the card.",
|
||||
"StripePaymentForm.submitConfirmPaymentInfo": "Confirm request",
|
||||
"StripePaymentForm.submitPaymentInfo": "Send request",
|
||||
"TermsOfServicePage.heading": "Terms of Service",
|
||||
"TermsOfServicePage.privacyTabTitle": "Privacy Policy",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue