Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bank transfer component #564

Merged
merged 3 commits into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 66 additions & 0 deletions packages/lib/src/components/BankTransfer/BankTransfer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { h } from 'preact';
import UIElement from '../UIElement';
import CoreProvider from '../../core/Context/CoreProvider';
import RedirectButton from '../internal/RedirectButton';
import { BankTransferProps, BankTransferState } from './types';
import BankTransferResult from './components/BankTransferResult';
import BankTransferInput from './components/BankTransferInput';

export class BankTransferElement extends UIElement<BankTransferProps> {
public static type = 'bankTransfer_IBAN';

public static defaultProps = {
showPayButton: true,
showEmailAddress: true
};

public state: BankTransferState = {
isValid: !this.props.showEmailAddress,
data: {}
};

get isValid() {
return !!this.state.isValid;
}

/**
* Formats the component data output
*/
formatData() {
const { shopperEmail } = this.state.data;

return {
paymentMethod: {
type: BankTransferElement.type
},
...(shopperEmail && { shopperEmail })
};
}

private handleRef = ref => {
this.componentRef = ref;
};

render() {
if (this.props.reference) {
return (
<CoreProvider i18n={this.props.i18n} loadingContext={this.props.loadingContext}>
<BankTransferResult ref={this.handleRef} {...this.props} />
</CoreProvider>
);
}

if (this.props.showPayButton) {
return (
<CoreProvider i18n={this.props.i18n} loadingContext={this.props.loadingContext}>
<BankTransferInput ref={this.handleRef} {...this.props} onChange={this.setState} />
<RedirectButton {...this.props} name={this.displayName} onSubmit={this.submit} payButton={this.payButton} />
</CoreProvider>
);
}

return null;
}
}

export default BankTransferElement;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@import '../../../../style/index';

.adyen-checkout__bankTransfer__introduction {
font-size: $font-size-small;
color: $color-black;
font-weight: 400;
margin: 0 0 $spacing-medium;
padding: 0;
}

.adyen-checkout__bankTransfer__emailField {
margin: 0 0 $spacing-medium;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { h } from 'preact';
import useCoreContext from '../../../../core/Context/useCoreContext';
import './BankTransferInput.scss';
import SendCopyToEmail from '../../../internal/SendCopyToEmail/SendCopyToEmail';
import { useEffect, useState } from 'preact/hooks';
import Validator from '../../../../utils/Validator';

const validator = new Validator({});

function BankTransferInput(props) {
const { i18n } = useCoreContext();
const [errors, setErrors] = useState({ shopperEmail: false });
const [data, setData] = useState({ shopperEmail: null });
const [valid, setValid] = useState({ shopperEmail: false });
const [showingEmail, setShowingEmail] = useState(false);

const toggleEmailField = () => setShowingEmail(!showingEmail);

const handleChangeFor = (key, validationMode) => e => {
const { value } = e.target;
const isValid = validator.validate(key, validationMode)(value);

setData({ ...data, [key]: value });
setValid({ ...valid, [key]: isValid });
setErrors({ ...errors, [key]: !isValid });
};

this.showValidation = () => {
setErrors({
...(showingEmail && { shopperEmail: !validator.validate('shopperEmail')(data.shopperEmail) })
});
};

useEffect(() => {
const emailRequired = showingEmail && props.showEmailAddress;
const emailAddressValid = emailRequired ? Boolean(validator.validate('shopperEmail', 'blur')(data.shopperEmail)) : true;
const shopperEmail = emailRequired ? data.shopperEmail : null;

props.onChange({ data: { ...data, shopperEmail }, isValid: emailAddressValid });
}, [data, valid, errors, showingEmail]);

return (
<div className="adyen-checkout__bankTransfer">
<p className="adyen-checkout__bankTransfer__introduction">{i18n.get('bankTransfer.introduction')}</p>
<SendCopyToEmail
classNames={'adyen-checkout__bankTransfer__emailField'}
value={data.shopperEmail}
errors={errors.shopperEmail}
onToggle={toggleEmailField}
onInput={handleChangeFor('shopperEmail', 'input')}
onChange={handleChangeFor('shopperEmail', 'blur')}
/>
</div>
);
}

export default BankTransferInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './BankTransferInput';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { h } from 'preact';
import Voucher from '../../../internal/Voucher';
import getImage from '../../../../utils/get-image';
import useCoreContext from '../../../../core/Context/useCoreContext';

export default function BankTransferResult(props) {
const { reference, totalAmount, paymentMethodType } = props;
const { loadingContext, i18n } = useCoreContext();

return (
<Voucher
paymentMethodType={paymentMethodType}
introduction={i18n.get('bankTransfer.instructions')}
imageUrl={getImage({ loadingContext })(paymentMethodType)}
amount={totalAmount && i18n.amount(totalAmount.value, totalAmount.currency)}
voucherDetails={[
{ label: i18n.get('bankTransfer.beneficiary'), value: props.beneficiary },
{ label: i18n.get('bankTransfer.iban'), value: props.iban },
{ label: i18n.get('bankTransfer.bic'), value: props.bic },
{ label: i18n.get('bankTransfer.reference'), value: reference }
]}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './BankTransferResult';
1 change: 1 addition & 0 deletions packages/lib/src/components/BankTransfer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './BankTransfer';
18 changes: 18 additions & 0 deletions packages/lib/src/components/BankTransfer/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { UIElementProps } from '../UIElement';

export interface BankTransferProps extends UIElementProps {
reference?: string;

/**
* Show/hide email address field
* @default true
*/
showEmailAddress?: boolean;
}

export interface BankTransferState extends UIElementProps {
data: {
shopperEmail?: string;
};
isValid: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Field from '../../../internal/FormFields/Field';
import Address from '../../../internal/Address';
import Validator from '../../../../utils/Validator';
import { boletoValidationRules } from './validate';
import SendCopyToEmail from './SendCopyToEmail';
import SendCopyToEmail from '../../../internal/SendCopyToEmail/SendCopyToEmail';
import { formatCPFCNPJ } from './utils';
import useCoreContext from '../../../../core/Context/useCoreContext';
import { BoletoInputDataState, BoletoInputErrorState, BoletoInputValidState } from '../../types';
Expand Down
4 changes: 3 additions & 1 deletion packages/lib/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ import Dropin from './Dropin';
import Ach from './Ach';
import MBWay from './MBWay';
import Blik from './Blik';
import uuid from '../utils/uuid';
import BankTransfer from './BankTransfer';
import Affirm from './Affirm';
import uuid from '../utils/uuid';

/**
* Maps each component with a Component element.
Expand All @@ -50,6 +51,7 @@ const componentsMap = {
afterpay_b2b: AfterPayB2B,
amex: Card,
applepay: ApplePay,
bankTransfer_IBAN: BankTransfer,
bcmc: Bancontact,
bcmc_mobile: BcmcMobile,
bcmc_mobile_QR: BcmcMobile,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { h } from 'preact';
import cx from 'classnames';
import { useState } from 'preact/hooks';
import { renderFormField } from '../../../internal/FormFields';
import useCoreContext from '../../../../core/Context/useCoreContext';
import Field from '../../../internal/FormFields/Field';
import { renderFormField } from '../FormFields';
import useCoreContext from '../../../core/Context/useCoreContext';
import Field from '../FormFields/Field';

export default function SendCopyToEmail(props) {
const { errors, value, onInput, onChange } = props;
Expand All @@ -15,7 +16,7 @@ export default function SendCopyToEmail(props) {
};

return (
<div className={'adyen-checkout__fieldset adyen-checkout__fieldset--sendCopyToEmail'}>
<div className={cx('adyen-checkout__fieldset', 'adyen-checkout__fieldset--sendCopyToEmail', props.classNames)}>
<Field classNameModifiers={['sendCopyToEmail']}>
{renderFormField('boolean', {
onChange: toggleEmailField,
Expand All @@ -28,7 +29,7 @@ export default function SendCopyToEmail(props) {
{sendCopyToEmail && (
<Field label={i18n.get('shopperEmail')} classNameModifiers={['shopperEmail']} errorMessage={errors}>
{renderFormField('emailAddress', {
name: 'boleto.shopperEmail',
name: 'shopperEmail',
autoCorrect: 'off',
spellCheck: false,
value,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './SendCopyToEmail';
12 changes: 7 additions & 5 deletions packages/lib/src/components/internal/Voucher/Voucher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@ export default function Voucher({ voucherDetails = [], className = '', ...props
)}
</div>

<div className="adyen-checkout__voucher-result__separator">
<div className="adyen-checkout__voucher-result__separator__inner" />
<div className="adyen-checkout__voucher-result__code__label">
<span className="adyen-checkout__voucher-result__code__label__text">{i18n.get('voucher.paymentReferenceLabel')}</span>
{props.reference && (
<div className="adyen-checkout__voucher-result__separator">
<div className="adyen-checkout__voucher-result__separator__inner" />
<div className="adyen-checkout__voucher-result__code__label">
<span className="adyen-checkout__voucher-result__code__label__text">{i18n.get('voucher.paymentReferenceLabel')}</span>
</div>
</div>
</div>
)}

<div className="adyen-checkout__voucher-result__bottom">
{props.reference && (
Expand Down
25 changes: 25 additions & 0 deletions packages/lib/src/components/internal/Voucher/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,42 @@ export interface VoucherDetail {
}

export interface VoucherProps {
/** Payment method used to generate the voucher. */
paymentMethodType: string;

/** Payment method image to be displayed on the voucher. */
imageUrl?: string;

/** If applicable, it will render an issuer image next to the payment method image. */
issuerImageUrl?: string;

/** Voucher instructions URL. */
instructionsUrl?: string;

/** Download URL for the voucher. It will display a button allowing the shopper to download it. */
downloadUrl?: string;

/** Introduction text on the voucher. */
introduction?: string;

/** Payment reference. */
reference?: string;

/** URL to a barcode image representing the payment reference. */
barcode?: string;

/** Total amount displayed on the voucher. */
amount?: string;

/** Any additional surcharge to the amount. */
surcharge?: any;

/** List of details that will be rendered on the voucher. */
voucherDetails?: VoucherDetail[];

/** Additional CSS classes. */
className?: string;

/** Show/Hide a button to copy the payment reference. It will only show if a reference is available. */
copyBtn?: boolean;
}
10 changes: 10 additions & 0 deletions packages/lib/src/core/ProcessResponse/PaymentAction/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ const actionTypes = {
onError: props.onError,
statusType: 'custom'
});
},

bankTransfer: (action: PaymentAction, props) => {
return getComponent(action.paymentMethodType, {
...action,
...props,
onComplete: props.onAdditionalDetails,
onError: props.onError,
statusType: 'custom'
});
}
};

Expand Down
6 changes: 6 additions & 0 deletions packages/lib/src/language/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@
"voucher.shopperReference": "Shopper Reference",
"voucher.collectionInstitutionNumber": "Collection Institution Number",
"voucher.econtext.telephoneNumber.invalid": "Telephone number must be 10 or 11 digits long",
"bankTransfer.beneficiary": "Beneficiary",
"bankTransfer.iban": "IBAN",
"bankTransfer.bic": "BIC",
"bankTransfer.reference": "Reference",
"bankTransfer.introduction": "Continue to create a new bank transfer payment. You can use the details in the following screen to finalize this payment.",
"bankTransfer.instructions": "Thank you for your purchase, please use the following information to complete your payment.",
"boletobancario.btnLabel": "Generate Boleto",
"boleto.sendCopyToEmail": "Send a copy to my email",
"button.copy": "Copy",
Expand Down
10 changes: 10 additions & 0 deletions packages/playground/src/pages/Components/Components.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ <h2>Would you like to donate?</h2>
</div>
</div>

<div class="merchant-checkout__payment-method">
<div class="merchant-checkout__payment-method__header">
<h2>SEPA Bank Transfer</h2>
</div>
<div class="merchant-checkout__payment-method__details">
<div class="bankTransfer-field"></div>
<div class="bankTransfer-result-field"></div>
</div>
</div>

<div class="merchant-checkout__payment-method">
<div class="merchant-checkout__payment-method__header">
<h2>MB Way</h2>
Expand Down