Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Add evidence matrix entry for fraudulent × physical product dispute combination
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,38 @@ describe( 'Cover Letter Generator', () => {
expect( result ).toContain( 'Other documents (Attachment E)' );
} );

it( 'should order all fraudulent attachments correctly with full evidence and physical_product product type', () => {
const fraudulentDispute: ExtendedDispute = {
...mockDispute,
reason: 'fraudulent' as DisputeReason,
evidence: {
receipt: 'receipt_url',
access_activity_log: 'access_activity_log_url',
customer_communication: 'customer_communication_url',
customer_signature: 'customer_signature_url',
refund_policy: 'refund_policy_url',
shipping_documentation: 'shipping_documentation_url',
uncategorized_file: 'uncategorized_file_url',
},
};
const result = generateAttachments(
fraudulentDispute,
undefined,
'physical_product'
);
expect( result ).toContain( 'Order receipt (Attachment A)' );
expect( result ).toContain(
'Prior undisputed transaction history (Attachment B)'
);
expect( result ).toContain(
'Customer communication (Attachment C)'
);
expect( result ).toContain( "Customer's signature (Attachment D)" );
expect( result ).toContain( 'Store refund policy (Attachment E)' );
expect( result ).toContain( 'Proof of shipping (Attachment F)' );
expect( result ).toContain( 'Other documents (Attachment G)' );
} );

it( 'should include "Customer\'s signature" only for physical_product product type', () => {
const disputeWithSignature: ExtendedDispute = {
...mockDispute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* IMPLEMENTATION STATUS (from evidence-matrix.ts):
* ✅ Implemented combinations:
* - fraudulent × booking_reservation
* - fraudulent × physical_product
* - product_not_received × booking_reservation
* - product_unacceptable × booking_reservation
* - subscription_canceled × booking_reservation
Expand All @@ -22,7 +23,7 @@
* - credit_not_processed × booking_reservation (refund_has_been_issued scenario)
*
* ⏳ Not yet implemented (in backlog):
* - All combinations with physical_product
* - Remaining combinations with physical_product
* - All combinations with digital_product_or_service
* - All combinations with offline_service
* - All combinations with event
Expand Down Expand Up @@ -79,6 +80,50 @@ interface CombinationSpec {
* Based on evidence-matrix.ts and the specification document.
*/
const implementedCombinations: CombinationSpec[] = [
// ============================================
// FRAUDULENT × PHYSICAL_PRODUCT
// ============================================
{
reason: 'fraudulent',
productType: 'physical_product',
description:
'Fraudulent dispute for physical product - needs receipt, prior history, signature, refund policy',
uiFields: {
shouldInclude: [
DOCUMENT_FIELD_KEYS.RECEIPT,
DOCUMENT_FIELD_KEYS.ACCESS_ACTIVITY_LOG,
DOCUMENT_FIELD_KEYS.CUSTOMER_COMMUNICATION,
DOCUMENT_FIELD_KEYS.CUSTOMER_SIGNATURE,
DOCUMENT_FIELD_KEYS.REFUND_POLICY,
DOCUMENT_FIELD_KEYS.UNCATEGORIZED_FILE,
],
shouldExclude: [
DOCUMENT_FIELD_KEYS.SHIPPING_DOCUMENTATION, // Shown separately in shipping step
],
expectedLabels: {
[ DOCUMENT_FIELD_KEYS.RECEIPT ]: 'Order receipt',
[ DOCUMENT_FIELD_KEYS.ACCESS_ACTIVITY_LOG ]:
'Prior undisputed transaction history',
[ DOCUMENT_FIELD_KEYS.CUSTOMER_COMMUNICATION ]:
'Customer communication',
[ DOCUMENT_FIELD_KEYS.CUSTOMER_SIGNATURE ]:
"Customer's signature",
[ DOCUMENT_FIELD_KEYS.REFUND_POLICY ]: 'Refund policy',
[ DOCUMENT_FIELD_KEYS.UNCATEGORIZED_FILE ]: 'Other documents',
},
},
coverLetterAttachments: {
shouldInclude: [
'Order receipt',
'Prior undisputed transaction history',
"Customer's signature",
'Store refund policy',
'Other documents',
],
shouldExclude: [],
},
},

// ============================================
// FRAUDULENT × BOOKING/RESERVATION
// ============================================
Expand Down Expand Up @@ -555,7 +600,6 @@ describe( 'Evidence Matrix Specification Validation', () => {

describe( 'Non-implemented combinations should return undefined', () => {
const notImplemented = [
{ reason: 'fraudulent', productType: 'physical_product' },
{
reason: 'fraudulent',
productType: 'digital_product_or_service',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ describe( 'Recommended Documents', () => {
expect( result[ 4 ].key ).toBe( 'uncategorized_file' );
} );

it( 'should return default fields for fraudulent + physical_product even when feature flag is enabled', () => {
it( 'should return matrix fields for fraudulent + physical_product when feature flag is enabled', () => {
global.wcpaySettings.featureFlags.isDisputeAdditionalEvidenceTypesEnabled = true;

const result = getRecommendedDocumentFields(
Expand All @@ -312,10 +312,21 @@ describe( 'Recommended Documents', () => {
'physical_product'
);

// Should fall back to default fraudulent fields since no matrix entry exists
expect( result ).toHaveLength( 5 );
// Matrix entry for fraudulent + physical_product
expect( result ).toHaveLength( 6 );
expect( result[ 0 ].key ).toBe( 'receipt' );
expect( result[ 2 ].key ).toBe( 'customer_signature' );
expect( result[ 0 ].label ).toBe( 'Order receipt' );
expect( result[ 1 ].key ).toBe( 'access_activity_log' );
expect( result[ 1 ].label ).toBe(
'Prior undisputed transaction history'
);
expect( result[ 2 ].key ).toBe( 'customer_communication' ); // Base field auto-merged
expect( result[ 3 ].key ).toBe( 'customer_signature' );
expect( result[ 3 ].label ).toBe( "Customer's signature" );
expect( result[ 4 ].key ).toBe( 'refund_policy' );
expect( result[ 4 ].label ).toBe( 'Refund policy' );
expect( result[ 5 ].key ).toBe( 'uncategorized_file' );
expect( result[ 5 ].label ).toBe( 'Other documents' );
} );

it( 'should return default fields for fraudulent when no product type is provided', () => {
Expand Down
52 changes: 52 additions & 0 deletions client/disputes/new-evidence/evidence-matrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,58 @@ const getCreditNotProcessedMatrix = (): {
const getFraudulentMatrix = (): {
[ productType: string ]: Array< RecommendedDocument >;
} => ( {
// Physical Product product type
physical_product: [
{
key: DOCUMENT_FIELD_KEYS.RECEIPT,
label: __( 'Order receipt', 'woocommerce-payments' ),
description: __(
"A copy of the customer's receipt, which can be found in the receipt history for this transaction.",
'woocommerce-payments'
),
order: 10,
},
{
key: DOCUMENT_FIELD_KEYS.ACCESS_ACTIVITY_LOG,
label: __(
'Prior undisputed transaction history',
'woocommerce-payments'
),
description: __(
'Proof of past undisputed transactions from the same customer, with matching billing and device details.',
'woocommerce-payments'
),
order: 15,
},
{
key: DOCUMENT_FIELD_KEYS.CUSTOMER_SIGNATURE,
label: __( "Customer's signature", 'woocommerce-payments' ),
description: __(
"Any relevant documents showing the customer's signature, such as signed proof of delivery.",
'woocommerce-payments'
),
order: 25,
},
{
key: DOCUMENT_FIELD_KEYS.REFUND_POLICY,
label: __( 'Refund policy', 'woocommerce-payments' ),
description: __(
"A screenshot of your store's refund policy.",
'woocommerce-payments'
),
order: 30,
},
{
key: DOCUMENT_FIELD_KEYS.UNCATEGORIZED_FILE,
label: __( 'Other documents', 'woocommerce-payments' ),
description: __(
'Any other relevant documents that will support your case.',
'woocommerce-payments'
),
order: 100,
},
],
// Booking/Reservation product type
booking_reservation: [
{
key: DOCUMENT_FIELD_KEYS.ACCESS_ACTIVITY_LOG,
Expand Down
Loading