Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added currency code to fee breakdown when currency symbol is same #10138

Merged
merged 9 commits into from
Jan 17, 2025
4 changes: 4 additions & 0 deletions changelog/fix-10006-add-currency-to-fee-details-in-order-note
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

Add currency code to fee breakdown when multi-currency is enabled, and currencies share the same symbol.
21 changes: 18 additions & 3 deletions client/payment-details/timeline/map-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { getAdminUrl } from 'wcpay/utils';
import { ShieldIcon } from 'wcpay/icons';
import { fraudOutcomeRulesetMapping, paymentFailureMapping } from './mappings';
import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time';
import { hasSameSymbol } from 'multi-currency/utils/currency';

/**
* Creates a timeline item about a payment status change
Expand Down Expand Up @@ -279,12 +280,19 @@ export const composeFeeString = ( event ) => {
);
}

const hasIdenticalSymbol = hasSameSymbol(
event.transaction_details.store_currency,
event.transaction_details.customer_currency
);

return sprintf(
'%1$s (%2$f%% + %3$s): %4$s',
'%1$s (%2$f%% + %3$s%4$s): %5$s%6$s',
baseFeeLabel,
formatFee( percentage ),
formatCurrency( fixed, fixedCurrency ),
formatCurrency( -feeAmount, feeCurrency )
hasIdenticalSymbol ? ` ${ fixedCurrency }` : '',
formatCurrency( -feeAmount, feeCurrency ),
hasIdenticalSymbol ? ` ${ feeCurrency }` : ''
);
};

Expand Down Expand Up @@ -453,7 +461,14 @@ export const feeBreakdown = ( event ) => {
} = fee;

const percentageRateFormatted = formatFee( percentageRate );
const fixedRateFormatted = formatCurrency( fixedRate, currency );
const fixedRateFormatted = `${ formatCurrency( fixedRate, currency ) }${
hasSameSymbol(
event.transaction_details.store_currency,
event.transaction_details.customer_currency
)
? ` ${ currency.toUpperCase() }`
: ''
}`;

const label = sprintf(
feeLabelMapping( fixedRate, isCapped )[ labelType ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ describe( 'Strings in captured events', () => {
decimalSeparator: '.',
precision: 2,
},
CA: {
code: 'CAD',
symbol: '$',
symbolPosition: 'left',
thousandSeparator: ',',
decimalSeparator: '.',
precision: 2,
},
JP: {
code: 'JPY',
symbol: '¥',
Expand Down
23 changes: 21 additions & 2 deletions includes/class-wc-payments-captured-event-note.php
mgascam marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,16 @@ public function compose_fee_string(): string {
WC_Payments_Utils::format_currency( - $fee_amount, $fee_currency )
);
}
$is_same_symbol = $this->has_same_currency_symbol( $data['transaction_details']['store_currency'], $data['transaction_details']['customer_currency'] );

return sprintf(
'%1$s (%2$s%% + %3$s): %4$s',
'%1$s (%2$s%% + %3$s%4$s): %5$s%6$s',
$base_fee_label,
self::format_fee( $percentage ),
WC_Payments_Utils::format_currency( $fixed, $fixed_currency ),
WC_Payments_Utils::format_currency( - $fee_amount, $fee_currency )
$is_same_symbol ? ' ' . $data['transaction_details']['customer_currency'] : '',
WC_Payments_Utils::format_currency( -$fee_amount, $fee_currency ),
$is_same_symbol ? " $fee_currency" : ''
);
}

Expand Down Expand Up @@ -238,6 +241,10 @@ public function get_fee_breakdown() {
$currency
);

if ( $this->has_same_currency_symbol( $data['transaction_details']['customer_currency'], $data['transaction_details']['store_currency'] ) ) {
$fix_rate_formatted = $fix_rate_formatted . ' ' . $data['transaction_details']['store_currency'];
}

$label = sprintf(
$this->fee_label_mapping( $fixed_rate, $is_capped )[ $label_type ],
$percentage_rate_formatted,
Expand Down Expand Up @@ -442,4 +449,16 @@ private function format_explicit_currency_with_base( float $amount, string $curr

return WC_Payments_Utils::format_explicit_currency( $amount, $currency, $skip_symbol, $custom_format );
}

/**
* Compare does two currencies have the same symbol.
*
* @param string $base_currency Base currency.
* @param string $currency Currency to compare.
*
* @return bool
*/
private function has_same_currency_symbol( string $base_currency, string $currency ): bool {
return strcasecmp( $base_currency, $currency ) !== 0 && get_woocommerce_currency_symbol( $base_currency ) === get_woocommerce_currency_symbol( $currency );
}
}
26 changes: 26 additions & 0 deletions includes/multi-currency/client/utils/currency/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,29 @@ function htmlDecode( input ) {
const doc = new DOMParser().parseFromString( input, 'text/html' );
return doc.documentElement.textContent;
}

/**
* Checks if two currencies share the same symbol. Don't use this function to compare the same currency.
*
* @param {string} currencyCode1 First currency code
* @param {string} currencyCode2 Second currency code
*
* @return {boolean} True if currencies share same symbol, false otherwise
*/
export const hasSameSymbol = ( currencyCode1, currencyCode2 ) => {
currencyCode1 = currencyCode1.toUpperCase();
currencyCode2 = currencyCode2.toUpperCase();
if ( currencyCode1 === currencyCode2 ) {
return false;
}
const { currencyData } = wcpaySettings;

const currency1 = find( currencyData, { code: currencyCode1 } );
const currency2 = find( currencyData, { code: currencyCode2 } );

if ( ! currency1 || ! currency2 ) {
return false;
}

return currency1.symbol === currency2.symbol;
};
99 changes: 47 additions & 52 deletions tests/fixtures/captured-payments/foreign-card.json
Original file line number Diff line number Diff line change
@@ -1,60 +1,55 @@
{
"title": "Payment with a foreign card",
"capturedEvent": {
"type": "captured",
"amount": 13000,
"amount_captured": 13000,
"fee": 676,
"fee_rates": {
"percentage": 0.049,
"fixed": 30,
"fixed_currency": "USD",
"history": [
{
"type": "base",
"percentage_rate": 0.029,
"fixed_rate": 30,
"currency": "usd"
},
{
"type": "additional",
"additional_type": "international",
"percentage_rate": 0.01,
"fixed_rate": 0,
"currency": "usd"
},
{
"type": "additional",
"additional_type": "fx",
"percentage_rate": 0.01,
"fixed_rate": 0,
"currency": "usd"
}
]
"title": "Payment with foreign card",
"capturedEvent":
{
"type": "captured",
"amount": 1800,
"amount_captured": 1800,
"fee": 109,
"fee_rates": {
"percentage": 0.044,
"fixed": 30,
"fixed_currency": "USD",
"history": [
{
"type": "base",
"additional_type": "",
"fee_id": "base-us-card-fee",
"percentage_rate": 0.029,
"fixed_rate": 30,
"currency": "usd"
},
{
"type": "additional",
"additional_type": "international",
"fee_id": "additional-us-intl-fee",
"percentage_rate": 0.015,
"fixed_rate": 0,
"currency": "usd"
}
]
},
"currency": "USD",
"datetime": 1736869781,
"deposit": null,
"transaction_id": "txn_3QhCN7R3NO9FvHYd0wKleSrw",
"transaction_details": {
"customer_currency": "USD",
"customer_amount": 1800,
"customer_amount_captured": 1800,
"customer_fee": 109,
"store_currency": "USD",
"store_amount": 1800,
"store_amount_captured": 1800,
"store_fee": 109
}
},
"currency": "CAD",
"datetime": 1651997332,
"deposit": null,
"transaction_id": "txn_3Kx5Ae2EFxam75ai0P2BCbp0",
"transaction_details": {
"customer_currency": "CAD",
"customer_amount": 13000,
"customer_amount_captured": 13000,
"customer_fee": 676,
"store_currency": "USD",
"store_amount": 10071,
"store_amount_captured": 10071,
"store_fee": 524
}
},
"expectation": {
"fxString": "1.00 CAD → 0.774692 USD: $100.71 USD",
"feeString": "Fee (4.9% + $0.30): -$5.24",
"feeString": "Fee (4.4% + $0.30): -$1.09",
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
"additional-international": "International card fee: 1%",
"additional-fx": "Currency conversion fee: 1%"
"additional-international": "International card fee: 1.5%"
},
"netString": "Net payout: $95.47 USD"
"netString": "Net payout: $16.91 USD"
}
}
56 changes: 56 additions & 0 deletions tests/fixtures/captured-payments/fx-same-currency-symbol.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"title": "FX Payment with a same currency symbol but different currency",
"capturedEvent":
{
"type": "captured",
"amount": 2400,
"amount_captured": 2400,
"fee": 132,
"fee_rates": {
"percentage": 0.039,
"fixed": 39,
"fixed_currency": "CAD",
"history": [
{
"type": "base",
"additional_type": "",
"fee_id": "base-us-card-fee",
"percentage_rate": 0.029,
"fixed_rate": 30,
"currency": "usd"
},
{
"type": "additional",
"additional_type": "fx",
"fee_id": "additional-us-card-fx-fee",
"percentage_rate": 0.01,
"fixed_rate": 0,
"currency": "usd"
}
]
},
"currency": "CAD",
"datetime": 1736871090,
"deposit": null,
"transaction_id": "txn_3QhCiER3NO9FvHYd2DQY4k1S",
"transaction_details": {
"customer_currency": "CAD",
"customer_amount": 2400,
"customer_amount_captured": 2400,
"customer_fee": 132,
"store_currency": "USD",
"store_amount": 1672,
"store_amount_captured": 1672,
"store_fee": 92
}
},
"expectation": {
"fxString": "1.00 CAD → 0.696667 USD: $16.72 USD",
"feeString": "Fee (3.9% + $0.39 CAD): -$0.92 USD",
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30 USD",
"additional-fx": "Currency conversion fee: 1%"
},
"netString": "Net payout: $15.80 USD"
}
}
Loading