Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
29810cd
Draft implementation of the transaction breakdown
dmvrtx Jan 29, 2025
7544293
Updates to keep consistency between displayed currencies
dmvrtx Jan 31, 2025
6c13873
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Jan 31, 2025
1a8e8e4
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 3, 2025
64c7030
Implement suggestions from the review and from the AI analysis
dmvrtx Feb 3, 2025
051cac7
Return `null` if capture event is not present
dmvrtx Feb 3, 2025
781d908
Add a clarification about capture event
dmvrtx Feb 3, 2025
f47257e
Component tests and related fixes
dmvrtx Feb 3, 2025
4498a50
Changelog entry
dmvrtx Feb 3, 2025
08540f8
Clarify type definitions with comments
dmvrtx Feb 4, 2025
7f9135c
Update imports in JavaScript tests
dmvrtx Feb 4, 2025
350c152
Update comments
dmvrtx Feb 4, 2025
56840fc
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 4, 2025
8bb43ef
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 5, 2025
7a1920d
Updated test snapshot
dmvrtx Feb 5, 2025
dbbc447
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 7, 2025
11df746
Merge remote-tracking branch 'origin/develop' into add/1342-transacti…
dmvrtx Feb 7, 2025
d1bd94d
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 10, 2025
06bfc90
Apply suggestions from code review
dmvrtx Feb 11, 2025
5092a11
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 11, 2025
23a859d
Restore transactionAmounts assignment to allow for undefined check ea…
dmvrtx Feb 11, 2025
bcd8374
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 12, 2025
bab1822
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 13, 2025
433e235
Extract FeesBreakdown component and include a handling of the discoun…
dmvrtx Feb 14, 2025
b1d1841
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 14, 2025
30695d1
Improve percentage rates display
dmvrtx Feb 14, 2025
e8adf91
Properly calculate discount on fees, spreading across fees when needed
dmvrtx Feb 14, 2025
a9c32f0
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 14, 2025
587103a
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 24, 2025
32a283e
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 25, 2025
4af77f6
Remove superfluous dependency on `jest`
dmvrtx Feb 25, 2025
9954c95
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 26, 2025
c32819d
Remove unused import
dmvrtx Feb 26, 2025
d917aee
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 26, 2025
1f659c2
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Feb 28, 2025
e1dd3fd
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 5, 2025
dce049a
Update how the fully discounted fee is displayed
dmvrtx Mar 10, 2025
7cb4a63
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 10, 2025
37cc78e
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 13, 2025
e3f5589
Update client/payment-details/transaction-breakdown/fees-breakdown/in…
dmvrtx Mar 13, 2025
46c9962
Clarify discounted fee application
dmvrtx Mar 13, 2025
ebc5f09
Linting fix
dmvrtx Mar 13, 2025
b49118e
Use proper conversion rate to display for the payment amount
dmvrtx Mar 14, 2025
51d24af
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 17, 2025
2103ccd
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 19, 2025
14fdcee
Implement mobile-specific layout
dmvrtx Mar 20, 2025
59be1c6
Fixes for tests and code
dmvrtx Mar 20, 2025
8e9ec20
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 20, 2025
4d0d4d7
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 28, 2025
4fbd38d
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Mar 31, 2025
0e59aba
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Apr 2, 2025
4700d5d
Implement latest design updates
dmvrtx Apr 2, 2025
76d16e9
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Apr 2, 2025
4b28952
Merge branch 'develop' into add/1342-transaction-breakdown-block
dmvrtx Apr 3, 2025
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
4 changes: 4 additions & 0 deletions changelog/add-1342-transaction-breakdown-block
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Transaction Fees breakdown component in the Payment details.
11 changes: 11 additions & 0 deletions client/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,14 @@ export * from './payment-intents/hooks';
export * from './authorizations/hooks';
export * from './files/hooks';
export * from './payment-activity/hooks';

import { TimelineItem } from './timeline/types';
import { ApiError } from '../types/errors';

export declare function useTimeline(
transactionId: string
): {
timeline: Array< TimelineItem >;
timelineError: ApiError | undefined;
isLoading: boolean;
};
63 changes: 63 additions & 0 deletions client/data/timeline/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export interface TimelineFeeRate {
type: string;
additional_type?: string;
fee_id: string;
percentage_rate: number;
fixed_rate: number;
currency: string;
}

export interface TimelineFeeExchangeRate {
from_currency: string;
to_currency: string;
from_amount: number;
to_amount: number;
rate: number;
}

export interface TimelineFeeRates {
percentage: number;
fixed: number;
fixed_currency: string;
history?: Array< TimelineFeeRate >;
fee_exchange_rate?: TimelineFeeExchangeRate;
}

export interface TimelineTransactionDetails {
customer_currency: string;
customer_amount: number;
customer_amount_captured: number;
customer_fee: number;
store_currency: string;
store_amount: number;
store_amount_captured: number;
store_fee: number;
}

export interface TimelineDeposit {
id: string;
arrival_date: number;
}

export interface TimelineItem {
type: string;
datetime: number;
acquirer_reference_number?: string;
acquirer_reference_number_status?: string;
amount?: number;
amount_captured?: number;
amount_refunded?: number;
currency?: string;
deposit?: TimelineDeposit;
dispute_id?: string;
evidence_due_by?: number;
failure_reason?: string;
failure_transaction_id?: string;
fee?: number;
fee_rates?: TimelineFeeRates;
loan_id?: string;
reason?: string;
transaction_details?: TimelineTransactionDetails;
transaction_id?: string;
user_id?: number;
}
5 changes: 5 additions & 0 deletions client/payment-details/payment-details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ErrorBoundary from '../../components/error-boundary';
import PaymentDetailsSummary from '../summary';
import PaymentDetailsTimeline from '../timeline';
import PaymentDetailsPaymentMethod from '../payment-method';
import PaymentTransactionBreakdown from '../transaction-breakdown';
import { ApiError } from '../../types/errors';
import { Charge } from '../../types/charges';
import { PaymentIntent } from '../../types/payment-intents';
Expand Down Expand Up @@ -74,6 +75,10 @@ const PaymentDetails: React.FC< PaymentDetailsProps > = ( {
</ErrorBoundary>
) }

<ErrorBoundary>
<PaymentTransactionBreakdown paymentIntentId={ id } />
</ErrorBoundary>

<ErrorBoundary>
<PaymentDetailsPaymentMethod
charge={ charge }
Expand Down
209 changes: 209 additions & 0 deletions client/payment-details/transaction-breakdown/fees-breakdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/** @format **/

/**
* External dependencies
*/
import React from 'react';
import { find } from 'lodash';

/** Internal dependencies */
import { formatCurrency } from 'multi-currency/interface/functions';
import { formatFeeType } from '../utils';
import { TimelineItem, TimelineFeeRate } from 'wcpay/data/timeline/types';
import { Flex, FlexItem } from '@wordpress/components';

const FeesBreakdown: React.FC< {
event: TimelineItem;
} > = ( { event } ) => {
if ( ! event.fee_rates || ! event.transaction_details ) {
return null;
}

const storeCurrency = event.transaction_details.store_currency;
const feeExchangeRate = event.fee_rates.fee_exchange_rate?.rate || 1;
const discountFee = event.fee_rates.history
? find(
event.fee_rates.history,
( fee: TimelineFeeRate ) => fee.type === 'discount'
)
: undefined;

let remainingPercentageDiscount = Math.abs(
discountFee?.percentage_rate || 0
);
let remainingFixedDiscount = Math.abs( discountFee?.fixed_rate || 0 );

const BreakdownFeeRate = ( {
percentage,
fixed,
currency,
displayFixedPart,
}: {
percentage: number;
fixed: number;
currency: string;
displayFixedPart?: boolean;
} ) => {
const formattedPercentage = percentage
? `${ Number.parseFloat( ( percentage * 100 ).toFixed( 2 ) ) }%`
: '0%';
const formattedFixed = formatCurrency( fixed, currency, storeCurrency );

return (
<>
{ formattedPercentage }
{ ( displayFixedPart || fixed > 0 ) && (
<>
{ ' + ' + formattedFixed }&nbsp;{ storeCurrency }
</>
) }
</>
);
};

const FeeRow = ( {
type,
additionalType,
percentage,
fixed,
currency,
isDiscounted,
displayFixedPart,
}: {
type: string;
additionalType?: string;
percentage: number;
fixed: number;
currency: string;
isDiscounted?: boolean;
displayFixedPart?: boolean;
} ) => {
const formattedFeeType = formatFeeType(
type,
additionalType,
isDiscounted
);
const feeType = type + ( additionalType ? `_${ additionalType }` : '' );

return (
<Flex
className={ `wcpay-transaction-breakdown__fee_info wcpay-transaction-breakdown__${ feeType }_fee_info` }
wrap={ true }
justify="space-between"
align="end"
>
<FlexItem className="wcpay-transaction-breakdown__fee_name">
{ formattedFeeType }
</FlexItem>
<FlexItem className="wcpay-transaction-breakdown__fee_rate">
<BreakdownFeeRate
percentage={ percentage }
fixed={ fixed }
currency={ currency }
displayFixedPart={ displayFixedPart }
/>
</FlexItem>
</Flex>
);
};

const fees = [];

if ( ! event.fee_rates.history ) {
fees.push(
<FeeRow
key="base"
type="base"
percentage={ event.fee_rates.percentage }
fixed={ event.fee_rates.fixed }
currency={ event.fee_rates.fixed_currency }
/>
);
} else {
event.fee_rates.history.map( ( fee: TimelineFeeRate ) => {
if ( 'discount' === fee.type ) {
/**
* Skip discount fees, because we will subtract discount fees from the other fees in the fee breadown.
*/
return null;
}

let percentage = fee.percentage_rate;
let fixed = fee.fixed_rate;
let isDiscounted = false;
/**
* If fee happens to be fully discounted, but had the fixed part
* before discount, we will display the fixed part in the fee
* breakdown.
*/
const displayFixedPart = fee.fixed_rate > 0;

/**
* For each fee we keep subtracting discount's percentage and
* fixed parts until the remaining dicount parts become 0.
*
* We do this because the fee history contains fees in the
* specific order, i.e. base followed by additional fees, and we
* want to apply the discount to the fees in the correct order.
*/
if ( remainingPercentageDiscount > 0 ) {
const percentageDiscount = Math.min(
remainingPercentageDiscount,
percentage
);
percentage = percentage - percentageDiscount;
remainingPercentageDiscount =
remainingPercentageDiscount - percentageDiscount;
isDiscounted = true;
}

if ( remainingFixedDiscount > 0 ) {
const fixedDiscount = Math.min( remainingFixedDiscount, fixed );
fixed = fixed - fixedDiscount;
remainingFixedDiscount = remainingFixedDiscount - fixedDiscount;
isDiscounted = true;
}

const feeType =
fee.type +
( fee.additional_type ? `_${ fee.additional_type }` : '' );

fees.push(
<FeeRow
key={ feeType }
type={ fee.type }
additionalType={ fee.additional_type }
percentage={ percentage }
fixed={ fixed }
currency={ fee.currency }
isDiscounted={ isDiscounted }
displayFixedPart={ displayFixedPart }
/>
);
return null;
} );
}

fees.push(
<FeeRow
key="total"
type="total"
percentage={ event.fee_rates.percentage }
fixed={ event.fee_rates.fixed / feeExchangeRate }
currency={ storeCurrency }
displayFixedPart={ true }
/>
);

return (
<div
className="wcpay-transaction-breakdown__fees-container"
role="table"
aria-label="Transaction fees breakdown"
>
{ fees }
</div>
);
};

export default FeesBreakdown;
Loading
Loading