Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions apps/frontend/src/components/ui/dashboard/WithdrawFeeBreakdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
<div class="flex items-center justify-between">
<span class="text-primary">{{ formatMessage(messages.feeBreakdownGiftCardValue) }}</span>
<span class="font-semibold text-contrast"
>{{ formatMoney(amount || 0) }} ({{ formattedLocalCurrency }})</span
>{{ formatMoney(amountInUsd) }} ({{ formattedLocalCurrencyAmount }})</span
>
</div>
</template>
<template v-else>
<div class="flex items-center justify-between">
<span class="text-primary">{{ formatMessage(messages.feeBreakdownAmount) }}</span>
<span class="font-semibold text-contrast">{{ formatMoney(amount || 0) }}</span>
<span class="font-semibold text-contrast">{{ formatMoney(amountInUsd) }}</span>
</div>
</template>

Expand All @@ -29,7 +29,7 @@
<template v-if="feeLoading">
<LoaderCircleIcon class="size-5 animate-spin !text-secondary" />
</template>
<template v-else>-{{ formatMoney(fee || 0) }}</template>
<template v-else>-{{ formatMoney(feeInUsd) }}</template>
</span>
</div>

Expand Down Expand Up @@ -79,9 +79,23 @@ const props = withDefaults(

const { formatMessage } = useVIntl()

const amountInUsd = computed(() => {
if (props.isGiftCard && shouldShowExchangeRate.value) {
return (props.amount || 0) / (props.exchangeRate || 1)
}
return props.amount || 0
})

const feeInUsd = computed(() => {
if (props.isGiftCard && shouldShowExchangeRate.value) {
return (props.fee || 0) / (props.exchangeRate || 1)
}
return props.fee || 0
})

const netAmount = computed(() => {
const amount = props.amount || 0
const fee = props.fee || 0
const amount = amountInUsd.value
const fee = feeInUsd.value
return Math.max(0, amount - fee)
})

Expand All @@ -96,6 +110,11 @@ const netAmountInLocalCurrency = computed(() => {
return netAmount.value * (props.exchangeRate || 0)
})

const localCurrencyAmount = computed(() => {
if (!shouldShowExchangeRate.value) return null
return props.amount || 0
})

const formattedLocalCurrency = computed(() => {
if (!shouldShowExchangeRate.value || !netAmountInLocalCurrency.value || !props.localCurrency)
return ''
Expand All @@ -112,6 +131,21 @@ const formattedLocalCurrency = computed(() => {
}
})

const formattedLocalCurrencyAmount = computed(() => {
if (!shouldShowExchangeRate.value || !localCurrencyAmount.value || !props.localCurrency) return ''

try {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: props.localCurrency,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(localCurrencyAmount.value)
} catch {
return `${props.localCurrency} ${localCurrencyAmount.value.toFixed(2)}`
}
})

const messages = defineMessages({
feeBreakdownAmount: {
id: 'dashboard.creator-withdraw-modal.fee-breakdown-amount',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,14 @@
</Combobox>
</div>
<span v-if="selectedMethodDetails" class="text-secondary">
{{ formatMoney(fixedDenominationMin ?? effectiveMinAmount)
{{
formatMoney(
selectedMethodCurrencyCode &&
selectedMethodCurrencyCode !== 'USD' &&
selectedMethodExchangeRate
? (fixedDenominationMin ?? effectiveMinAmount) / selectedMethodExchangeRate
: (fixedDenominationMin ?? effectiveMinAmount),
)
}}<template v-if="selectedMethodCurrencyCode && selectedMethodCurrencyCode !== 'USD'">
({{
formatAmountForDisplay(
Expand All @@ -103,9 +110,15 @@
min,
{{
formatMoney(
fixedDenominationMax ??
selectedMethodDetails.interval?.standard?.max ??
effectiveMaxAmount,
selectedMethodCurrencyCode &&
selectedMethodCurrencyCode !== 'USD' &&
selectedMethodExchangeRate
? (fixedDenominationMax ??
selectedMethodDetails.interval?.standard?.max ??
effectiveMaxAmount) / selectedMethodExchangeRate
: (fixedDenominationMax ??
selectedMethodDetails.interval?.standard?.max ??
effectiveMaxAmount),
)
}}<template v-if="selectedMethodCurrencyCode && selectedMethodCurrencyCode !== 'USD'">
({{
Expand All @@ -124,7 +137,15 @@
v-if="selectedMethodDetails && effectiveMinAmount > roundedMaxAmount"
class="text-sm text-red"
>
You need at least {{ formatMoney(effectiveMinAmount)
You need at least
{{
formatMoney(
selectedMethodCurrencyCode &&
selectedMethodCurrencyCode !== 'USD' &&
selectedMethodExchangeRate
? effectiveMinAmount / selectedMethodExchangeRate
: effectiveMinAmount,
)
}}<template v-if="selectedMethodCurrencyCode && selectedMethodCurrencyCode !== 'USD'">
({{
formatAmountForDisplay(
Expand Down Expand Up @@ -186,7 +207,7 @@
formatMessage(messages.balanceWorthHint, {
usdBalance: formatMoney(roundedMaxAmount),
localBalance: formatAmountForDisplay(
roundedMaxAmount,
roundedMaxAmount * selectedMethodExchangeRate,
selectedMethodCurrencyCode,
selectedMethodExchangeRate,
),
Expand Down Expand Up @@ -252,7 +273,7 @@
formatMessage(messages.balanceWorthHint, {
usdBalance: formatMoney(roundedMaxAmount),
localBalance: formatAmountForDisplay(
roundedMaxAmount,
roundedMaxAmount * selectedMethodExchangeRate,
selectedMethodCurrencyCode,
selectedMethodExchangeRate,
),
Expand Down Expand Up @@ -573,14 +594,13 @@ const giftCardExchangeRate = computed(() => {
})

function formatAmountForDisplay(
usdAmount: number,
localAmount: number,
currencyCode: string | null | undefined,
rate: number | null | undefined,
): string {
if (!currencyCode || currencyCode === 'USD' || !rate) {
return formatMoney(usdAmount)
return formatMoney(localAmount)
}
const localAmount = usdAmount * rate
try {
return new Intl.NumberFormat('en-US', {
style: 'currency',
Expand Down
19 changes: 12 additions & 7 deletions apps/frontend/src/providers/creator-withdraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ export interface PayoutMethod {
category?: string
image_url: string | null
image_logo_url: string | null
fee: {
percentage: number
min: number
max: number | null
}
interval: {
standard: {
min: number
Expand Down Expand Up @@ -130,6 +125,7 @@ export interface TaxData {
export interface CalculationData {
amount: number
fee: number | null
netUsd: number | null
exchangeRate: number | null
}

Expand Down Expand Up @@ -400,6 +396,7 @@ export function createWithdrawContext(
calculation: {
amount: 0,
fee: null,
netUsd: null,
exchangeRate: null,
},
providerData: {
Expand Down Expand Up @@ -841,14 +838,20 @@ export function createWithdrawContext(
apiVersion: 3,
method: 'POST',
body: payload,
})) as { fee: number | string | null; exchange_rate: number | string | null }
})) as {
net_usd: number | string | null
fee: number | string | null
exchange_rate: number | string | null
}

const parsedFee = response.fee ? Number.parseFloat(String(response.fee)) : 0
const parsedNetUsd = response.net_usd ? Number.parseFloat(String(response.net_usd)) : null
const parsedExchangeRate = response.exchange_rate
? Number.parseFloat(String(response.exchange_rate))
: null

withdrawData.value.calculation.fee = parsedFee
withdrawData.value.calculation.netUsd = parsedNetUsd
withdrawData.value.calculation.exchangeRate = parsedExchangeRate

return {
Expand All @@ -872,7 +875,9 @@ export function createWithdrawContext(
created: new Date(),
amount: withdrawData.value.calculation.amount,
fee: withdrawData.value.calculation.fee || 0,
netAmount: withdrawData.value.calculation.amount - (withdrawData.value.calculation.fee || 0),
netAmount:
withdrawData.value.calculation.netUsd ??
withdrawData.value.calculation.amount - (withdrawData.value.calculation.fee || 0),
methodType: getMethodDisplayName(withdrawData.value.selection.method),
recipientDisplay: getRecipientDisplay(withdrawData.value),
}
Expand Down

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

This file was deleted.

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

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

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

11 changes: 9 additions & 2 deletions apps/labrinth/src/models/v3/payouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ impl Payout {
}
}

#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
pub struct Withdrawal {
pub amount: Decimal,
#[serde(flatten)]
pub method: PayoutMethodRequest,
pub method_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[serde(tag = "method", rename_all = "lowercase")]
#[expect(
Expand Down Expand Up @@ -238,14 +246,13 @@ pub struct PayoutMethod {
pub image_url: Option<String>,
pub image_logo_url: Option<String>,
pub interval: PayoutInterval,
pub fee: PayoutMethodFee,
pub currency_code: Option<String>,
/// USD to the given `currency_code`.
#[serde(with = "rust_decimal::serde::float_option")]
pub exchange_rate: Option<Decimal>,
}

#[derive(Serialize, Deserialize, Clone)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct PayoutMethodFee {
#[serde(with = "rust_decimal::serde::float")]
pub percentage: Decimal,
Expand Down
Loading