OPS-481: Invoice randomize amount (#313)

This commit is contained in:
Ildar Galeev 2024-07-05 16:50:43 +07:00 committed by GitHub
parent 43a667f6a5
commit 0adbe879d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 60 additions and 12 deletions

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "إكمال الدفع",
"form.p2p.complete.loading": "يرجى الانتظار",
"form.p2p.destination.amount": "المبلغ:",
"form.p2p.destination.randomizeAmountDescription": "لقد قمنا بتغيير مبلغ التحويل. يساعد ذلك في زيادة احتمال نجاح الدفع.",
"form.p2p.alert.li": ["حول المبلغ الدقيق", "قم بالتحويل كامل المبلغ في تحويلة واحدة"],
"form.p2p.alert.p": "وإلا، لن يتم اعتماد الدفع.",
"form.p2p.gateway.selector.heading": "اختر طريقة أو بنك",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "Köçürmə tamamlandı",
"form.p2p.complete.loading": "Lütfən, gözləyin",
"form.p2p.destination.amount": "Köçürmə məbləği:",
"form.p2p.destination.randomizeAmountDescription": "Biz köçürmə məbləğini dəyişdik. Bu, ödənişin uğurlu olma ehtimalını artırır.",
"form.p2p.alert.li": ["Ərizədə göstərilən dəqiq məbləği köçürün", "Bütün məbləği bir köçürmədə ödəyin"],
"form.p2p.alert.p": "Əks halda ödəniş hesaba alınmayacaq.",
"form.p2p.gateway.selector.heading": "Bir metod və ya bank seçin",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "ট্রান্সফার সম্পন্ন",
"form.p2p.complete.loading": "অনুগ্রহ করে অপেক্ষা করুন",
"form.p2p.destination.amount": "পরিমাণ:",
"form.p2p.destination.randomizeAmountDescription": "আমরা স্থানান্তরের পরিমাণ পরিবর্তন করেছি। এটি সফল পেমেন্টের সম্ভাবনা বাড়াতে সাহায্য করে।",
"form.p2p.alert.li": ["অর্ডারে উল্লিখিত সঠিক পরিমাণ ট্রান্সফার করুন।", "পুরো পরিমাণ একবারে পরিশোধ করুন।"],
"form.p2p.alert.p": "অন্যথায়, পেমেন্ট গৃহীত হবে না।",
"form.p2p.gateway.selector.heading": "একটি পদ্ধতি বা ব্যাংক চয়ন করুন",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "Complete payment",
"form.p2p.complete.loading": "Please, wait",
"form.p2p.destination.amount": "Amount:",
"form.p2p.destination.randomizeAmountDescription": "We have adjusted the transfer amount. This helps increase the chance of a successful payment.",
"form.p2p.alert.li": ["Transfer the exact amount", "Transfer the entire amount in one transaction"],
"form.p2p.alert.p": "Otherwise, the payment will not be credited.",
"form.p2p.gateway.selector.heading": "Choose a method or bank",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "支払い完了",
"form.p2p.complete.loading": "読み込み中...",
"form.p2p.destination.amount": "金額:",
"form.p2p.destination.randomizeAmountDescription": "送金額を変更しました。これにより、支払いが成功する可能性が高まります。",
"form.p2p.alert.li": ["正確な金額を振り込んでください", "全額を一回の取引で振り込んでください"],
"form.p2p.alert.p": "そうしない場合、支払いは認められません。",
"form.p2p.gateway.selector.heading": "方法または銀行を選択してください",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "이체 완료",
"form.p2p.complete.loading": "기다려 주십시오",
"form.p2p.destination.amount": "금액:",
"form.p2p.destination.randomizeAmountDescription": "송금 금액을 조정했습니다. 이는 성공적인 결제 가능성을 높이는 데 도움이 됩니다.",
"form.p2p.alert.li": ["요청한 정확한 금액을 이체하십시오.", "전체 금액을 한 번에 이체하십시오."],
"form.p2p.alert.p": "그렇지 않으면 결제가 인정되지 않습니다.",
"form.p2p.gateway.selector.heading": "방법 또는 은행 선택",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "Transferência concluída",
"form.p2p.complete.loading": "Por favor, aguarde",
"form.p2p.destination.amount": "Quantia:",
"form.p2p.destination.randomizeAmountDescription": "Alteramos o valor da transferência. Isso ajuda a aumentar a probabilidade de um pagamento bem-sucedido.",
"form.p2p.alert.li": [
"Transfira a quantia exata especificada no pedido.",
"Pague a quantia total em uma única transferência."

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "Перевод выполнен",
"form.p2p.complete.loading": "Пожалуйста, подождите",
"form.p2p.destination.amount": "Сумма:",
"form.p2p.destination.randomizeAmountDescription": "Мы изменили сумму перевода. Это помогает повысить вероятность успешного платежа.",
"form.p2p.alert.li": ["Переводите точную сумму, указанную в заявке.", "Оплачивайте всю сумму одним переводом."],
"form.p2p.alert.p": "В противном случае платеж не будет зачислен.",
"form.p2p.gateway.selector.heading": "Выберите метод или банк",

View File

@ -51,6 +51,7 @@
"form.p2p.complete.button": "Transfer tamamlandı",
"form.p2p.complete.loading": "Lütfen bekleyin",
"form.p2p.destination.amount": "Miktar:",
"form.p2p.destination.randomizeAmountDescription": "Transfer tutarını değiştirdik. Bu, başarılı bir ödeme olasılığını artırır.",
"form.p2p.alert.li": ["Talep edilen kesin miktarı transfer edin.", "Tüm miktarı tek bir transferde ödeyin."],
"form.p2p.alert.p": "Aksi takdirde ödeme alınmayacaktır.",
"form.p2p.gateway.selector.heading": "Bir yöntem veya banka seçin",

View File

@ -1,5 +1,10 @@
import { ServiceProviderMetadata } from './serviceProviderMetadata';
export type InvoiceAmountRandomized = {
original: number;
randomized: number;
};
export type ClientInfo = {
fingerprint: string;
ip?: string;
@ -275,4 +280,5 @@ export type Invoice = {
reason: string;
cart: InvoiceLine[];
externalID?: string;
amountRandomized?: InvoiceAmountRandomized;
};

View File

@ -1,8 +1,10 @@
import { isNil } from 'checkout/utils';
import { InvoiceAndToken } from '../backend/payments';
import { InvoiceContext } from '../paymentModel';
export const invoiceToInvoiceContext = ({
invoice: { id, externalID, dueDate, status },
invoice: { id, externalID, dueDate, status, amountRandomized },
invoiceAccessToken,
}: InvoiceAndToken): InvoiceContext => ({
type: 'InvoiceContext',
@ -13,4 +15,5 @@ export const invoiceToInvoiceContext = ({
externalID,
dueDate,
status,
isAmountRandomized: !isNil(amountRandomized),
});

View File

@ -1,3 +1,5 @@
import { isNil } from 'checkout/utils';
import { backendModelToPaymentAmount } from './backendModelToPaymentAmount';
import { backendModelToPaymentMethods } from './backendModelToPaymentMethods';
import { BackendModel, BackendModelInvoice, BackendModelInvoiceTemplate, getBackendModel } from './getBackendModel';
@ -8,13 +10,14 @@ import { InitParams } from '../init';
const applyInvoice = (
{ invoiceParams, type }: Partial<InvoiceContext>,
{ invoice: { dueDate, externalID, status } }: BackendModelInvoice,
{ invoice: { dueDate, externalID, status, amountRandomized } }: BackendModelInvoice,
): InvoiceContext => ({
type,
invoiceParams,
dueDate,
externalID,
status,
isAmountRandomized: !isNil(amountRandomized),
});
const applyInvoiceTemplate = (

View File

@ -83,6 +83,7 @@ export type InvoiceContext = {
readonly dueDate: string;
readonly status: InvoiceStatus;
readonly externalID?: string;
readonly isAmountRandomized: boolean;
};
export type PaymentModelInvoice = InvoiceContext & CommonPaymentModel;

View File

@ -1,8 +1,9 @@
import { VStack } from '@chakra-ui/react';
import { VStack, Alert, Text, Divider, AlertIcon } from '@chakra-ui/react';
import { useContext } from 'react';
import { Destination } from 'checkout/backend/p2p';
import { LocaleContext, PaymentModelContext, ViewModelContext } from 'checkout/contexts';
import { LocaleContext, PaymentConditionsContext, PaymentModelContext, ViewModelContext } from 'checkout/contexts';
import { InvoiceDetermined, PaymentCondition } from 'checkout/paymentCondition';
import { DestinationBankAccountInfo } from './DestinationBankAccountInfo';
import { InfoItem } from './InfoItem';
@ -10,6 +11,9 @@ import { formatCardPan, formatPhoneNumber } from './utils';
import { SBPIcon } from '../icons/SBPIcon';
import { getGatewayIcon, mapGatewayName } from '../utils';
const isInvoiceDetermined = (condition: PaymentCondition): condition is InvoiceDetermined =>
condition.name === 'invoiceDetermined';
export type DestinationInfoProps = {
destination: Destination;
};
@ -17,16 +21,29 @@ export type DestinationInfoProps = {
export function DestinationInfo({ destination }: DestinationInfoProps) {
const { l } = useContext(LocaleContext);
const { viewAmount } = useContext(ViewModelContext);
const { paymentModel } = useContext(PaymentModelContext);
const { conditions } = useContext(PaymentConditionsContext);
const {
paymentModel: {
paymentAmount: { currency },
},
} = useContext(PaymentModelContext);
invoiceContext: { isAmountRandomized },
} = conditions.find<InvoiceDetermined>(isInvoiceDetermined);
const {
paymentAmount: { currency },
} = paymentModel;
return (
<VStack align="stretch">
<InfoItem label={l['form.p2p.destination.amount']} value={viewAmount} />
<VStack align="stretch">
{isAmountRandomized && (
<Alert borderRadius="xl" p={3} status="warning">
<AlertIcon />
<Text fontSize="sm">{l['form.p2p.destination.randomizeAmountDescription']}</Text>
</Alert>
)}
<InfoItem isDivider={false} label={l['form.p2p.destination.amount']} value={viewAmount} />
<Divider />
</VStack>
{destination.destinationType === 'BankCard' && (
<InfoItem
formatter={formatCardPan}

View File

@ -1,13 +1,15 @@
import { VStack, Text, Flex, Spacer, Divider, useClipboard, useToast, IconButton, createIcon } from '@chakra-ui/react';
import { ReactElement, cloneElement, useContext, useEffect, useState } from 'react';
import { ReactElement, cloneElement, useContext, useEffect, useMemo, useState } from 'react';
import { LocaleContext } from 'checkout/contexts';
import { isNil } from 'checkout/utils';
export type InfoItemProps = {
label: string;
value: string;
icon?: ReactElement;
isCopyable?: boolean;
isDivider?: boolean;
formatter?: (value: string) => Promise<string>;
};
@ -19,12 +21,19 @@ export const CopyIcon = createIcon({
),
});
export function InfoItem({ label, value, isCopyable, formatter, icon }: InfoItemProps) {
export function InfoItem({ label, value, isCopyable, formatter, icon, isDivider }: InfoItemProps) {
const { l } = useContext(LocaleContext);
const { onCopy, hasCopied } = useClipboard(value);
const [displayValue, setDisplayValue] = useState(value);
const toast = useToast();
const isDividerVisible = useMemo(() => {
if (isNil(isDivider)) {
return true;
}
return isDivider;
}, [isDivider]);
useEffect(() => {
if (!formatter) return;
formatter(value).then(setDisplayValue);
@ -63,7 +72,7 @@ export function InfoItem({ label, value, isCopyable, formatter, icon }: InfoItem
{isCopyable && <IconButton aria-label="Copy" icon={<CopyIcon />} size="xs" onClick={onCopy} />}
</Flex>
</Flex>
<Divider />
{isDividerVisible && <Divider />}
</VStack>
);
}