diff --git a/package-lock.json b/package-lock.json index 47c68d4..d45c413 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" }, @@ -3044,6 +3045,11 @@ "node": ">=4" } }, + "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 3a9e683..d046585 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,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 81c798b..11b1ffa 100644 --- a/src/core/web-components/web-component-tag-name.enum.ts +++ b/src/core/web-components/web-component-tag-name.enum.ts @@ -2,6 +2,8 @@ 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', StatusComponent = 'psdk-status', } 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 ad5ce2e..0956e57 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 { PaymentMethodsComponent } from '../../features/headless-checkout/web-components/payment-methods/payment-methods.component'; import { LegalComponent } from '../../features/headless-checkout/web-components/legal/legal.component'; import { StatusComponent } from '../../features/headless-checkout/web-components/status/status.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'; import { WebComponentTagName } from './web-component-tag-name.enum'; export const webComponents: { @@ -11,6 +13,8 @@ export const webComponents: { [WebComponentTagName.TextComponent]: TextComponent, [WebComponentTagName.SubmitButtonComponent]: SubmitButtonComponent, [WebComponentTagName.PaymentMethodsComponent]: PaymentMethodsComponent, + [WebComponentTagName.PriceTextComponent]: PriceTextComponent, + [WebComponentTagName.FinanceDetailsComponent]: FinanceDetailsComponent, [WebComponentTagName.LegalComponent]: LegalComponent, [WebComponentTagName.StatusComponent]: StatusComponent, }; 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 c1610ec..61de9a5 100644 --- a/src/features/headless-checkout/headless-checkout.ts +++ b/src/features/headless-checkout/headless-checkout.ts @@ -24,6 +24,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 { @@ -150,6 +152,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/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 73b1ffe..59a1b2f 100644 --- a/src/translations/ar.json +++ b/src/translations/ar.json @@ -12,7 +12,27 @@ "status.processing.description" : "في انتظار أن يتم السداد...", "status.processing.title" : "جارٍ تنفيذ السداد", "status.success.description" : "أرسلنا إيصالك إلى {{email}}", - "status.success.title" : "نجحت عملية السداد" + "status.success.title" : "نجحت عملية السداد", + "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" : "الإجمالي" } } } diff --git a/src/translations/bg.json b/src/translations/bg.json index f509790..62593b9 100644 --- a/src/translations/bg.json +++ b/src/translations/bg.json @@ -12,7 +12,27 @@ "status.processing.description" : "Изчаква завършване на плащане...", "status.processing.title" : "Плащането се обработва", "status.success.description" : "Изпратихме касовата Ви бележка на {{email}}", - "status.success.title" : "Успешно плащане" + "status.success.title" : "Успешно плащане", + "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" : "Общо" } } } diff --git a/src/translations/cs.json b/src/translations/cs.json index a5a2fad..a8c10a0 100644 --- a/src/translations/cs.json +++ b/src/translations/cs.json @@ -12,7 +12,27 @@ "status.processing.description" : "Čekání na dokončení platby...", "status.processing.title" : "Zpracovávání platby", "status.success.description" : "Vaši stvrzenku jsme zaslali na {{email}}", - "status.success.title" : "Platba byla úspěšná" + "status.success.title" : "Platba byla úspěšná", + "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" } } } diff --git a/src/translations/de.json b/src/translations/de.json index c131da3..609efdd 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -12,7 +12,27 @@ "status.processing.description" : "Zahlung wird durchgeführt …", "status.processing.title" : "Zahlung wird verarbeitet", "status.success.description" : "Wir haben den Beleg an {{email}} gesendet", - "status.success.title" : "Zahlung erfolgreich" + "status.success.title" : "Zahlung erfolgreich", + "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" } } } diff --git a/src/translations/en.json b/src/translations/en.json index 38030df..e559e82 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -12,7 +12,27 @@ "status.processing.description": "Waiting for payment to complete...", "status.error.title": "Payment failed", "status.success.title": "Payment successful", - "status.success.description": "We sent your receipt to {{email}}" + "status.success.description": "We sent your receipt to {{email}}", + "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 a94d885..be1fe09 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -12,7 +12,27 @@ "status.processing.description" : "Esperando la confirmación del pago...", "status.processing.title" : "Procesando el pago", "status.success.description" : "Hemos enviado el recibo a {{email}}", - "status.success.title" : "Pago efectuado" + "status.success.title" : "Pago efectuado", + "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" } } } diff --git a/src/translations/fr.json b/src/translations/fr.json index f09cc1c..9873909 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -12,7 +12,27 @@ "status.processing.description" : "En attente de l'achèvement du paiement…", "status.processing.title" : "Traitement du paiement", "status.success.description" : "Nous avons envoyé votre reçu à {{email}}", - "status.success.title" : "Paiement réussi" + "status.success.title" : "Paiement réussi", + "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" } } } diff --git a/src/translations/he.json b/src/translations/he.json index d0eb0b5..d770ced 100644 --- a/src/translations/he.json +++ b/src/translations/he.json @@ -12,7 +12,27 @@ "status.processing.description" : "בהמתנה לסיום התשלום...", "status.processing.title" : "התשלום בעיבוד", "status.success.description" : "שלחנו את הקבלה אל {{email}}", - "status.success.title" : "התשלום התקבל" + "status.success.title" : "התשלום התקבל", + "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" : "סה\"כ" } } } diff --git a/src/translations/it.json b/src/translations/it.json index 60670ac..236c1e9 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -12,7 +12,27 @@ "status.processing.description" : "In attesa che il pagamento venga completato…", "status.processing.title" : "Elaborazione pagamento", "status.success.description" : "Abbiamo inviato una ricevuta a {{email}}", - "status.success.title" : "Pagamento effettuato con successo" + "status.success.title" : "Pagamento effettuato con successo", + "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" } } } diff --git a/src/translations/ja.json b/src/translations/ja.json index a813961..2196f36 100644 --- a/src/translations/ja.json +++ b/src/translations/ja.json @@ -12,7 +12,27 @@ "status.processing.description" : "決済の完了待ち...", "status.processing.title" : "決済処理中", "status.success.description" : "領収書を{{email}}に送信しました", - "status.success.title" : "決済完了" + "status.success.title" : "決済完了", + "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" : "合計" } } } diff --git a/src/translations/ko.json b/src/translations/ko.json index 402d4fe..6a64b75 100644 --- a/src/translations/ko.json +++ b/src/translations/ko.json @@ -12,7 +12,27 @@ "status.processing.description" : "결제 완료 대기 중...", "status.processing.title" : "결제 처리 중", "status.success.description" : "{{email}}(으)로 영수증을 보내드렸습니다.", - "status.success.title" : "결제 성공함" + "status.success.title" : "결제 성공함", + "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" : "합계" } } } diff --git a/src/translations/pl.json b/src/translations/pl.json index 7ef8d52..0efdf67 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -12,7 +12,27 @@ "status.processing.description" : "Oczekiwanie na sfinalizowanie płatności...", "status.processing.title" : "Trwa przetwarzanie płatności", "status.success.description" : "Wysłaliśmy rachunek na adres {{email}}", - "status.success.title" : "Płatność zrealizowana" + "status.success.title" : "Płatność zrealizowana", + "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" } } } diff --git a/src/translations/pt.json b/src/translations/pt.json index 6d9c9d7..0f93b5d 100644 --- a/src/translations/pt.json +++ b/src/translations/pt.json @@ -12,7 +12,27 @@ "status.processing.description" : "Aguardando a conclusão do pagamento", "status.processing.title" : "Pagamento em processamento", "status.success.description" : "Enviamos seu recibo para {{email}}", - "status.success.title" : "Pagamento bem-sucedido" + "status.success.title" : "Pagamento bem-sucedido", + "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" } } } diff --git a/src/translations/ro.json b/src/translations/ro.json index bac4afa..f73d280 100644 --- a/src/translations/ro.json +++ b/src/translations/ro.json @@ -12,7 +12,27 @@ "status.processing.description" : "Se aşteaptă încheierea plății...", "status.processing.title" : "Procesare plată", "status.success.description" : "Am trimis chitanța dumneavoastră la {{email}}", - "status.success.title" : "Plată realizată cu succes" + "status.success.title" : "Plată realizată cu succes", + "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" } } } diff --git a/src/translations/ru.json b/src/translations/ru.json index fcbe74a..2d1a8d7 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -12,7 +12,27 @@ "status.processing.description" : "Ожидаем завершения оплаты...", "status.processing.title" : "Обработка платежа", "status.success.description" : "Мы отправили чек на {{email}}", - "status.success.title" : "Платеж прошел" + "status.success.title" : "Платеж прошел", + "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" : "К оплате" } } } diff --git a/src/translations/th.json b/src/translations/th.json index 563c8ac..76f7372 100644 --- a/src/translations/th.json +++ b/src/translations/th.json @@ -12,7 +12,27 @@ "status.processing.description" : "กำลังรอการชำระเงินให้เสร็จสิ้น...", "status.processing.title" : "กำลังดำเนินการชำระเงิน", "status.success.description" : "เราส่งใบเสร็จรับเงินไปยัง {{email}} แล้ว", - "status.success.title" : "ชำระเงินสำเร็จแล้ว" + "status.success.title" : "ชำระเงินสำเร็จแล้ว", + "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" : "ยอดรวม" } } } diff --git a/src/translations/tr.json b/src/translations/tr.json index 68f8abd..1d866e5 100644 --- a/src/translations/tr.json +++ b/src/translations/tr.json @@ -12,7 +12,27 @@ "status.processing.description" : "Ödeme işleminin tamamlanması bekleniyor...", "status.processing.title" : "Ödeme işlemi gerçekleştiriliyor", "status.success.description" : "Makbuzunuzu {{email}} adresine gönderdik", - "status.success.title" : "Ödeme alındı" + "status.success.title" : "Ödeme alındı", + "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" } } } diff --git a/src/translations/vi.json b/src/translations/vi.json index 844a028..912fe3c 100644 --- a/src/translations/vi.json +++ b/src/translations/vi.json @@ -12,7 +12,27 @@ "status.processing.description" : "Đợi thanh toán hoàn tất...", "status.processing.title" : "Đang xử lý thanh toán", "status.success.description" : "Biên nhận đã được gửi đến {{email}}", - "status.success.title" : "Thanh toán thành công" + "status.success.title" : "Thanh toán thành công", + "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" } } } diff --git a/src/translations/zh_HANS.json b/src/translations/zh_HANS.json index 7c0d71c..299bd9a 100644 --- a/src/translations/zh_HANS.json +++ b/src/translations/zh_HANS.json @@ -12,7 +12,27 @@ "status.processing.description" : "正在等待支付完成...", "status.processing.title" : "正在处理付款", "status.success.description" : "您的收据已发送至{{email}}", - "status.success.title" : "支付成功" + "status.success.title" : "支付成功", + "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" : "总计" } } } diff --git a/src/translations/zh_HANT.json b/src/translations/zh_HANT.json index 0632546..1e65e21 100644 --- a/src/translations/zh_HANT.json +++ b/src/translations/zh_HANT.json @@ -12,7 +12,27 @@ "status.processing.description" : "等待完成付款......", "status.processing.title" : "正在處理付款", "status.success.description" : "我們已將您的收據發送至 {{email}}", - "status.success.title" : "付款成功" + "status.success.title" : "付款成功", + "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" : "總計" } } } diff --git a/src/web-components.ts b/src/web-components.ts index 5416fe5..60866c6 100644 --- a/src/web-components.ts +++ b/src/web-components.ts @@ -3,11 +3,15 @@ import { TextComponent } from './features/headless-checkout/web-components/text- 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 { StatusComponent } from './features/headless-checkout/web-components/status/status.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, StatusComponent, };