Skip to content

Commit

Permalink
fix(payment): missing feedback when submitting coupon
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristiaanScheermeijer authored and AntonLantukh committed Apr 3, 2024
1 parent 961bcd1 commit 5097e60
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 36 deletions.
32 changes: 11 additions & 21 deletions packages/common/src/controllers/CheckoutController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,32 +91,22 @@ export default class CheckoutController {
};

updateOrder = async (order: Order, paymentMethodId?: number, couponCode?: string | null): Promise<void> => {
let response;

try {
response = await this.checkoutService.updateOrder({ order, paymentMethodId, couponCode });
} catch (error: unknown) {
// TODO: we currently (falsely) assume that the only error caught is because the coupon is not valid, but there
// could be a network failure as well (JWPCheckoutService)
throw new FormValidationError({ couponCode: [i18next.t('account:checkout.coupon_not_valid')] });
}
const response = await this.checkoutService.updateOrder({ order, paymentMethodId, couponCode });

if (response.errors.length > 0) {
// clear the order when the order doesn't exist on the server
if (response.errors[0].includes(`Order with ${order.id} not found`)) {
useCheckoutStore.getState().setOrder(null);
if (response.responseData.order) {
useCheckoutStore.getState().setOrder(response.responseData?.order);
}

// TODO: this handles the `Coupon ${couponCode} not found` message (CleengCheckoutService)
if (response.errors[0].includes(`not found`)) {
throw new FormValidationError({ couponCode: [i18next.t('account:checkout.coupon_not_valid')] });
} catch (error: unknown) {
if (error instanceof Error) {
if (error.message === 'Order not found') {
useCheckoutStore.getState().setOrder(null);
} else if (error.message === 'Invalid coupon code') {
throw new FormValidationError({ couponCode: [i18next.t('account:checkout.coupon_not_valid')] });
}
}

throw new FormValidationError({ form: response.errors });
}

if (response.responseData.order) {
useCheckoutStore.getState().setOrder(response.responseData?.order);
throw new FormValidationError({ form: [i18next.t('error:unknown_error')] });
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import type {
PaymentWithPayPal,
SwitchSubscription,
UpdateOrder,
UpdateOrderResponse,
UpdatePaymentWithPayPal,
} from '../../../../types/checkout';
import CheckoutService from '../CheckoutService';
import { GET_CUSTOMER_IP } from '../../../modules/types';
import type { GetCustomerIP } from '../../../../types/get-customer-ip';
import type { ServiceResponse } from '../../../../types/service';

import CleengService from './CleengService';

Expand Down Expand Up @@ -83,7 +85,21 @@ export default class CleengCheckoutService extends CheckoutService {
};

updateOrder: UpdateOrder = async ({ order, ...payload }) => {
return this.cleengService.patch(`/orders/${order.id}`, JSON.stringify(payload), { authenticate: true });
const response = await this.cleengService.patch<ServiceResponse<UpdateOrderResponse>>(`/orders/${order.id}`, JSON.stringify(payload), {
authenticate: true,
});

if (response.errors.length) {
if (response.errors[0].includes(`Order with ${order.id} not found`)) {
throw new Error('Order not found');
}

if (response.errors[0].includes(`Coupon ${payload.couponCode} not found`)) {
throw new Error('Invalid coupon code');
}
}

return response;
};

getPaymentMethods: GetPaymentMethods = async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export default class CleengService {
return await resp.json();
} catch (error: unknown) {
return {
errors: Array.of(error as string),
errors: Array.of(error instanceof Error ? error.message : String(error)),
};
}
};
Expand Down
34 changes: 23 additions & 11 deletions packages/common/src/services/integrations/jwp/JWPCheckoutService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
} from '../../../../types/checkout';
import CheckoutService from '../CheckoutService';
import type { ServiceResponse } from '../../../../types/service';
import { isCommonError } from '../../../utils/api';

@injectable()
export default class JWPCheckoutService extends CheckoutService {
Expand Down Expand Up @@ -179,26 +180,37 @@ export default class JWPCheckoutService extends CheckoutService {
voucherCode: `${couponCode}`,
accessFeeId: order.id,
});
order.discount = {
applied: true,
type: 'coupon',
periods: response.data.discount_duration,
};

const discountedAmount = order.totalPrice - response.data.amount;
order.totalPrice = response.data.amount;
order.priceBreakdown.discountAmount = discountedAmount;
order.priceBreakdown.discountedPrice = discountedAmount;
const updatedOrder = {
...order,
totalPrice: response.data.amount,
priceBreakdown: {
...order.priceBreakdown,
discountedAmount,
discountedPrice: discountedAmount,
},
discount: {
applied: true,
type: 'coupon',
periods: response.data.discount_duration,
},
};

return {
errors: [],
responseData: {
message: 'successfully updated',
order: order,
order: updatedOrder,
success: true,
},
};
} catch {
throw new Error('Invalid coupon code');
} catch (error: unknown) {
if (isCommonError(error) && error.response.data.message === 'Voucher not found') {
throw new Error('Invalid coupon code');
}

throw new Error('An unknown error occurred');
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Props = {
onCouponInputChange: React.ChangeEventHandler<HTMLInputElement>;
onRedeemCouponButtonClick: () => void;
onCloseCouponFormClick: () => void;
error?: string;
couponFormOpen: boolean;
couponFormError?: string;
couponFormApplied?: boolean;
Expand All @@ -45,6 +46,7 @@ const CheckoutForm: React.FC<Props> = ({
offerType,
onBackButtonClick,
onPaymentMethodChange,
error,
couponFormOpen,
couponInputValue,
couponFormError,
Expand Down Expand Up @@ -89,6 +91,7 @@ const CheckoutForm: React.FC<Props> = ({
const orderTitle = offerType === 'svod' ? (offer.period === 'month' ? t('checkout.monthly') : t('checkout.yearly')) : offer.offerTitle;
return (
<div>
{error ? <FormFeedback variant="error">{error}</FormFeedback> : null}
<h1 className={styles.title}>{t('checkout.payment_method')}</h1>
<div className={styles.order}>
<div className={styles.orderInfo}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ const Checkout = () => {
handleSubmit,
} = useForm({
initialValues: { couponCode: '', paymentMethodId: paymentMethods?.[0]?.id?.toString() || '' },
onSubmit: async ({ couponCode, paymentMethodId }) => {
onSubmit: ({ couponCode, paymentMethodId }) => {
setShowCouponCodeSuccess(false);

return await updateOrder.mutateAsync({ couponCode, paymentMethodId: parseInt(paymentMethodId) });
return updateOrder.mutateAsync({ couponCode, paymentMethodId: parseInt(paymentMethodId) });
},
onSubmitSuccess: ({ couponCode }): void => setShowCouponCodeSuccess(!!couponCode),
onSubmitError: ({ error }) => {
Expand Down Expand Up @@ -117,6 +117,7 @@ const Checkout = () => {
order={order}
offer={selectedOffer}
offerType={offerType}
error={errors.form}
onBackButtonClick={backButtonClickHandler}
paymentMethods={paymentMethods}
paymentMethodId={paymentMethodId}
Expand Down

0 comments on commit 5097e60

Please sign in to comment.