From 7cba00afb751d097589ddb0212f279e952a34038 Mon Sep 17 00:00:00 2001 From: Ildar Galeev Date: Tue, 5 Mar 2024 01:03:12 +0700 Subject: [PATCH] Impl P2P Bank Account Destination (#279) --- src/common/backend/index.ts | 0 src/common/backend/p2p/complete.ts | 16 +++++++ src/common/backend/p2p/getDestinations.ts | 24 ++++++++++ src/common/backend/p2p/getGateways.ts | 22 +++++++++ src/common/backend/p2p/index.ts | 5 +++ src/common/backend/p2p/types.ts | 26 +++++++++++ src/common/utils/fetchApi.test.ts | 45 +++++++++++++++++++ src/common/utils/fetchApi.ts | 32 +++++++++++++ src/common/utils/index.ts | 1 + .../ApiExtensionView/ApiExtensionView.tsx | 3 +- .../DestinationInfo/DestinationInfo.tsx | 6 ++- .../DestinationInfoBankAccount.tsx | 34 ++++++++++++++ .../DestinationInfoBankCard.tsx | 2 +- .../DestinationInfo/DestinationInfoSpb.tsx | 2 +- .../ApiExtensionView/GatewaySelector.tsx | 7 +-- .../ApiExtensionView/useComplete.ts | 2 +- .../ApiExtensionView/useDestinations.ts | 2 +- .../ApiExtensionView/useGateways.ts | 2 +- src/locale/az.json | 5 ++- src/locale/bn.json | 3 ++ src/locale/en.json | 3 ++ src/locale/ja.json | 3 ++ src/locale/ko.json | 3 ++ src/locale/pt.json | 3 ++ src/locale/ru.json | 5 ++- src/locale/tr.json | 3 ++ 26 files changed, 246 insertions(+), 13 deletions(-) create mode 100644 src/common/backend/index.ts create mode 100644 src/common/backend/p2p/complete.ts create mode 100644 src/common/backend/p2p/getDestinations.ts create mode 100644 src/common/backend/p2p/getGateways.ts create mode 100644 src/common/backend/p2p/index.ts create mode 100644 src/common/backend/p2p/types.ts create mode 100644 src/common/utils/fetchApi.test.ts create mode 100644 src/common/utils/fetchApi.ts create mode 100644 src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankAccount.tsx diff --git a/src/common/backend/index.ts b/src/common/backend/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/common/backend/p2p/complete.ts b/src/common/backend/p2p/complete.ts new file mode 100644 index 00000000..ff2b7b26 --- /dev/null +++ b/src/common/backend/p2p/complete.ts @@ -0,0 +1,16 @@ +import { extractError, fetchApi } from '../../../common/utils'; + +export type CompleteInfo = { + invoiceId: string; + paymentId: string; + payerTransactionId?: string; +}; + +export const complete = async (capiEndpoint: string, accessToken: string, info: CompleteInfo): Promise => { + try { + await fetchApi(capiEndpoint, accessToken, 'POST', 'p2p/payments/complete', info); + } catch (error) { + console.error(`Failed to fetch destinations: ${extractError(error)}`); + throw new Error(`Failed to fetch destinations: ${extractError(error)}`); + } +}; diff --git a/src/common/backend/p2p/getDestinations.ts b/src/common/backend/p2p/getDestinations.ts new file mode 100644 index 00000000..8b1e7983 --- /dev/null +++ b/src/common/backend/p2p/getDestinations.ts @@ -0,0 +1,24 @@ +import { Destination } from './types'; +import { extractError, fetchApi } from '../../../common/utils'; + +export const getDestinations = async ( + capiEndpoint: string, + accessToken: string, + invoiceID: string, + paymentID: string, + gatewayID: string, +): Promise => { + const queryParams = new URLSearchParams({ + invoiceId: invoiceID, + paymentId: paymentID, + gatewayId: gatewayID, + }).toString(); + const path = `p2p/payments/destinations?${queryParams}`; + try { + const response = await fetchApi(capiEndpoint, accessToken, 'GET', path); + return await response.json(); + } catch (error) { + console.error(`Failed to fetch destinations: ${extractError(error)}`); + throw new Error(`Failed to fetch destinations: ${extractError(error)}`); + } +}; diff --git a/src/common/backend/p2p/getGateways.ts b/src/common/backend/p2p/getGateways.ts new file mode 100644 index 00000000..f498112f --- /dev/null +++ b/src/common/backend/p2p/getGateways.ts @@ -0,0 +1,22 @@ +import { Gateway } from './types'; +import { extractError, fetchApi } from '../../../common/utils'; + +export const getGateways = async ( + capiEndpoint: string, + accessToken: string, + invoiceID: string, + paymentID: string, +): Promise => { + const queryParams = new URLSearchParams({ + invoiceId: invoiceID, + paymentId: paymentID, + }).toString(); + const path = `p2p/payments/gateways?${queryParams}`; + try { + const response = await fetchApi(capiEndpoint, accessToken, 'GET', path); + return await response.json(); + } catch (error) { + console.error(`Failed to fetch gateways: ${extractError(error)}`); + throw new Error(`Failed to fetch gateways: ${extractError(error)}`); + } +}; diff --git a/src/common/backend/p2p/index.ts b/src/common/backend/p2p/index.ts new file mode 100644 index 00000000..5f092839 --- /dev/null +++ b/src/common/backend/p2p/index.ts @@ -0,0 +1,5 @@ +export { complete } from './complete'; +export { getDestinations } from './getDestinations'; +export { getGateways } from './getGateways'; + +export type { Gateway, Destination, DestinationBankAccount, DestinationBankCard, DestinationSBP } from './types'; diff --git a/src/common/backend/p2p/types.ts b/src/common/backend/p2p/types.ts new file mode 100644 index 00000000..84609740 --- /dev/null +++ b/src/common/backend/p2p/types.ts @@ -0,0 +1,26 @@ +export type Gateway = { + id: string; + name: string; +}; + +export type DestinationBankCard = { + destinationType: 'BankCard'; + pan: string; + bankName?: string; +}; + +export type DestinationSBP = { + destinationType: 'DestinationSBP'; + phoneNumber: string; + bankName?: string; + recipientName?: string; +}; + +export type DestinationBankAccount = { + destinationType: 'BankAccount'; + account: string; + bankName?: string; + recipientName?: string; +}; + +export type Destination = DestinationBankCard | DestinationSBP | DestinationBankAccount; diff --git a/src/common/utils/fetchApi.test.ts b/src/common/utils/fetchApi.test.ts new file mode 100644 index 00000000..dcf42b40 --- /dev/null +++ b/src/common/utils/fetchApi.test.ts @@ -0,0 +1,45 @@ +import { fetchApi } from './fetchApi'; + +beforeEach(() => { + global.fetch = jest.fn(); +}); + +describe('fetchApi', () => { + it('handles successful responses correctly', async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ data: 'success' }), + }); + + const response = await fetchApi('https://api.example.com', 'token123', 'GET', 'path'); + const data = await response.json(); + expect(data).toEqual({ data: 'success' }); + }); + + it('throws a generic error for non-JSON error responses', async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + status: 500, + // Simulating a failure in response.json() method + json: () => Promise.reject(new Error('Failed to parse JSON')), + }); + + await expect(fetchApi('https://api.example.com', 'token123', 'POST', 'path', {})).rejects.toThrow( + `API error: 500 Endpoint: https://api.example.com/path`, + ); + }); + + it('throws an error with JSON error details for 400 responses', async () => { + const errorDetails = { error: 'Bad Request', message: 'Invalid parameters' }; + + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + status: 400, + json: () => Promise.resolve({ error: 'Bad Request', message: 'Invalid parameters' }), + }); + + await expect(fetchApi('https://api.example.com', 'token123', 'POST', 'path', {})).rejects.toThrow( + `API error: 400 Endpoint: https://api.example.com/path ${JSON.stringify(errorDetails)}`, + ); + }); +}); diff --git a/src/common/utils/fetchApi.ts b/src/common/utils/fetchApi.ts new file mode 100644 index 00000000..3164a513 --- /dev/null +++ b/src/common/utils/fetchApi.ts @@ -0,0 +1,32 @@ +import guid from 'checkout/utils/guid'; + +export async function fetchApi( + endpoint: string, + accessToken: string, + method: string, + path: string, + body?: any, +): Promise { + const response = await fetch(`${endpoint}/${path}`, { + method, + headers: { + 'Content-Type': 'application/json;charset=utf-8', + Authorization: `Bearer ${accessToken}`, + 'X-Request-ID': guid(), + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + let errorDetails = `API error: ${response.status} Endpoint: ${endpoint}/${path}`; + try { + const errorBody = await response.json(); + errorDetails += ` ${JSON.stringify(errorBody)}`; + } catch (ex) { + // Ignore error + } + throw new Error(errorDetails); + } + + return response; +} diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 26c1c6af..b73ffec6 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -15,6 +15,7 @@ export { isString } from './isString'; export { getEncodedUrlParams } from './getEncodedUrlParams'; export { findMetadata } from './findMetadata'; export { extractError } from './extractError'; +export { fetchApi } from './fetchApi'; export type { CountrySubdivision, Country } from './countries'; export { countries } from './countries'; diff --git a/src/components/ViewContainer/ApiExtensionView/ApiExtensionView.tsx b/src/components/ViewContainer/ApiExtensionView/ApiExtensionView.tsx index fdb18853..82af1c24 100644 --- a/src/components/ViewContainer/ApiExtensionView/ApiExtensionView.tsx +++ b/src/components/ViewContainer/ApiExtensionView/ApiExtensionView.tsx @@ -1,11 +1,10 @@ import { useContext, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; -import { Gateway } from 'checkout/backend'; - import { CompletePayment } from './CompletePayment'; import { Destinations } from './Destinations'; import { GatewaySelector } from './GatewaySelector'; +import { Gateway } from '../../../common/backend/p2p'; import { LocaleContext, PaymentConditionsContext, PaymentContext, PaymentModelContext } from '../../../common/contexts'; import { InvoiceDetermined, PaymentStarted } from '../../../common/paymentCondition'; import { isNil } from '../../../common/utils'; diff --git a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfo.tsx b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfo.tsx index 4d4207c9..2ccabb71 100644 --- a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfo.tsx +++ b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfo.tsx @@ -1,10 +1,11 @@ import { useContext } from 'react'; -import { Destination } from 'checkout/backend'; import { Locale } from 'checkout/locale'; +import { DestinationInfoBankAccount } from './DestinationInfoBankAccount'; import { DestinationInfoBankCard } from './DestinationInfoBankCard'; import { DestinationInfoSpb } from './DestinationInfoSpb'; +import { Destination } from '../../../../common/backend/p2p'; import { ViewModelContext } from '../../../../common/contexts'; import { Info, Container, Row, Label, Value, Alert } from '../commonComponents'; @@ -37,6 +38,9 @@ export const DestinationInfo = ({ locale, destination }: DestinationInfoProps) = {destination.destinationType === 'DestinationSBP' && ( )} + {destination.destinationType === 'BankAccount' && ( + + )} ); }; diff --git a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankAccount.tsx b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankAccount.tsx new file mode 100644 index 00000000..27810dce --- /dev/null +++ b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankAccount.tsx @@ -0,0 +1,34 @@ +import { Locale } from 'checkout/locale'; + +import { CopyToClipboard } from './CopyToClipboard'; +import { DestinationBankAccount } from '../../../../common/backend/p2p'; +import { Container, Label, Row, Value } from '../commonComponents'; + +export type DestinationInfoBankCardInfo = { + locale: Locale; + destination: DestinationBankAccount; +}; + +export const DestinationInfoBankAccount = ({ locale, destination }: DestinationInfoBankCardInfo) => ( + + + + + {destination.account} + + + + {destination?.bankName && ( + + + {destination.bankName} + + )} + {destination?.recipientName && ( + + + {destination.recipientName} + + )} + +); diff --git a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankCard.tsx b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankCard.tsx index 11ef0da2..c384de68 100644 --- a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankCard.tsx +++ b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoBankCard.tsx @@ -1,7 +1,7 @@ -import { DestinationBankCard } from 'checkout/backend/p2p'; import { Locale } from 'checkout/locale'; import { CopyToClipboard } from './CopyToClipboard'; +import { DestinationBankCard } from '../../../../common/backend/p2p'; import { Container, Label, Row, Value } from '../commonComponents'; export type DestinationInfoBankCardInfo = { diff --git a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoSpb.tsx b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoSpb.tsx index 94b38543..37d580e6 100644 --- a/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoSpb.tsx +++ b/src/components/ViewContainer/ApiExtensionView/DestinationInfo/DestinationInfoSpb.tsx @@ -1,7 +1,7 @@ -import { DestinationSBP } from 'checkout/backend'; import { Locale } from 'checkout/locale'; import { CopyToClipboard } from './CopyToClipboard'; +import { DestinationSBP } from '../../../../common/backend/p2p'; import { Container, Label, Row, Value } from '../commonComponents'; export type DestinationInfoSpbProps = { diff --git a/src/components/ViewContainer/ApiExtensionView/GatewaySelector.tsx b/src/components/ViewContainer/ApiExtensionView/GatewaySelector.tsx index cbf515d6..2acc92bd 100644 --- a/src/components/ViewContainer/ApiExtensionView/GatewaySelector.tsx +++ b/src/components/ViewContainer/ApiExtensionView/GatewaySelector.tsx @@ -1,10 +1,11 @@ import { useEffect, useState } from 'react'; -import { Gateway } from 'checkout/backend'; -import { Select } from 'checkout/components/ui'; -import { useGateways } from 'checkout/hooks/p2p'; import { Locale } from 'checkout/locale'; +import { useGateways } from './useGateways'; +import { Gateway } from '../../../common/backend/p2p'; +import { Select } from '../../../components/legacy'; + export type GatewaySelectorProps = { locale: Locale; capiEndpoint: string; diff --git a/src/components/ViewContainer/ApiExtensionView/useComplete.ts b/src/components/ViewContainer/ApiExtensionView/useComplete.ts index 86d4304d..01a4fd80 100644 --- a/src/components/ViewContainer/ApiExtensionView/useComplete.ts +++ b/src/components/ViewContainer/ApiExtensionView/useComplete.ts @@ -1,6 +1,6 @@ import { useCallback, useReducer } from 'react'; -import { complete as completeApi } from 'checkout/backend/p2p'; +import { complete as completeApi } from '../../../common/backend/p2p'; type State = | { status: 'PRISTINE' } diff --git a/src/components/ViewContainer/ApiExtensionView/useDestinations.ts b/src/components/ViewContainer/ApiExtensionView/useDestinations.ts index d07194a4..eeacbcc4 100644 --- a/src/components/ViewContainer/ApiExtensionView/useDestinations.ts +++ b/src/components/ViewContainer/ApiExtensionView/useDestinations.ts @@ -1,6 +1,6 @@ import { useCallback, useReducer, useRef } from 'react'; -import { Destination, getDestinations as getApiDestinations } from 'checkout/backend'; +import { Destination, getDestinations as getApiDestinations } from '../../../common/backend/p2p'; type State = | { status: 'PRISTINE' } diff --git a/src/components/ViewContainer/ApiExtensionView/useGateways.ts b/src/components/ViewContainer/ApiExtensionView/useGateways.ts index df4338e8..57d30188 100644 --- a/src/components/ViewContainer/ApiExtensionView/useGateways.ts +++ b/src/components/ViewContainer/ApiExtensionView/useGateways.ts @@ -1,6 +1,6 @@ import { useCallback, useReducer } from 'react'; -import { Gateway, getGateways as getApiGateways } from 'checkout/backend'; +import { Gateway, getGateways as getApiGateways } from '../../../common/backend/p2p'; type State = | { status: 'PRISTINE' } diff --git a/src/locale/az.json b/src/locale/az.json index faa02615..094e5279 100644 --- a/src/locale/az.json +++ b/src/locale/az.json @@ -45,7 +45,7 @@ "form.pay.paymentTerminalBankCard.providerSelectorDescription": "Ödəniş sistemini seçin:", "form.qr.code": "Ödənişi tamamlamaq üçün bank tətbiqinizlə və ya telefon kamera ilə QR kodunu skan edin", "form.p2p.loading": "Yüklənir...", - "form.p2p.error": "Server xətası baş verdi", + "form.p2p.error": "Fərqli bir məbləğ üçün bir ərizə yaradın və ya daha sonra təkrarlamağa çalışın", "form.p2p.select.destination": "Təyinat yeri seçin...", "form.p2p.destination.info": "Sifariş ödənişi:", "form.p2p.destination.bank.card.pan": "Bu karta:", @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "Bankın adı:", "form.p2p.destination.spb.phone": "Telefon nömrəsi:", "form.p2p.destination.spb.recipient": "Adı:", + "form.p2p.destination.bank.account.account": "Hesab nömrəsi:", + "form.p2p.destination.bank.account.bank": "Bankın adı:", + "form.p2p.destination.bank.account.recipient": "Adı:", "form.p2p.complete.info": "Təqdim etdikdən sonra \"Köçürmə tamamlandı\" düyməsini basın. Ödənişin işlənməsi 5 dəqiqəyə qədər çəkir.", "form.p2p.complete.button": "Köçürmə tamamlandı", "form.p2p.destination.amount": "Köçürmə məbləği:", diff --git a/src/locale/bn.json b/src/locale/bn.json index 2fbb2c7e..814b0e5e 100644 --- a/src/locale/bn.json +++ b/src/locale/bn.json @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "ব্যাংকের নাম:", "form.p2p.destination.spb.phone": "ফোন নম্বর:", "form.p2p.destination.spb.recipient": "নাম:", + "form.p2p.destination.bank.account.account": "অ্যাকাউন্ট নম্বর:", + "form.p2p.destination.bank.account.bank": "ব্যাংকের নাম:", + "form.p2p.destination.bank.account.recipient": "নাম:", "form.p2p.complete.info": "ট্রান্সফার সম্পূর্ণ হওয়ার পর, নীচের বোতাম চাপুন।", "form.p2p.complete.button": "পেমেন্ট সম্পন্ন করুন", "form.p2p.destination.amount": "পরিমাণ:", diff --git a/src/locale/en.json b/src/locale/en.json index b80b76be..6754249d 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "Bank name:", "form.p2p.destination.spb.phone": "Phone number:", "form.p2p.destination.spb.recipient": "Name:", + "form.p2p.destination.bank.account.account": "Account number:", + "form.p2p.destination.bank.account.bank": "Bank name:", + "form.p2p.destination.bank.account.recipient": "Name:", "form.p2p.complete.info": "After completing the transfer, press the button below.", "form.p2p.complete.button": "Complete payment", "form.p2p.destination.amount": "Amount:", diff --git a/src/locale/ja.json b/src/locale/ja.json index 0a43ee98..ef36e6d5 100644 --- a/src/locale/ja.json +++ b/src/locale/ja.json @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "銀行名:", "form.p2p.destination.spb.phone": "電話番号:", "form.p2p.destination.spb.recipient": "名前:", + "form.p2p.destination.bank.account.account": "口座番号:", + "form.p2p.destination.bank.account.bank": "銀行名:", + "form.p2p.destination.bank.account.recipient": "名前:", "form.p2p.complete.info": "送金完了後、以下のボタンを押してください。", "form.p2p.complete.button": "支払い完了", "form.p2p.destination.amount": "金額:", diff --git a/src/locale/ko.json b/src/locale/ko.json index b7a2d7c8..cceb8956 100644 --- a/src/locale/ko.json +++ b/src/locale/ko.json @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "은행 이름:", "form.p2p.destination.spb.phone": "전화번호:", "form.p2p.destination.spb.recipient": "이름:", + "form.p2p.destination.bank.account.account": "계좌 번호:", + "form.p2p.destination.bank.account.bank": "은행 이름:", + "form.p2p.destination.bank.account.recipient": "이름:", "form.p2p.complete.info": "이체를 완료한 후 아래 버튼을 누르세요.", "form.p2p.complete.button": "결제 완료", "form.p2p.destination.amount": "금액:", diff --git a/src/locale/pt.json b/src/locale/pt.json index d1593226..ff0b3654 100644 --- a/src/locale/pt.json +++ b/src/locale/pt.json @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "Nome do banco:", "form.p2p.destination.spb.phone": "Número de telefone:", "form.p2p.destination.spb.recipient": "Nome:", + "form.p2p.destination.bank.account.account": "Número da conta:", + "form.p2p.destination.bank.account.bank": "Nome do banco:", + "form.p2p.destination.bank.account.recipient": "Nome:", "form.p2p.complete.info": "Após completar a transferência, pressione o botão abaixo.", "form.p2p.complete.button": "Concluir pagamento", "form.p2p.destination.amount": "Valor:", diff --git a/src/locale/ru.json b/src/locale/ru.json index 4b8a7c7b..ecc478b4 100644 --- a/src/locale/ru.json +++ b/src/locale/ru.json @@ -45,7 +45,7 @@ "form.pay.paymentTerminalBankCard.providerSelectorDescription": "Выберите платежную систему:", "form.qr.code": "Для оплаты отсканируйте QR-код в мобильном приложении банка или штатной камерой телефона", "form.p2p.loading": "Загрузка...", - "form.p2p.error": "Произошла ошибка сервера", + "form.p2p.error": "Создайте заявку на другую сумму или попробуйте повторить позднее", "form.p2p.select.destination": "Выберите банк...", "form.p2p.destination.info": "Осуществите перевод, используя следующие реквизиты:", "form.p2p.destination.bank.card.pan": "Номер карты:", @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "Банк:", "form.p2p.destination.spb.phone": "Номер телефона:", "form.p2p.destination.spb.recipient": "Получатель:", + "form.p2p.destination.bank.account.account": "Номер счета:", + "form.p2p.destination.bank.account.bank": "Банк:", + "form.p2p.destination.bank.account.recipient": "Получатель:", "form.p2p.complete.info": "После совершения перевода нажмите кнопку ниже. Обработка платежа занимает до 5 минут.", "form.p2p.complete.button": "Перевод выполнен", "form.p2p.destination.amount": "Сумма:", diff --git a/src/locale/tr.json b/src/locale/tr.json index 4af681d2..5882634e 100644 --- a/src/locale/tr.json +++ b/src/locale/tr.json @@ -53,6 +53,9 @@ "form.p2p.destination.spb.bank.name": "Banka adı:", "form.p2p.destination.spb.phone": "Telefon numarası:", "form.p2p.destination.spb.recipient": "İsim:", + "form.p2p.destination.bank.account.account": "Hesap numarası:", + "form.p2p.destination.bank.account.bank": "Banka adı:", + "form.p2p.destination.bank.account.recipient": "Adı:", "form.p2p.complete.info": "Transferi tamamladıktan sonra, aşağıdaki butona basın.", "form.p2p.complete.button": "Ödemeyi Tamamla", "form.p2p.destination.amount": "Tutar:",