Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68503ea01d | ||
|
|
dd234421a4 |
@@ -0,0 +1,6 @@
|
|||||||
|
import { PaymentMethod } from '../modules/payment/types/PaymentMethod';
|
||||||
|
|
||||||
|
export const paymentMethodIconUrl: Record<PaymentMethod, string> = {
|
||||||
|
[PaymentMethod.Xmr]: '/shop/assets/img/xmr.png',
|
||||||
|
[PaymentMethod.Btc]: '/shop/assets/img/btc.png'
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import { Throttle } from '@nestjs/throttler';
|
|||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import { throttleProfiles } from '../../../config/throttleProfiles';
|
import { throttleProfiles } from '../../../config/throttleProfiles';
|
||||||
import type { Config } from '../../../types/Config';
|
import type { Config } from '../../../types/Config';
|
||||||
|
import { paymentMethodIconUrl } from '../../../consts/paymentMethodIconUrl';
|
||||||
import { paymentMethodLabel } from '../../../consts/paymentMethodLabel';
|
import { paymentMethodLabel } from '../../../consts/paymentMethodLabel';
|
||||||
import { StorefrontExceptionFilter } from '../../storefrontCore/filters/StorefrontExceptionFilter';
|
import { StorefrontExceptionFilter } from '../../storefrontCore/filters/StorefrontExceptionFilter';
|
||||||
import { StorefrontCartCookieService } from '../../storefrontCore/services/StorefrontCartCookieService';
|
import { StorefrontCartCookieService } from '../../storefrontCore/services/StorefrontCartCookieService';
|
||||||
@@ -70,7 +71,8 @@ export class StorefrontCartController {
|
|||||||
|
|
||||||
const paymentMethods = enabledPaymentMethods.map(paymentMethod => ({
|
const paymentMethods = enabledPaymentMethods.map(paymentMethod => ({
|
||||||
paymentMethod,
|
paymentMethod,
|
||||||
paymentMethodLabel: paymentMethodLabel[paymentMethod]
|
paymentMethodLabel: paymentMethodLabel[paymentMethod],
|
||||||
|
iconUrl: paymentMethodIconUrl[paymentMethod]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return res.render('cart-summary', {
|
return res.render('cart-summary', {
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ describe('StorefrontCheckoutViewService', () => {
|
|||||||
let toStorefrontInvoiceViewSpy: jest.SpiedFunction<typeof toStorefrontInvoiceViewModule.toStorefrontInvoiceView>;
|
let toStorefrontInvoiceViewSpy: jest.SpiedFunction<typeof toStorefrontInvoiceViewModule.toStorefrontInvoiceView>;
|
||||||
|
|
||||||
const checkoutInvoiceView = {
|
const checkoutInvoiceView = {
|
||||||
cryptoCurrency: 'XMR',
|
paymentLabel: 'XMR',
|
||||||
amountFiat: 9
|
amountFiat: 9
|
||||||
} as unknown as StorefrontInvoiceView;
|
} as unknown as StorefrontInvoiceView;
|
||||||
|
|
||||||
|
|||||||
@@ -355,9 +355,31 @@ a:hover {
|
|||||||
gap: var(--sf-space-1);
|
gap: var(--sf-space-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sf-rates {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--sf-space-3);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.sf-rate {
|
.sf-rate {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--sf-space-1);
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
color: var(--sf-text-muted);
|
color: var(--sf-text-muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-crypto-icon {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
object-fit: contain;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-btn--pay {
|
||||||
|
gap: var(--sf-space-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-category-nav {
|
.sf-category-nav {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -145,8 +145,8 @@ describe('StorefrontShopViewService', () => {
|
|||||||
feedback: { type: 'success', text: 'Added to cart' },
|
feedback: { type: 'success', text: 'Added to cart' },
|
||||||
shopFiatCurrency: 'USD',
|
shopFiatCurrency: 'USD',
|
||||||
cryptoRates: [
|
cryptoRates: [
|
||||||
{ cryptoCurrency: 'XMR', fiatPerCrypto: 200 },
|
{ paymentLabel: 'XMR', fiatPerCrypto: 200, iconUrl: '/shop/assets/img/xmr.png' },
|
||||||
{ cryptoCurrency: 'BTC', fiatPerCrypto: 80_000 }
|
{ paymentLabel: 'BTC', fiatPerCrypto: 80_000, iconUrl: '/shop/assets/img/btc.png' }
|
||||||
],
|
],
|
||||||
logoUrl: '/uploads/logo.png',
|
logoUrl: '/uploads/logo.png',
|
||||||
faviconUrl: '/uploads/favicon.ico',
|
faviconUrl: '/uploads/favicon.ico',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { formatShortOrderId } from '../../../utils/order/formatShortOrderId';
|
|||||||
import { toAbsoluteUrl } from '../../../utils/toAbsoluteUrl';
|
import { toAbsoluteUrl } from '../../../utils/toAbsoluteUrl';
|
||||||
import { ShopSettingsService } from '../../shopSettings/services/ShopSettingsService';
|
import { ShopSettingsService } from '../../shopSettings/services/ShopSettingsService';
|
||||||
import { ExchangeRateService } from '../../exchangeRate/services/ExchangeRateService';
|
import { ExchangeRateService } from '../../exchangeRate/services/ExchangeRateService';
|
||||||
|
import { paymentMethodIconUrl } from '../../../consts/paymentMethodIconUrl';
|
||||||
import { paymentMethodLabel } from '../../../consts/paymentMethodLabel';
|
import { paymentMethodLabel } from '../../../consts/paymentMethodLabel';
|
||||||
import { PaymentMethod } from '../../payment/types/PaymentMethod';
|
import { PaymentMethod } from '../../payment/types/PaymentMethod';
|
||||||
import type { AuthorizedOrderNavItem } from '../types/AuthorizedOrderNavItem';
|
import type { AuthorizedOrderNavItem } from '../types/AuthorizedOrderNavItem';
|
||||||
@@ -145,8 +146,9 @@ export class StorefrontShopViewService {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
cryptoCurrency: paymentMethodLabel[paymentMethod],
|
paymentLabel: paymentMethodLabel[paymentMethod],
|
||||||
fiatPerCrypto
|
fiatPerCrypto,
|
||||||
|
iconUrl: paymentMethodIconUrl[paymentMethod]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export type StorefrontCryptoRate = {
|
export type StorefrontCryptoRate = {
|
||||||
cryptoCurrency: string;
|
paymentLabel: string;
|
||||||
fiatPerCrypto: number;
|
fiatPerCrypto: number;
|
||||||
|
iconUrl: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { InvoiceStatusLabel } from '../../../utils/invoice/types/InvoiceSta
|
|||||||
import type { InvoiceStatusVariant } from '../../../utils/invoice/types/InvoiceStatusVariant';
|
import type { InvoiceStatusVariant } from '../../../utils/invoice/types/InvoiceStatusVariant';
|
||||||
|
|
||||||
export type StorefrontInvoiceView = {
|
export type StorefrontInvoiceView = {
|
||||||
cryptoCurrency: string;
|
paymentLabel: string;
|
||||||
expectedTotalCrypto: string;
|
expectedTotalCrypto: string;
|
||||||
receivedTotalCrypto: string | null;
|
receivedTotalCrypto: string | null;
|
||||||
paymentAddress: string;
|
paymentAddress: string;
|
||||||
|
|||||||
@@ -23,14 +23,14 @@
|
|||||||
<p class='sf-payment-status sf-payment-status--confirmed'>Payment received</p>
|
<p class='sf-payment-status sf-payment-status--confirmed'>Payment received</p>
|
||||||
<p class='sf-payment-detail'>
|
<p class='sf-payment-detail'>
|
||||||
Received:
|
Received:
|
||||||
<strong>{{checkout.checkoutInvoice.receivedTotalCrypto}} {{checkout.checkoutInvoice.cryptoCurrency}}</strong>
|
<strong>{{checkout.checkoutInvoice.receivedTotalCrypto}} {{checkout.checkoutInvoice.paymentLabel}}</strong>
|
||||||
</p>
|
</p>
|
||||||
<p class='sf-payment-detail'>
|
<p class='sf-payment-detail'>
|
||||||
Your order is being prepared. This page will redirect automatically once it is ready.
|
Your order is being prepared. This page will redirect automatically once it is ready.
|
||||||
</p>
|
</p>
|
||||||
{{> refresh-link href=checkout.refreshHref showAutoRefreshNote=true}}
|
{{> refresh-link href=checkout.refreshHref showAutoRefreshNote=true}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<h2 class='sf-section-title'>Pay with {{checkout.checkoutInvoice.cryptoCurrency}}</h2>
|
<h2 class='sf-section-title'>Pay with {{checkout.checkoutInvoice.paymentLabel}}</h2>
|
||||||
{{#with checkout.checkoutInvoice}}
|
{{#with checkout.checkoutInvoice}}
|
||||||
{{> invoice-payment refreshHref=../checkout.refreshHref showAutoRefreshNote=true}}
|
{{> invoice-payment refreshHref=../checkout.refreshHref showAutoRefreshNote=true}}
|
||||||
{{/with}}
|
{{/with}}
|
||||||
|
|||||||
@@ -80,9 +80,16 @@
|
|||||||
type='submit'
|
type='submit'
|
||||||
name='paymentMethod'
|
name='paymentMethod'
|
||||||
value='{{paymentMethod}}'
|
value='{{paymentMethod}}'
|
||||||
class='sf-btn sf-btn--primary'
|
class='sf-btn sf-btn--primary sf-btn--pay'
|
||||||
>
|
>
|
||||||
Pay with {{paymentMethodLabel}}
|
Pay with {{paymentMethodLabel}}
|
||||||
|
{{> storefront-image
|
||||||
|
class='sf-crypto-icon'
|
||||||
|
src=iconUrl
|
||||||
|
alt=''
|
||||||
|
width='20'
|
||||||
|
height='20'
|
||||||
|
}}
|
||||||
</button>
|
</button>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,27 +3,27 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showProminentAmount}}
|
{{#if showProminentAmount}}
|
||||||
<p class='sf-payment-amount'>{{expectedTotalCrypto}} {{cryptoCurrency}}</p>
|
<p class='sf-payment-amount'>{{expectedTotalCrypto}} {{paymentLabel}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showReceivedTotal}}
|
{{#if showReceivedTotal}}
|
||||||
<p class='sf-payment-detail'>
|
<p class='sf-payment-detail'>
|
||||||
Received:
|
Received:
|
||||||
<strong>{{receivedTotalCrypto}} {{cryptoCurrency}}</strong>
|
<strong>{{receivedTotalCrypto}} {{paymentLabel}}</strong>
|
||||||
</p>
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showExpectedTotal}}
|
{{#if showExpectedTotal}}
|
||||||
<p class='sf-payment-detail'>
|
<p class='sf-payment-detail'>
|
||||||
Expected total:
|
Expected total:
|
||||||
<strong>{{expectedTotalCrypto}} {{cryptoCurrency}}</strong>
|
<strong>{{expectedTotalCrypto}} {{paymentLabel}}</strong>
|
||||||
</p>
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showInstruction}}
|
{{#if showInstruction}}
|
||||||
<p class='sf-payment-detail'>
|
<p class='sf-payment-detail'>
|
||||||
{{instructionPrefix}}
|
{{instructionPrefix}}
|
||||||
<strong>{{instructionAmountCrypto}} {{cryptoCurrency}}</strong>
|
<strong>{{instructionAmountCrypto}} {{paymentLabel}}</strong>
|
||||||
{{instructionSuffix}}
|
{{instructionSuffix}}
|
||||||
{{#if showExpiry}}
|
{{#if showExpiry}}
|
||||||
Payment expires in
|
Payment expires in
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
{{#if showQr}}
|
{{#if showQr}}
|
||||||
<div class='sf-qr-wrap'>
|
<div class='sf-qr-wrap'>
|
||||||
<img src='{{qrCodeUrl}}' width='220' height='220' alt='{{cryptoCurrency}} payment QR code' />
|
<img src='{{qrCodeUrl}}' width='220' height='220' alt='{{paymentLabel}} payment QR code' />
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<ul class='sf-list-reset sf-tx-list'>
|
<ul class='sf-list-reset sf-tx-list'>
|
||||||
{{#each payments}}
|
{{#each payments}}
|
||||||
<li class='sf-tx-item'>
|
<li class='sf-tx-item'>
|
||||||
<div><strong>{{amountCrypto}} {{../cryptoCurrency}}</strong></div>
|
<div><strong>{{amountCrypto}} {{../paymentLabel}}</strong></div>
|
||||||
<div class='sf-mono sf-text-subtle'>{{txHash}}</div>
|
<div class='sf-mono sf-text-subtle'>{{txHash}}</div>
|
||||||
<div class='sf-tx-status sf-tx-status--{{confirmationStatusVariant}}'>{{confirmationStatus}}</div>
|
<div class='sf-tx-status sf-tx-status--{{confirmationStatusVariant}}'>{{confirmationStatus}}</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -32,7 +32,16 @@
|
|||||||
{{#if cryptoRates.length}}
|
{{#if cryptoRates.length}}
|
||||||
<div class='sf-rates'>
|
<div class='sf-rates'>
|
||||||
{{#each cryptoRates}}
|
{{#each cryptoRates}}
|
||||||
<div class='sf-rate'>1 {{cryptoCurrency}} = {{fiatPerCrypto}} {{../shopFiatCurrency}}</div>
|
<span class='sf-rate'>
|
||||||
|
{{> storefront-image
|
||||||
|
class='sf-crypto-icon'
|
||||||
|
src=iconUrl
|
||||||
|
alt=paymentLabel
|
||||||
|
width='20'
|
||||||
|
height='20'
|
||||||
|
}}
|
||||||
|
<span>{{fiatPerCrypto}} {{../shopFiatCurrency}}</span>
|
||||||
|
</span>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ describe('StorefrontOrderViewService', () => {
|
|||||||
expect(view.totals.shippingCostFiat).toBe(5);
|
expect(view.totals.shippingCostFiat).toBe(5);
|
||||||
expect(view.totals.grandTotalFiat).toBe(15);
|
expect(view.totals.grandTotalFiat).toBe(15);
|
||||||
expect(view.shippingInvoice).toMatchObject({
|
expect(view.shippingInvoice).toMatchObject({
|
||||||
cryptoCurrency: 'XMR',
|
paymentLabel: 'XMR',
|
||||||
paymentAddress: '4shipping'
|
paymentAddress: '4shipping'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ describe('toStorefrontInvoiceView', () => {
|
|||||||
const view = await toStorefrontInvoiceView(invoice);
|
const view = await toStorefrontInvoiceView(invoice);
|
||||||
|
|
||||||
expect(view).toMatchObject({
|
expect(view).toMatchObject({
|
||||||
cryptoCurrency: 'XMR',
|
paymentLabel: 'XMR',
|
||||||
expectedTotalCrypto: '1.00000000',
|
expectedTotalCrypto: '1.00000000',
|
||||||
receivedTotalCrypto: null,
|
receivedTotalCrypto: null,
|
||||||
paymentAddress,
|
paymentAddress,
|
||||||
@@ -154,7 +154,7 @@ describe('toStorefrontInvoiceView', () => {
|
|||||||
const view = await toStorefrontInvoiceView(invoice);
|
const view = await toStorefrontInvoiceView(invoice);
|
||||||
|
|
||||||
expect(view).toMatchObject({
|
expect(view).toMatchObject({
|
||||||
cryptoCurrency: 'BTC',
|
paymentLabel: 'BTC',
|
||||||
expectedTotalCrypto: '1.00000000',
|
expectedTotalCrypto: '1.00000000',
|
||||||
paymentAddress: 'bc1qstorefronttest'
|
paymentAddress: 'bc1qstorefronttest'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ const buildStorefrontInvoiceView = async ({
|
|||||||
const statusVariant = resolveInvoiceStatusVariant(invoiceState);
|
const statusVariant = resolveInvoiceStatusVariant(invoiceState);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cryptoCurrency: paymentLabel,
|
paymentLabel,
|
||||||
expectedTotalCrypto,
|
expectedTotalCrypto,
|
||||||
receivedTotalCrypto,
|
receivedTotalCrypto,
|
||||||
paymentAddress: invoice.paymentAddress,
|
paymentAddress: invoice.paymentAddress,
|
||||||
|
|||||||
Reference in New Issue
Block a user