[CLD-6219] Add Company Name field to Purchase Modal (#24428)
Automatic Merge
Этот коммит содержится в:
@@ -266,10 +266,11 @@ type CloudWorkspaceOwner struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SubscriptionChange struct {
|
type SubscriptionChange struct {
|
||||||
ProductID string `json:"product_id"`
|
ProductID string `json:"product_id"`
|
||||||
Seats int `json:"seats"`
|
Seats int `json:"seats"`
|
||||||
Feedback *Feedback `json:"downgrade_feedback"`
|
Feedback *Feedback `json:"downgrade_feedback"`
|
||||||
ShippingAddress *Address `json:"shipping_address"`
|
ShippingAddress *Address `json:"shipping_address"`
|
||||||
|
Customer *CloudCustomerInfo `json:"customer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilesLimits struct {
|
type FilesLimits struct {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import type {Stripe} from '@stripe/stripe-js';
|
import type {Stripe} from '@stripe/stripe-js';
|
||||||
import {getCode} from 'country-list';
|
import {getCode} from 'country-list';
|
||||||
|
|
||||||
import type {Address, Feedback, WorkspaceDeletionRequest} from '@mattermost/types/cloud';
|
import type {Address, CloudCustomerPatch, Feedback, WorkspaceDeletionRequest} from '@mattermost/types/cloud';
|
||||||
|
|
||||||
import {CloudTypes} from 'mattermost-redux/action_types';
|
import {CloudTypes} from 'mattermost-redux/action_types';
|
||||||
import {getCloudCustomer, getCloudProducts, getCloudSubscription, getInvoices} from 'mattermost-redux/actions/cloud';
|
import {getCloudCustomer, getCloudProducts, getCloudSubscription, getInvoices} from 'mattermost-redux/actions/cloud';
|
||||||
@@ -89,6 +89,7 @@ export function subscribeCloudSubscription(
|
|||||||
shippingAddress: Address = getBlankAddressWithCountry(),
|
shippingAddress: Address = getBlankAddressWithCountry(),
|
||||||
seats = 0,
|
seats = 0,
|
||||||
downgradeFeedback?: Feedback,
|
downgradeFeedback?: Feedback,
|
||||||
|
customerPatch?: CloudCustomerPatch,
|
||||||
) {
|
) {
|
||||||
return async () => {
|
return async () => {
|
||||||
try {
|
try {
|
||||||
@@ -97,6 +98,7 @@ export function subscribeCloudSubscription(
|
|||||||
shippingAddress,
|
shippingAddress,
|
||||||
seats,
|
seats,
|
||||||
downgradeFeedback,
|
downgradeFeedback,
|
||||||
|
customerPatch,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {data: subscription};
|
return {data: subscription};
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
import type {
|
import type {
|
||||||
StripeCardElementChangeEvent,
|
StripeCardElementChangeEvent,
|
||||||
} from '@stripe/stripe-js';
|
} from '@stripe/stripe-js';
|
||||||
import {getName} from 'country-list';
|
import React, {useRef} from 'react';
|
||||||
import React from 'react';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
|
|
||||||
import type {PaymentMethod} from '@mattermost/types/cloud';
|
import type {CloudCustomer, PaymentMethod} from '@mattermost/types/cloud';
|
||||||
|
|
||||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ import DropdownInput from 'components/dropdown_input';
|
|||||||
import Input from 'components/widgets/inputs/input/input';
|
import Input from 'components/widgets/inputs/input/input';
|
||||||
|
|
||||||
import {COUNTRIES} from 'utils/countries';
|
import {COUNTRIES} from 'utils/countries';
|
||||||
import * as Utils from 'utils/utils';
|
|
||||||
|
|
||||||
import type {BillingDetails} from 'types/cloud/sku';
|
import type {BillingDetails} from 'types/cloud/sku';
|
||||||
|
|
||||||
@@ -37,6 +35,7 @@ type Props = {
|
|||||||
onInputChange?: (billing: BillingDetails) => void;
|
onInputChange?: (billing: BillingDetails) => void;
|
||||||
onInputBlur?: (billing: BillingDetails) => void;
|
onInputBlur?: (billing: BillingDetails) => void;
|
||||||
buttonFooter?: JSX.Element;
|
buttonFooter?: JSX.Element;
|
||||||
|
customer?: CloudCustomer | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@@ -48,57 +47,27 @@ type State = {
|
|||||||
postalCode: string;
|
postalCode: string;
|
||||||
name: string;
|
name: string;
|
||||||
changePaymentMethod: boolean;
|
changePaymentMethod: boolean;
|
||||||
|
company_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PaymentForm extends React.PureComponent<Props, State> {
|
const PaymentForm: React.FC<Props> = (props: Props) => {
|
||||||
static defaultProps = {
|
const {className, paymentMethod, buttonFooter, theme} = props;
|
||||||
showSaveCard: false,
|
const {formatMessage} = useIntl();
|
||||||
className: '',
|
const cardRef = useRef<CardInputType>(null);
|
||||||
};
|
|
||||||
|
|
||||||
cardRef: React.RefObject<CardInputType>;
|
const [state, setState] = React.useState<State>({
|
||||||
|
address: '',
|
||||||
|
address2: '',
|
||||||
|
city: '',
|
||||||
|
state: '',
|
||||||
|
country: '',
|
||||||
|
postalCode: '',
|
||||||
|
name: '',
|
||||||
|
changePaymentMethod: paymentMethod == null,
|
||||||
|
company_name: props.customer?.name || '',
|
||||||
|
});
|
||||||
|
|
||||||
public constructor(props: Props) {
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.cardRef = React.createRef<CardInputType>();
|
|
||||||
|
|
||||||
this.state = this.getResetState(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: Props) {
|
|
||||||
if (prevProps.paymentMethod == null && this.props.paymentMethod != null) {
|
|
||||||
this.resetState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.initialBillingDetails === undefined && this.props.initialBillingDetails !== undefined) {
|
|
||||||
this.resetState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private resetState = () => {
|
|
||||||
this.setState(this.getResetState());
|
|
||||||
};
|
|
||||||
|
|
||||||
private getResetState = (props = this.props) => {
|
|
||||||
const {initialBillingDetails, paymentMethod} = props;
|
|
||||||
|
|
||||||
const billingDetails = initialBillingDetails || {} as BillingDetails;
|
|
||||||
|
|
||||||
return {
|
|
||||||
address: billingDetails.address,
|
|
||||||
address2: billingDetails.address2,
|
|
||||||
city: billingDetails.city,
|
|
||||||
state: billingDetails.state,
|
|
||||||
country: getName(billingDetails.country || '') || getName('US') || '',
|
|
||||||
postalCode: billingDetails.postalCode,
|
|
||||||
name: billingDetails.name,
|
|
||||||
changePaymentMethod: paymentMethod == null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleInputChange = (event: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
const name = target.name;
|
const name = target.name;
|
||||||
const value = target.value;
|
const value = target.value;
|
||||||
@@ -107,277 +76,268 @@ export default class PaymentForm extends React.PureComponent<Props, State> {
|
|||||||
[name]: value,
|
[name]: value,
|
||||||
} as unknown as Pick<State, keyof State>;
|
} as unknown as Pick<State, keyof State>;
|
||||||
|
|
||||||
this.setState(newStateValue);
|
setState({...state, ...newStateValue});
|
||||||
|
|
||||||
const {onInputChange} = this.props;
|
const {onInputChange} = props;
|
||||||
if (onInputChange) {
|
if (onInputChange) {
|
||||||
onInputChange({...this.state, ...newStateValue, card: this.cardRef.current?.getCard()} as BillingDetails);
|
onInputChange({...state, ...newStateValue, card: cardRef.current?.getCard()} as BillingDetails);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleCardInputChange = (event: StripeCardElementChangeEvent) => {
|
const handleCardInputChange = (event: StripeCardElementChangeEvent) => {
|
||||||
if (this.props.onCardInputChange) {
|
if (props.onCardInputChange) {
|
||||||
this.props.onCardInputChange(event);
|
props.onCardInputChange(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleStateChange = (stateValue: string) => {
|
const handleStateChange = (stateValue: string) => {
|
||||||
const newStateValue = {
|
const newStateValue = {
|
||||||
state: stateValue,
|
state: stateValue,
|
||||||
} as unknown as Pick<State, keyof State>;
|
} as unknown as Pick<State, keyof State>;
|
||||||
this.setState(newStateValue);
|
setState({...state, ...newStateValue});
|
||||||
|
|
||||||
if (this.props.onInputChange) {
|
if (props.onInputChange) {
|
||||||
this.props.onInputChange({...this.state, ...newStateValue, card: this.cardRef.current?.getCard()} as BillingDetails);
|
props.onInputChange({...state, ...newStateValue, card: cardRef.current?.getCard()} as BillingDetails);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleCountryChange = (option: any) => {
|
const handleCountryChange = (option: any) => {
|
||||||
const newStateValue = {
|
const newStateValue = {
|
||||||
country: option.value,
|
country: option.value,
|
||||||
} as unknown as Pick<State, keyof State>;
|
} as unknown as Pick<State, keyof State>;
|
||||||
this.setState(newStateValue);
|
setState({...state, ...newStateValue});
|
||||||
|
|
||||||
if (this.props.onInputChange) {
|
if (props.onInputChange) {
|
||||||
this.props.onInputChange({...this.state, ...newStateValue, card: this.cardRef.current?.getCard()} as BillingDetails);
|
props.onInputChange({...state, ...newStateValue, card: cardRef.current?.getCard()} as BillingDetails);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onBlur = () => {
|
const onBlur = () => {
|
||||||
const {onInputBlur} = this.props;
|
const {onInputBlur} = props;
|
||||||
if (onInputBlur) {
|
if (onInputBlur) {
|
||||||
onInputBlur({...this.state, card: this.cardRef.current?.getCard()} as BillingDetails);
|
onInputBlur({...state, card: cardRef.current?.getCard()} as BillingDetails);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private changePaymentMethod = (event: React.MouseEvent<HTMLElement>) => {
|
const changePaymentMethod = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.setState({changePaymentMethod: true});
|
setState({...state, changePaymentMethod: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
let paymentDetails: JSX.Element;
|
||||||
const {className, paymentMethod, buttonFooter, theme} = this.props;
|
if (state.changePaymentMethod) {
|
||||||
const {changePaymentMethod} = this.state;
|
paymentDetails = (
|
||||||
|
<React.Fragment>
|
||||||
let paymentDetails: JSX.Element;
|
<div className='form-row'>
|
||||||
if (changePaymentMethod) {
|
<Input
|
||||||
paymentDetails = (
|
name='company_name'
|
||||||
<React.Fragment>
|
type='text'
|
||||||
<div className='form-row'>
|
value={state.company_name}
|
||||||
<CardInput
|
onChange={handleInputChange}
|
||||||
forwardedRef={this.cardRef}
|
onBlur={onBlur}
|
||||||
required={true}
|
placeholder={formatMessage({id: 'payment_form.company_name', defaultMessage: 'Company Name'})}
|
||||||
onBlur={this.onBlur}
|
required={true}
|
||||||
onCardInputChange={this.handleCardInputChange}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-row'>
|
|
||||||
<Input
|
|
||||||
name='name'
|
|
||||||
type='text'
|
|
||||||
value={this.state.name}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
onBlur={this.onBlur}
|
|
||||||
placeholder={Utils.localizeMessage(
|
|
||||||
'payment_form.name_on_card',
|
|
||||||
'Name on Card',
|
|
||||||
)}
|
|
||||||
required={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='section-title'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='payment_form.billing_address'
|
|
||||||
defaultMessage='Billing address'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<DropdownInput
|
|
||||||
onChange={this.handleCountryChange}
|
|
||||||
value={
|
|
||||||
this.state.country ? {value: this.state.country, label: this.state.country} : undefined
|
|
||||||
}
|
|
||||||
options={COUNTRIES.map((country) => ({
|
|
||||||
value: country.name,
|
|
||||||
label: country.name,
|
|
||||||
}))}
|
|
||||||
legend={Utils.localizeMessage(
|
|
||||||
'payment_form.country',
|
|
||||||
'Country',
|
|
||||||
)}
|
|
||||||
placeholder={Utils.localizeMessage(
|
|
||||||
'payment_form.country',
|
|
||||||
'Country',
|
|
||||||
)}
|
|
||||||
name={'billing_dropdown'}
|
|
||||||
/>
|
/>
|
||||||
<div className='form-row'>
|
</div>
|
||||||
<Input
|
<div className='form-row'>
|
||||||
name='address'
|
<CardInput
|
||||||
type='text'
|
forwardedRef={cardRef}
|
||||||
value={this.state.address}
|
required={true}
|
||||||
onChange={this.handleInputChange}
|
onBlur={onBlur}
|
||||||
onBlur={this.onBlur}
|
onCardInputChange={handleCardInputChange}
|
||||||
placeholder={Utils.localizeMessage(
|
theme={theme}
|
||||||
'payment_form.address',
|
/>
|
||||||
'Address',
|
</div>
|
||||||
)}
|
<div className='form-row'>
|
||||||
required={true}
|
<Input
|
||||||
/>
|
name='name'
|
||||||
</div>
|
type='text'
|
||||||
<div className='form-row'>
|
value={state.name}
|
||||||
<Input
|
onChange={handleInputChange}
|
||||||
name='address2'
|
onBlur={onBlur}
|
||||||
type='text'
|
placeholder={formatMessage({id: 'payment_form.name_on_card', defaultMessage: 'Name on Card'})}
|
||||||
value={this.state.address2}
|
required={true}
|
||||||
onChange={this.handleInputChange}
|
/>
|
||||||
onBlur={this.onBlur}
|
</div>
|
||||||
placeholder={Utils.localizeMessage(
|
<div className='section-title'>
|
||||||
'payment_form.address_2',
|
|
||||||
'Address 2',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-row'>
|
|
||||||
<Input
|
|
||||||
name='city'
|
|
||||||
type='text'
|
|
||||||
value={this.state.city}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
onBlur={this.onBlur}
|
|
||||||
placeholder={Utils.localizeMessage(
|
|
||||||
'payment_form.city',
|
|
||||||
'City',
|
|
||||||
)}
|
|
||||||
required={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-row'>
|
|
||||||
<div className='form-row-third-1 selector second-dropdown-sibling-wrapper'>
|
|
||||||
<StateSelector
|
|
||||||
country={this.state.country}
|
|
||||||
state={this.state.state}
|
|
||||||
onChange={this.handleStateChange}
|
|
||||||
onBlur={this.onBlur}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-row-third-2'>
|
|
||||||
<Input
|
|
||||||
name='postalCode'
|
|
||||||
type='text'
|
|
||||||
value={this.state.postalCode}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
onBlur={this.onBlur}
|
|
||||||
placeholder={Utils.localizeMessage(
|
|
||||||
'payment_form.zipcode',
|
|
||||||
'Zip/Postal Code',
|
|
||||||
)}
|
|
||||||
required={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{changePaymentMethod ? buttonFooter : null}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let cardContent: JSX.Element | null = null;
|
|
||||||
|
|
||||||
if (paymentMethod) {
|
|
||||||
let cardDetails = (
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='payment_form.no_credit_card'
|
id='payment_form.billing_address'
|
||||||
defaultMessage='No credit card added'
|
defaultMessage='Billing address'
|
||||||
/>
|
/>
|
||||||
);
|
</div>
|
||||||
if (paymentMethod.last_four) {
|
<DropdownInput
|
||||||
cardDetails = (
|
onChange={handleCountryChange}
|
||||||
<React.Fragment>
|
value={
|
||||||
<CardImage brand={paymentMethod.card_brand}/>
|
state.country ? {value: state.country, label: state.country} : undefined
|
||||||
{`Card ending in ${paymentMethod.last_four}`}
|
}
|
||||||
<br/>
|
options={COUNTRIES.map((country) => ({
|
||||||
{`Expires ${paymentMethod.exp_month}/${paymentMethod.exp_year}`}
|
value: country.name,
|
||||||
</React.Fragment>
|
label: country.name,
|
||||||
);
|
}))}
|
||||||
}
|
legend={formatMessage({id: 'payment_form.country', defaultMessage: 'Country'})}
|
||||||
let addressDetails = (
|
placeholder={formatMessage({id: 'payment_form.country', defaultMessage: 'Country'})}
|
||||||
<i>
|
name={'billing_dropdown'}
|
||||||
<FormattedMessage
|
/>
|
||||||
id='payment_form.no_billing_address'
|
<div className='form-row'>
|
||||||
defaultMessage='No billing address added'
|
<Input
|
||||||
|
name='address'
|
||||||
|
type='text'
|
||||||
|
value={state.address}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
placeholder={formatMessage({id: 'payment_form.address', defaultMessage: 'Address'})}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form-row'>
|
||||||
|
<Input
|
||||||
|
name='address2'
|
||||||
|
type='text'
|
||||||
|
value={state.address2}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
placeholder={formatMessage({id: 'payment_form.address_2', defaultMessage: 'Address 2'})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form-row'>
|
||||||
|
<Input
|
||||||
|
name='city'
|
||||||
|
type='text'
|
||||||
|
value={state.city}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
placeholder={formatMessage({id: 'payment_form.city', defaultMessage: 'City'})}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form-row'>
|
||||||
|
<div className='form-row-third-1 selector second-dropdown-sibling-wrapper'>
|
||||||
|
<StateSelector
|
||||||
|
country={state.country}
|
||||||
|
state={state.state}
|
||||||
|
onChange={handleStateChange}
|
||||||
|
onBlur={onBlur}
|
||||||
/>
|
/>
|
||||||
</i>);
|
</div>
|
||||||
if (this.state.state) {
|
<div className='form-row-third-2'>
|
||||||
addressDetails = (
|
<Input
|
||||||
<React.Fragment>
|
name='postalCode'
|
||||||
{this.state.address}
|
type='text'
|
||||||
{this.state.address2}
|
value={state.postalCode}
|
||||||
<br/>
|
onChange={handleInputChange}
|
||||||
{`${this.state.city}, ${this.state.state}, ${this.state.country}`}
|
onBlur={onBlur}
|
||||||
<br/>
|
placeholder={formatMessage({id: 'payment_form.zipcode', defaultMessage: 'Zip/Postal Code'})}
|
||||||
{this.state.postalCode}
|
required={true}
|
||||||
</React.Fragment>
|
/>
|
||||||
);
|
</div>
|
||||||
}
|
</div>
|
||||||
|
{state.changePaymentMethod ? buttonFooter : null}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let cardContent: JSX.Element | null = null;
|
||||||
|
|
||||||
cardContent = (
|
if (paymentMethod) {
|
||||||
|
let cardDetails = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='payment_form.no_credit_card'
|
||||||
|
defaultMessage='No credit card added'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (paymentMethod.last_four) {
|
||||||
|
cardDetails = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className='PaymentForm-saved-card'>
|
<CardImage brand={paymentMethod.card_brand}/>
|
||||||
{cardDetails}
|
{`Card ending in ${paymentMethod.last_four}`}
|
||||||
</div>
|
<br/>
|
||||||
<div className='PaymentForm-saved-address'>
|
{`Expires ${paymentMethod.exp_month}/${paymentMethod.exp_year}`}
|
||||||
{addressDetails}
|
</React.Fragment>
|
||||||
</div>
|
);
|
||||||
|
}
|
||||||
|
let addressDetails = (
|
||||||
|
<i>
|
||||||
|
<FormattedMessage
|
||||||
|
id='payment_form.no_billing_address'
|
||||||
|
defaultMessage='No billing address added'
|
||||||
|
/>
|
||||||
|
</i>);
|
||||||
|
if (state.state) {
|
||||||
|
addressDetails = (
|
||||||
|
<React.Fragment>
|
||||||
|
{state.address}
|
||||||
|
{state.address2}
|
||||||
|
<br/>
|
||||||
|
{`${state.city}, ${state.state}, ${state.country}`}
|
||||||
|
<br/>
|
||||||
|
{state.postalCode}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentDetails = (
|
cardContent = (
|
||||||
<div
|
<React.Fragment>
|
||||||
id='console_payment_saved'
|
<div className='PaymentForm-saved-card'>
|
||||||
className='PaymentForm-saved'
|
{cardDetails}
|
||||||
>
|
|
||||||
<div className='PaymentForm-saved-title'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='payment_form.saved_payment_method'
|
|
||||||
defaultMessage='Saved Payment Method'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{cardContent}
|
<div className='PaymentForm-saved-address'>
|
||||||
<button
|
{addressDetails}
|
||||||
className='Form-btn-link PaymentForm-change'
|
</div>
|
||||||
onClick={this.changePaymentMethod}
|
</React.Fragment>
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='payment_form.change_payment_method'
|
|
||||||
defaultMessage='Change Payment Method'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
paymentDetails = (
|
||||||
<form
|
<div
|
||||||
id='payment_form'
|
id='console_payment_saved'
|
||||||
className={`PaymentForm ${className}`}
|
className='PaymentForm-saved'
|
||||||
>
|
>
|
||||||
<GatherIntent
|
<div className='PaymentForm-saved-title'>
|
||||||
typeGatherIntent='monthlySubscription'
|
|
||||||
modalComponent={GatherIntentModal}
|
|
||||||
gatherIntentText={
|
|
||||||
<FormattedMessage
|
|
||||||
id='payment_form.gather_wire_transfer_intent'
|
|
||||||
defaultMessage='Looking for other payment options?'
|
|
||||||
/>}
|
|
||||||
/>
|
|
||||||
<div className='section-title'>
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='payment_form.credit_card'
|
id='payment_form.saved_payment_method'
|
||||||
defaultMessage='Credit Card'
|
defaultMessage='Saved Payment Method'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{paymentDetails}
|
{cardContent}
|
||||||
</form>
|
<button
|
||||||
|
className='Form-btn-link PaymentForm-change'
|
||||||
|
onClick={changePaymentMethod}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='payment_form.change_payment_method'
|
||||||
|
defaultMessage='Change Payment Method'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
id='payment_form'
|
||||||
|
className={`PaymentForm ${className}`}
|
||||||
|
>
|
||||||
|
<GatherIntent
|
||||||
|
typeGatherIntent='monthlySubscription'
|
||||||
|
modalComponent={GatherIntentModal}
|
||||||
|
gatherIntentText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='payment_form.gather_wire_transfer_intent'
|
||||||
|
defaultMessage='Looking for other payment options?'
|
||||||
|
/>}
|
||||||
|
/>
|
||||||
|
<div className='section-title'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='payment_form.credit_card'
|
||||||
|
defaultMessage='Credit Card'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{paymentDetails}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PaymentForm.defaultProps = {
|
||||||
|
className: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentForm;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type {IntlShape} from 'react-intl';
|
|||||||
import {withRouter} from 'react-router-dom';
|
import {withRouter} from 'react-router-dom';
|
||||||
import type {RouteComponentProps} from 'react-router-dom';
|
import type {RouteComponentProps} from 'react-router-dom';
|
||||||
|
|
||||||
import type {Address, Feedback, Product} from '@mattermost/types/cloud';
|
import type {Address, CloudCustomerPatch, Feedback, Product} from '@mattermost/types/cloud';
|
||||||
import type {Team} from '@mattermost/types/teams';
|
import type {Team} from '@mattermost/types/teams';
|
||||||
|
|
||||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||||
@@ -48,7 +48,7 @@ type Props = RouteComponentProps & {
|
|||||||
cwsMockMode: boolean
|
cwsMockMode: boolean
|
||||||
) => Promise<boolean | null>;
|
) => Promise<boolean | null>;
|
||||||
subscribeCloudSubscription:
|
subscribeCloudSubscription:
|
||||||
| ((productId: string, shippingAddress: Address, seats?: number, downgradeFeedback?: Feedback) => Promise<ActionResult<Subscription, ComplianceError>>)
|
| ((productId: string, shippingAddress: Address, seats?: number, downgradeFeedback?: Feedback, customerPatch?: CloudCustomerPatch) => Promise<ActionResult<Subscription, ComplianceError>>)
|
||||||
| null;
|
| null;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -139,7 +139,10 @@ class ProcessPaymentSetup extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (subscribeCloudSubscription) {
|
if (subscribeCloudSubscription) {
|
||||||
const result = await subscribeCloudSubscription(this.props.selectedProduct?.id as string, this.props.shippingAddress as Address, this.props.usersCount);
|
const customerPatch = {
|
||||||
|
name: billingDetails?.company_name,
|
||||||
|
} as CloudCustomerPatch;
|
||||||
|
const result = await subscribeCloudSubscription(this.props.selectedProduct?.id as string, this.props.shippingAddress as Address, this.props.usersCount, undefined, customerPatch);
|
||||||
|
|
||||||
// the action subscribeCloudSubscription returns a true boolean when successful and an error when it fails
|
// the action subscribeCloudSubscription returns a true boolean when successful and an error when it fails
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
|
|||||||
@@ -836,6 +836,7 @@ class PurchaseModal extends React.PureComponent<Props, State> {
|
|||||||
onCardInputChange={this.handleCardInputChange}
|
onCardInputChange={this.handleCardInputChange}
|
||||||
initialBillingDetails={initialBillingDetails}
|
initialBillingDetails={initialBillingDetails}
|
||||||
theme={this.props.theme}
|
theme={this.props.theme}
|
||||||
|
customer={this.props.customer}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className='PaymentDetails'>
|
<div className='PaymentDetails'>
|
||||||
|
|||||||
@@ -4248,6 +4248,7 @@
|
|||||||
"payment_form.billing_address": "Billing address",
|
"payment_form.billing_address": "Billing address",
|
||||||
"payment_form.change_payment_method": "Change Payment Method",
|
"payment_form.change_payment_method": "Change Payment Method",
|
||||||
"payment_form.city": "City",
|
"payment_form.city": "City",
|
||||||
|
"payment_form.company_name": "Company Name",
|
||||||
"payment_form.country": "Country",
|
"payment_form.country": "Country",
|
||||||
"payment_form.credit_card": "Credit Card",
|
"payment_form.credit_card": "Credit Card",
|
||||||
"payment_form.gather_wire_transfer_intent": "Looking for other payment options?",
|
"payment_form.gather_wire_transfer_intent": "Looking for other payment options?",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export type BillingDetails = {
|
|||||||
name: string;
|
name: string;
|
||||||
card: StripeCardElement;
|
card: StripeCardElement;
|
||||||
agreedTerms?: boolean;
|
agreedTerms?: boolean;
|
||||||
|
company_name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const areBillingDetailsValid = (
|
export const areBillingDetailsValid = (
|
||||||
|
|||||||
@@ -3870,7 +3870,7 @@ export default class Client4 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeCloudProduct = (productId: string, shippingAddress?: Address, seats = 0, downgradeFeedback?: Feedback) => {
|
subscribeCloudProduct = (productId: string, shippingAddress?: Address, seats = 0, downgradeFeedback?: Feedback, customerPatch?: CloudCustomerPatch) => {
|
||||||
const body = {
|
const body = {
|
||||||
product_id: productId,
|
product_id: productId,
|
||||||
seats,
|
seats,
|
||||||
@@ -3879,6 +3879,10 @@ export default class Client4 {
|
|||||||
if (shippingAddress) {
|
if (shippingAddress) {
|
||||||
body.shipping_address = shippingAddress;
|
body.shipping_address = shippingAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (customerPatch) {
|
||||||
|
body.customer = customerPatch;
|
||||||
|
}
|
||||||
return this.doFetch<Subscription>(
|
return this.doFetch<Subscription>(
|
||||||
`${this.getCloudRoute()}/subscription`,
|
`${this.getCloudRoute()}/subscription`,
|
||||||
{method: 'put', body: JSON.stringify(body)},
|
{method: 'put', body: JSON.stringify(body)},
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user