Skip to content

Commit

Permalink
feat(PAYMENTS-15236): add finance details component
Browse files Browse the repository at this point in the history
  • Loading branch information
simbirromanmakarov committed Aug 9, 2023
1 parent 42692f6 commit 64ddf84
Show file tree
Hide file tree
Showing 41 changed files with 885 additions and 7 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"webpack-cli": "^5.1.1"
},
"dependencies": {
"currency-format": "^1.0.13",
"i18next": "^22.5.0",
"tsyringe": "^4.7.0"
}
Expand Down
5 changes: 5 additions & 0 deletions src/core/country/country-code.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum CountryCode {
Croatia = 'HR',
Ghana = 'GH',
India = 'IN',
}
3 changes: 3 additions & 0 deletions src/core/currency/currency.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum Currency {
EUR = 'EUR',
}
1 change: 1 addition & 0 deletions src/core/event-name.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export const enum EventName {
getLegalComponentConfig = 'getLegalComponentConfig',
legalComponentPing = 'legalComponentPing',
legalComponentPong = 'legalComponentPong',
financeDetails = 'financeDetails',
nextAction = 'nextAction',
}
15 changes: 15 additions & 0 deletions src/core/finance-details/cart-item.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CartLine } from './cart-line.interface';
import { Price } from './price.interface';

export interface CartItem {
key?: string;
imgSrc?: string;
hasDefaultImg?: boolean;
title: string;
price: Price;
priceBeforeDiscount?: Price;
description?: string | null;
tax?: CartLine | null;
quantity?: number;
isBonus: boolean;
}
10 changes: 10 additions & 0 deletions src/core/finance-details/cart-line.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Price } from './price.interface';

export interface CartLine {
key?: string;
title?: string;
content?: string;
money?: Price;
rate?: number;
isDateLine?: boolean;
}
11 changes: 11 additions & 0 deletions src/core/finance-details/cart-summary.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CartLine } from './cart-line.interface';

export interface CartSummary {
transactionDetails?: CartLine[];
shipping?: CartLine[];
subtotal?: CartLine;
subtotalPayment?: CartLine;
subtotalDetails?: CartLine[];
total: CartLine;
totalDetails?: CartLine[];
}
11 changes: 11 additions & 0 deletions src/core/finance-details/checkout-item.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface CheckoutItem {
quantity: number;
amount: number | null;
amount_before_discount?: number | null;
name: string;
image_url: string;
description: string | null;
currency: string;
is_bonus: boolean;
indirect_tax_rate: number;
}
12 changes: 12 additions & 0 deletions src/core/finance-details/finance-details.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CartItem } from './cart-item.interface';
import { CartSummary } from './cart-summary.interface';
import { XpsFinance } from './xps-finance.interface';
import { XpsPurchase } from './xps-purchase.interface';

export interface FinanceDetails {
purchase: XpsPurchase;
finance: XpsFinance;
cartItems: CartItem[];
cartSummary: CartSummary;
paymentCountry: string;
}
4 changes: 4 additions & 0 deletions src/core/finance-details/price.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Price {
amount: number | null;
currency: string;
}
10 changes: 10 additions & 0 deletions src/core/finance-details/virtual-currency.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface VirtualCurrency {
quantity: number;
amount: number | null;
name: string;
image_url: string;
description: string;
longDescription: string | null;
is_bonus: boolean;
currency: string;
}
32 changes: 32 additions & 0 deletions src/core/finance-details/xps-finance.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export interface XpsFinance {
payment_country: { iso: string };
sub_total: {
amount: number;
currency: string;
payment_amount: number;
payment_currency: string;
};
discount: { amount: number; currency: string };
vat_user?: {
amount: number;
percent: number;
currency: string;
visible: boolean;
};
sales_tax: { amount: number; percent: number; currency: string };
sales_tax_user?: { amount: number; percent: number; currency: string };
fee: { amount: number; currency: string };
total: { amount: number; currency: string };
xsolla_credits?: {
payment_amount: number;
payment_currency: string;
};
vat: {
amount: number;
percent: number;
currency: string;
visible: boolean;
};
user_balance?: { amount: number; currency: string };
grand_total?: { amount: number; currency: string };
}
14 changes: 14 additions & 0 deletions src/core/finance-details/xps-purchase.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CheckoutItem } from './checkout-item.interface';
import { VirtualCurrency } from './virtual-currency.interface';

export interface XpsPurchase {
virtual_currency?: VirtualCurrency[];
checkout_items?: CheckoutItem[];
virtual_items?: VirtualCurrency[];
checkout?: {
amount: number;
currency: string;
description: string;
is_bonus?: boolean;
};
}
14 changes: 14 additions & 0 deletions src/core/guards/finance-details-message.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { EventName } from '../../core/event-name.enum';
import { Message } from '../../core/message.interface';
import { FinanceDetails } from '../finance-details/finance-details.interface';
import { isEventMessage } from './event-message.guard';

export const isFinanceDetailsEventMessage = (
messageData: unknown
): messageData is Message<FinanceDetails> => {
if (isEventMessage(messageData)) {
return messageData.name === EventName.financeDetails;
}

return false;
};
8 changes: 5 additions & 3 deletions src/core/i18n/dictionary.interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Lang } from './lang.enum';

interface Translation {
[key: string]: string | Translation;
}

export type Dictionary = {
[key in Lang]?: {
translation: {
[key: string]: string;
};
translation: Translation;
};
};
3 changes: 3 additions & 0 deletions src/core/i18n/localize.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Lang } from './lang.enum';

@singleton()
export class LocalizeService {
public language = Lang.EN;

public async initDictionaries(): Promise<void> {
await i18next.init({
lng: Lang.EN,
Expand All @@ -25,6 +27,7 @@ export class LocalizeService {
lang = Lang.ZH_HANS;
}

this.language = lang;
await i18next.changeLanguage(lang);
}
}
12 changes: 12 additions & 0 deletions src/core/pipes/currency/currency-format.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
interface SymbolFormat {
grapheme: string;
template: string;
rtl: boolean;
}

export interface CurrencyFormat {
name: string;
fractionSize: number;
symbol: SymbolFormat;
uniqSymbol: SymbolFormat | null;
}
59 changes: 59 additions & 0 deletions src/core/pipes/currency/currency.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import currencyFormat from 'currency-format/currency-format.json';
import { DecimalPipe } from '../decimal/decimal.pipe';
import { PipeTransform } from '../pipe-transform.interface';
import { CurrencyFormat } from './currency-format.interface';

export class CurrencyPipe implements PipeTransform {
public transform(
value: number | string | null,
currencyCode?: string
): string | null {
if (!value || !currencyCode) {
return '';
}

return this.transformWithFormat(value, currencyCode);
}

private getCurrencyConfig(currencyCode: string): CurrencyFormat | null {
// @ts-expect-error json don't have string type index
const formatForCurrency = currencyFormat[
currencyCode.toUpperCase()
] as unknown as CurrencyFormat | undefined;

if (!formatForCurrency) {
return null;
}

return formatForCurrency;
}

private transformWithFormat(
value: number | string,
currencyCode: string
): string | null {
const decimalPipe = new DecimalPipe();
const formatForCurrency = this.getCurrencyConfig(currencyCode);

if (!formatForCurrency?.uniqSymbol) {
return null;
}

const amount = decimalPipe.transform(
value,
1,
formatForCurrency.fractionSize,
formatForCurrency.fractionSize
);

if (!amount) {
return null;
}

const formattedCurrency = formatForCurrency.uniqSymbol.template
.replace('1', amount)
.replace('$', formatForCurrency.uniqSymbol.grapheme);

return formattedCurrency;
}
}
28 changes: 28 additions & 0 deletions src/core/pipes/decimal/decimal.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { LocalizeService } from '../../i18n/localize.service';
import { container } from 'tsyringe';
import { PipeTransform } from '../pipe-transform.interface';
import { localeValidator } from './locale.validator';

export class DecimalPipe implements PipeTransform {
public transform(
value: number | string,
minIntegerDigits = 1,
minFracDigits = 2,
maxFracDigits = 2,
locale = null
): string {
value = Number(value);

if (isNaN(value)) {
return '';
}

const localizeService = container.resolve(LocalizeService);
const formatLocale = localeValidator(locale ?? localizeService.language);
return new Intl.NumberFormat(formatLocale, {
minimumIntegerDigits: minIntegerDigits,
minimumFractionDigits: minFracDigits,
maximumFractionDigits: maxFracDigits,
}).format(value);
}
}
14 changes: 14 additions & 0 deletions src/core/pipes/decimal/locale.validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Lang } from '../../i18n/lang.enum';

const specialLocaleMap = new Map<Lang, Lang>([
[Lang.ZH_HANS, Lang.CN],
[Lang.ZH_HANT, Lang.CN],
]);

export const localeValidator = (locale: Lang): Lang => {
if (specialLocaleMap.has(locale)) {
return specialLocaleMap.get(locale)!;
}

return locale;
};
3 changes: 3 additions & 0 deletions src/core/pipes/pipe-transform.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface PipeTransform {
transform(value: unknown, ...args: unknown[]): unknown;
}
2 changes: 2 additions & 0 deletions src/core/web-components/web-component-tag-name.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ export enum WebComponentTagName {
TextComponent = 'psdk-text-component',
SubmitButtonComponent = 'psdk-submit-button',
PaymentMethodsComponent = 'psdk-payment-methods',
PriceTextComponent = 'psdk-price-text',
FinanceDetailsComponent = 'psdk-finance-details',
LegalComponent = 'psdk-legal',
}
16 changes: 16 additions & 0 deletions src/core/web-components/web-component.abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,20 @@ export abstract class WebComponentAbstract extends HTMLElement {
protected attributeChangedCallback(): void {
this.render();
}

protected getNumberAttribute(name: string): number | null {
const stringValue = this.getAttribute(name);

if (!stringValue) {
return null;
}

const numberValue = parseFloat(stringValue);

if (isNaN(numberValue)) {
return null;
}

return numberValue;
}
}
4 changes: 4 additions & 0 deletions src/core/web-components/web-components.map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import { SubmitButtonComponent } from '../../features/headless-checkout/web-comp
import { WebComponentTagName } from './web-component-tag-name.enum';
import { PaymentMethodsComponent } from '../../features/headless-checkout/web-components/payment-methods/payment-methods.component';
import { LegalComponent } from '../../features/headless-checkout/web-components/legal/legal.component';
import { FinanceDetailsComponent } from '../../features/headless-checkout/web-components/finance-details/finance-details.component';
import { PriceTextComponent } from '../../features/headless-checkout/web-components/finance-details/price-text/price-text.component';

export const webComponents: {
[key in WebComponentTagName]: CustomElementConstructor;
} = {
[WebComponentTagName.TextComponent]: TextComponent,
[WebComponentTagName.SubmitButtonComponent]: SubmitButtonComponent,
[WebComponentTagName.PaymentMethodsComponent]: PaymentMethodsComponent,
[WebComponentTagName.PriceTextComponent]: PriceTextComponent,
[WebComponentTagName.FinanceDetailsComponent]: FinanceDetailsComponent,
[WebComponentTagName.LegalComponent]: LegalComponent,
};
3 changes: 1 addition & 2 deletions src/features/headless-checkout/environment.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export const headlessCheckoutAppUrl =
'https://secure.xsolla.com/headless-checkout';
export const headlessCheckoutAppUrl = 'http://localhost:4400';
export const cdnUrl = 'https://cdn3.xsolla.com';
Loading

0 comments on commit 64ddf84

Please sign in to comment.