diff --git a/.changeset/brown-buses-juggle.md b/.changeset/brown-buses-juggle.md
new file mode 100644
index 0000000000..9d6bba405e
--- /dev/null
+++ b/.changeset/brown-buses-juggle.md
@@ -0,0 +1,5 @@
+---
+"@bigcommerce/catalyst-core": minor
+---
+
+Allow applying and removing coupons in cart
diff --git a/apps/core/app/[locale]/(default)/cart/_actions/apply-coupon-code.ts b/apps/core/app/[locale]/(default)/cart/_actions/apply-coupon-code.ts
new file mode 100644
index 0000000000..076ebf2a68
--- /dev/null
+++ b/apps/core/app/[locale]/(default)/cart/_actions/apply-coupon-code.ts
@@ -0,0 +1,36 @@
+'use server';
+
+import { revalidateTag } from 'next/cache';
+import { z } from 'zod';
+
+import { applyCheckoutCoupon } from '~/client/mutations/apply-checkout-coupon';
+
+const ApplyCouponCodeSchema = z.object({
+ checkoutEntityId: z.string(),
+ couponCode: z.string(),
+});
+
+export async function applyCouponCode(formData: FormData) {
+ try {
+ const parsedData = ApplyCouponCodeSchema.parse({
+ checkoutEntityId: formData.get('checkoutEntityId'),
+ couponCode: formData.get('couponCode'),
+ });
+
+ const checkout = await applyCheckoutCoupon(parsedData.checkoutEntityId, parsedData.couponCode);
+
+ if (!checkout?.entityId) {
+ return { status: 'error', error: 'Coupon code is invalid' };
+ }
+
+ revalidateTag('checkout');
+
+ return { status: 'success', data: checkout };
+ } catch (e: unknown) {
+ if (e instanceof Error || e instanceof z.ZodError) {
+ return { status: 'error', error: e.message };
+ }
+
+ return { status: 'error' };
+ }
+}
diff --git a/apps/core/app/[locale]/(default)/cart/_actions/remove-coupon-code.ts b/apps/core/app/[locale]/(default)/cart/_actions/remove-coupon-code.ts
new file mode 100644
index 0000000000..159c7a188f
--- /dev/null
+++ b/apps/core/app/[locale]/(default)/cart/_actions/remove-coupon-code.ts
@@ -0,0 +1,39 @@
+'use server';
+
+import { revalidateTag } from 'next/cache';
+import { z } from 'zod';
+
+import { unapplyCheckoutCoupon } from '~/client/mutations/unapply-checkout-coupon';
+
+const RemoveCouponCodeSchema = z.object({
+ checkoutEntityId: z.string(),
+ couponCode: z.string(),
+});
+
+export async function removeCouponCode(formData: FormData) {
+ try {
+ const parsedData = RemoveCouponCodeSchema.parse({
+ checkoutEntityId: formData.get('checkoutEntityId'),
+ couponCode: formData.get('couponCode'),
+ });
+
+ const checkout = await unapplyCheckoutCoupon(
+ parsedData.checkoutEntityId,
+ parsedData.couponCode,
+ );
+
+ if (!checkout?.entityId) {
+ return { status: 'error', error: 'Error ocurred removing coupon' };
+ }
+
+ revalidateTag('checkout');
+
+ return { status: 'success', data: checkout };
+ } catch (e: unknown) {
+ if (e instanceof Error || e instanceof z.ZodError) {
+ return { status: 'error', error: e.message };
+ }
+
+ return { status: 'error' };
+ }
+}
diff --git a/apps/core/app/[locale]/(default)/cart/_components/checkout-summary.tsx b/apps/core/app/[locale]/(default)/cart/_components/checkout-summary.tsx
index e7d7282af7..2ae43e0449 100644
--- a/apps/core/app/[locale]/(default)/cart/_components/checkout-summary.tsx
+++ b/apps/core/app/[locale]/(default)/cart/_components/checkout-summary.tsx
@@ -7,6 +7,7 @@ import { getCheckout } from '~/client/queries/get-checkout';
import { getShippingCountries } from '../_actions/get-shipping-countries';
+import { CouponCode } from './coupon-code';
import { ShippingEstimator } from './shipping-estimator';
export const CheckoutSummary = async ({ cartId, locale }: { cartId: string; locale: string }) => {
@@ -49,6 +50,10 @@ export const CheckoutSummary = async ({ cartId, locale }: { cartId: string; loca
)}
+