diff --git a/package-lock.json b/package-lock.json index 7b4e922..515ced2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "ISC", "dependencies": { + "currency-format": "^1.0.13", "i18next": "^22.5.0", "tsyringe": "^4.7.0" }, @@ -3121,6 +3122,11 @@ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", "dev": true }, + "node_modules/currency-format": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/currency-format/-/currency-format-1.0.13.tgz", + "integrity": "sha512-mokQiLDVtf0GgGVGbPE0Epx587z0d2WhOA0ITvjSDunyZ/DexmOR5hRrK9RU7hbyWe3G6Sf7iE29YKhiGjexSQ==" + }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", diff --git a/package.json b/package.json index 4060097..66c0b79 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "webpack-cli": "^5.1.1" }, "dependencies": { + "currency-format": "^1.0.13", "i18next": "^22.5.0", "tsyringe": "^4.7.0" } diff --git a/src/core/country/country-code.enum.ts b/src/core/country/country-code.enum.ts new file mode 100644 index 0000000..a96c5cb --- /dev/null +++ b/src/core/country/country-code.enum.ts @@ -0,0 +1,5 @@ +export enum CountryCode { + Croatia = 'HR', + Ghana = 'GH', + India = 'IN', +} diff --git a/src/core/currency/currency.enum.ts b/src/core/currency/currency.enum.ts new file mode 100644 index 0000000..16d20dc --- /dev/null +++ b/src/core/currency/currency.enum.ts @@ -0,0 +1,4 @@ +export enum Currency { + USD = 'USD', + EUR = 'EUR', +} diff --git a/src/core/event-name.enum.ts b/src/core/event-name.enum.ts index 5d5db4f..f79762c 100644 --- a/src/core/event-name.enum.ts +++ b/src/core/event-name.enum.ts @@ -11,5 +11,6 @@ export const enum EventName { getPaymentStatus = 'getPaymentStatus', legalComponentPing = 'legalComponentPing', legalComponentPong = 'legalComponentPong', + financeDetails = 'financeDetails', nextAction = 'nextAction', } diff --git a/src/core/finance-details/cart-item.interface.ts b/src/core/finance-details/cart-item.interface.ts new file mode 100644 index 0000000..34d2595 --- /dev/null +++ b/src/core/finance-details/cart-item.interface.ts @@ -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; +} diff --git a/src/core/finance-details/cart-line.interface.ts b/src/core/finance-details/cart-line.interface.ts new file mode 100644 index 0000000..69e1f4c --- /dev/null +++ b/src/core/finance-details/cart-line.interface.ts @@ -0,0 +1,10 @@ +import { Price } from './price.interface'; + +export interface CartLine { + key?: string; + title?: string; + content?: string; + money?: Price; + rate?: number; + isDateLine?: boolean; +} diff --git a/src/core/finance-details/cart-summary.interface.ts b/src/core/finance-details/cart-summary.interface.ts new file mode 100644 index 0000000..ebb59c0 --- /dev/null +++ b/src/core/finance-details/cart-summary.interface.ts @@ -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[]; +} diff --git a/src/core/finance-details/checkout-item.interface.ts b/src/core/finance-details/checkout-item.interface.ts new file mode 100644 index 0000000..db442ba --- /dev/null +++ b/src/core/finance-details/checkout-item.interface.ts @@ -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; +} diff --git a/src/core/finance-details/finance-details.interface.ts b/src/core/finance-details/finance-details.interface.ts new file mode 100644 index 0000000..312bb5a --- /dev/null +++ b/src/core/finance-details/finance-details.interface.ts @@ -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; +} diff --git a/src/core/finance-details/price.interface.ts b/src/core/finance-details/price.interface.ts new file mode 100644 index 0000000..785afa7 --- /dev/null +++ b/src/core/finance-details/price.interface.ts @@ -0,0 +1,4 @@ +export interface Price { + amount: number | null; + currency: string; +} diff --git a/src/core/finance-details/virtual-currency.interface.ts b/src/core/finance-details/virtual-currency.interface.ts new file mode 100644 index 0000000..8bc74a0 --- /dev/null +++ b/src/core/finance-details/virtual-currency.interface.ts @@ -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; +} diff --git a/src/core/finance-details/xps-finance.interface.ts b/src/core/finance-details/xps-finance.interface.ts new file mode 100644 index 0000000..0e5a9f7 --- /dev/null +++ b/src/core/finance-details/xps-finance.interface.ts @@ -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 }; +} diff --git a/src/core/finance-details/xps-purchase.interface.ts b/src/core/finance-details/xps-purchase.interface.ts new file mode 100644 index 0000000..24f4e6c --- /dev/null +++ b/src/core/finance-details/xps-purchase.interface.ts @@ -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; + }; +} diff --git a/src/core/guards/finance-details-message.guard.ts b/src/core/guards/finance-details-message.guard.ts new file mode 100644 index 0000000..c35a4a0 --- /dev/null +++ b/src/core/guards/finance-details-message.guard.ts @@ -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 => { + if (isEventMessage(messageData)) { + return messageData.name === EventName.financeDetails; + } + + return false; +}; diff --git a/src/core/pipes/currency/currency-format.interface.ts b/src/core/pipes/currency/currency-format.interface.ts new file mode 100644 index 0000000..88edea0 --- /dev/null +++ b/src/core/pipes/currency/currency-format.interface.ts @@ -0,0 +1,12 @@ +interface SymbolFormat { + grapheme: string; + template: string; + rtl: boolean; +} + +export interface CurrencyFormat { + name: string; + fractionSize: number; + symbol: SymbolFormat; + uniqSymbol: SymbolFormat | null; +} diff --git a/src/core/pipes/currency/currency.pipe.spec.ts b/src/core/pipes/currency/currency.pipe.spec.ts new file mode 100644 index 0000000..8ab5a9c --- /dev/null +++ b/src/core/pipes/currency/currency.pipe.spec.ts @@ -0,0 +1,55 @@ +import { container } from 'tsyringe'; +import { DecimalPipe } from '../decimal/decimal.pipe'; +import { CurrencyPipe } from './currency.pipe'; + +describe('CurrencyPipe', () => { + let pipe: CurrencyPipe; + let decimalPipe: DecimalPipe; + + beforeEach(() => { + container.clearInstances(); + decimalPipe = { + transform: (value) => value.toString(), + }; + pipe = container + .createChildContainer() + .register(DecimalPipe, { + useValue: decimalPipe, + }) + .resolve(CurrencyPipe); + }); + + it('Should return null for no value', () => { + const currency = pipe.transform(null, 'USD'); + expect(currency).toBeNull(); + }); + + it('Should return null for no currency', () => { + const currency = pipe.transform(1, ''); + expect(currency).toBeNull(); + }); + + it('Should format known currency with configuration', () => { + const amount = 1234567890; + const currency = pipe.transform(amount, 'USD'); + expect(currency).toEqual('US$1234567890'); + }); + + it('Should return null for known currency and invalid amount', () => { + spyOn(decimalPipe, 'transform').and.returnValue(''); + const currency = pipe.transform(1, 'USD'); + expect(currency).toBeNull(); + }); + + it('Should format unknown currency with default template', () => { + const amount = 1234567890; + const currency = pipe.transform(amount, 'AAA'); + expect(currency).toEqual('1234567890 AAA'); + }); + + it('Should return null for unknown currency and invalid amount', () => { + spyOn(decimalPipe, 'transform').and.returnValue(''); + const currency = pipe.transform(1, 'AAA'); + expect(currency).toBeNull(); + }); +}); diff --git a/src/core/pipes/currency/currency.pipe.ts b/src/core/pipes/currency/currency.pipe.ts new file mode 100644 index 0000000..799af89 --- /dev/null +++ b/src/core/pipes/currency/currency.pipe.ts @@ -0,0 +1,75 @@ +import currencyFormat from 'currency-format/currency-format.json'; +import { singleton } from 'tsyringe'; +import { DecimalPipe } from '../decimal/decimal.pipe'; +import { PipeTransform } from '../pipe-transform.interface'; +import { CurrencyFormat } from './currency-format.interface'; + +@singleton() +export class CurrencyPipe implements PipeTransform { + public constructor(private readonly decimalPipe: DecimalPipe) {} + + public transform( + value: number | string | null, + currencyCode?: string + ): string | null { + if (!value || !currencyCode) { + return null; + } + + return this.formatCurrency(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 formatCurrency( + value: number | string, + currencyCode: string + ): string | null { + const currencyConfig = this.getCurrencyConfig(currencyCode); + + if (!currencyConfig?.uniqSymbol) { + return this.formatCurrencyWithNoConfig(value, currencyCode); + } + + const amount = this.decimalPipe.transform( + value, + 1, + currencyConfig.fractionSize, + currencyConfig.fractionSize + ); + + if (!amount) { + return null; + } + + const formattedCurrency = currencyConfig.uniqSymbol.template + .replace('1', amount) + .replace('$', currencyConfig.uniqSymbol.grapheme); + + return formattedCurrency; + } + + private formatCurrencyWithNoConfig( + value: number | string, + currencyCode: string + ): string | null { + const amount = this.decimalPipe.transform(value, 1, 2, 2); + + if (!amount) { + return null; + } + + return `${amount} ${currencyCode}`; + } +} diff --git a/src/core/pipes/decimal/decimal.pipe.spec.ts b/src/core/pipes/decimal/decimal.pipe.spec.ts new file mode 100644 index 0000000..9b6afd6 --- /dev/null +++ b/src/core/pipes/decimal/decimal.pipe.spec.ts @@ -0,0 +1,35 @@ +import { Lang } from '../../../core/i18n/lang.enum'; +import { container } from 'tsyringe'; +import { DecimalPipe } from './decimal.pipe'; + +describe('DecimalPipe', () => { + let pipe: DecimalPipe; + + beforeEach(() => { + pipe = container.createChildContainer().resolve(DecimalPipe); + }); + + it('Should return empty string for invalid number', () => { + const value = pipe.transform('no-number'); + expect(value).toEqual(''); + }); + + it('Should format string number with default settings', () => { + const value = pipe.transform('1234567890.123'); + expect(value).toEqual('1,234,567,890.12'); + }); + + it('Should format string number with custom settings', () => { + const minIntDigits = 2; + const minFracDigits = 1; + const maxFracDigits = 3; + const value = pipe.transform( + '1.234', + minIntDigits, + minFracDigits, + maxFracDigits, + Lang.RU + ); + expect(value).toEqual('01,234'); + }); +}); diff --git a/src/core/pipes/decimal/decimal.pipe.ts b/src/core/pipes/decimal/decimal.pipe.ts new file mode 100644 index 0000000..aa860db --- /dev/null +++ b/src/core/pipes/decimal/decimal.pipe.ts @@ -0,0 +1,29 @@ +import i18next from 'i18next'; +import { singleton } from 'tsyringe'; +import { Lang } from '../../../core/i18n/lang.enum'; +import { PipeTransform } from '../pipe-transform.interface'; +import { localeValidator } from './locale.validator'; + +@singleton() +export class DecimalPipe implements PipeTransform { + public transform( + value: number | string, + minIntegerDigits = 1, + minFracDigits = 2, + maxFracDigits = 2, + locale: Lang | null = null + ): string { + value = Number(value); + + if (isNaN(value)) { + return ''; + } + + const formatLocale = localeValidator(locale ?? (i18next.language as Lang)); + return new Intl.NumberFormat(formatLocale, { + minimumIntegerDigits: minIntegerDigits, + minimumFractionDigits: minFracDigits, + maximumFractionDigits: maxFracDigits, + }).format(value); + } +} diff --git a/src/core/pipes/decimal/locale.validator.spec.ts b/src/core/pipes/decimal/locale.validator.spec.ts new file mode 100644 index 0000000..80d1a09 --- /dev/null +++ b/src/core/pipes/decimal/locale.validator.spec.ts @@ -0,0 +1,16 @@ +import { Lang } from '../../../core/i18n/lang.enum'; +import { localeValidator } from './locale.validator'; + +describe('localeValidator', () => { + it('Should map ZH_HANS into CN locale', () => { + expect(localeValidator(Lang.ZH_HANS)).toEqual(Lang.CN); + }); + + it('Should map ZH_HANT into CN locale', () => { + expect(localeValidator(Lang.ZH_HANT)).toEqual(Lang.CN); + }); + + it('Should not map other locales', () => { + expect(localeValidator(Lang.EN)).toEqual(Lang.EN); + }); +}); diff --git a/src/core/pipes/decimal/locale.validator.ts b/src/core/pipes/decimal/locale.validator.ts new file mode 100644 index 0000000..908b6b6 --- /dev/null +++ b/src/core/pipes/decimal/locale.validator.ts @@ -0,0 +1,14 @@ +import { Lang } from '../../i18n/lang.enum'; + +const specialLocaleMap = new Map([ + [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; +}; diff --git a/src/core/pipes/pipe-transform.interface.ts b/src/core/pipes/pipe-transform.interface.ts new file mode 100644 index 0000000..ea73a2e --- /dev/null +++ b/src/core/pipes/pipe-transform.interface.ts @@ -0,0 +1,3 @@ +export interface PipeTransform { + transform(value: unknown, ...args: unknown[]): unknown; +} diff --git a/src/core/web-components/web-component-tag-name.enum.ts b/src/core/web-components/web-component-tag-name.enum.ts index ac9918f..18505f8 100644 --- a/src/core/web-components/web-component-tag-name.enum.ts +++ b/src/core/web-components/web-component-tag-name.enum.ts @@ -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', } diff --git a/src/core/web-components/web-component.abstract.ts b/src/core/web-components/web-component.abstract.ts index 09fef1c..a229248 100644 --- a/src/core/web-components/web-component.abstract.ts +++ b/src/core/web-components/web-component.abstract.ts @@ -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; + } } diff --git a/src/core/web-components/web-components.map.ts b/src/core/web-components/web-components.map.ts index ec4472e..6e4aad1 100644 --- a/src/core/web-components/web-components.map.ts +++ b/src/core/web-components/web-components.map.ts @@ -3,6 +3,8 @@ 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; @@ -10,5 +12,7 @@ export const webComponents: { [WebComponentTagName.TextComponent]: TextComponent, [WebComponentTagName.SubmitButtonComponent]: SubmitButtonComponent, [WebComponentTagName.PaymentMethodsComponent]: PaymentMethodsComponent, + [WebComponentTagName.PriceTextComponent]: PriceTextComponent, + [WebComponentTagName.FinanceDetailsComponent]: FinanceDetailsComponent, [WebComponentTagName.LegalComponent]: LegalComponent, }; diff --git a/src/features/headless-checkout/headless-checkout.spec.ts b/src/features/headless-checkout/headless-checkout.spec.ts index c280f4a..69d8bb4 100644 --- a/src/features/headless-checkout/headless-checkout.spec.ts +++ b/src/features/headless-checkout/headless-checkout.spec.ts @@ -5,6 +5,7 @@ import { EventName } from '../../core/event-name.enum'; import { Message } from '../../core/message.interface'; import { Handler } from '../../core/post-messages-client/handler.type'; import { LocalizeService } from '../../core/i18n/localize.service'; +import { getFinanceDetailsHandler } from './post-messages-handlers/get-finance-details.handler'; const mockMessage: Message = { name: EventName.initPayment, @@ -129,6 +130,15 @@ describe('HeadlessCheckout', () => { } }); + it('Should getFinanceDetails', async () => { + const spy = spyOn(postMessagesClient, 'send'); + await headlessCheckout.getFinanceDetails(); + expect(spy).toHaveBeenCalledWith( + { name: EventName.financeDetails }, + getFinanceDetailsHandler + ); + }); + it('Should getRegularMethods', async () => { const spy = spyOn(postMessagesClient, 'send'); await headlessCheckout.getRegularMethods(); diff --git a/src/features/headless-checkout/headless-checkout.ts b/src/features/headless-checkout/headless-checkout.ts index 7a3d599..62137e2 100644 --- a/src/features/headless-checkout/headless-checkout.ts +++ b/src/features/headless-checkout/headless-checkout.ts @@ -25,6 +25,8 @@ import { getUserBalanceHandler } from './post-messages-handlers/get-user-balance import { nextActionHandler } from './post-messages-handlers/next-action.handler'; import { getPaymentStatusHandler } from './post-messages-handlers/get-payment-status/get-payment-status.handler'; import { headlessCheckoutAppUrl } from './environment'; +import { FinanceDetails } from '../../core/finance-details/finance-details.interface'; +import { getFinanceDetailsHandler } from './post-messages-handlers/get-finance-details.handler'; @singleton() export class HeadlessCheckout { @@ -166,6 +168,21 @@ export class HeadlessCheckout { ); } + /** + * Returns finance details for created payment + * @returns promise that returns finance details + */ + public async getFinanceDetails(): Promise { + const msg: Message = { + name: EventName.financeDetails, + }; + + return this.postMessagesClient.send( + msg, + getFinanceDetailsHandler + ) as Promise; + } + /** * Returns available payment methods except quick methods * @param country Country that quick methods should be loaded for. diff --git a/src/features/headless-checkout/post-messages-handlers/get-finance-details.handler.spec.ts b/src/features/headless-checkout/post-messages-handlers/get-finance-details.handler.spec.ts new file mode 100644 index 0000000..e495fd5 --- /dev/null +++ b/src/features/headless-checkout/post-messages-handlers/get-finance-details.handler.spec.ts @@ -0,0 +1,26 @@ +import { EventName } from '../../../core/event-name.enum'; +import { FinanceDetails } from '../../../core/finance-details/finance-details.interface'; +import { Message } from '../../../core/message.interface'; +import { getFinanceDetailsHandler } from './get-finance-details.handler'; + +const financeDetails = {} as unknown as FinanceDetails; + +const mockMessage: Message = { + name: EventName.financeDetails, + data: financeDetails, +}; + +describe('getFinanceDetailsHandler', () => { + it('Should handle financeDetails event', () => { + expect(getFinanceDetailsHandler(mockMessage)).toEqual({ + isHandled: true, + value: financeDetails, + }); + }); + + it('Should not handle not financeDetails event', () => { + expect( + getFinanceDetailsHandler({ name: EventName.initPayment }) + ).toBeNull(); + }); +}); diff --git a/src/features/headless-checkout/post-messages-handlers/get-finance-details.handler.ts b/src/features/headless-checkout/post-messages-handlers/get-finance-details.handler.ts new file mode 100644 index 0000000..8ae0df3 --- /dev/null +++ b/src/features/headless-checkout/post-messages-handlers/get-finance-details.handler.ts @@ -0,0 +1,16 @@ +import { FinanceDetails } from '../../../core/finance-details/finance-details.interface'; +import { isFinanceDetailsEventMessage } from '../../../core/guards/finance-details-message.guard'; +import { Message } from '../../../core/message.interface'; +import { Handler } from '../../../core/post-messages-client/handler.type'; + +export const getFinanceDetailsHandler: Handler = ( + message: Message +): { isHandled: boolean; value?: FinanceDetails | null } | null => { + if (isFinanceDetailsEventMessage(message)) { + return { + isHandled: true, + value: message.data, + }; + } + return null; +}; diff --git a/src/features/headless-checkout/web-components/finance-details/cart-items.template.spec.ts b/src/features/headless-checkout/web-components/finance-details/cart-items.template.spec.ts new file mode 100644 index 0000000..80edc47 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/cart-items.template.spec.ts @@ -0,0 +1,85 @@ +import i18next from 'i18next'; +import { CartItem } from '../../../../core/finance-details/cart-item.interface'; +import { getCartItemsTemplate } from './cart-items.template'; + +const item: CartItem = { + key: 'key', + hasDefaultImg: false, + title: 'title', + price: { + amount: 10, + currency: 'USD', + }, + description: 'description', + quantity: 1, + isBonus: false, +}; + +const itemWithImage = { + ...item, + imgSrc: 'imgSrc', +}; + +const itemWithDiscount: CartItem = { + ...item, + priceBeforeDiscount: { + amount: 1, + currency: 'USD', + }, +}; + +const itemWithTax: CartItem = { + ...item, + tax: { + key: 'vat', + title: 'tax-title', + money: { + amount: 1, + currency: 'USD', + }, + }, +}; + +const itemWithQuantity: CartItem = { + ...item, + quantity: 2, +}; + +const itemCheckout: CartItem = { + ...item, + key: 'checkout', + title: '', +}; + +describe('getCartItemsTemplate', () => { + it('Should draw empty items', () => { + expect(getCartItemsTemplate([])).toEqual(''); + }); + + it('Should not draw image', () => { + expect(getCartItemsTemplate([item])).not.toContain('img'); + }); + + it('Should draw image', () => { + expect(getCartItemsTemplate([itemWithImage])).toContain('img'); + }); + + it('Should draw price before discount', () => { + expect(getCartItemsTemplate([itemWithDiscount])).toContain( + 'price-before-discount' + ); + }); + + it('Should draw tax', () => { + expect(getCartItemsTemplate([itemWithTax])).toContain('tax'); + }); + + it('Should draw item quantity', () => { + expect(getCartItemsTemplate([itemWithQuantity])).toContain('2 x'); + }); + + it('Should draw checkout item', () => { + spyOn(i18next, 't').and.returnValue('In-game purchase'); + expect(getCartItemsTemplate([itemCheckout])).toContain('In-game purchase'); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/cart-items.template.ts b/src/features/headless-checkout/web-components/finance-details/cart-items.template.ts new file mode 100644 index 0000000..98d4bee --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/cart-items.template.ts @@ -0,0 +1,102 @@ +import i18next from 'i18next'; +import { CartItem } from '../../../../core/finance-details/cart-item.interface'; +import { getPriceTextTemplate } from './price-text.template'; + +const titleTranslationMap = new Map([ + ['checkout', 'finance-details-cart-items-checkout-title'], +]); + +const taxTranslationMap = new Map([ + ['vat', 'finance-details-cart-items-vat'], + ['vat-india', 'finance-details-cart-items-vat-india'], + ['vat-ghana', 'finance-details-cart-items-vat-ghana'], + ['sales-tax', 'finance-details-cart-items-sales-tax'], +]); + +function translateCartItems(items: CartItem[] = []): CartItem[] { + return items.map((item) => { + const title = + !item.title && item.key && titleTranslationMap.has(item.key) + ? i18next.t(titleTranslationMap.get(item.key)!) + : item.title; + + const content = + item.tax?.key && taxTranslationMap.has(item.tax.key) + ? i18next.t(taxTranslationMap.get(item.tax.key)!, { + percent: item.tax.rate, + }) + : item.tax?.content; + + return { + ...item, + title: title ?? item.title, + tax: { + ...item.tax, + content, + }, + }; + }); +} + +function getCartItemImage(item: CartItem): string { + if (!item.imgSrc) { + return ''; + } + + return ` +
+ ${item.title} +
+ `; +} + +function getCartItemText(item: CartItem): string { + const title = + item.quantity && item.quantity > 1 + ? `${item.quantity} x ${item.title}` + : item.title; + const description = item.description?.replace(/\\n/g, '
'); + return ` +
+
${title ?? ''}
+
${description ?? ''}
+ ${item.tax ? getPriceTextTemplate(item.tax, null, 'tax') : ''} + ${getPriceTextTemplate(null, item.price, 'price')} + ${ + item.priceBeforeDiscount?.amount + ? getPriceTextTemplate( + null, + item.priceBeforeDiscount, + 'price-before-discount' + ) + : '' + } +
+ `; +} + +export const getCartItemsTemplate = (cartItems: CartItem[] = []): string => { + if (!cartItems.length) { + return ''; + } + + const items = translateCartItems(cartItems); + const itemLines = items.map((item) => { + return ` +
+ ${getCartItemImage(item)} + ${getCartItemText(item)} +
+ `; + }); + + return ` +
+ ${itemLines.join('')} +
+ `; +}; diff --git a/src/features/headless-checkout/web-components/finance-details/croatian-exchange-rate.template.spec.ts b/src/features/headless-checkout/web-components/finance-details/croatian-exchange-rate.template.spec.ts new file mode 100644 index 0000000..538e5af --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/croatian-exchange-rate.template.spec.ts @@ -0,0 +1,39 @@ +import { CountryCode } from '../../../../core/country/country-code.enum'; +import { FinanceDetails } from '../../../../core/finance-details/finance-details.interface'; +import { getCroatianExchangeRateTemplate } from './croatian-exchange-rate.template'; +import { Currency } from '../../../../core/currency/currency.enum'; + +const financeDetails = { + paymentCountry: CountryCode.India, + cartSummary: { + total: { + money: { + currency: Currency.USD, + }, + }, + }, +} as unknown as FinanceDetails; + +const financeDetailsCroatia = { + paymentCountry: CountryCode.Croatia, + cartSummary: { + total: { + money: { + amount: 1, + currency: Currency.EUR, + }, + }, + }, +} as unknown as FinanceDetails; + +describe('getCroatianExchangeRateTemplate', () => { + it('Should not draw', () => { + expect(getCroatianExchangeRateTemplate(financeDetails)).toEqual(''); + }); + + it('Should draw for Croatia country and EUR currency', () => { + const template = getCroatianExchangeRateTemplate(financeDetailsCroatia); + expect(template).toContain('equal-value'); + expect(template).toContain('details'); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/croatian-exchange-rate.template.ts b/src/features/headless-checkout/web-components/finance-details/croatian-exchange-rate.template.ts new file mode 100644 index 0000000..9e31e80 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/croatian-exchange-rate.template.ts @@ -0,0 +1,39 @@ +import i18next from 'i18next'; +import { CountryCode } from '../../../../core/country/country-code.enum'; +import { Currency } from '../../../../core/currency/currency.enum'; +import { FinanceDetails } from '../../../../core/finance-details/finance-details.interface'; + +export const getCroatianExchangeRateTemplate = ( + financeDetails: FinanceDetails +): string => { + const cartSummary = financeDetails.cartSummary; + const isCroatia = financeDetails.paymentCountry === CountryCode.Croatia; + const isEuroCurrency = cartSummary.total.money?.currency === Currency.EUR; + const croatianCurrencyExchangeRate = 7.5345; + + if (!isCroatia || !isEuroCurrency) { + return ''; + } + + function getCroatianCurrencyRate(): number { + const amount = cartSummary.total.money?.amount + ? cartSummary.total.money?.amount + : 0; + const ceilDecimals = 100; + + return ( + Math.ceil(croatianCurrencyExchangeRate * amount * ceilDecimals) / + ceilDecimals + ); + } + + return ` +
+
${i18next.t('finance-details-hrk-equal', { + value: getCroatianCurrencyRate(), + })}
+
${i18next.t( + 'finance-details-hrk-exchange-rate' + )}
+
`; +}; diff --git a/src/features/headless-checkout/web-components/finance-details/finance-details.component.spec.ts b/src/features/headless-checkout/web-components/finance-details/finance-details.component.spec.ts new file mode 100644 index 0000000..dfb62c1 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/finance-details.component.spec.ts @@ -0,0 +1,127 @@ +import { container } from 'tsyringe'; +import { Currency } from '../../../../core/currency/currency.enum'; +import { FinanceDetails } from '../../../../core/finance-details/finance-details.interface'; +import { Handler } from '../../../../core/post-messages-client/handler.type'; +import { HeadlessCheckoutSpy } from '../../../../core/spy/headless-checkout-spy/headless-checkout-spy'; +import { WebComponentTagName } from '../../../../core/web-components/web-component-tag-name.enum'; +import { noopStub } from '../../../../tests/stubs/noop.stub'; +import { HeadlessCheckout } from '../../headless-checkout'; +import { FinanceDetailsComponent } from './finance-details.component'; + +class HeadlessCheckoutMock { + public events = { + onCoreEvent: ( + name: string, + handler: Handler, + callback: (value: FinanceDetails | null) => void + ) => { + this.addEventListener(callback); + }, + }; + private readonly listenerts: Array<(value: FinanceDetails | null) => void> = + []; + + public addEventListener( + calback: (value: FinanceDetails | null) => void + ): void { + this.listenerts.push(calback); + } + + public emitEvent(value: FinanceDetails | null): void { + this.listenerts.forEach((callback) => callback(value)); + } +} + +const financeDetails = { + finance: {}, + cartItems: [], + cartSummary: { + total: { + money: { + amount: 1, + currency: Currency.USD, + }, + }, + }, +} as unknown as FinanceDetails; + +function createComponent(): void { + const element = document.createElement( + WebComponentTagName.FinanceDetailsComponent + ); + + (document.getElementById('container')! as HTMLElement).appendChild(element); +} + +describe('FinanceDetailsComponent', () => { + let headlessCheckout: HeadlessCheckoutMock; + let headlessCheckoutSpy: HeadlessCheckoutSpy; + + window.customElements.define( + WebComponentTagName.FinanceDetailsComponent, + FinanceDetailsComponent + ); + + beforeEach(() => { + document.body.innerHTML = '
'; + + headlessCheckout = new HeadlessCheckoutMock(); + headlessCheckoutSpy = { + listenAppInit: noopStub, + get appWasInit() { + return; + }, + } as unknown as HeadlessCheckoutSpy; + + container + .register(HeadlessCheckoutSpy, { + useValue: headlessCheckoutSpy, + }) + .register(HeadlessCheckout, { + useValue: headlessCheckout as unknown as HeadlessCheckout, + }); + }); + + afterEach(() => { + document.body.innerHTML = ''; + }); + + it('Should create component', () => { + createComponent(); + expect( + document.querySelector(WebComponentTagName.FinanceDetailsComponent) + ).not.toBeNull(); + }); + + it('Should subscribe to finance details event', () => { + const spy = spyOn(headlessCheckout.events, 'onCoreEvent'); + spyOnProperty(headlessCheckoutSpy, 'appWasInit', 'get').and.returnValue( + true + ); + createComponent(); + expect(spy).toHaveBeenCalled(); + }); + + it('Should not draw for empty finance details event', () => { + spyOnProperty(headlessCheckoutSpy, 'appWasInit', 'get').and.returnValue( + true + ); + createComponent(); + headlessCheckout.emitEvent(null); + const element = document.querySelector( + WebComponentTagName.FinanceDetailsComponent + ); + expect(element!.innerHTML).toEqual(''); + }); + + it('Should draw on finance details event', () => { + spyOnProperty(headlessCheckoutSpy, 'appWasInit', 'get').and.returnValue( + true + ); + createComponent(); + headlessCheckout.emitEvent(financeDetails as unknown as FinanceDetails); + expect( + document.querySelector(WebComponentTagName.FinanceDetailsComponent) + ).not.toBeNull(); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/finance-details.component.ts b/src/features/headless-checkout/web-components/finance-details/finance-details.component.ts new file mode 100644 index 0000000..eb0068d --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/finance-details.component.ts @@ -0,0 +1,48 @@ +import { EventName } from '../../../../core/event-name.enum'; +import { container } from 'tsyringe'; +import { FinanceDetails } from '../../../../core/finance-details/finance-details.interface'; +import { HeadlessCheckoutSpy } from '../../../../core/spy/headless-checkout-spy/headless-checkout-spy'; +import { WebComponentAbstract } from '../../../../core/web-components/web-component.abstract'; +import { HeadlessCheckout } from '../../headless-checkout'; +import { getFinanceDetailsHandler } from '../../post-messages-handlers/get-finance-details.handler'; +import { getFinanceDetailsTemplate } from './finance-details.template'; + +export class FinanceDetailsComponent extends WebComponentAbstract { + private readonly headlessCheckout: HeadlessCheckout; + private readonly headlessCheckoutSpy: HeadlessCheckoutSpy; + private financeDetails?: FinanceDetails | null = null; + + public constructor() { + super(); + this.headlessCheckoutSpy = container.resolve(HeadlessCheckoutSpy); + this.headlessCheckout = container.resolve(HeadlessCheckout); + } + + protected connectedCallback(): void { + if (!this.headlessCheckoutSpy.appWasInit) { + this.headlessCheckoutSpy.listenAppInit(() => this.connectedCallback()); + return; + } + void this.headlessCheckout.events.onCoreEvent( + EventName.financeDetails, + getFinanceDetailsHandler, + (financeDetails) => this.financeDetailsLoadedHandler(financeDetails) + ); + } + + protected getHtml(): string { + if (this.financeDetails) { + return getFinanceDetailsTemplate(this.financeDetails); + } + + return ''; + } + + private readonly financeDetailsLoadedHandler = ( + financeDetails?: FinanceDetails | null + ): void => { + this.financeDetails = financeDetails; + + this.render(); + }; +} diff --git a/src/features/headless-checkout/web-components/finance-details/finance-details.template.ts b/src/features/headless-checkout/web-components/finance-details/finance-details.template.ts new file mode 100644 index 0000000..84ae925 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/finance-details.template.ts @@ -0,0 +1,23 @@ +import { FinanceDetails } from '../../../../core/finance-details/finance-details.interface'; +import { getCartItemsTemplate } from './cart-items.template'; +import { getCroatianExchangeRateTemplate } from './croatian-exchange-rate.template'; +import { getShippingTemplate } from './shipping.template'; +import { getSubtotalDetailsTemplate } from './subtotal-details.template'; +import { getTotalDetailsTemplate } from './total-details.template'; +import { getTotalRowTemplate } from './total-row.template'; + +export const getFinanceDetailsTemplate = ( + financeDetails: FinanceDetails +): string => { + return [ + getCartItemsTemplate(financeDetails.cartItems), + getShippingTemplate(financeDetails.cartSummary), + getSubtotalDetailsTemplate( + financeDetails.finance, + financeDetails.cartSummary + ), + getTotalRowTemplate(financeDetails.cartSummary), + getCroatianExchangeRateTemplate(financeDetails), + getTotalDetailsTemplate(financeDetails.finance, financeDetails.cartSummary), + ].join(''); +}; diff --git a/src/features/headless-checkout/web-components/finance-details/price-text.template.ts b/src/features/headless-checkout/web-components/finance-details/price-text.template.ts new file mode 100644 index 0000000..0b513a1 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/price-text.template.ts @@ -0,0 +1,25 @@ +import { Price } from '../../../../core/finance-details/price.interface'; +import { CartLine } from '../../../../core/finance-details/cart-line.interface'; + +export const getPriceTextTemplate = ( + cartLine?: CartLine | null, + price?: Price | null, + className?: string +): string => { + const priceLineContent = cartLine?.content ?? ''; + const priceLineAmount = cartLine?.money?.amount ?? ''; + const priceLineCurrency = cartLine?.money?.currency ?? ''; + const amount = price?.amount ?? ''; + const currency = price?.currency ?? ''; + + return ` + + `; +}; diff --git a/src/features/headless-checkout/web-components/finance-details/price-text/price-text-attributes.enum.ts b/src/features/headless-checkout/web-components/finance-details/price-text/price-text-attributes.enum.ts new file mode 100644 index 0000000..81d042b --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/price-text/price-text-attributes.enum.ts @@ -0,0 +1,7 @@ +export enum PriceTextAttributes { + priceLineContent = 'price-line-content', + priceLineAmount = 'price-line-amount', + priceLineCurrency = 'price-line-currency', + amount = 'amount', + currency = 'currency', +} diff --git a/src/features/headless-checkout/web-components/finance-details/price-text/price-text.component.spec.ts b/src/features/headless-checkout/web-components/finance-details/price-text/price-text.component.spec.ts new file mode 100644 index 0000000..7fda02e --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/price-text/price-text.component.spec.ts @@ -0,0 +1,49 @@ +import { container } from 'tsyringe'; +import { CurrencyPipe } from '../../../../../core/pipes/currency/currency.pipe'; +import { WebComponentTagName } from '../../../../../core/web-components/web-component-tag-name.enum'; +import { PriceTextComponent } from './price-text.component'; + +function createComponent(): void { + const element = document.createElement( + WebComponentTagName.PriceTextComponent + ); + + element.setAttribute('price-line-content', 'content'); + element.setAttribute('price-line-amount', '1'); + element.setAttribute('price-line-currency', 'USD'); + element.setAttribute('amount', '10'); + element.setAttribute('currency', 'USD'); + + (document.getElementById('container')! as HTMLElement).appendChild(element); +} + +describe('PriceTextComponent', () => { + window.customElements.define( + WebComponentTagName.PriceTextComponent, + PriceTextComponent + ); + + const currencyPipe = { transform: () => '' } as unknown as CurrencyPipe; + + beforeEach(() => { + document.body.innerHTML = '
'; + container.register(CurrencyPipe, { useValue: currencyPipe }); + spyOn(container, 'resolve').and.returnValue(currencyPipe); + }); + + afterEach(() => { + document.body.innerHTML = ''; + }); + + it('Should draw price content', () => { + createComponent(); + expect(document.querySelector('.content')!.textContent).toContain( + 'content' + ); + }); + + it('Should draw price ', () => { + createComponent(); + expect(document.querySelector('.price')).not.toBeNull(); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/price-text/price-text.component.ts b/src/features/headless-checkout/web-components/finance-details/price-text/price-text.component.ts new file mode 100644 index 0000000..402c004 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/price-text/price-text.component.ts @@ -0,0 +1,63 @@ +import { WebComponentAbstract } from '../../../../../core/web-components/web-component.abstract'; +import { PriceTextAttributes } from './price-text-attributes.enum'; +import { getPriceTextTemplate } from './price-text.template'; + +export class PriceTextComponent extends WebComponentAbstract { + private priceLineContent: string | null = null; + private priceLineAmount: number | null = null; + private priceLineCurrency: string | null = null; + private amount: number | null = null; + private currency: string | null = null; + + public static get observedAttributes(): string[] { + return [ + PriceTextAttributes.priceLineContent, + PriceTextAttributes.priceLineAmount, + PriceTextAttributes.priceLineCurrency, + PriceTextAttributes.amount, + PriceTextAttributes.currency, + ]; + } + + protected connectedCallback(): void { + this.readAttributes(); + this.render(); + } + + protected getHtml(): string { + const priceLine = + this.priceLineAmount === null + ? null + : { + amount: this.priceLineAmount, + currency: this.priceLineCurrency ?? '', + }; + + const price = + this.amount === null + ? null + : { + amount: this.amount, + currency: this.currency ?? '', + }; + + return getPriceTextTemplate(this.priceLineContent, priceLine, price); + } + + private readAttributes(): void { + this.priceLineContent = this.getAttribute( + PriceTextAttributes.priceLineContent + ); + this.priceLineContent = this.getAttribute( + PriceTextAttributes.priceLineContent + ); + this.priceLineAmount = this.getNumberAttribute( + PriceTextAttributes.priceLineAmount + ); + this.priceLineCurrency = this.getAttribute( + PriceTextAttributes.priceLineCurrency + ); + this.amount = this.getNumberAttribute(PriceTextAttributes.amount); + this.currency = this.getAttribute(PriceTextAttributes.currency); + } +} diff --git a/src/features/headless-checkout/web-components/finance-details/price-text/price-text.template.ts b/src/features/headless-checkout/web-components/finance-details/price-text/price-text.template.ts new file mode 100644 index 0000000..08e18a9 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/price-text/price-text.template.ts @@ -0,0 +1,34 @@ +import { CurrencyPipe } from '../../../../../core/pipes/currency/currency.pipe'; +import { Price } from '../../../../../core/finance-details/price.interface'; +import { container } from 'tsyringe'; + +export const getPriceTextTemplate = ( + content: string | null, + priceLine: Price | null, + price: Price | null +): string => { + const lines = []; + const curencyPipe = container.resolve(CurrencyPipe); + + if (content) { + lines.push(`
${content}
`); + } + + if (priceLine) { + lines.push( + `
${ + curencyPipe.transform(priceLine.amount, priceLine.currency) ?? '' + }
` + ); + } + + if (price) { + lines.push( + `
${ + curencyPipe.transform(price.amount, price.currency) ?? '' + }
` + ); + } + + return lines.join(''); +}; diff --git a/src/features/headless-checkout/web-components/finance-details/shipping.template.spec.ts b/src/features/headless-checkout/web-components/finance-details/shipping.template.spec.ts new file mode 100644 index 0000000..6870245 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/shipping.template.spec.ts @@ -0,0 +1,22 @@ +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { getShippingTemplate } from './shipping.template'; + +const cartSummary = {} as unknown as CartSummary; +const cartSummaryWithShipping = { + shipping: [ + { + title: 'title', + }, + ], +} as unknown as CartSummary; + +describe('getShippingTemplate', () => { + it('Should not draw for empty shipping', () => { + expect(getShippingTemplate(cartSummary)).toEqual(''); + }); + + it('Should draw for Croatia country and EUR currency', () => { + const template = getShippingTemplate(cartSummaryWithShipping); + expect(template).toContain('shipping-row'); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/shipping.template.ts b/src/features/headless-checkout/web-components/finance-details/shipping.template.ts new file mode 100644 index 0000000..5aaca38 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/shipping.template.ts @@ -0,0 +1,24 @@ +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { getPriceTextTemplate } from './price-text.template'; + +export const getShippingTemplate = (cartSummary: CartSummary): string => { + const shipping = cartSummary.shipping; + + if (!shipping?.length) { + return ''; + } + + const lines = shipping.map((item) => { + return ` +
+
${item.title ?? ''}
+ ${getPriceTextTemplate(item)} +
+ `; + }); + + return ` +
+ ${lines.join('')} +
`; +}; diff --git a/src/features/headless-checkout/web-components/finance-details/subtotal-details.templat.spec.ts b/src/features/headless-checkout/web-components/finance-details/subtotal-details.templat.spec.ts new file mode 100644 index 0000000..60b3d19 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/subtotal-details.templat.spec.ts @@ -0,0 +1,67 @@ +import i18next from 'i18next'; +import { Currency } from '../../../../core/currency/currency.enum'; +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { XpsFinance } from '../../../../core/finance-details/xps-finance.interface'; +import { getSubtotalDetailsTemplate } from './subtotal-details.template'; + +const finance = { + vat_user: { + percent: 10, + }, +} as unknown as XpsFinance; +const summary = { + subtotalDetails: [ + { + title: 'title', + money: { + amount: 1, + currency: Currency.USD, + }, + }, + ], +} as unknown as CartSummary; +const summaryWithVatItem = { + subtotalDetails: [ + { + key: 'vat', + title: 'vat', + money: { + amount: 1, + currency: Currency.USD, + }, + }, + ], +} as unknown as CartSummary; +const summaryWithConvertion = { + ...summary, + subtotal: { + money: { + amount: 1, + currency: Currency.USD, + }, + }, + subtotalPayment: { + money: { + amount: 1, + currency: Currency.EUR, + }, + }, +} as unknown as CartSummary; + +describe('getSubtotalDetailsTemplate', () => { + it('Should draw title', () => { + const template = getSubtotalDetailsTemplate(finance, summaryWithConvertion); + expect(template).toContain('title'); + }); + + it('Should draw converted price', () => { + const template = getSubtotalDetailsTemplate(finance, summaryWithConvertion); + expect(template).toContain('converted-price'); + }); + + it('Should draw vat', () => { + spyOn(i18next, 't').and.returnValue('vat'); + const template = getSubtotalDetailsTemplate(finance, summaryWithVatItem); + expect(template).toContain('vat'); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/subtotal-details.template.ts b/src/features/headless-checkout/web-components/finance-details/subtotal-details.template.ts new file mode 100644 index 0000000..0e81db9 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/subtotal-details.template.ts @@ -0,0 +1,104 @@ +import i18next from 'i18next'; +import { CartLine } from '../../../../core/finance-details/cart-line.interface'; +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { XpsFinance } from '../../../../core/finance-details/xps-finance.interface'; +import { getPriceTextTemplate } from './price-text.template'; + +const translationMap = new Map([ + ['fee', 'finance-details-subtotal-details-fee'], + ['discount', 'finance-details-subtotal-details-discount'], + ['user-balance', 'finance-details-subtotal-details-user-balance'], + ['vat', 'finance-details-subtotal-details-vat'], + ['vat-india', 'finance-details-subtotal-details-vat-india'], + ['vat-ghana', 'finance-details-subtotal-details-vat-ghana'], + ['sales-tax', 'finance-details-subtotal-details-sales-tax'], +]); + +function translateSubtotalDetails( + finance: XpsFinance, + items: CartLine[] = [] +): CartLine[] { + const vatPercent = finance.vat_user?.percent; + return items.map((item) => { + if (!item.key || !translationMap.has(item.key)) { + return item; + } + + return { + ...item, + title: i18next.t(translationMap.get(item.key)!, { + percent: vatPercent, + }), + }; + }); +} + +function getSubtotalTitle(cartSummary: CartSummary): string { + const lines = []; + + if (cartSummary.subtotal) { + lines.push( + `
${i18next.t('finance-details-subtotal-title')}
` + ); + } + + if (cartSummary.subtotal && cartSummary.subtotalPayment) { + const hasConversion = + cartSummary.subtotal.money?.currency !== + cartSummary.subtotalPayment.money?.currency; + + const conversionPrice = hasConversion + ? `${getPriceTextTemplate( + cartSummary.subtotal, + null, + 'price converted-price' + )} =` + : ''; + + lines.push(` +
+ ${conversionPrice} + ${getPriceTextTemplate(cartSummary.subtotalPayment, null, 'price')} +
+ `); + } + + return lines.length + ? ` +
+ ${lines.join('')} +
` + : ''; +} + +function getSubtotalContent( + finance: XpsFinance, + cartSummary: CartSummary +): string { + const lines = translateSubtotalDetails( + finance, + cartSummary.subtotalDetails + ).map((details) => { + if (!details.money?.amount && !details.content) { + return ''; + } + + return ` +
+
${details.title ?? ''}
+ ${getPriceTextTemplate(details, null, 'price')} +
`; + }); + + return lines?.join('') ?? ''; +} + +export const getSubtotalDetailsTemplate = ( + finance: XpsFinance, + cartSummary: CartSummary +): string => { + return [ + getSubtotalTitle(cartSummary), + getSubtotalContent(finance, cartSummary), + ].join(''); +}; diff --git a/src/features/headless-checkout/web-components/finance-details/total-details.template.spec.ts b/src/features/headless-checkout/web-components/finance-details/total-details.template.spec.ts new file mode 100644 index 0000000..b578b26 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/total-details.template.spec.ts @@ -0,0 +1,46 @@ +import i18next from 'i18next'; +import { Currency } from '../../../../core/currency/currency.enum'; +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { XpsFinance } from '../../../../core/finance-details/xps-finance.interface'; +import { getTotalDetailsTemplate } from './total-details.template'; + +const finance = { + vat: { + percent: 10, + }, +} as unknown as XpsFinance; +const summary = { + totalDetails: [ + { + title: 'title', + money: { + amount: 1, + currency: Currency.USD, + }, + }, + ], +} as unknown as CartSummary; +const summaryWithVatItem = { + totalDetails: [ + { + key: 'vat', + money: { + amount: 1, + currency: Currency.USD, + }, + }, + ], +} as unknown as CartSummary; + +describe('getTotalDetailsTemplate', () => { + it('Should draw title', () => { + const template = getTotalDetailsTemplate(finance, summary); + expect(template).toContain('title'); + }); + + it('Should draw vat', () => { + spyOn(i18next, 't').and.returnValue('vat'); + const template = getTotalDetailsTemplate(finance, summaryWithVatItem); + expect(template).toContain('vat'); + }); +}); diff --git a/src/features/headless-checkout/web-components/finance-details/total-details.template.ts b/src/features/headless-checkout/web-components/finance-details/total-details.template.ts new file mode 100644 index 0000000..512c768 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/total-details.template.ts @@ -0,0 +1,49 @@ +import i18next from 'i18next'; +import { CartLine } from '../../../../core/finance-details/cart-line.interface'; +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { XpsFinance } from '../../../../core/finance-details/xps-finance.interface'; +import { getPriceTextTemplate } from './price-text.template'; + +const translationMap = new Map([ + ['vat', 'finance-details-total-details-vat'], + ['vat-india', 'finance-details-total-details-vat-india'], + ['vat-ghana', 'finance-details-total-details-vat-ghana'], + ['sales-tax', 'finance-details-total-details-sales-tax'], +]); + +function translateTotalDetails( + finance: XpsFinance, + items: CartLine[] = [] +): CartLine[] { + const vatPercent = finance.vat?.percent; + return items.map((item) => { + if (!item.key || !translationMap.has(item.key)) { + return item; + } + + return { + ...item, + title: i18next.t(translationMap.get(item.key)!, { + percent: vatPercent, + }), + }; + }); +} + +export const getTotalDetailsTemplate = ( + finance: XpsFinance, + cartSummary: CartSummary +): string => { + const totalDetailsRows = translateTotalDetails( + finance, + cartSummary.totalDetails + )?.map((totalDetails) => { + return ` +
+
${totalDetails.title ?? ''}
+ ${getPriceTextTemplate(null, totalDetails.money)} +
`; + }); + + return totalDetailsRows?.join(''); +}; diff --git a/src/features/headless-checkout/web-components/finance-details/total-row.template.ts b/src/features/headless-checkout/web-components/finance-details/total-row.template.ts new file mode 100644 index 0000000..28b4613 --- /dev/null +++ b/src/features/headless-checkout/web-components/finance-details/total-row.template.ts @@ -0,0 +1,11 @@ +import i18next from 'i18next'; +import { CartSummary } from '../../../../core/finance-details/cart-summary.interface'; +import { getPriceTextTemplate } from './price-text.template'; + +export const getTotalRowTemplate = (cartSummary: CartSummary): string => { + return ` +
+
${i18next.t('finance-details-total-title')}
+ ${getPriceTextTemplate(null, cartSummary.total.money)} +
`; +}; diff --git a/src/features/headless-checkout/web-components/legal/secure-connection.component.template.ts b/src/features/headless-checkout/web-components/legal/secure-connection.component.template.ts index fa21ed0..2b8205a 100644 --- a/src/features/headless-checkout/web-components/legal/secure-connection.component.template.ts +++ b/src/features/headless-checkout/web-components/legal/secure-connection.component.template.ts @@ -1,6 +1,7 @@ import { LegalComponentConfig } from './legal-component.config.interface'; import logo from '../../../../assets/icons/logo.svg'; import i18next from 'i18next'; + export const getSecureConnectionTemplate = ( secureConnection?: LegalComponentConfig['secureConnection'] ): string => { diff --git a/src/features/headless-checkout/web-components/payment-methods/payment-methods.component.ts b/src/features/headless-checkout/web-components/payment-methods/payment-methods.component.ts index ad0911e..310d9e0 100644 --- a/src/features/headless-checkout/web-components/payment-methods/payment-methods.component.ts +++ b/src/features/headless-checkout/web-components/payment-methods/payment-methods.component.ts @@ -29,6 +29,7 @@ export class PaymentMethodsComponent extends WebComponentAbstract { public constructor() { super(); + this.headlessCheckoutSpy = container.resolve(HeadlessCheckoutSpy); this.headlessCheckout = container.resolve(HeadlessCheckout); } diff --git a/src/features/headless-checkout/web-components/submit-button/submit-button.component.spec.ts b/src/features/headless-checkout/web-components/submit-button/submit-button.component.spec.ts index e9e7c98..202d553 100644 --- a/src/features/headless-checkout/web-components/submit-button/submit-button.component.spec.ts +++ b/src/features/headless-checkout/web-components/submit-button/submit-button.component.spec.ts @@ -12,7 +12,7 @@ function createComponent(): void { (document.getElementById('container')! as HTMLElement).appendChild(element); } -describe('HeadlessCheckout', () => { +describe('SubmitButtonComponent', () => { let postMessagesClient: PostMessagesClient; window.customElements.define( WebComponentTagName.SubmitButtonComponent, diff --git a/src/translations/ar.json b/src/translations/ar.json index d2eca88..66cc579 100644 --- a/src/translations/ar.json +++ b/src/translations/ar.json @@ -1,13 +1,33 @@ { - "ar": { - "translation": { - "cookie-policy": "سياسة ملفات تعريف الارتباط", - "hello": "مرحبًا بالعالم!", - "legal": "قانوني", - "privacy-policy": "سياسة الخصوصية", - "refund-policy": "سياسة الاسترداد", - "sctl-indications": "SCTL Indications", - "secure-connection": "اتصال آمن" - } - } + "ar" : { + "translation" : { + "cookie-policy" : "سياسة ملفات تعريف الارتباط", + "finance-details-cart-items-checkout-title" : "الشراء من داخل اللعبة", + "finance-details-cart-items-sales-tax" : "ضريبة المبيعات {{percent}}%", + "finance-details-cart-items-vat" : "ضريبة القيمة المضافة{{percent}}%", + "finance-details-cart-items-vat-ghana" : "ضريبة القيمة المضافة، وضريبة التأمين الصحي الوطنية، وضريبة GETFund وضريبة كوفيد-19", + "finance-details-cart-items-vat-india" : "ضرائب البضائع والخدمات{{percent}}%", + "finance-details-hrk-equal" : "يساوي {{value}} كرونا سويدية", + "finance-details-hrk-exchange-rate" : "سعر الصرف: 1 يورو = 7.5345 كرونا سويدية", + "finance-details-subtotal-details-discount" : "خصم", + "finance-details-subtotal-details-fee" : "رسوم نظام الدفع", + "finance-details-subtotal-details-sales-tax" : "ضريبة المبيعات", + "finance-details-subtotal-details-user-balance" : "الرصيد", + "finance-details-subtotal-details-vat" : "ضريبة القيمة المضافة{{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "ضريبة القيمة المضافة، وضريبة التأمين الصحي الوطنية، وضريبة GETFund وضريبة كوفيد-19", + "finance-details-subtotal-details-vat-india" : "ضرائب البضائع والخدمات{{percent}}%", + "finance-details-subtotal-title" : "الإجمالي الفرعي", + "finance-details-total-details-sales-tax" : "شامل ضريبة المبيعات", + "finance-details-total-details-vat" : "شامل {{percent}}% ضريبة قيمة مضافة", + "finance-details-total-details-vat-ghana" : "متضمنة ضريبة القيمة المضافة، وضريبة التأمين الصحي الوطنية، وضريبة GETFund وضريبة كوفيد-19", + "finance-details-total-details-vat-india" : "شامل {{percent}}% ضريبة البضائع والخدمات", + "finance-details-total-title" : "الإجمالي", + "hello" : "مرحبًا بالعالم!", + "legal" : "قانوني", + "privacy-policy" : "سياسة الخصوصية", + "refund-policy" : "سياسة الاسترداد", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "اتصال آمن" + } + } } diff --git a/src/translations/bg.json b/src/translations/bg.json index 80fba55..a3815ac 100644 --- a/src/translations/bg.json +++ b/src/translations/bg.json @@ -1,13 +1,33 @@ { - "bg": { - "translation": { - "cookie-policy": "Политика за бисквитките", - "hello": "Здравей, свят!", - "legal": "Правни документи", - "privacy-policy": "Политика за поверителност", - "refund-policy": "Политика за връщане на средства", - "sctl-indications": "SCTL Indications", - "secure-connection": "Сигурна връзка" - } - } + "bg" : { + "translation" : { + "cookie-policy" : "Политика за бисквитките", + "finance-details-cart-items-checkout-title" : "Покупка вътре в играта", + "finance-details-cart-items-sales-tax" : "Данък продажби {{percent}}%", + "finance-details-cart-items-vat" : "ДДС {{percent}}%", + "finance-details-cart-items-vat-ghana" : "ДДС, NHIL, облагане за GETFund и облагане за Covid-19", + "finance-details-cart-items-vat-india" : "ДСУ {{percent}}%", + "finance-details-hrk-equal" : "Равнява се на {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Обменен курс: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Отстъпка", + "finance-details-subtotal-details-fee" : "Такса за система за плащане", + "finance-details-subtotal-details-sales-tax" : "Данък продажби", + "finance-details-subtotal-details-user-balance" : "Баланс", + "finance-details-subtotal-details-vat" : "ДДС {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "ДДС, NHIL, облагане за GETFund и облагане за Covid-19", + "finance-details-subtotal-details-vat-india" : "ДСУ {{percent}}%", + "finance-details-subtotal-title" : "Междинна сума", + "finance-details-total-details-sales-tax" : "С включен данък продажби", + "finance-details-total-details-vat" : "Включително {{percent}}% ДДС", + "finance-details-total-details-vat-ghana" : "С включени ДДС, GST, NHIL, облагане за GETFund и облагане за Covid-19", + "finance-details-total-details-vat-india" : "С включен {{percent}}% ДСУ", + "finance-details-total-title" : "Общо", + "hello" : "Здравей, свят!", + "legal" : "Правни документи", + "privacy-policy" : "Политика за поверителност", + "refund-policy" : "Политика за връщане на средства", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Сигурна връзка" + } + } } diff --git a/src/translations/cs.json b/src/translations/cs.json index 4f85a98..1a9dcfe 100644 --- a/src/translations/cs.json +++ b/src/translations/cs.json @@ -1,13 +1,33 @@ { - "cs": { - "translation": { - "cookie-policy": "Zásady používání cookies", - "hello": "Ahoj světe!", - "legal": "Právní informace", - "privacy-policy": "Zásady ochrany osobních údajů", - "refund-policy": "Podmínky vrácení peněz", - "sctl-indications": "SCTL Indications", - "secure-connection": "Zabezpečené připojení" - } - } + "cs" : { + "translation" : { + "cookie-policy" : "Zásady používání cookies", + "finance-details-cart-items-checkout-title" : "Nákup ve hře", + "finance-details-cart-items-sales-tax" : "Daň z obratu {{percent}} %", + "finance-details-cart-items-vat" : "DPH {{percent}} %", + "finance-details-cart-items-vat-ghana" : "DPH, NHIL, odvod GETFund a odvod Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}} %", + "finance-details-hrk-equal" : "Což odpovídá {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Směnný kurz: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Sleva", + "finance-details-subtotal-details-fee" : "Poplatek za platební systém", + "finance-details-subtotal-details-sales-tax" : "Daň z obratu", + "finance-details-subtotal-details-user-balance" : "Zůstatek", + "finance-details-subtotal-details-vat" : "DPH {{percent}} %", + "finance-details-subtotal-details-vat-ghana" : "DPH, NHIL, odvod GETFund a odvod Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}} %", + "finance-details-subtotal-title" : "Mezisoučet", + "finance-details-total-details-sales-tax" : "Včetně daně z obratu", + "finance-details-total-details-vat" : "Včetně {{percent}}% DPH", + "finance-details-total-details-vat-ghana" : "Včetně DPH, NHIL, odvodu GETFund a odvodu Covid-19", + "finance-details-total-details-vat-india" : "Včetně {{percent}}% GST", + "finance-details-total-title" : "Celkem", + "hello" : "Ahoj světe!", + "legal" : "Právní informace", + "privacy-policy" : "Zásady ochrany osobních údajů", + "refund-policy" : "Podmínky vrácení peněz", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Zabezpečené připojení" + } + } } diff --git a/src/translations/de.json b/src/translations/de.json index af4ea06..e9dd56a 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -1,13 +1,33 @@ { - "de": { - "translation": { - "cookie-policy": "Cookie-Richtlinie", - "hello": "Hallo Welt!", - "legal": "Rechtliches", - "privacy-policy": "Datenschutzerklärung", - "refund-policy": "Erstattungsrichtlinie", - "sctl-indications": "SCTL Indications", - "secure-connection": "Sichere Verbindung" - } - } + "de" : { + "translation" : { + "cookie-policy" : "Cookie-Richtlinie", + "finance-details-cart-items-checkout-title" : "Ingame-Kauf", + "finance-details-cart-items-sales-tax" : "{{percent}} % Umsatzsteuer", + "finance-details-cart-items-vat" : "{{percent}} % MwSt.", + "finance-details-cart-items-vat-ghana" : "VAT, NHIL, GETFund Levy und Covid-19 Levy", + "finance-details-cart-items-vat-india" : "{{percent}} % GST", + "finance-details-hrk-equal" : "Entspricht {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Wechselkurs: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Rabatt", + "finance-details-subtotal-details-fee" : "Zahlungssystemgebühr", + "finance-details-subtotal-details-sales-tax" : "Sales Tax", + "finance-details-subtotal-details-user-balance" : "Guthaben", + "finance-details-subtotal-details-vat" : "{{percent}} % MwSt.", + "finance-details-subtotal-details-vat-ghana" : "VAT, NHIL, GETFund Levy und Covid-19 Levy", + "finance-details-subtotal-details-vat-india" : "{{percent}} % GST", + "finance-details-subtotal-title" : "Zwischensumme", + "finance-details-total-details-sales-tax" : "Inklusive Sales Tax", + "finance-details-total-details-vat" : "Einschließlich {{percent}} % MWSt.", + "finance-details-total-details-vat-ghana" : "Inklusive VAT, NHIL, GETFund Levy und Covid-19 Levy", + "finance-details-total-details-vat-india" : "Inklusive {{percent}} % GST", + "finance-details-total-title" : "Summe", + "hello" : "Hallo Welt!", + "legal" : "Rechtliches", + "privacy-policy" : "Datenschutzerklärung", + "refund-policy" : "Erstattungsrichtlinie", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Sichere Verbindung" + } + } } diff --git a/src/translations/en.json b/src/translations/en.json index 5a3a115..7b589c9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1,14 +1,33 @@ { "en": { "translation": { - "disclaimer": "disclaimer", "hello": "Hello world!", "legal": "Legal", "cookie-policy": "Cookie policy", "privacy-policy": "Privacy policy", "refund-policy": "Refund policy", "sctl-indications": "SCTL Indications", - "secure-connection": "Secure connection" + "secure-connection": "Secure connection", + "finance-details-total-title": "Total", + "finance-details-subtotal-title": "Subtotal", + "finance-details-hrk-equal": "Equals {{value}} HRK", + "finance-details-hrk-exchange-rate": "Exchange rate: 1 EUR = 7.5345 HRK", + "finance-details-total-details-vat": "Including {{percent}}% VAT", + "finance-details-total-details-vat-india": "Including {{percent}}% GST", + "finance-details-total-details-vat-ghana": "Including VAT, NHIL, GETFund Levy, and Covid-19 Levy", + "finance-details-total-details-sales-tax": "Sales tax included", + "finance-details-subtotal-details-fee": "Payment system fee", + "finance-details-subtotal-details-discount": "Discount", + "finance-details-subtotal-details-user-balance": "Balance", + "finance-details-subtotal-details-vat": "VAT {{percent}}%", + "finance-details-subtotal-details-vat-india": "GST {{percent}}%", + "finance-details-subtotal-details-vat-ghana": "VAT, NHIL, GETFund Levy, and Covid-19 Levy", + "finance-details-subtotal-details-sales-tax": "Sales tax", + "finance-details-cart-items-checkout-title": "In-game purchase", + "finance-details-cart-items-vat": "VAT {{percent}}%", + "finance-details-cart-items-vat-india": "GST {{percent}}%", + "finance-details-cart-items-vat-ghana": "VAT, NHIL, GETFund Levy, and Covid-19 Levy", + "finance-details-cart-items-sales-tax": "Sales tax {{percent}}%" } } } diff --git a/src/translations/es.json b/src/translations/es.json index cefcd9c..0dbecfc 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -1,13 +1,33 @@ { - "es": { - "translation": { - "cookie-policy": "Política de cookies", - "hello": "¡Hola mundo!", - "legal": "Legal", - "privacy-policy": "Política de privacidad", - "refund-policy": "Política de reembolso", - "sctl-indications": "SCTL Indications", - "secure-connection": "Conexión segura" - } - } + "es" : { + "translation" : { + "cookie-policy" : "Política de cookies", + "finance-details-cart-items-checkout-title" : "Compra en el juego", + "finance-details-cart-items-sales-tax" : "Impuesto sobre las ventas {{percent}} %", + "finance-details-cart-items-vat" : "IVA {{percent}} %", + "finance-details-cart-items-vat-ghana" : "IVA, NHIL y los impuestos GETFund y Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}} %", + "finance-details-hrk-equal" : "Es igual a {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Tipo de cambio: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Descuento", + "finance-details-subtotal-details-fee" : "Comisión del sistema de pago", + "finance-details-subtotal-details-sales-tax" : "Impuesto sobre las ventas", + "finance-details-subtotal-details-user-balance" : "Saldo", + "finance-details-subtotal-details-vat" : "IVA {{percent}} %", + "finance-details-subtotal-details-vat-ghana" : "IVA, NHIL y los impuestos GETFund y Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}} %", + "finance-details-subtotal-title" : "Subtotal", + "finance-details-total-details-sales-tax" : "Impuesto sobre las ventas incluido", + "finance-details-total-details-vat" : "{{percent}} % IVA incluido​", + "finance-details-total-details-vat-ghana" : "Incluyendo el IVA, GST, NHIL y los impuestos GETFund y Covid-19", + "finance-details-total-details-vat-india" : "{{percent}} % GST incluido​", + "finance-details-total-title" : "Total", + "hello" : "¡Hola mundo!", + "legal" : "Legal", + "privacy-policy" : "Política de privacidad", + "refund-policy" : "Política de reembolso", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Conexión segura" + } + } } diff --git a/src/translations/fr.json b/src/translations/fr.json index 9297f93..600fbad 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -1,13 +1,33 @@ { - "fr": { - "translation": { - "cookie-policy": "Politique en matière de cookies", - "hello": "Bonjour monde!", - "legal": "Juridique", - "privacy-policy": "Politique de confidentialité", - "refund-policy": "Politique de remboursement", - "sctl-indications": "SCTL Indications", - "secure-connection": "Connexion sécurisée" - } - } + "fr" : { + "translation" : { + "cookie-policy" : "Politique en matière de cookies", + "finance-details-cart-items-checkout-title" : "Achat intégré", + "finance-details-cart-items-sales-tax" : "Taxe de vente de {{percent}} %", + "finance-details-cart-items-vat" : "TVA de {{percent}} %", + "finance-details-cart-items-vat-ghana" : "La TVA, la NHIL, le prélèvement GETFund et le prélèvement Covid-19", + "finance-details-cart-items-vat-india" : "TPS de {{percent}} %", + "finance-details-hrk-equal" : "Égal {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Taux de change : 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Remise", + "finance-details-subtotal-details-fee" : "Frais du système de paiement", + "finance-details-subtotal-details-sales-tax" : "Taxe de vente", + "finance-details-subtotal-details-user-balance" : "Solde", + "finance-details-subtotal-details-vat" : "TVA de {{percent}} %", + "finance-details-subtotal-details-vat-ghana" : "La TVA, la NHIL, le prélèvement GETFund et le prélèvement Covid-19", + "finance-details-subtotal-details-vat-india" : "TPS de {{percent}} %", + "finance-details-subtotal-title" : "Sous-total", + "finance-details-total-details-sales-tax" : "Taxe de vente incluse", + "finance-details-total-details-vat" : "TVA de {{percent}} % incluse", + "finance-details-total-details-vat-ghana" : "Y compris la TVA, la NHIL, le prélèvement GETFund et le prélèvement Covid-19", + "finance-details-total-details-vat-india" : "TPS de {{percent}} % incluse", + "finance-details-total-title" : "Total", + "hello" : "Bonjour monde!", + "legal" : "Juridique", + "privacy-policy" : "Politique de confidentialité", + "refund-policy" : "Politique de remboursement", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Connexion sécurisée" + } + } } diff --git a/src/translations/he.json b/src/translations/he.json index 67d3d64..198b749 100644 --- a/src/translations/he.json +++ b/src/translations/he.json @@ -1,13 +1,33 @@ { - "he": { - "translation": { - "cookie-policy": "מדיניות בנושא קובצי Cookie", - "hello": "שלום עולם!", - "legal": "משפטי", - "privacy-policy": "מדיניות פרטיות", - "refund-policy": "מדיניות החזרים כספיים", - "sctl-indications": "SCTL Indications", - "secure-connection": "חיבור מאובטח" - } - } + "he" : { + "translation" : { + "cookie-policy" : "מדיניות בנושא קובצי Cookie", + "finance-details-cart-items-checkout-title" : "רכישה מתוך המשחק", + "finance-details-cart-items-sales-tax" : "מס מכירה {{percent}}%", + "finance-details-cart-items-vat" : "מע\"מ {{percent}}%", + "finance-details-cart-items-vat-ghana" : "מע\"מ,‏ NHIL, מס GETFund ומס Covid-19", + "finance-details-cart-items-vat-india" : "מס סחורות ושירותים {{percent}}%", + "finance-details-hrk-equal" : "שווה-ערך ל-{{value}} קונה קרואטי", + "finance-details-hrk-exchange-rate" : "שער המרה: 1 אירו = 7.5345 קונה קרואטי", + "finance-details-subtotal-details-discount" : "הנחה", + "finance-details-subtotal-details-fee" : "עמלת מערכת תשלומים", + "finance-details-subtotal-details-sales-tax" : "מס מכירה", + "finance-details-subtotal-details-user-balance" : "יתרה", + "finance-details-subtotal-details-vat" : "מע\"מ {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "מע\"מ,‏ NHIL, מס GETFund ומס Covid-19", + "finance-details-subtotal-details-vat-india" : "מס סחורות ושירותים {{percent}}%", + "finance-details-subtotal-title" : "סיכום ביניים", + "finance-details-total-details-sales-tax" : "כולל מס מכירה", + "finance-details-total-details-vat" : "כולל {{percent}}% מע\"מ", + "finance-details-total-details-vat-ghana" : "כולל מע\"מ,‏ NHIL, מס GETFund ומס Covid-19", + "finance-details-total-details-vat-india" : "כולל {{percent}}% מס סחורות ושירותים", + "finance-details-total-title" : "סה\"כ", + "hello" : "שלום עולם!", + "legal" : "משפטי", + "privacy-policy" : "מדיניות פרטיות", + "refund-policy" : "מדיניות החזרים כספיים", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "חיבור מאובטח" + } + } } diff --git a/src/translations/it.json b/src/translations/it.json index 3e0cc24..f867678 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -1,13 +1,33 @@ { - "it": { - "translation": { - "cookie-policy": "Informativa sui Cookie", - "hello": "Ciao mondo!", - "legal": "Note legali", - "privacy-policy": "Informativa sulla Privacy", - "refund-policy": "Politica di Rimborso", - "sctl-indications": "SCTL Indications", - "secure-connection": "Connessione sicura" - } - } + "it" : { + "translation" : { + "cookie-policy" : "Informativa sui Cookie", + "finance-details-cart-items-checkout-title" : "Acquisto in-game", + "finance-details-cart-items-sales-tax" : "Imposta sulle vendite {{percent}}%", + "finance-details-cart-items-vat" : "IVA {{percent}}%", + "finance-details-cart-items-vat-ghana" : "IVA, NHIL, contributo GETFund e contributo Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "Pari a {{value}} HKR", + "finance-details-hrk-exchange-rate" : "Tasso di cambio: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Sconto", + "finance-details-subtotal-details-fee" : "Commissione sistema di pagamento", + "finance-details-subtotal-details-sales-tax" : "Imposta sulle vendite", + "finance-details-subtotal-details-user-balance" : "Saldo", + "finance-details-subtotal-details-vat" : "IVA {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "IVA, NHIL, contributo GETFund e contributo Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "Subtotale", + "finance-details-total-details-sales-tax" : "Imposta sulle vendite", + "finance-details-total-details-vat" : "Include {{percent}}% IVA", + "finance-details-total-details-vat-ghana" : "Inclusi IVA, NHIL, contributo GETFund e contributo Covid-19", + "finance-details-total-details-vat-india" : "Compresa GST {{percent}}% ", + "finance-details-total-title" : "Totale", + "hello" : "Ciao mondo!", + "legal" : "Note legali", + "privacy-policy" : "Informativa sulla Privacy", + "refund-policy" : "Politica di Rimborso", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Connessione sicura" + } + } } diff --git a/src/translations/ja.json b/src/translations/ja.json index dfd6b57..e31a40b 100644 --- a/src/translations/ja.json +++ b/src/translations/ja.json @@ -1,13 +1,33 @@ { - "ja": { - "translation": { - "cookie-policy": "クッキーポリシー", - "hello": "こんにちは、世界!", - "legal": "法的文書", - "privacy-policy": "プライバシーポリシー", - "refund-policy": "返金ポリシー", - "sctl-indications": "特商法取引に基づく表示", - "secure-connection": "安全な接続" - } - } + "ja" : { + "translation" : { + "cookie-policy" : "クッキーポリシー", + "finance-details-cart-items-checkout-title" : "ゲーム内購入", + "finance-details-cart-items-sales-tax" : "売上税 {{percent}}%", + "finance-details-cart-items-vat" : "消費税 {{percent}}%", + "finance-details-cart-items-vat-ghana" : "VAT、NHIL、GETFund税及びCovid-19税", + "finance-details-cart-items-vat-india" : "財・サービス税 {{percent}}%", + "finance-details-hrk-equal" : "{{value}} HRKに相当する", + "finance-details-hrk-exchange-rate" : "為替レート:1ユーロ=7.5345HRK", + "finance-details-subtotal-details-discount" : "割引", + "finance-details-subtotal-details-fee" : "決済システム料金", + "finance-details-subtotal-details-sales-tax" : "消費税", + "finance-details-subtotal-details-user-balance" : "残高", + "finance-details-subtotal-details-vat" : "消費税 {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "VAT、NHIL、GETFund税及びCovid-19税", + "finance-details-subtotal-details-vat-india" : "財・サービス税 {{percent}}%", + "finance-details-subtotal-title" : "小計", + "finance-details-total-details-sales-tax" : "消費税込み", + "finance-details-total-details-vat" : "{{percent}}% 消費税込み", + "finance-details-total-details-vat-ghana" : "VAT、NHIL、GETFund税及びCovid-19税を含む", + "finance-details-total-details-vat-india" : "{{percent}}% 商品サービス税込み", + "finance-details-total-title" : "合計", + "hello" : "こんにちは、世界!", + "legal" : "法的文書", + "privacy-policy" : "プライバシーポリシー", + "refund-policy" : "返金ポリシー", + "sctl-indications" : "特商法取引に基づく表示", + "secure-connection" : "安全な接続" + } + } } diff --git a/src/translations/ko.json b/src/translations/ko.json index a081c99..9fa7130 100644 --- a/src/translations/ko.json +++ b/src/translations/ko.json @@ -1,13 +1,33 @@ { - "ko": { - "translation": { - "cookie-policy": "쿠키 정책", - "hello": "안녕하세요 세상!", - "legal": "법률", - "privacy-policy": "개인정보 보호정책", - "refund-policy": "환불 정책", - "sctl-indications": "SCTL Indications", - "secure-connection": "보안 연결" - } - } + "ko" : { + "translation" : { + "cookie-policy" : "쿠키 정책", + "finance-details-cart-items-checkout-title" : "인게임 구매", + "finance-details-cart-items-sales-tax" : "판매 세금 {{percent}}%", + "finance-details-cart-items-vat" : "VAT {{percent}}%", + "finance-details-cart-items-vat-ghana" : "VAT, NHIL, GETFund Levy 및 Covid-19 Levy", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "= {{value}} HRK", + "finance-details-hrk-exchange-rate" : "환율: 1 EUR = 7.5345 HRK", + "finance-details-subtotal-details-discount" : "할인", + "finance-details-subtotal-details-fee" : "결제 시스템 수수료", + "finance-details-subtotal-details-sales-tax" : "판매 세금", + "finance-details-subtotal-details-user-balance" : "잔액", + "finance-details-subtotal-details-vat" : "VAT {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "VAT, NHIL, GETFund Levy 및 Covid-19 Levy", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "소계", + "finance-details-total-details-sales-tax" : "판매 세금 포함됨", + "finance-details-total-details-vat" : "{{percent}}% VAT 포함", + "finance-details-total-details-vat-ghana" : "VAT, NHIL, GETFund Levy 및 Covid-19 Levy 등 포함", + "finance-details-total-details-vat-india" : "{{percent}}% GST 포함", + "finance-details-total-title" : "합계", + "hello" : "안녕하세요 세상!", + "legal" : "법률", + "privacy-policy" : "개인정보 보호정책", + "refund-policy" : "환불 정책", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "보안 연결" + } + } } diff --git a/src/translations/pl.json b/src/translations/pl.json index ec8eeeb..db05455 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -1,13 +1,33 @@ { - "pl": { - "translation": { - "cookie-policy": "Polityka plików cookie", - "hello": "Witaj świecie!", - "legal": "Informacje prawne", - "privacy-policy": "Polityka prywatności", - "refund-policy": "Polityka zwrotów", - "sctl-indications": "SCTL Indications", - "secure-connection": "Bezpieczne połączenie" - } - } + "pl" : { + "translation" : { + "cookie-policy" : "Polityka plików cookie", + "finance-details-cart-items-checkout-title" : "Zakup w grze", + "finance-details-cart-items-sales-tax" : "Podatek od sprzedaży {{percent}}%", + "finance-details-cart-items-vat" : "VAT {{percent}}%", + "finance-details-cart-items-vat-ghana" : "VAT, NHIL, opłaty GETFund i Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "Wynosi {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Kurs wymiany: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Rabat", + "finance-details-subtotal-details-fee" : "Prowizja systemu płatniczego", + "finance-details-subtotal-details-sales-tax" : "Podatek od sprzedaży", + "finance-details-subtotal-details-user-balance" : "Saldo", + "finance-details-subtotal-details-vat" : "VAT {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "VAT, NHIL, opłaty GETFund i Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "Suma częściowa", + "finance-details-total-details-sales-tax" : "Kwota zawiera podatek od sprzedaży", + "finance-details-total-details-vat" : "Zawiera {{percent}}% VAT", + "finance-details-total-details-vat-ghana" : "Zawiera VAT, NHIL, opłaty GETFund i Covid-19", + "finance-details-total-details-vat-india" : "Zawiera {{percent}}% GST", + "finance-details-total-title" : "Suma zamówienia", + "hello" : "Witaj świecie!", + "legal" : "Informacje prawne", + "privacy-policy" : "Polityka prywatności", + "refund-policy" : "Polityka zwrotów", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Bezpieczne połączenie" + } + } } diff --git a/src/translations/pt.json b/src/translations/pt.json index 95e6eae..aa4e9e2 100644 --- a/src/translations/pt.json +++ b/src/translations/pt.json @@ -1,13 +1,33 @@ { - "pt": { - "translation": { - "cookie-policy": "Política de Cookies", - "hello": "Olá mundo!", - "legal": "Legal", - "privacy-policy": "Política de Privacidade", - "refund-policy": "Política de Reembolso", - "sctl-indications": "SCTL Indications", - "secure-connection": "Conexão segura" - } - } + "pt" : { + "translation" : { + "cookie-policy" : "Política de Cookies", + "finance-details-cart-items-checkout-title" : "Compra no jogo", + "finance-details-cart-items-sales-tax" : "Imposto sobre venda {{percent}}%", + "finance-details-cart-items-vat" : "VAT {{percent}}%", + "finance-details-cart-items-vat-ghana" : "VAT, NHIL, Taxa GETFund e Taxa Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "Equivale {{value}}HRK", + "finance-details-hrk-exchange-rate" : "Taxa de câmbio: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Desconto", + "finance-details-subtotal-details-fee" : "Taxa do sistema de pagamento", + "finance-details-subtotal-details-sales-tax" : "Imposto sobre venda", + "finance-details-subtotal-details-user-balance" : "Saldo", + "finance-details-subtotal-details-vat" : "VAT {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "VAT, NHIL, Taxa GETFund e Taxa Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "Subtotal", + "finance-details-total-details-sales-tax" : "Imposto de venda incluso", + "finance-details-total-details-vat" : "Incluindo {{percent}}% de VAT", + "finance-details-total-details-vat-ghana" : "Incluindo VAT, NHIL, Taxa GETFund e Taxa Covid-19", + "finance-details-total-details-vat-india" : "Incluindo {{percent}}% de GST", + "finance-details-total-title" : "Total", + "hello" : "Olá mundo!", + "legal" : "Legal", + "privacy-policy" : "Política de Privacidade", + "refund-policy" : "Política de Reembolso", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Conexão segura" + } + } } diff --git a/src/translations/ro.json b/src/translations/ro.json index 3d0582f..25a8137 100644 --- a/src/translations/ro.json +++ b/src/translations/ro.json @@ -1,13 +1,33 @@ { - "ro": { - "translation": { - "cookie-policy": "Politica privind modulele cookie", - "hello": "Salutare, lume!", - "legal": "Juridic", - "privacy-policy": "Politica de confidențialitate", - "refund-policy": "Politica de retur", - "sctl-indications": "Indicații SCTL", - "secure-connection": "Conexiune securizată" - } - } + "ro" : { + "translation" : { + "cookie-policy" : "Politica privind modulele cookie", + "finance-details-cart-items-checkout-title" : "Achiziție în joc", + "finance-details-cart-items-sales-tax" : "Taxa pe vânzări {{percent}}%", + "finance-details-cart-items-vat" : "TVA {{percent}}%", + "finance-details-cart-items-vat-ghana" : "VAT, NHIL, GETFund Levy și Covid-19 Levy", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "Echivalent al sumei de {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Curs de schimb: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Reducere", + "finance-details-subtotal-details-fee" : "Taxă sistem de plată", + "finance-details-subtotal-details-sales-tax" : "Taxa pe vânzări", + "finance-details-subtotal-details-user-balance" : "Sold", + "finance-details-subtotal-details-vat" : "TVA {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "VAT, NHIL, GETFund Levy și Covid-19 Levy", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "Subtotal", + "finance-details-total-details-sales-tax" : "Taxe pe vânzări incluse", + "finance-details-total-details-vat" : "Inclusiv {{percent}}% TVA", + "finance-details-total-details-vat-ghana" : "Inclusiv VAT, NHIL, GETFund Levy și Covid-19 Levy", + "finance-details-total-details-vat-india" : "Inclusiv {{percent}}% GST", + "finance-details-total-title" : "Total", + "hello" : "Salutare, lume!", + "legal" : "Juridic", + "privacy-policy" : "Politica de confidențialitate", + "refund-policy" : "Politica de retur", + "sctl-indications" : "Indicații SCTL", + "secure-connection" : "Conexiune securizată" + } + } } diff --git a/src/translations/ru.json b/src/translations/ru.json index 486775b..cdda925 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -1,13 +1,33 @@ { - "ru": { - "translation": { - "cookie-policy": "Политика использования cookie", - "hello": "Привет, мир!", - "legal": "Юридические документы", - "privacy-policy": "Политика конфиденциальности", - "refund-policy": "Политика возврата", - "sctl-indications": "SCTL Indications", - "secure-connection": "Безопасное соединение" - } - } + "ru" : { + "translation" : { + "cookie-policy" : "Политика использования cookie", + "finance-details-cart-items-checkout-title" : "Внутриигровая покупка", + "finance-details-cart-items-sales-tax" : "Налог с продаж {{percent}}%", + "finance-details-cart-items-vat" : "НДС {{percent}}%", + "finance-details-cart-items-vat-ghana" : "НДС и взносы NHIL, GETFund и Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "Равняется {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Обменный курс: 1 EUR = 7.5345 HRK", + "finance-details-subtotal-details-discount" : "Скидка", + "finance-details-subtotal-details-fee" : "Комиссия платежной системы", + "finance-details-subtotal-details-sales-tax" : "Налог с продаж", + "finance-details-subtotal-details-user-balance" : "Баланс", + "finance-details-subtotal-details-vat" : "НДС {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "НДС и взносы NHIL, GETFund и Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "Сумма", + "finance-details-total-details-sales-tax" : "Включая налог с продаж", + "finance-details-total-details-vat" : "Включая НДС {{percent}}%", + "finance-details-total-details-vat-ghana" : "Включая НДС и взносы NHIL, GETFund и Covid-19", + "finance-details-total-details-vat-india" : "Включая {{percent}}% GST", + "finance-details-total-title" : "К оплате", + "hello" : "Привет, мир!", + "legal" : "Юридические документы", + "privacy-policy" : "Политика конфиденциальности", + "refund-policy" : "Политика возврата", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Безопасное соединение" + } + } } diff --git a/src/translations/th.json b/src/translations/th.json index 4ab9d46..61d0594 100644 --- a/src/translations/th.json +++ b/src/translations/th.json @@ -1,13 +1,33 @@ { - "th": { - "translation": { - "cookie-policy": "นโยบายคุกกี้", - "hello": "สวัสดีชาวโลก!", - "legal": "ข้อมูลด้านกฎหมาย", - "privacy-policy": "นโยบายความเป็นส่วนตัว", - "refund-policy": "นโยบายการคืนเงิน", - "sctl-indications": "SCTL Indications", - "secure-connection": "การเชื่อมต่อที่ปลอดภัย" - } - } + "th" : { + "translation" : { + "cookie-policy" : "นโยบายคุกกี้", + "finance-details-cart-items-checkout-title" : "การซื้อภายในเกม", + "finance-details-cart-items-sales-tax" : "ภาษีขาย {{percent}}%", + "finance-details-cart-items-vat" : "VAT {{percent}}%", + "finance-details-cart-items-vat-ghana" : "การจัดเก็บภาษี VAT, NHIL, GETFund และ Covid-19", + "finance-details-cart-items-vat-india" : "GST {{percent}}%", + "finance-details-hrk-equal" : "เท่ากับ {{value}} HRK", + "finance-details-hrk-exchange-rate" : "อัตราแลกเปลี่ยน: 1 EUR = 7.5345 HRK", + "finance-details-subtotal-details-discount" : "ส่วนลด", + "finance-details-subtotal-details-fee" : "ค่าธรรมเนียมระบบการชำระเงิน", + "finance-details-subtotal-details-sales-tax" : "ภาษีขาย", + "finance-details-subtotal-details-user-balance" : "ยอดคงเหลือ", + "finance-details-subtotal-details-vat" : "VAT {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "การจัดเก็บภาษี VAT, NHIL, GETFund และ Covid-19", + "finance-details-subtotal-details-vat-india" : "GST {{percent}}%", + "finance-details-subtotal-title" : "ยอดรวมย่อย", + "finance-details-total-details-sales-tax" : "รวมภาษีการขายแล้ว", + "finance-details-total-details-vat" : "รวม VAT {{percent}}% ", + "finance-details-total-details-vat-ghana" : "รวมถึง การจัดเก็บภาษี VAT, NHIL, GETFund และ Covid-19", + "finance-details-total-details-vat-india" : "รวม GST {{percent}}%", + "finance-details-total-title" : "ยอดรวม", + "hello" : "สวัสดีชาวโลก!", + "legal" : "ข้อมูลด้านกฎหมาย", + "privacy-policy" : "นโยบายความเป็นส่วนตัว", + "refund-policy" : "นโยบายการคืนเงิน", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "การเชื่อมต่อที่ปลอดภัย" + } + } } diff --git a/src/translations/tr.json b/src/translations/tr.json index ba56653..5fef0e4 100644 --- a/src/translations/tr.json +++ b/src/translations/tr.json @@ -1,13 +1,33 @@ { - "tr": { - "translation": { - "cookie-policy": "Çerez Politikası", - "hello": "Merhaba dünya!", - "legal": "Yasal", - "privacy-policy": "Gizlilik Politikası", - "refund-policy": "İade Politikası", - "sctl-indications": "SCTL Indications", - "secure-connection": "Güvenli bağlantı" - } - } + "tr" : { + "translation" : { + "cookie-policy" : "Çerez Politikası", + "finance-details-cart-items-checkout-title" : "Oyun içi satın alma", + "finance-details-cart-items-sales-tax" : "Satış vergisi %{{percent}}", + "finance-details-cart-items-vat" : "KDV %{{percent}}", + "finance-details-cart-items-vat-ghana" : "KDV, NHIL, GETFund Vergisi ve Covid-19 Vergisi", + "finance-details-cart-items-vat-india" : "GST %{{percent}}", + "finance-details-hrk-equal" : "{{value}} Hırvat kunasına eşittir", + "finance-details-hrk-exchange-rate" : "Döviz kuru: 1 euro = 7,5345 Hırvat kunası", + "finance-details-subtotal-details-discount" : "İndirim", + "finance-details-subtotal-details-fee" : "Ödeme sistemi ücreti", + "finance-details-subtotal-details-sales-tax" : "Satış vergisi", + "finance-details-subtotal-details-user-balance" : "Bakiye", + "finance-details-subtotal-details-vat" : "KDV %{{percent}}", + "finance-details-subtotal-details-vat-ghana" : "KDV, NHIL, GETFund Vergisi ve Covid-19 Vergisi", + "finance-details-subtotal-details-vat-india" : "GST %{{percent}}", + "finance-details-subtotal-title" : "Ara Toplam", + "finance-details-total-details-sales-tax" : "Satış vergisi dahildir", + "finance-details-total-details-vat" : "%{{percent}} KDV dahildir", + "finance-details-total-details-vat-ghana" : "KDV, NHIL, GETFund Vergisi ve Covid-19 Vergisi dahil", + "finance-details-total-details-vat-india" : "%{{percent}} GST dahildir", + "finance-details-total-title" : "Toplam", + "hello" : "Merhaba dünya!", + "legal" : "Yasal", + "privacy-policy" : "Gizlilik Politikası", + "refund-policy" : "İade Politikası", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Güvenli bağlantı" + } + } } diff --git a/src/translations/vi.json b/src/translations/vi.json index 4f5d1ac..7231d36 100644 --- a/src/translations/vi.json +++ b/src/translations/vi.json @@ -1,13 +1,33 @@ { - "vi": { - "translation": { - "cookie-policy": "Chính sách cookie", - "hello": "Xin chào thế giới!", - "legal": "Pháp lý", - "privacy-policy": "Chính sách Quyền riêng tư", - "refund-policy": "Chính sách hoàn tiền", - "sctl-indications": "SCTL Indications", - "secure-connection": "Kết nối bảo mật" - } - } + "vi" : { + "translation" : { + "cookie-policy" : "Chính sách cookie", + "finance-details-cart-items-checkout-title" : "Giao dịch trong trò chơi", + "finance-details-cart-items-sales-tax" : "Thuế doanh thu {{percent}}%", + "finance-details-cart-items-vat" : "Thuế giá trị gia tăng (VAT) {{percent}}%", + "finance-details-cart-items-vat-ghana" : "Thuế Giá trị gia tăng (VAT), Thuế phụ thu bảo hiểm y tế quốc gia (NHIL), Thuế phụ thu GETFund và Thuế phụ thu Covid-19", + "finance-details-cart-items-vat-india" : "Thuế hàng hóa và dịch vụ (GST) {{percent}}%", + "finance-details-hrk-equal" : "Tương đương {{value}} HRK", + "finance-details-hrk-exchange-rate" : "Tỷ giá: 1 EUR = 7,5345 HRK", + "finance-details-subtotal-details-discount" : "Giảm giá", + "finance-details-subtotal-details-fee" : "Phí hệ thống thanh toán", + "finance-details-subtotal-details-sales-tax" : "Thuế doanh thu", + "finance-details-subtotal-details-user-balance" : "Số dư", + "finance-details-subtotal-details-vat" : "Thuế giá trị gia tăng (VAT) {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "Thuế Giá trị gia tăng (VAT), Thuế phụ thu bảo hiểm y tế quốc gia (NHIL), Thuế phụ thu GETFund và Thuế phụ thu Covid-19", + "finance-details-subtotal-details-vat-india" : "Thuế hàng hóa và dịch vụ (GST) {{percent}}%", + "finance-details-subtotal-title" : "Tạm tính", + "finance-details-total-details-sales-tax" : "Đã bao gồm thuế doanh thu", + "finance-details-total-details-vat" : "Bao gồm {{percent}}% thuế GTGT", + "finance-details-total-details-vat-ghana" : "Bao gồm thuế Giá trị gia tăng (VAT), Thuế phụ thu bảo hiểm y tế quốc gia (NHIL), Thuế phụ thu GETFund và Thuế phụ thu Covid-19", + "finance-details-total-details-vat-india" : "Bao gồm {{percent}}% thuế hàng hóa và dịch vụ (GST)", + "finance-details-total-title" : "Tổng", + "hello" : "Xin chào thế giới!", + "legal" : "Pháp lý", + "privacy-policy" : "Chính sách Quyền riêng tư", + "refund-policy" : "Chính sách hoàn tiền", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "Kết nối bảo mật" + } + } } diff --git a/src/translations/zh_HANS.json b/src/translations/zh_HANS.json index cc040c5..9629e85 100644 --- a/src/translations/zh_HANS.json +++ b/src/translations/zh_HANS.json @@ -1,13 +1,33 @@ { - "zh_HANS": { - "translation": { - "cookie-policy": "Cookie政策", - "hello": "你好,世界!", - "legal": "法律信息", - "privacy-policy": "隐私政策", - "refund-policy": "退款政策", - "sctl-indications": "SCTL Indications", - "secure-connection": "安全连接" - } - } + "zh_HANS" : { + "translation" : { + "cookie-policy" : "Cookie政策", + "finance-details-cart-items-checkout-title" : "游戏内购买", + "finance-details-cart-items-sales-tax" : "销售税 {{percent}}%", + "finance-details-cart-items-vat" : "增值税 {{percent}}%", + "finance-details-cart-items-vat-ghana" : "VAT、NHIL、GETFund征税和Covid-19征税", + "finance-details-cart-items-vat-india" : "商品和服务税 {{percent}}%", + "finance-details-hrk-equal" : "等于 {{value}} HRK", + "finance-details-hrk-exchange-rate" : "汇率:1 欧元 = 7.5345 HRK", + "finance-details-subtotal-details-discount" : "折扣", + "finance-details-subtotal-details-fee" : "支付系统使用费", + "finance-details-subtotal-details-sales-tax" : "销售税", + "finance-details-subtotal-details-user-balance" : "余额", + "finance-details-subtotal-details-vat" : "增值税 {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "VAT、NHIL、GETFund征税和Covid-19征税", + "finance-details-subtotal-details-vat-india" : "商品和服务税 {{percent}}%", + "finance-details-subtotal-title" : "小计", + "finance-details-total-details-sales-tax" : "含销售税", + "finance-details-total-details-vat" : "含{{percent}}%增值税", + "finance-details-total-details-vat-ghana" : "含VAT、NHIL、GETFund征税和Covid-19征税", + "finance-details-total-details-vat-india" : "含{{percent}}%商品和服务税", + "finance-details-total-title" : "总计", + "hello" : "你好,世界!", + "legal" : "法律信息", + "privacy-policy" : "隐私政策", + "refund-policy" : "退款政策", + "sctl-indications" : "SCTL Indications", + "secure-connection" : "安全连接" + } + } } diff --git a/src/translations/zh_HANT.json b/src/translations/zh_HANT.json index c723101..f383547 100644 --- a/src/translations/zh_HANT.json +++ b/src/translations/zh_HANT.json @@ -1,13 +1,33 @@ { - "zh_HANT": { - "translation": { - "cookie-policy": "Cookie 政策", - "hello": "你好,世界!", - "legal": "法律文件", - "privacy-policy": "隱私權政策", - "refund-policy": "退款政策", - "sctl-indications": "特定商業交易法聲明", - "secure-connection": "安全連線" - } - } + "zh_HANT" : { + "translation" : { + "cookie-policy" : "Cookie 政策", + "finance-details-cart-items-checkout-title" : "遊戲內選購", + "finance-details-cart-items-sales-tax" : "營業稅 {{percent}}%", + "finance-details-cart-items-vat" : "加值稅 {{percent}}%", + "finance-details-cart-items-vat-ghana" : "含增值稅,徵收國民健康保險、迦納教育信託基金和 Covid-19 等稅金", + "finance-details-cart-items-vat-india" : "商品及服務稅 {{percent}}%", + "finance-details-hrk-equal" : "相當於 {{value}} 克羅埃西亞庫納(HRK)", + "finance-details-hrk-exchange-rate" : "匯率換算:1 歐元 (EUR) = 7.5345 克羅埃西亞庫納 (HRK)", + "finance-details-subtotal-details-discount" : "折扣", + "finance-details-subtotal-details-fee" : "支付系統費用", + "finance-details-subtotal-details-sales-tax" : "營業稅", + "finance-details-subtotal-details-user-balance" : "餘額", + "finance-details-subtotal-details-vat" : "加值稅 {{percent}}%", + "finance-details-subtotal-details-vat-ghana" : "含增值稅,徵收國民健康保險、迦納教育信託基金和 Covid-19 等稅金", + "finance-details-subtotal-details-vat-india" : "商品及服務稅 {{percent}}%", + "finance-details-subtotal-title" : "小計", + "finance-details-total-details-sales-tax" : "含營業稅", + "finance-details-total-details-vat" : "含{{percent}}% 增值稅", + "finance-details-total-details-vat-ghana" : "含增值稅,徵收國民健康保險、迦納教育信託基金和 Covid-19 等稅金", + "finance-details-total-details-vat-india" : "含{{percent}}%商品及服務稅", + "finance-details-total-title" : "總計", + "hello" : "你好,世界!", + "legal" : "法律文件", + "privacy-policy" : "隱私權政策", + "refund-policy" : "退款政策", + "sctl-indications" : "特定商業交易法聲明", + "secure-connection" : "安全連線" + } + } } diff --git a/src/web-components.ts b/src/web-components.ts index f7e44a8..e767dc5 100644 --- a/src/web-components.ts +++ b/src/web-components.ts @@ -2,10 +2,14 @@ import { SubmitButtonComponent } from './features/headless-checkout/web-componen import { TextComponent } from './features/headless-checkout/web-components/text-component/text.component'; 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 { SubmitButtonComponent, TextComponent, PaymentMethodsComponent, + PriceTextComponent, + FinanceDetailsComponent, LegalComponent, };