diff --git a/changelog/mobile-tpv-tracking-channel b/changelog/mobile-tpv-tracking-channel
new file mode 100644
index 00000000000..a7b990214df
--- /dev/null
+++ b/changelog/mobile-tpv-tracking-channel
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details.
diff --git a/client/data/transactions/hooks.ts b/client/data/transactions/hooks.ts
index d0a983e75f9..e65ba57a03f 100644
--- a/client/data/transactions/hooks.ts
+++ b/client/data/transactions/hooks.ts
@@ -32,7 +32,7 @@ export interface Transaction {
transaction_id: string;
date: string;
type: 'charge' | 'refund' | 'financing_payout' | 'financing_paydown';
- channel: 'in_person' | 'online';
+ channel: 'in_person' | 'in_person_pos' | 'online';
// A field to identify the payment's source.
// Usually last 4 digits for card payments, bank name for bank transfers...
source_identifier: string;
diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx
index 06699f07147..1e5b6bfcac9 100644
--- a/client/payment-details/summary/index.tsx
+++ b/client/payment-details/summary/index.tsx
@@ -123,7 +123,8 @@ const composePaymentSummaryItems = ( {
{ isTapToPay( metadata?.reader_model )
? getTapToPayChannel( metadata?.platform )
: getChargeChannel(
- charge.payment_method_details?.type
+ charge.payment_method_details?.type,
+ metadata
) }
),
diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx
index b2c1cb4c9a7..a8c437d6d2c 100644
--- a/client/transactions/list/index.tsx
+++ b/client/transactions/list/index.tsx
@@ -55,7 +55,7 @@ import {
formatExplicitCurrency,
formatExportAmount,
} from 'multi-currency/interface/functions';
-import { getChargeChannel } from 'utils/charge';
+import { getTransactionChannel } from 'utils/charge';
import Deposit from './deposit';
import ConvertedAmount from './converted-amount';
import autocompleter from 'transactions/autocompleter';
@@ -473,10 +473,10 @@ export const TransactionsList = (
),
},
channel: {
- value: getChargeChannel( txn.channel ),
+ value: getTransactionChannel( txn.channel ),
display: clickable(
- { getChargeChannel( txn.channel ) }
+ { getTransactionChannel( txn.channel ) }
{ txn.source_device && getSourceDeviceIcon( txn ) }
),
diff --git a/client/utils/charge/index.ts b/client/utils/charge/index.ts
index 2690279e2cb..0169a45b4e0 100755
--- a/client/utils/charge/index.ts
+++ b/client/utils/charge/index.ts
@@ -173,24 +173,48 @@ export const getChargeAmounts = ( charge: Charge ): ChargeAmounts => {
};
/**
- * Displays the transaction's channel: Online | In-Person.
+ * Displays the transaction's channel: Online | In-Person | In-Person (POS).
+ * This method is called on the list of transactions page.
*
- * This method is called in two places: The individual transaction page, and the list of transactions page.
- * In the individual transaction page, we are getting the data from Stripe, so we pass the transaction.type
- * which can be card_present or interac_present for In-Person payments.
* In the list of transactions, the type holds the brand of the payment method, so we aren't passing it.
- * Instead, we pass the transaction.channel directly, which might be in_person|online.
+ * Instead, we pass the transaction.channel directly, which might be in_person|in_person_pos|online.
*
- * @param {string} type The transaction type.
- * @return {string} Online or In-Person.
+ * @param {string} channel The transaction channel.
+ * @return {string} Online, In-Person, or In-Person (POS).
+ */
+export const getTransactionChannel = ( channel: string ): string => {
+ switch ( channel ) {
+ case 'in_person':
+ return __( 'In-Person', 'woocommerce-payments' );
+ case 'in_person_pos':
+ return __( 'In-Person (POS)', 'woocommerce-payments' );
+ default:
+ return __( 'Online', 'woocommerce-payments' );
+ }
+};
+
+/**
+ * Displays the channel based on the charge data from Stripe and metadata for a transaction: Online | In-Person | In-Person (POS).
+ * This method is called in the individual transaction page.
+ *
+ * In the individual transaction page, we are getting the data from Stripe, so we pass the charge.type
+ * which can be card_present or interac_present for In-Person payments. In addition, we pass the transaction metadata
+ * whose ipp_channel value can be mobile_store_management or mobile_pos that indicates whether the channel is from store
+ * management or POS in the mobile apps.
+ *
+ * @param {string} type The transaction charge type, which can be card_present or interac_present for In-Person payments.
+ * @param {Record} metadata The transaction metadata, which may include ipp_channel indicating the channel source.
+ * @return {string} Returns 'Online', 'In-Person', or 'In-Person (POS)' based on the transaction type and metadata.
*
*/
-export const getChargeChannel = ( type: string ): string => {
- if (
- type === 'card_present' ||
- type === 'interac_present' ||
- type === 'in_person'
- ) {
+export const getChargeChannel = (
+ type: string,
+ metadata: Record< string, any >
+): string => {
+ if ( type === 'card_present' || type === 'interac_present' ) {
+ if ( metadata?.ipp_channel === 'mobile_pos' ) {
+ return __( 'In-Person (POS)', 'woocommerce-payments' );
+ }
return __( 'In-Person', 'woocommerce-payments' );
}
diff --git a/client/utils/charge/test/index.js b/client/utils/charge/test/index.js
index 99bc3172f07..07b7f64f312 100755
--- a/client/utils/charge/test/index.js
+++ b/client/utils/charge/test/index.js
@@ -482,3 +482,59 @@ describe( 'Charge utilities / getChargeAmounts', () => {
} );
} );
} );
+
+jest.mock( '@wordpress/i18n', () => ( {
+ __: jest.fn( ( text ) => text ),
+} ) );
+
+describe( 'Charge utilities / get channel string', () => {
+ describe( 'getTransactionChannel', () => {
+ test( 'should return "In-Person (POS)" for in_person_pos channel', () => {
+ const result = utils.getTransactionChannel( 'in_person_pos' );
+ expect( result ).toBe( 'In-Person (POS)' );
+ } );
+
+ test( 'should return "In-Person" for in_person channel', () => {
+ const result = utils.getTransactionChannel( 'in_person' );
+ expect( result ).toBe( 'In-Person' );
+ } );
+
+ test( 'should return "Online" for online channel', () => {
+ const result = utils.getTransactionChannel( 'online' );
+ expect( result ).toBe( 'Online' );
+ } );
+
+ test( 'should return "Online" for null channel', () => {
+ const result = utils.getTransactionChannel( null );
+ expect( result ).toBe( 'Online' );
+ } );
+ } );
+
+ describe( 'getChargeChannel', () => {
+ test( 'should return "In-Person (POS)" for card_present type with mobile_pos metadata', () => {
+ const result = utils.getChargeChannel( 'card_present', {
+ ipp_channel: 'mobile_pos',
+ } );
+ expect( result ).toBe( 'In-Person (POS)' );
+ } );
+
+ test( 'should return "In-Person" for card_present type with mobile_store_management metadata', () => {
+ const result = utils.getChargeChannel( 'card_present', {
+ ipp_channel: 'mobile_store_management',
+ } );
+ expect( result ).toBe( 'In-Person' );
+ } );
+
+ test( 'should return "In-Person" for card_present type with null ipp_channel metadata', () => {
+ const result = utils.getChargeChannel( 'card_present', {} );
+ expect( result ).toBe( 'In-Person' );
+ } );
+
+ test( 'should return "Online" for online type', () => {
+ const result = utils.getChargeChannel( 'online', {
+ ipp_channel: 'mobile_pos',
+ } );
+ expect( result ).toBe( 'Online' );
+ } );
+ } );
+} );