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

WIP - Show failure messages for failed payouts. #10462

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
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
4 changes: 4 additions & 0 deletions changelog/add-8203-payout-failure-notice
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Show failure reason in details page for failed payout. This will help merchants get better support, or understand the next steps needed to fix the failing payouts.
17 changes: 16 additions & 1 deletion client/deposits/details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ import { CopyButton } from 'components/copy-button';
import Page from 'components/page';
import ErrorBoundary from 'components/error-boundary';
import { TestModeNotice } from 'components/test-mode-notice';
import BannerNotice from 'components/banner-notice';
import InlineNotice from 'components/inline-notice';
import {
formatCurrency,
formatExplicitCurrency,
} from 'multi-currency/interface/functions';
import { depositStatusLabels } from '../strings';
import { depositStatusLabels, payoutFailureMessages } from '../strings';
import './style.scss';
import { formatDateTimeFromString } from 'wcpay/utils/date-time';

Expand Down Expand Up @@ -227,6 +228,20 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( {
] }
</SummaryList>
) }
{ deposit.status === 'failed' && (
<BannerNotice
status="error"
isDismissible={ false }
key="payout-failure-notice"
>
<strong>
{ __( 'Failure reason: ', 'woocommerce-payments' ) }
</strong>
{ payoutFailureMessages[ deposit.failure_code ] ||
deposit.failure_message ||
__( 'Unknown', 'woocommerce-payments' ) }
</BannerNotice>
) }
<Card>
<CardHeader>
<Text size={ 16 } weight={ 600 }>
Expand Down
105 changes: 104 additions & 1 deletion client/deposits/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/

import type { DepositStatus } from 'wcpay/types/deposits';
import type { DepositStatus, PayoutFailureCode } from 'wcpay/types/deposits';

export const displayType = {
deposit: __( 'Payout', 'woocommerce-payments' ),
Expand All @@ -32,3 +32,106 @@ export const depositStatusLabels: Record<
canceled: __( 'Canceled', 'woocommerce-payments' ),
failed: __( 'Failed', 'woocommerce-payments' ),
};

/**
* Mapping of payout failure code to display string.
*/
export const payoutFailureMessages: Record< PayoutFailureCode, string > = {
insufficient_funds: __(
'Your account has insufficient funds to cover the transfer.',
'woocommerce-payments'
),
bank_account_restricted: __(
'The bank account has restrictions on either the type or number of transfers allowed. This normally indicates that the bank account is a savings or other non-checking account.',
'woocommerce-payments'
),
debit_not_authorized: __(
'Debit transactions are not approved on the destination bank account. Bank accounts need to be set up for both credit and debit transfers.',
'woocommerce-payments'
),
invalid_card: __(
'The card used was invalid. This usually means the card number is invalid or the account has been closed. Please verify card details before retrying.',
'woocommerce-payments'
),
declined: __(
'The bank has declined this transfer. Please contact the bank before retrying.',
'woocommerce-payments'
),
invalid_transaction: __(
'The transfer was refused by the issuing bank because this type of payment is not permitted for this card. Please contact the issuing bank for clarification.',
'woocommerce-payments'
),
refer_to_card_issuer: __(
'The transfer was refused by the card issuer. Please contact the issuing bank for clarification before trying again.',
'woocommerce-payments'
),
unsupported_card: __(
'The bank no longer supports transfers to this card.',
'woocommerce-payments'
),
lost_or_stolen_card: __(
'The card used has been reported lost or stolen. Please contact the issuing bank for clarification.',
'woocommerce-payments'
),
invalid_issuer: __(
'The issuer specified by the card number does not exist. Please verify card details before retrying.',
'woocommerce-payments'
),
expired_card: __(
'The card used has expired. Please switch to a different card or payment method. Contact the issuing bank for clarification before trying again.',
'woocommerce-payments'
),
could_not_process: __(
// The same failure code is used if processing is failed by the bank or Stripe.
'The bank or the payment processor could not process this transfer.',
'woocommerce-payments'
),
invalid_account_number: __(
'The bank account details on file are probably incorrect. The routing number seems correct, but the account number is invalid.',
'woocommerce-payments'
),
incorrect_account_holder_name: __(
'The bank account details on file are probably incorrect.',
'woocommerce-payments'
),
account_closed: __(
'The bank account has been closed.',
'woocommerce-payments'
),
no_account: __(
'The bank account details on file are probably incorrect. No bank account could be located with those details.',
'woocommerce-payments'
),
exceeds_amount_limit: __(
'The card issuer has declined the transaction as it will exceed the card limit. Please switch to a different card or payment method. Contact the issuing bank for clarification.',
'woocommerce-payments'
),
account_frozen: __(
'The bank account has been frozen.',
'woocommerce-payments'
),
issuer_unavailable: __(
'The issuing bank is currently unavailable. Please wait a bit before trying again, or switch to a different payment method or card.',
'woocommerce-payments'
),
invalid_currency: __(
'The bank was unable to process this transfer because of its currency. This is probably because the bank account cannot accept payments in that currency.',
'woocommerce-payments'
),
incorrect_account_type: __(
'The bank account type is incorrect. This value can only be checking or savings in most countries. In Japan, it can only be futsu or toza.',
'woocommerce-payments'
),
incorrect_account_holder_details: __(
'The bank could not process this transfer. Please check that the entered bank account details match the corresponding account bank statement exactly.',
'woocommerce-payments'
),
bank_ownership_changed: __(
'The destination bank account is no longer valid because its branch has changed ownership.',
'woocommerce-payments'
),
exceeds_count_limit: __(
'The selected card has exceeded its card usage frequency limit. Please switch to a different card or payment method. Contact the issuing bank for clarification.',
'woocommerce-payments'
),
};
28 changes: 28 additions & 0 deletions client/types/deposits.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export interface CachedDeposit {
bankAccount: string;
automatic: boolean;
bank_reference_key: string;
failure_code: PayoutFailureCode;
failure_message: string;
}

export interface DepositsSummaryCache {
Expand All @@ -52,3 +54,29 @@ export type DepositStatus =
| 'in_transit'
| 'canceled'
| 'failed';

export type PayoutFailureCode =
| 'insufficient_funds'
| 'bank_account_restricted'
| 'debit_not_authorized'
| 'invalid_card'
| 'declined'
| 'invalid_transaction'
| 'refer_to_card_issuer'
| 'unsupported_card'
| 'lost_or_stolen_card'
| 'invalid_issuer'
| 'expired_card'
| 'could_not_process'
| 'invalid_account_number'
| 'incorrect_account_holder_name'
| 'account_closed'
| 'no_account'
| 'exceeds_amount_limit'
| 'account_frozen'
| 'issuer_unavailable'
| 'invalid_currency'
| 'incorrect_account_type'
| 'incorrect_account_holder_details'
| 'bank_ownership_changed'
| 'exceeds_count_limit';
Loading