fix: Show the Stripe payment error message and change the status to 400 for charge card issues (#22463)
* fix: show stripe payment error message * Update PaymentService.ts * Update chargeCard.handler.ts * Update chargeCard.handler.ts * Update PaymentService.ts * update * update
This commit is contained in:
@@ -21,15 +21,17 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useUtils();
|
||||
const { isOpenDialog, setIsOpenDialog, bookingId } = props;
|
||||
const [chargeError, setChargeError] = useState(false);
|
||||
const [chargeError, setChargeError] = useState<string | null>(null);
|
||||
|
||||
const chargeCardMutation = trpc.viewer.payments.chargeCard.useMutation({
|
||||
onSuccess: () => {
|
||||
utils.viewer.bookings.invalidate();
|
||||
setIsOpenDialog(false);
|
||||
setChargeError(null);
|
||||
showToast("Charge successful", "success");
|
||||
},
|
||||
onError: () => {
|
||||
setChargeError(true);
|
||||
onError: (error) => {
|
||||
setChargeError(error.message || t("error_charging_card"));
|
||||
},
|
||||
});
|
||||
|
||||
@@ -52,7 +54,7 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
|
||||
{chargeError && (
|
||||
<div className="mt-4 flex text-red-500">
|
||||
<Icon name="triangle-alert" className="mr-2 h-5 w-5 " aria-hidden="true" />
|
||||
<p className="text-sm">{t("error_charging_card")}</p>
|
||||
<p className="text-sm">{chargeError}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -60,12 +62,13 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
|
||||
<DialogClose />
|
||||
<Button
|
||||
data-testid="send_request"
|
||||
disabled={chargeCardMutation.isPending || chargeError}
|
||||
onClick={() =>
|
||||
disabled={chargeCardMutation.isPending}
|
||||
onClick={() => {
|
||||
setChargeError(null);
|
||||
chargeCardMutation.mutate({
|
||||
bookingId,
|
||||
})
|
||||
}>
|
||||
});
|
||||
}}>
|
||||
{t("charge_attendee", currencyStringParams)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -2196,6 +2196,10 @@
|
||||
"error_charging_card": "Something went wrong charging the no-show fee. Please try again later.",
|
||||
"collect_no_show_fee": "Collect no-show fee",
|
||||
"no_show_fee_charged": "No-show fee charged",
|
||||
"your_card_was_declined": "Payment declined. Please try a different card or contact your bank for assistance.",
|
||||
"your_card_does_not_support_this_type_of_purchase": "This card type isn't supported for this purchase. Please use a different payment method.",
|
||||
"amount_must_convert_to_at_least": "Minimum payment amount is $0.50 USD. Please increase your payment amount.",
|
||||
"could_not_charge_card": "Could not charge card for payment.",
|
||||
"insights": "Insights",
|
||||
"routing_forms": "Routing Forms",
|
||||
"testing_workflow_info_message": "When testing this workflow, be aware that Emails and SMS can only be scheduled at least 1 hour in advance",
|
||||
|
||||
@@ -6,6 +6,7 @@ import z from "zod";
|
||||
import { sendAwaitingPaymentEmailAndSMS } from "@calcom/emails";
|
||||
import { ErrorCode } from "@calcom/lib/errorCodes";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { ErrorWithCode } from "@calcom/lib/errors";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import prisma from "@calcom/prisma";
|
||||
@@ -296,7 +297,28 @@ export class PaymentService implements IAbstractPaymentService {
|
||||
return paymentData;
|
||||
} catch (error) {
|
||||
log.error("Stripe: Could not charge card for payment", _bookingId, safeStringify(error));
|
||||
throw new Error(ErrorCode.ChargeCardFailure);
|
||||
|
||||
const errorMappings = {
|
||||
"your card was declined": "your_card_was_declined",
|
||||
"your card does not support this type of purchase":
|
||||
"your_card_does_not_support_this_type_of_purchase",
|
||||
"amount must convert to at least": "amount_must_convert_to_at_least",
|
||||
};
|
||||
|
||||
let userMessage = "could_not_charge_card";
|
||||
|
||||
if (error instanceof Error) {
|
||||
const errorMessage = error.message.toLowerCase();
|
||||
|
||||
for (const [key, message] of Object.entries(errorMappings)) {
|
||||
if (errorMessage.includes(key)) {
|
||||
userMessage = message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ErrorWithCode(ErrorCode.ChargeCardFailure, userMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ const test400Codes = [
|
||||
ErrorCode.BookingNotAllowedByRestrictionSchedule,
|
||||
ErrorCode.BookerLimitExceeded,
|
||||
ErrorCode.BookerLimitExceededReschedule,
|
||||
ErrorCode.ChargeCardFailure,
|
||||
];
|
||||
|
||||
const test404Codes = [
|
||||
@@ -33,7 +34,6 @@ const test409Codes = [
|
||||
ErrorCode.NotEnoughAvailableSeats,
|
||||
ErrorCode.BookingConflict,
|
||||
ErrorCode.PaymentCreationFailure,
|
||||
ErrorCode.ChargeCardFailure,
|
||||
];
|
||||
|
||||
describe("getServerErrorFromUnknown", () => {
|
||||
|
||||
@@ -112,6 +112,7 @@ function getStatusCode(cause: Error | ErrorWithCode): number {
|
||||
case ErrorCode.BookerLimitExceeded:
|
||||
case ErrorCode.BookerLimitExceededReschedule:
|
||||
case ErrorCode.EventTypeNoHosts:
|
||||
case ErrorCode.ChargeCardFailure:
|
||||
return 400;
|
||||
// 409 Conflict
|
||||
case ErrorCode.NoAvailableUsersFound:
|
||||
@@ -122,7 +123,6 @@ function getStatusCode(cause: Error | ErrorWithCode): number {
|
||||
case ErrorCode.NotEnoughAvailableSeats:
|
||||
case ErrorCode.BookingConflict:
|
||||
case ErrorCode.PaymentCreationFailure:
|
||||
case ErrorCode.ChargeCardFailure:
|
||||
return 409;
|
||||
// 404 Not Found
|
||||
case ErrorCode.EventTypeNotFound:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import appStore from "@calcom/app-store";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { sendNoShowFeeChargedEmail } from "@calcom/emails";
|
||||
import { ErrorCode } from "@calcom/lib/errorCodes";
|
||||
import { ErrorWithCode } from "@calcom/lib/errors";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
|
||||
@@ -141,9 +143,13 @@ export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions
|
||||
|
||||
return paymentData;
|
||||
} catch (err) {
|
||||
let errorMessage = `Error processing payment with error ${err}`;
|
||||
if (err instanceof ErrorWithCode && err.code === ErrorCode.ChargeCardFailure) {
|
||||
errorMessage = err.message;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: `Error processing payment with error ${err}`,
|
||||
code: "BAD_REQUEST",
|
||||
message: tOrganizer(errorMessage),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user