mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-12-01 12:24:28 +01:00
integrate the payzen form widget into the modal
Also: do not fetch the api from unmounted components
This commit is contained in:
parent
5e2c50a85f
commit
fe5c4e6233
@ -49,6 +49,9 @@ const cgvFile = CustomAssetAPI.get(CustomAssetName.CgvFile);
|
||||
|
||||
/**
|
||||
* This component is an abstract modal that must be extended by each payment gateway to include its payment form.
|
||||
*
|
||||
* This component must not be called directly but must be extended for each implemented payment gateway
|
||||
* @see https://reactjs.org/docs/composition-vs-inheritance.html
|
||||
*/
|
||||
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cartItems, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName }) => {
|
||||
// customer's wallet
|
||||
|
@ -25,6 +25,10 @@ interface PaymentModalProps {
|
||||
// initial request to the API
|
||||
const paymentGateway = SettingAPI.get(SettingName.PaymentGateway);
|
||||
|
||||
/**
|
||||
* This component open a modal dialog for the configured payment gateway, allowing the user to input his card data
|
||||
* to process an online payment.
|
||||
*/
|
||||
const PaymentModal: React.FC<PaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, currentUser, schedule , cartItems, customer }) => {
|
||||
const gateway = paymentGateway.read();
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
import React, { FormEvent, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import KRGlue from "@lyracom/embedded-form-glue";
|
||||
import { CartItems } from '../../../models/payment';
|
||||
import { User } from '../../../models/user';
|
||||
import SettingAPI from '../../../api/setting';
|
||||
import { SettingName } from '../../../models/setting';
|
||||
|
||||
interface PayzenFormProps {
|
||||
onSubmit: () => void,
|
||||
onSuccess: (result: any) => void,
|
||||
onError: (message: string) => void,
|
||||
customer: User,
|
||||
operator: User,
|
||||
className?: string,
|
||||
paymentSchedule?: boolean,
|
||||
cartItems?: CartItems,
|
||||
formId: string,
|
||||
}
|
||||
|
||||
/**
|
||||
* A form component to collect the credit card details and to create the payment method on Stripe.
|
||||
* The form validation button must be created elsewhere, using the attribute form={formId}.
|
||||
*/
|
||||
export const PayzenForm: React.FC<PayzenFormProps> = ({ onSubmit, onSuccess, onError, children, className, paymentSchedule = false, cartItems, customer, operator, formId }) => {
|
||||
|
||||
const { t } = useTranslation('shared');
|
||||
|
||||
useEffect(() => {
|
||||
const api = new SettingAPI();
|
||||
api.query([SettingName.PayZenEndpoint, SettingName.PayZenPublicKey]).then(settings => {
|
||||
const formToken = "DEMO-TOKEN-TO-BE-REPLACED";
|
||||
|
||||
KRGlue.loadLibrary(settings.get(SettingName.PayZenEndpoint), settings.get(SettingName.PayZenPublicKey)) /* Load the remote library */
|
||||
.then(({ KR }) =>
|
||||
KR.setFormConfig({
|
||||
/* set the minimal configuration */
|
||||
formToken: formToken,
|
||||
"kr-language": "en-US" /* to update initialization parameter */
|
||||
})
|
||||
)
|
||||
.then(({ KR }) =>
|
||||
KR.addForm("#payzenPaymentForm")
|
||||
) /* add a payment form to myPaymentForm div*/
|
||||
.then(({ KR, result }) =>
|
||||
KR.showForm(result.formId)
|
||||
); /* show the payment form */
|
||||
}).catch(error => console.error(error));
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle the submission of the form.
|
||||
*/
|
||||
const handleSubmit = async (event: FormEvent): Promise<void> => {
|
||||
event.preventDefault();
|
||||
onSubmit();
|
||||
|
||||
|
||||
try {
|
||||
onSuccess(null);
|
||||
} catch (err) {
|
||||
// catch api errors
|
||||
onError(err);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} id={formId} className={className ? className : ''}>
|
||||
<div className="container">
|
||||
<div id="payzenPaymentForm" />
|
||||
</div>
|
||||
{children}
|
||||
</form>
|
||||
);
|
||||
}
|
@ -20,9 +20,6 @@ const payZenSettings: Array<SettingName> = [SettingName.PayZenUsername, SettingN
|
||||
// settings related the to PayZen REST API (server side)
|
||||
const restApiSettings: Array<SettingName> = [SettingName.PayZenUsername, SettingName.PayZenPassword, SettingName.PayZenEndpoint, SettingName.PayZenHmacKey];
|
||||
|
||||
// initial request to the API
|
||||
const payZenKeys = SettingAPI.query(payZenSettings);
|
||||
|
||||
// Prevent multiples call to the payzen keys validation endpoint.
|
||||
// this cannot be handled by a React state because of their asynchronous nature
|
||||
let pendingKeysValidation = false;
|
||||
@ -48,7 +45,10 @@ const PayZenKeysFormComponent: React.FC<PayZenKeysFormProps> = ({ onValidKeys })
|
||||
* When the component loads for the first time, initialize the keys with the values fetched from the API (if any)
|
||||
*/
|
||||
useEffect(() => {
|
||||
updateSettings(new Map(payZenKeys.read()));
|
||||
const api = new SettingAPI();
|
||||
api.query(payZenSettings).then(payZenKeys => {
|
||||
updateSettings(new Map(payZenKeys));
|
||||
}).catch(error => console.error(error));
|
||||
}, []);
|
||||
|
||||
/**
|
||||
|
@ -1,8 +1,5 @@
|
||||
import React, { FunctionComponent, ReactNode } from 'react';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { GatewayFormProps, AbstractPaymentModal } from '../abstract-payment-modal';
|
||||
import { Loader } from '../../base/loader';
|
||||
import { IApplication } from '../../../models/application';
|
||||
import { CartItems, PaymentConfirmation } from '../../../models/payment';
|
||||
import { PaymentSchedule } from '../../../models/payment-schedule';
|
||||
import { User } from '../../../models/user';
|
||||
@ -10,8 +7,8 @@ import { User } from '../../../models/user';
|
||||
import payzenLogo from '../../../../../images/payzen-secure.png';
|
||||
import mastercardLogo from '../../../../../images/mastercard.png';
|
||||
import visaLogo from '../../../../../images/visa.png';
|
||||
import { PayzenForm } from './payzen-form';
|
||||
|
||||
declare var Application: IApplication;
|
||||
|
||||
interface PayZenModalProps {
|
||||
isOpen: boolean,
|
||||
@ -26,6 +23,9 @@ interface PayZenModalProps {
|
||||
/**
|
||||
* This component enables the user to input his card data or process payments, using the PayZen gateway.
|
||||
* Supports Strong-Customer Authentication (SCA).
|
||||
*
|
||||
* This component should not be called directly. Prefer using <PaymentModal> which can handle the configuration
|
||||
* of a different payment gateway.
|
||||
*/
|
||||
export const PayZenModal: React.FC<PayZenModalProps> = ({ isOpen, toggleModal, afterSuccess, cartItems, currentUser, schedule, customer }) => {
|
||||
/**
|
||||
@ -46,13 +46,16 @@ export const PayZenModal: React.FC<PayZenModalProps> = ({ isOpen, toggleModal, a
|
||||
*/
|
||||
const renderForm: FunctionComponent<GatewayFormProps> = ({ onSubmit, onSuccess, onError, operator, className, formId, cartItems, customer, paymentSchedule, children}) => {
|
||||
return (
|
||||
<form onSubmit={onSubmit} className={className} id={formId}>
|
||||
<h3>PayZen</h3>
|
||||
<input type="text" placeholder="card #"/>
|
||||
<span>Operated by {operator.name}</span>
|
||||
<span>User: {customer.name}</span>
|
||||
<PayzenForm onSubmit={onSubmit}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
customer={customer}
|
||||
operator={operator}
|
||||
formId={formId}
|
||||
className={className}
|
||||
paymentSchedule={paymentSchedule}>
|
||||
{children}
|
||||
</form>
|
||||
</PayzenForm>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -38,13 +38,6 @@ const icons:Map<SettingName, string> = new Map([
|
||||
[SettingName.PayZenPublicKey, 'info']
|
||||
])
|
||||
|
||||
// initial requests to the API
|
||||
const payZenKeys = SettingAPI.query(payZenPublicSettings.concat(payZenOtherSettings));
|
||||
const isPresent = {
|
||||
[SettingName.PayZenPassword]: SettingAPI.isPresent(SettingName.PayZenPassword),
|
||||
[SettingName.PayZenHmacKey]: SettingAPI.isPresent(SettingName.PayZenHmacKey)
|
||||
};
|
||||
|
||||
/**
|
||||
* This component displays a summary of the PayZen account keys, with a button triggering the modal to edit them
|
||||
*/
|
||||
@ -61,11 +54,18 @@ export const PayzenSettings: React.FC<PayzenSettingsProps> = ({ onEditKeys, onCu
|
||||
* For the private settings, we initialize them with the placeholder value, if the setting is set.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const map = new Map(payZenKeys.read());
|
||||
for (const setting of payZenPrivateSettings) {
|
||||
map.set(setting, isPresent[setting].read() ? PAYZEN_HIDDEN : '');
|
||||
}
|
||||
updateSettings(map);
|
||||
const api = new SettingAPI();
|
||||
api.query(payZenPublicSettings.concat(payZenOtherSettings)).then(payZenKeys => {
|
||||
api.isPresent(SettingName.PayZenPassword).then(pzPassword => {
|
||||
api.isPresent(SettingName.PayZenHmacKey).then(pzHmac => {
|
||||
const map = new Map(payZenKeys);
|
||||
map.set(SettingName.PayZenPassword, pzPassword ? PAYZEN_HIDDEN : '');
|
||||
map.set(SettingName.PayZenHmacKey, pzHmac ? PAYZEN_HIDDEN : '');
|
||||
|
||||
updateSettings(map);
|
||||
}).catch(error => { console.error(error); })
|
||||
}).catch(error => { console.error(error); });
|
||||
}).catch(error => { console.error(error); });
|
||||
}, []);
|
||||
|
||||
|
||||
|
@ -139,7 +139,7 @@ export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onE
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} id={formId} className={className}>
|
||||
<form onSubmit={handleSubmit} id={formId} className={className ? className : ''}>
|
||||
<CardElement options={cardOptions} />
|
||||
{children}
|
||||
</form>
|
||||
|
@ -12,9 +12,6 @@ interface StripeKeysFormProps {
|
||||
onValidKeys: (stripePublic: string, stripeSecret:string) => void
|
||||
}
|
||||
|
||||
// initial request to the API
|
||||
const stripeKeys = SettingAPI.query([SettingName.StripePublicKey, SettingName.StripeSecretKey]);
|
||||
|
||||
/**
|
||||
* Form to set the stripe's public and private keys
|
||||
*/
|
||||
@ -44,9 +41,12 @@ const StripeKeysFormComponent: React.FC<StripeKeysFormProps> = ({ onValidKeys })
|
||||
*/
|
||||
useEffect(() => {
|
||||
mounted.current = true;
|
||||
const keys = stripeKeys.read();
|
||||
setPublicKey(keys.get(SettingName.StripePublicKey));
|
||||
setSecretKey(keys.get(SettingName.StripeSecretKey));
|
||||
|
||||
const api = new SettingAPI();
|
||||
api.query([SettingName.StripePublicKey, SettingName.StripeSecretKey]).then(stripeKeys => {
|
||||
setPublicKey(stripeKeys.get(SettingName.StripePublicKey));
|
||||
setSecretKey(stripeKeys.get(SettingName.StripeSecretKey));
|
||||
}).catch(error => console.error(error));
|
||||
|
||||
// when the component unmounts, mark it as unmounted
|
||||
return () => {
|
||||
|
@ -1,11 +1,8 @@
|
||||
import React, { FunctionComponent, ReactNode } from 'react';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { SetupIntent } from '@stripe/stripe-js';
|
||||
import { StripeElements } from './stripe-elements';
|
||||
import { StripeForm } from './stripe-form';
|
||||
import { GatewayFormProps, AbstractPaymentModal } from '../abstract-payment-modal';
|
||||
import { Loader } from '../../base/loader';
|
||||
import { IApplication } from '../../../models/application';
|
||||
import { CartItems, PaymentConfirmation } from '../../../models/payment';
|
||||
import { PaymentSchedule } from '../../../models/payment-schedule';
|
||||
import { User } from '../../../models/user';
|
||||
@ -14,7 +11,6 @@ import stripeLogo from '../../../../../images/powered_by_stripe.png';
|
||||
import mastercardLogo from '../../../../../images/mastercard.png';
|
||||
import visaLogo from '../../../../../images/visa.png';
|
||||
|
||||
declare var Application: IApplication;
|
||||
|
||||
interface StripeModalProps {
|
||||
isOpen: boolean,
|
||||
@ -29,6 +25,9 @@ interface StripeModalProps {
|
||||
/**
|
||||
* This component enables the user to input his card data or process payments, using the Stripe gateway.
|
||||
* Supports Strong-Customer Authentication (SCA).
|
||||
*
|
||||
* This component should not be called directly. Prefer using <PaymentModal> which can handle the configuration
|
||||
* of a different payment gateway.
|
||||
*/
|
||||
export const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuccess, cartItems, currentUser, schedule, customer }) => {
|
||||
/**
|
||||
|
@ -1,4 +1,9 @@
|
||||
.payzen-modal {
|
||||
.payzen-form {
|
||||
.container {
|
||||
display: flex; justify-content: center;
|
||||
}
|
||||
}
|
||||
.payzen-modal-icons {
|
||||
text-align: center;
|
||||
|
||||
|
@ -38,7 +38,8 @@ class SettingPolicy < ApplicationPolicy
|
||||
fablab_name name_genre event_explications_alert space_explications_alert link_name home_content phone_required
|
||||
tracking_id book_overlapping_slots slot_duration events_in_calendar spaces_module plans_module invoicing_module
|
||||
recaptcha_site_key feature_tour_display disqus_shortname allowed_cad_extensions openlab_app_id openlab_default
|
||||
online_payment_module stripe_public_key confirmation_required wallet_module trainings_module address_required payment_gateway]
|
||||
online_payment_module stripe_public_key confirmation_required wallet_module trainings_module address_required
|
||||
payment_gateway payzen_endpoint payzen_public_key]
|
||||
end
|
||||
|
||||
##
|
||||
|
Loading…
Reference in New Issue
Block a user