import React, { ReactNode, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { enableMapSet } from 'immer'; import { useImmer } from 'use-immer'; import { HtmlTranslate } from '../../base/html-translate'; import { FabInput } from '../../base/fab-input'; import { Loader } from '../../base/loader'; import { SettingName } from '../../../models/setting'; import SettingAPI from '../../../api/setting'; import PayzenAPI from '../../../api/payzen'; enableMapSet(); interface PayZenKeysFormProps { onValidKeys: (payZenSettings: Map) => void } // all settings related to PayZen that are requested by this form const payZenSettings: Array = [SettingName.PayZenUsername, SettingName.PayZenPassword, SettingName.PayZenEndpoint, SettingName.PayZenHmacKey, SettingName.PayZenPublicKey]; // settings related the to PayZen REST API (server side) const restApiSettings: Array = [SettingName.PayZenUsername, SettingName.PayZenPassword, SettingName.PayZenEndpoint, SettingName.PayZenHmacKey]; // 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; /** * Form to set the PayZen's username, password and public key */ const PayZenKeysFormComponent: React.FC = ({ onValidKeys }) => { const { t } = useTranslation('admin'); // values of the PayZen settings const [settings, updateSettings] = useImmer>(new Map(payZenSettings.map(name => [name, '']))); // Icon of the fieldset for the PayZen's keys concerning the REST API. Used to display if the key is valid. const [restApiAddOn, setRestApiAddOn] = useState(null); // Style class for the add-on icon, for the REST API const [restApiAddOnClassName, setRestApiAddOnClassName] = useState<'key-invalid' | 'key-valid' | ''>(''); // Icon of the input field for the PayZen's public key. Used to display if the key is valid. const [publicKeyAddOn, setPublicKeyAddOn] = useState(null); // Style class for the add-on icon, for the public key const [publicKeyAddOnClassName, setPublicKeyAddOnClassName] = useState<'key-invalid' | 'key-valid' | ''>(''); /** * When the component loads for the first time, initialize the keys with the values fetched from the API (if any) */ useEffect(() => { const api = new SettingAPI(); api.query(payZenSettings).then(payZenKeys => { updateSettings(new Map(payZenKeys)); }).catch(error => console.error(error)); }, []); /** * When the style class for the public key, and the REST API are updated, check if they indicate valid keys. * If both are valid, run the 'onValidKeys' callback */ useEffect(() => { const validClassName = 'key-valid'; if (publicKeyAddOnClassName === validClassName && restApiAddOnClassName === validClassName) { onValidKeys(settings); } }, [publicKeyAddOnClassName, restApiAddOnClassName, settings]); useEffect(() => { testRestApi(); }, [settings]) /** * Assign the inputted key to the settings and check if it is valid. * Depending on the test result, assign an add-on icon and a style to notify the user. */ const testPublicKey = (key: string) => { if (!key.match(/^[0-9]+:/)) { setPublicKeyAddOn(); setPublicKeyAddOnClassName('key-invalid'); return; } updateSettings(draft => draft.set(SettingName.PayZenPublicKey, key)); setPublicKeyAddOn(); setPublicKeyAddOnClassName('key-valid'); } /** * Send a test call to the payZen REST API to check if the inputted settings key are valid. * Depending on the test result, assign an add-on icon and a style to notify the user. */ const testRestApi = () => { let valid: boolean = restApiSettings.map(s => !!settings.get(s)) .reduce((acc, val) => acc && val, true); if (valid && !pendingKeysValidation) { pendingKeysValidation = true; PayzenAPI.chargeSDKTest( settings.get(SettingName.PayZenEndpoint), settings.get(SettingName.PayZenUsername), settings.get(SettingName.PayZenPassword) ).then(result => { pendingKeysValidation = false; if (result.success) { setRestApiAddOn(); setRestApiAddOnClassName('key-valid'); } else { setRestApiAddOn(); setRestApiAddOnClassName('key-invalid'); } }, () => { pendingKeysValidation = false; setRestApiAddOn(); setRestApiAddOnClassName('key-invalid'); }); } if (!valid) { setRestApiAddOn(); setRestApiAddOnClassName('key-invalid'); } } /** * Assign the inputted key to the given settings */ const setApiKey = (setting: SettingName.PayZenUsername | SettingName.PayZenPassword | SettingName.PayZenEndpoint | SettingName.PayZenHmacKey) => { return (key: string) => { updateSettings(draft => draft.set(setting, key)); } } /** * Check if an add-on icon must be shown for the API settings */ const hasApiAddOn = () => { return restApiAddOn !== null; } return (
{t('app.admin.invoices.payment.client_keys')}
} defaultValue={settings.get(SettingName.PayZenPublicKey)} onChange={testPublicKey} addOn={publicKeyAddOn} addOnClassName={publicKeyAddOnClassName} debounce={200} required />
{t('app.admin.invoices.payment.api_keys')} {hasApiAddOn() && {restApiAddOn}}
} defaultValue={settings.get(SettingName.PayZenUsername)} onChange={setApiKey(SettingName.PayZenUsername)} debounce={200} required />
} defaultValue={settings.get(SettingName.PayZenPassword)} onChange={setApiKey(SettingName.PayZenPassword)} debounce={200} required />
} defaultValue={settings.get(SettingName.PayZenEndpoint)} onChange={setApiKey(SettingName.PayZenEndpoint)} debounce={200} required />
} defaultValue={settings.get(SettingName.PayZenHmacKey)} onChange={setApiKey(SettingName.PayZenHmacKey)} debounce={200} required />
); } export const PayZenKeysForm: React.FC = ({ onValidKeys }) => { return ( ); }