updated card and bank error handling messages
This commit is contained in:
336
backend/tests/unit/utils/stripeErrors.test.js
Normal file
336
backend/tests/unit/utils/stripeErrors.test.js
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
const {
|
||||||
|
parseStripeError,
|
||||||
|
PaymentError,
|
||||||
|
DECLINE_MESSAGES,
|
||||||
|
} = require('../../../utils/stripeErrors');
|
||||||
|
|
||||||
|
describe('Stripe Errors Utility', () => {
|
||||||
|
describe('DECLINE_MESSAGES', () => {
|
||||||
|
const requiredProperties = [
|
||||||
|
'ownerMessage',
|
||||||
|
'renterMessage',
|
||||||
|
'canOwnerRetry',
|
||||||
|
'requiresNewPaymentMethod',
|
||||||
|
];
|
||||||
|
|
||||||
|
test('each decline code has all required properties', () => {
|
||||||
|
for (const [code, messages] of Object.entries(DECLINE_MESSAGES)) {
|
||||||
|
for (const prop of requiredProperties) {
|
||||||
|
expect(messages).toHaveProperty(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ACH decline codes', () => {
|
||||||
|
const achCodes = [
|
||||||
|
'bank_account_closed',
|
||||||
|
'bank_account_frozen',
|
||||||
|
'bank_account_restricted',
|
||||||
|
'bank_account_unusable',
|
||||||
|
'bank_account_invalid_details',
|
||||||
|
'debit_not_authorized',
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each(achCodes)('%s exists in DECLINE_MESSAGES', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES).toHaveProperty(code);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(achCodes)('%s has all required properties', (code) => {
|
||||||
|
const messages = DECLINE_MESSAGES[code];
|
||||||
|
expect(messages).toHaveProperty('ownerMessage');
|
||||||
|
expect(messages).toHaveProperty('renterMessage');
|
||||||
|
expect(messages).toHaveProperty('canOwnerRetry');
|
||||||
|
expect(messages).toHaveProperty('requiresNewPaymentMethod');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(achCodes)('%s requires new payment method', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES[code].requiresNewPaymentMethod).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(achCodes)('%s does not allow owner retry', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES[code].canOwnerRetry).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(achCodes)('%s has non-empty messages', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES[code].ownerMessage.length).toBeGreaterThan(0);
|
||||||
|
expect(DECLINE_MESSAGES[code].renterMessage.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Additional card decline codes', () => {
|
||||||
|
const additionalCardCodes = [
|
||||||
|
'call_issuer',
|
||||||
|
'do_not_try_again',
|
||||||
|
'duplicate_transaction',
|
||||||
|
'issuer_not_available',
|
||||||
|
'restricted_card',
|
||||||
|
'withdrawal_count_limit_exceeded',
|
||||||
|
'not_permitted',
|
||||||
|
'invalid_amount',
|
||||||
|
'security_violation',
|
||||||
|
'stop_payment_order',
|
||||||
|
'transaction_not_allowed',
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each(additionalCardCodes)('%s exists in DECLINE_MESSAGES', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES).toHaveProperty(code);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(additionalCardCodes)('%s has all required properties', (code) => {
|
||||||
|
const messages = DECLINE_MESSAGES[code];
|
||||||
|
expect(messages).toHaveProperty('ownerMessage');
|
||||||
|
expect(messages).toHaveProperty('renterMessage');
|
||||||
|
expect(messages).toHaveProperty('canOwnerRetry');
|
||||||
|
expect(messages).toHaveProperty('requiresNewPaymentMethod');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(additionalCardCodes)('%s has non-empty messages', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES[code].ownerMessage.length).toBeGreaterThan(0);
|
||||||
|
expect(DECLINE_MESSAGES[code].renterMessage.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Codes that allow retry (temporary issues)
|
||||||
|
const retryableCodes = [
|
||||||
|
'call_issuer',
|
||||||
|
'duplicate_transaction',
|
||||||
|
'issuer_not_available',
|
||||||
|
'withdrawal_count_limit_exceeded',
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each(retryableCodes)('%s allows owner retry', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES[code].canOwnerRetry).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Codes that require new payment method (permanent issues)
|
||||||
|
const permanentCodes = [
|
||||||
|
'do_not_try_again',
|
||||||
|
'restricted_card',
|
||||||
|
'not_permitted',
|
||||||
|
'security_violation',
|
||||||
|
'stop_payment_order',
|
||||||
|
'transaction_not_allowed',
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each(permanentCodes)('%s requires new payment method', (code) => {
|
||||||
|
expect(DECLINE_MESSAGES[code].requiresNewPaymentMethod).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('do_not_try_again has isPermanent flag', () => {
|
||||||
|
expect(DECLINE_MESSAGES.do_not_try_again.isPermanent).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('invalid_amount does not require new payment method', () => {
|
||||||
|
// This is a platform/amount issue, not a card issue
|
||||||
|
expect(DECLINE_MESSAGES.invalid_amount.requiresNewPaymentMethod).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseStripeError', () => {
|
||||||
|
test('parses card decline error with known code', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'insufficient_funds',
|
||||||
|
message: 'Your card has insufficient funds.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('insufficient_funds');
|
||||||
|
expect(result.ownerMessage).toBe("The renter's card has insufficient funds.");
|
||||||
|
expect(result.renterMessage).toBe('Your card has insufficient funds.');
|
||||||
|
expect(result.canOwnerRetry).toBe(false);
|
||||||
|
expect(result.requiresNewPaymentMethod).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses card decline error - do_not_try_again (permanent)', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'do_not_try_again',
|
||||||
|
message: 'Do not try again.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('do_not_try_again');
|
||||||
|
expect(result.ownerMessage).toBe("The renter's card has been permanently declined.");
|
||||||
|
expect(result.requiresNewPaymentMethod).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses card decline error - call_issuer (retryable)', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'call_issuer',
|
||||||
|
message: 'Call issuer.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('call_issuer');
|
||||||
|
expect(result.canOwnerRetry).toBe(true);
|
||||||
|
expect(result.requiresNewPaymentMethod).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses card decline error - withdrawal_count_limit_exceeded', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'withdrawal_count_limit_exceeded',
|
||||||
|
message: 'Withdrawal count limit exceeded.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('withdrawal_count_limit_exceeded');
|
||||||
|
expect(result.renterMessage).toContain('daily limit');
|
||||||
|
expect(result.canOwnerRetry).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses ACH decline error - bank_account_closed', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'bank_account_closed',
|
||||||
|
message: 'The bank account has been closed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('bank_account_closed');
|
||||||
|
expect(result.ownerMessage).toBe("The renter's bank account has been closed.");
|
||||||
|
expect(result.renterMessage).toBe('Your bank account has been closed. Please add a different payment method.');
|
||||||
|
expect(result.requiresNewPaymentMethod).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses ACH decline error - bank_account_frozen', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'bank_account_frozen',
|
||||||
|
message: 'The bank account is frozen.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('bank_account_frozen');
|
||||||
|
expect(result.ownerMessage).toBe("The renter's bank account is frozen.");
|
||||||
|
expect(result.renterMessage).toContain('frozen');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses ACH decline error - debit_not_authorized', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'debit_not_authorized',
|
||||||
|
message: 'Debit not authorized.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('debit_not_authorized');
|
||||||
|
expect(result.renterMessage).toContain('not authorized');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns default error for unknown decline code', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'unknown_code_xyz',
|
||||||
|
message: 'Unknown error',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('unknown_code_xyz');
|
||||||
|
expect(result.ownerMessage).toBe('The payment could not be processed.');
|
||||||
|
expect(result.requiresNewPaymentMethod).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles StripeAPIError', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeAPIError',
|
||||||
|
message: 'API error',
|
||||||
|
code: 'api_error',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('api_error');
|
||||||
|
expect(result.canOwnerRetry).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles StripeRateLimitError', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeRateLimitError',
|
||||||
|
message: 'Rate limit',
|
||||||
|
code: 'rate_limit_error',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result.code).toBe('rate_limit');
|
||||||
|
expect(result.canOwnerRetry).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('includes original error info for logging', () => {
|
||||||
|
const error = {
|
||||||
|
type: 'StripeCardError',
|
||||||
|
code: 'card_declined',
|
||||||
|
decline_code: 'bank_account_frozen',
|
||||||
|
message: 'Original Stripe message',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseStripeError(error);
|
||||||
|
|
||||||
|
expect(result._originalMessage).toBe('Original Stripe message');
|
||||||
|
expect(result._stripeCode).toBe('card_declined');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PaymentError', () => {
|
||||||
|
test('creates PaymentError with all properties', () => {
|
||||||
|
const parsedError = {
|
||||||
|
code: 'bank_account_frozen',
|
||||||
|
ownerMessage: "The renter's bank account is frozen.",
|
||||||
|
renterMessage: 'Your bank account is frozen.',
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
_originalMessage: 'Original message',
|
||||||
|
_stripeCode: 'card_declined',
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = new PaymentError(parsedError);
|
||||||
|
|
||||||
|
expect(error).toBeInstanceOf(Error);
|
||||||
|
expect(error.name).toBe('PaymentError');
|
||||||
|
expect(error.code).toBe('bank_account_frozen');
|
||||||
|
expect(error.ownerMessage).toBe("The renter's bank account is frozen.");
|
||||||
|
expect(error.renterMessage).toBe('Your bank account is frozen.');
|
||||||
|
expect(error.requiresNewPaymentMethod).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toJSON returns serializable object', () => {
|
||||||
|
const parsedError = {
|
||||||
|
code: 'bank_account_closed',
|
||||||
|
ownerMessage: "The renter's bank account has been closed.",
|
||||||
|
renterMessage: 'Your bank account has been closed.',
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
_originalMessage: 'Should not appear',
|
||||||
|
_stripeCode: 'card_declined',
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = new PaymentError(parsedError);
|
||||||
|
const json = error.toJSON();
|
||||||
|
|
||||||
|
expect(json).toHaveProperty('code', 'bank_account_closed');
|
||||||
|
expect(json).toHaveProperty('ownerMessage');
|
||||||
|
expect(json).toHaveProperty('renterMessage');
|
||||||
|
expect(json).not.toHaveProperty('_originalMessage');
|
||||||
|
expect(json).not.toHaveProperty('_stripeCode');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
const DECLINE_MESSAGES = {
|
const DECLINE_MESSAGES = {
|
||||||
authentication_required: {
|
authentication_required: {
|
||||||
ownerMessage: "The renter's card requires additional authentication.",
|
ownerMessage: "The renter's card requires additional authentication.",
|
||||||
renterMessage: "Your card requires authentication to complete this payment.",
|
renterMessage:
|
||||||
|
"Your card requires authentication to complete this payment.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
requiresNewPaymentMethod: false,
|
requiresNewPaymentMethod: false,
|
||||||
requires3DS: true,
|
requires3DS: true,
|
||||||
@@ -20,7 +21,7 @@ const DECLINE_MESSAGES = {
|
|||||||
requiresNewPaymentMethod: false, // renter might add funds
|
requiresNewPaymentMethod: false, // renter might add funds
|
||||||
},
|
},
|
||||||
card_declined: {
|
card_declined: {
|
||||||
ownerMessage: "The renter's card was declined by their bank.",
|
ownerMessage: "The renter's card was declined.",
|
||||||
renterMessage:
|
renterMessage:
|
||||||
"Your card was declined. Please contact your bank or try a different card.",
|
"Your card was declined. Please contact your bank or try a different card.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
@@ -81,7 +82,7 @@ const DECLINE_MESSAGES = {
|
|||||||
requiresNewPaymentMethod: true,
|
requiresNewPaymentMethod: true,
|
||||||
},
|
},
|
||||||
incorrect_zip: {
|
incorrect_zip: {
|
||||||
ownerMessage: "Billing address verification failed.",
|
ownerMessage: "Renter's billing address couldn't be verified.",
|
||||||
renterMessage:
|
renterMessage:
|
||||||
"Your billing address couldn't be verified. Please update your payment method.",
|
"Your billing address couldn't be verified. Please update your payment method.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
@@ -96,7 +97,7 @@ const DECLINE_MESSAGES = {
|
|||||||
requiresNewPaymentMethod: false,
|
requiresNewPaymentMethod: false,
|
||||||
},
|
},
|
||||||
do_not_honor: {
|
do_not_honor: {
|
||||||
ownerMessage: "The renter's card was declined by their bank.",
|
ownerMessage: "The renter's card was declined.",
|
||||||
renterMessage:
|
renterMessage:
|
||||||
"Your card was declined. Please contact your bank or try a different card.",
|
"Your card was declined. Please contact your bank or try a different card.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
@@ -104,41 +105,170 @@ const DECLINE_MESSAGES = {
|
|||||||
},
|
},
|
||||||
invalid_account: {
|
invalid_account: {
|
||||||
ownerMessage: "The renter's card account is invalid.",
|
ownerMessage: "The renter's card account is invalid.",
|
||||||
renterMessage:
|
renterMessage: "Your card account is invalid. Please use a different card.",
|
||||||
"Your card account is invalid. Please use a different card.",
|
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
requiresNewPaymentMethod: true,
|
requiresNewPaymentMethod: true,
|
||||||
},
|
},
|
||||||
new_account_information_available: {
|
new_account_information_available: {
|
||||||
ownerMessage: "The renter's card information needs to be updated.",
|
ownerMessage: "The renter's card information needs to be updated.",
|
||||||
renterMessage: "Your card information needs to be updated. Please re-enter your payment details.",
|
renterMessage:
|
||||||
|
"Your card information needs to be updated. Please re-enter your payment details.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
requiresNewPaymentMethod: true,
|
requiresNewPaymentMethod: true,
|
||||||
},
|
},
|
||||||
card_not_supported: {
|
card_not_supported: {
|
||||||
ownerMessage: "The renter's card type is not supported.",
|
ownerMessage: "The renter's card type is not supported.",
|
||||||
renterMessage: "This card type is not supported. Please use a different card.",
|
renterMessage:
|
||||||
|
"This card type is not supported. Please use a different card.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
requiresNewPaymentMethod: true,
|
requiresNewPaymentMethod: true,
|
||||||
},
|
},
|
||||||
currency_not_supported: {
|
currency_not_supported: {
|
||||||
ownerMessage: "The renter's card doesn't support this currency.",
|
ownerMessage: "The renter's card doesn't support this currency.",
|
||||||
renterMessage: "Your card doesn't support USD payments. Please use a different card.",
|
renterMessage:
|
||||||
|
"Your card doesn't support USD payments. Please use a different card.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
requiresNewPaymentMethod: true,
|
requiresNewPaymentMethod: true,
|
||||||
},
|
},
|
||||||
try_again_later: {
|
try_again_later: {
|
||||||
ownerMessage: "The payment processor is temporarily unavailable. Please try again.",
|
ownerMessage:
|
||||||
renterMessage: "A temporary error occurred. The owner can try again shortly.",
|
"The payment processor is temporarily unavailable. Please try again.",
|
||||||
|
renterMessage:
|
||||||
|
"A temporary error occurred. The owner can try again shortly.",
|
||||||
canOwnerRetry: true,
|
canOwnerRetry: true,
|
||||||
requiresNewPaymentMethod: false,
|
requiresNewPaymentMethod: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Additional Card Declines
|
||||||
|
call_issuer: {
|
||||||
|
ownerMessage: "The renter needs to contact their bank.",
|
||||||
|
renterMessage:
|
||||||
|
"Please call your bank to authorize this transaction, then try again.",
|
||||||
|
canOwnerRetry: true,
|
||||||
|
requiresNewPaymentMethod: false,
|
||||||
|
},
|
||||||
|
do_not_try_again: {
|
||||||
|
ownerMessage: "The renter's card has been permanently declined.",
|
||||||
|
renterMessage:
|
||||||
|
"This card cannot be used. Please add a different payment method.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
isPermanent: true,
|
||||||
|
},
|
||||||
|
duplicate_transaction: {
|
||||||
|
ownerMessage: "This appears to be a duplicate charge attempt.",
|
||||||
|
renterMessage:
|
||||||
|
"This looks like a duplicate charge. Please wait a moment before trying again.",
|
||||||
|
canOwnerRetry: true,
|
||||||
|
requiresNewPaymentMethod: false,
|
||||||
|
},
|
||||||
|
issuer_not_available: {
|
||||||
|
ownerMessage: "The renter's bank is temporarily unavailable.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank is temporarily unavailable. Please try again in a few minutes.",
|
||||||
|
canOwnerRetry: true,
|
||||||
|
requiresNewPaymentMethod: false,
|
||||||
|
},
|
||||||
|
restricted_card: {
|
||||||
|
ownerMessage: "The renter's card has restrictions.",
|
||||||
|
renterMessage:
|
||||||
|
"This card has restrictions. Please contact your bank or use a different card.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
withdrawal_count_limit_exceeded: {
|
||||||
|
ownerMessage: "The renter has exceeded their card's daily limit.",
|
||||||
|
renterMessage:
|
||||||
|
"You've reached your card's daily limit. Please try tomorrow or use a different card.",
|
||||||
|
canOwnerRetry: true,
|
||||||
|
requiresNewPaymentMethod: false,
|
||||||
|
},
|
||||||
|
not_permitted: {
|
||||||
|
ownerMessage: "This transaction is not permitted on the renter's card.",
|
||||||
|
renterMessage:
|
||||||
|
"This transaction is not permitted on your card. Please use a different card.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
invalid_amount: {
|
||||||
|
ownerMessage: "The payment amount is invalid.",
|
||||||
|
renterMessage:
|
||||||
|
"There was an issue with the payment amount. Please contact support.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: false,
|
||||||
|
},
|
||||||
|
security_violation: {
|
||||||
|
ownerMessage: "The payment was blocked for security reasons.",
|
||||||
|
renterMessage:
|
||||||
|
"Your payment was blocked for security reasons. Please contact your bank.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
stop_payment_order: {
|
||||||
|
ownerMessage: "A stop payment order exists on this card.",
|
||||||
|
renterMessage:
|
||||||
|
"A stop payment has been placed. Please contact your bank or use a different card.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
transaction_not_allowed: {
|
||||||
|
ownerMessage: "Renter's card does not allow this transaction type.",
|
||||||
|
renterMessage:
|
||||||
|
"This transaction is not allowed on your card. Please use a different card.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ACH/Bank Account Declines
|
||||||
|
bank_account_closed: {
|
||||||
|
ownerMessage: "The renter's bank account has been closed.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank account has been closed. Please add a different payment method.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
bank_account_frozen: {
|
||||||
|
ownerMessage: "The renter's bank account is frozen.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank account is frozen. Please contact your bank or use a different payment method.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
bank_account_restricted: {
|
||||||
|
ownerMessage: "The renter's bank account has restrictions.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank account has restrictions. Please contact your bank or use a different payment method.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
bank_account_unusable: {
|
||||||
|
ownerMessage: "The renter's bank account cannot be used.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank account cannot be used for payments. Please add a different payment method.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
bank_account_invalid_details: {
|
||||||
|
ownerMessage: "The renter's bank account details are invalid.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank account details appear to be invalid. Please re-enter your bank information.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
|
debit_not_authorized: {
|
||||||
|
ownerMessage: "The renter's bank account is not authorized for debits.",
|
||||||
|
renterMessage:
|
||||||
|
"Your bank account is not authorized for automatic debits. Please enable ACH payments or use a card.",
|
||||||
|
canOwnerRetry: false,
|
||||||
|
requiresNewPaymentMethod: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default error for unknown decline codes
|
// Default error for unknown decline codes
|
||||||
const DEFAULT_ERROR = {
|
const DEFAULT_ERROR = {
|
||||||
ownerMessage: "The payment could not be processed.",
|
ownerMessage: "The payment could not be processed.",
|
||||||
renterMessage: "Your payment could not be processed. Please try a different payment method.",
|
renterMessage:
|
||||||
|
"Your payment could not be processed. Please try a different payment method.",
|
||||||
canOwnerRetry: false,
|
canOwnerRetry: false,
|
||||||
requiresNewPaymentMethod: true,
|
requiresNewPaymentMethod: true,
|
||||||
};
|
};
|
||||||
@@ -179,7 +309,10 @@ function parseStripeError(error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.type === "StripeAPIError" || error.type === "StripeConnectionError") {
|
if (
|
||||||
|
error.type === "StripeAPIError" ||
|
||||||
|
error.type === "StripeConnectionError"
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
code: "api_error",
|
code: "api_error",
|
||||||
ownerMessage: "A temporary error occurred. Please try again.",
|
ownerMessage: "A temporary error occurred. Please try again.",
|
||||||
|
|||||||
Reference in New Issue
Block a user