Stripe error handling and now you can rent an item for a different time while having an upcoming or active rental

This commit is contained in:
jackiettran
2026-01-10 13:29:09 -05:00
parent 8aea3c38ed
commit 860b6d6160
10 changed files with 178 additions and 196 deletions

View File

@@ -5,6 +5,8 @@
* Provides structured error information for frontend handling.
*/
const logger = require('./logger');
const DECLINE_MESSAGES = {
authentication_required: {
ownerMessage: "The renter's card requires additional authentication.",
@@ -218,50 +220,6 @@ const DECLINE_MESSAGES = {
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
@@ -273,6 +231,37 @@ const DEFAULT_ERROR = {
requiresNewPaymentMethod: true,
};
// Mapping for StripeInvalidRequestError codes
const INVALID_REQUEST_MESSAGES = {
resource_missing: {
ownerMessage: "The renter's payment method is no longer valid.",
renterMessage:
"Your payment method is no longer valid. Please add a new payment method.",
canOwnerRetry: false,
requiresNewPaymentMethod: true,
},
payment_method_invalid: {
ownerMessage: "The renter's payment method is invalid.",
renterMessage:
"Your payment method is invalid. Please add a new payment method.",
canOwnerRetry: false,
requiresNewPaymentMethod: true,
},
payment_intent_unexpected_state: {
ownerMessage: "This payment is in an unexpected state.",
renterMessage: "There was an issue with your payment. Please try again.",
canOwnerRetry: true,
requiresNewPaymentMethod: false,
},
customer_deleted: {
ownerMessage: "The renter's payment profile has been deleted.",
renterMessage:
"Your payment profile needs to be set up again. Please add a new payment method.",
canOwnerRetry: false,
requiresNewPaymentMethod: true,
},
};
/**
* Parse a Stripe error and return structured error information
* @param {Error} error - The error object from Stripe
@@ -284,6 +273,16 @@ function parseStripeError(error) {
const declineCode = error.decline_code || error.code || "card_declined";
const errorInfo = DECLINE_MESSAGES[declineCode] || DEFAULT_ERROR;
// Log if we're falling back to default for an unknown decline code
if (!DECLINE_MESSAGES[declineCode]) {
logger.warn("[StripeErrors] Unknown decline code - please add mapping", {
declineCode,
errorCode: error.code,
errorType: error.type,
errorMessage: error.message,
});
}
return {
code: declineCode,
ownerMessage: errorInfo.ownerMessage,
@@ -298,8 +297,62 @@ function parseStripeError(error) {
// Handle other Stripe error types
if (error.type === "StripeInvalidRequestError") {
// First, check if there's a decline_code in the error (some bank errors include this)
if (error.decline_code && DECLINE_MESSAGES[error.decline_code]) {
const errorInfo = DECLINE_MESSAGES[error.decline_code];
return {
code: error.decline_code,
ownerMessage: errorInfo.ownerMessage,
renterMessage: errorInfo.renterMessage,
canOwnerRetry: errorInfo.canOwnerRetry,
requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
_originalMessage: error.message,
_stripeCode: error.code,
};
}
// Check if error.code has a specific mapping
const errorCode = error.code;
if (errorCode && INVALID_REQUEST_MESSAGES[errorCode]) {
const errorInfo = INVALID_REQUEST_MESSAGES[errorCode];
return {
code: errorCode,
ownerMessage: errorInfo.ownerMessage,
renterMessage: errorInfo.renterMessage,
canOwnerRetry: errorInfo.canOwnerRetry,
requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
_originalMessage: error.message,
_stripeCode: error.code,
};
}
// Also check DECLINE_MESSAGES for the error code (some codes appear in both contexts)
if (errorCode && DECLINE_MESSAGES[errorCode]) {
const errorInfo = DECLINE_MESSAGES[errorCode];
return {
code: errorCode,
ownerMessage: errorInfo.ownerMessage,
renterMessage: errorInfo.renterMessage,
canOwnerRetry: errorInfo.canOwnerRetry,
requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
_originalMessage: error.message,
_stripeCode: error.code,
};
}
// Log unhandled StripeInvalidRequestError codes for future mapping
logger.warn(
"[StripeErrors] Unhandled StripeInvalidRequestError - please add mapping",
{
errorCode: error.code,
declineCode: error.decline_code,
errorMessage: error.message,
param: error.param,
}
);
return {
code: "invalid_request",
code: errorCode || "invalid_request",
ownerMessage: "There was a problem processing this payment.",
renterMessage: "There was a problem with your payment method.",
canOwnerRetry: false,
@@ -337,6 +390,13 @@ function parseStripeError(error) {
}
// Default fallback for unknown errors
logger.warn("[StripeErrors] Unknown error type - please investigate", {
errorType: error.type,
errorCode: error.code,
declineCode: error.decline_code,
errorMessage: error.message,
});
return {
code: "unknown_error",
...DEFAULT_ERROR,