APM-79: Add self redirect for payment terminal (#82)

This commit is contained in:
Ildar Galeev 2022-04-19 17:08:29 +03:00 committed by GitHub
parent 7b5383d0fd
commit 85efc4a2fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 71 additions and 39 deletions

View File

@ -1,14 +1,15 @@
import { Payer } from './payer';
import { PaymentToolDetails } from '../payment-tool-details';
import { ClientInfo } from '../client-info';
import { ContactInfo } from '../contact-info';
import { PayerType } from './payer-type';
import { PaymentToolDetails } from '../payment-tool-details';
export class PaymentResourcePayer extends Payer {
payerType: PayerType.PaymentResourcePayer;
paymentToolToken: string;
paymentSession: string;
contactInfo: ContactInfo;
sessionInfo?: {
redirectUrl: string;
};
paymentToolDetails?: PaymentToolDetails;
clientInfo?: ClientInfo;
}

View File

@ -43,11 +43,15 @@ export const RedirectForm: React.FC = () => {
const dispatch = useAppDispatch();
useEffect(() => {
const prepared = prepareForm(origin, request, '_blank');
const prepared = prepareForm(origin, request, '_self');
containerRef.current.appendChild(prepared);
setForm(prepared);
}, []);
useEffect(() => {
form && form.submit();
}, [form]);
const redirect = () => {
form.submit();
dispatch(finishInteraction());

View File

@ -19,13 +19,20 @@ export const toPaymentFlow = (c: InitConfig): PaymentFlow => {
return c.paymentFlowHold ? hold : instant;
};
const getSessionInfo = (redirectUrl: string) => ({
sessionInfo: {
redirectUrl
}
});
export function* createPayment(
endpoint: string,
token: string,
invoiceID: string,
formEmail: string,
resource: PaymentResource,
initConfig: InitConfig
initConfig: InitConfig,
redirectUrl?: string
): Iterator<Effects> {
const email = initConfig.email || formEmail;
const { paymentToolToken, paymentSession } = resource;
@ -37,7 +44,8 @@ export function* createPayment(
paymentSession,
contactInfo: {
email
}
},
...(redirectUrl && getSessionInfo(redirectUrl))
},
makeRecurrent: initConfig.recurring,
metadata: initConfig.metadata

View File

@ -7,26 +7,47 @@ import { createPayment } from './create-payment';
import { pollInvoiceEvents } from '../../poll-events';
import { TypeKeys } from 'checkout/actions';
import { SetAcceptedError } from 'checkout/actions/error-actions/set-accepted-error';
import { serializeUrlParams } from '../../../../serialize-url-params';
type CreatePaymentResourceFn = (invoiceAccessToken: any) => Iterator<PaymentResource>;
const prepareRedirectUrl = (origin: string, invoiceID: string, invoiceAccessToken: string, configRedirectUrl: string) =>
`${origin}/v1/checkout.html?${serializeUrlParams({
invoiceID,
invoiceAccessToken,
configRedirectUrl
})}`;
export function* makePayment(
config: Config,
model: ModelState,
values: PayableFormValues,
amountInfo: AmountInfoState,
fn: CreatePaymentResourceFn,
withPolling = true
setRedirect = false
) {
const { initConfig, appConfig } = config;
const { initConfig, appConfig, origin } = config;
const { capiEndpoint } = appConfig;
const {
invoice: { id },
invoiceAccessToken
} = yield call(getPayableInvoice, initConfig, capiEndpoint, model, amountInfo, values.amount);
const paymentResource = yield call(fn, invoiceAccessToken);
let redirectUrl;
if (setRedirect) {
redirectUrl = prepareRedirectUrl(origin, id, invoiceAccessToken, initConfig.redirectUrl);
}
try {
yield call(createPayment, capiEndpoint, invoiceAccessToken, id, values.email, paymentResource, initConfig);
yield call(
createPayment,
capiEndpoint,
invoiceAccessToken,
id,
values.email,
paymentResource,
initConfig,
redirectUrl
);
} catch (e) {
switch (e.code) {
case LogicErrorCode.invalidInvoiceStatus:
@ -37,7 +58,5 @@ export function* makePayment(
throw e;
}
}
if (withPolling) {
yield call(pollInvoiceEvents, capiEndpoint, invoiceAccessToken, id);
}
yield call(pollInvoiceEvents, capiEndpoint, invoiceAccessToken, id);
}

View File

@ -16,7 +16,7 @@ const createPaymentResource = (endpoint: string, formValues: MobileCommerceFormV
export function* payWithMobileCommerce(c: Config, m: ModelState, a: AmountInfoState, v: MobileCommerceFormValues) {
const fn = createPaymentResource(c.appConfig.capiEndpoint, v);
yield call(makePayment, c, m, v, a, fn, false);
yield call(makePayment, c, m, v, a, fn);
yield put({
type: TypeKeys.SET_MODAL_STATE,
payload: new ModalForms([new MobileCommerceReceiptFormInfo()], true)

View File

@ -14,5 +14,5 @@ export function* payWithPaymentTerminal(
v: PaymentTerminalFormValues
): Iterator<CallEffect> {
const fn = createPaymentResource(c.appConfig.capiEndpoint, v.provider, v.metadata);
yield call(makePayment, c, m, v, a, fn);
yield call(makePayment, c, m, v, a, fn, true);
}

View File

@ -8,7 +8,7 @@ import {
import { Transaction } from 'checkout/backend/model';
import { listen } from 'cross-origin-communicator';
import { detectLocale } from '../../../../../locale/detect-locale';
import { serialize } from '../../../../../initializer/popup-initializer';
import { serializeUrlParams } from '../../../../../serialize-url-params';
export async function getResultData(transaction: Transaction, serviceId: string, locale: string): Promise<ResultData> {
const connectTransport = await listen(communicatorInstanceName, 5000);
@ -20,7 +20,7 @@ export async function getResultData(transaction: Transaction, serviceId: string,
href: transaction.href,
serviceId,
callbackURL: URL,
cancelURL: `${URL}?${serialize({ type: Type.ERROR })}`,
cancelURL: `${URL}?${serializeUrlParams({ type: Type.ERROR })}`,
countryCode: detectLocale(locale),
publicKeyMod: transaction.encInfo.mod,
publicKeyExp: transaction.encInfo.exp,

View File

@ -3,32 +3,11 @@ import { initialize } from 'cross-origin-communicator';
import { Initializer } from './initializer';
import { CommunicatorEvents, communicatorInstanceName } from '../communicator-constants';
import { OpenConfig } from '../app/config';
export const serialize = (params: { [name: string]: any }): string => {
const urlParams: string[] = [];
for (const prop in params) {
if (params.hasOwnProperty(prop)) {
let value = params[prop];
if (typeof value === 'function' || value === undefined || value === null) {
continue;
}
if (typeof value === 'object') {
try {
value = JSON.stringify(value);
} catch (e) {
console.error(e);
continue;
}
}
urlParams.push(`${prop}=${encodeURIComponent(value)}`);
}
}
return urlParams.join('&');
};
import { serializeUrlParams } from '../serialize-url-params';
export class PopupInitializer extends Initializer {
open(openConfig: OpenConfig = {}) {
const url = `${this.origin}/v1/checkout.html?${serialize({ ...this.config, ...openConfig })}`;
const url = `${this.origin}/v1/checkout.html?${serializeUrlParams({ ...this.config, ...openConfig })}`;
const target = window.open(url);
initialize(target, this.origin, communicatorInstanceName).then((transport) => {
this.opened();

View File

@ -0,0 +1,21 @@
export const serializeUrlParams = (params: { [name: string]: any }): string => {
const urlParams: string[] = [];
for (const prop in params) {
if (params.hasOwnProperty(prop)) {
let value = params[prop];
if (typeof value === 'function' || value === undefined || value === null) {
continue;
}
if (typeof value === 'object') {
try {
value = JSON.stringify(value);
} catch (e) {
console.error(e);
continue;
}
}
urlParams.push(`${prop}=${encodeURIComponent(value)}`);
}
}
return urlParams.join('&');
};