Repository: nextorders/food
Pull request #368: feat: checkout receiver
Author: login: hmbanan666
Labels: name: enhancement
description: New feature or request
Created: 2025-04-04T17:10:35Z | Merged: 2025-04-04T17:45:16Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Enhanced menu creation experience: the modal now automatically closes upon form submission or successful completion.
  - Expanded checkout functionality: receiver details are now integrated into order processing, providing more comprehensive transaction management.
  - Strengthened access control: additional security measures ensure that operations involving checkout receivers are managed with improved safeguards.
  - New components for creating and updating checkout receivers, enhancing user interaction and management.
  - Localization updates for checkout receivers, improving user experience across different languages.

- **Bug Fixes**
  - Resolved issues with form submissions that previously did not trigger the appropriate success notifications.

- **Documentation**
  - Updated localization files to reflect new keys and improved phrases for better clarity and usability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/web-app/app/components/CommandCenter/CheckoutReceiverCard.vue b/apps/web-app/app/components/CommandCenter/CheckoutReceiverCard.vue
new file mode 100644
index 00000000..37e3aa1a
--- /dev/null
+++ b/apps/web-app/app/components/CommandCenter/CheckoutReceiverCard.vue
@@ -0,0 +1,13 @@
+<template>
+  <CommandCenterActiveCard class="text-center">
+    <UIcon name="i-lucide-combine" class="size-8 text-(--ui-text-dimmed)" />
+
+    <h3 class="text-xl font-semibold">
+      {{ name }}
+    </h3>
+  </CommandCenterActiveCard>
+</template>
+
+<script setup lang="ts">
+defineProps<{ name: string }>()
+</script>
diff --git a/apps/web-app/app/components/Form/CreateCheckoutReceiver.vue b/apps/web-app/app/components/Form/CreateCheckoutReceiver.vue
new file mode 100644
index 00000000..eba8ceb8
--- /dev/null
+++ b/apps/web-app/app/components/Form/CreateCheckoutReceiver.vue
@@ -0,0 +1,137 @@
+<template>
+  <UForm
+    :schema="receiverCreateSchema"
+    :state="state"
+    class="flex flex-col gap-3"
+    @submit="onSubmit"
+  >
+    <UFormField :label="$t('center.data.name')" name="name">
+      <UInput
+        v-model="state.name"
+        size="xl"
+        class="w-full items-center justify-center"
+      />
+    </UFormField>
+
+    <UFormField :label="$t('center.link.url')" name="url">
+      <UInput
+        v-model="state.url"
+        size="xl"
+        class="w-full items-center justify-center"
+      />
+    </UFormField>
+
+    <UFormField :label="$t('center.receiver.method')" name="method">
+      <USelect
+        v-model="state.method"
+        :items="['POST', 'PUT']"
+        :placeholder="$t('common.select')"
+        size="xl"
+        class="w-full"
+      />
+    </UFormField>
+
+    <UFormField :label="$t('center.receiver.authorization')" name="authorization">
+      <UInput
+        v-model="state.authorization"
+        size="xl"
+        class="w-full items-center justify-center"
+      />
+    </UFormField>
+
+    <UFormField
+      :label="$t('center.receiver.body')"
+      name="body"
+    >
+      <div class="flex flex-col gap-2">
+        <div
+          v-for="obj in bodyState"
+          :key="obj.id"
+          class="flex flex-row gap-3"
+        >
+          <UInput
+            v-model="obj.key"
+            size="xl"
+            :placeholder="$t('common.key')"
+            class="w-full"
+          />
+          <UInput
+            v-model="obj.value"
+            size="xl"
+            :placeholder="$t('common.value')"
+            class="w-full"
+          />
+        </div>
+
+        <UButton
+          variant="subtle"
+          color="neutral"
+          size="lg"
+          icon="i-lucide-plus"
+          class="w-fit"
+          :label="$t('app.cart.add')"
+          @click="addNewKeyToBody"
+        />
+      </div>
+    </UFormField>
+
+    <UButton
+      type="submit"
+      variant="solid"
+      color="primary"
+      size="xl"
+      block
+      class="mt-3"
+    >
+      {{ $t('center.create.title') }}
+    </UButton>
+  </UForm>
+</template>
+
+<script setup lang="ts">
+import type { ReceiverCreateSchema } from '@nextorders/core/shared/services/receiver'
+import type { FormSubmitEvent } from '@nuxt/ui'
+import { receiverCreateSchema } from '@nextorders/core/shared/services/receiver'
+
+const emit = defineEmits(['success', 'submitted'])
+
+const { t } = useI18n()
+const actionToast = useActionToast()
+const channel = useChannelStore()
+
+const bodyState = ref<{ id: string, key: string, value: string }[]>([{ id: useId(), key: '', value: '' }])
+function addNewKeyToBody() {
+  bodyState.value.push({ id: useId(), key: '', value: '' })
+}
+
+const state = ref<Partial<ReceiverCreateSchema>>({
+  name: '',
+  url: undefined,
+  method: 'POST',
+  authorization: undefined,
+  body: undefined,
+})
+
+async function onSubmit(event: FormSubmitEvent<ReceiverCreateSchema>) {
+  actionToast.start()
+  emit('submitted')
+
+  // Prepare body
+  bodyState.value = bodyState.value.filter((obj) => obj.key && obj.value)
+  state.value.body = Object.fromEntries(bodyState.value.map((obj) => [obj.key, obj.value]))
+
+  try {
+    await $fetch('/api/checkout/receiver', {
+      method: 'POST',
+      body: event.data,
+    })
+
+    await channel.update()
+    actionToast.success(t('toast.receiver-created'))
+    emit('success')
+  } catch (error) {
+    console.error(error)
+    actionToast.error()
+  }
+}
+</script>
diff --git a/apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue b/apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue
new file mode 100644
index 00000000..7fe1c45e
--- /dev/null
+++ b/apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue
@@ -0,0 +1,121 @@
+<template>
+  <UForm
+    :schema="receiverUpdateSchema"
+    :state="state"
+    class="flex flex-col gap-3"
+    @submit="onSubmit"
+  >
+    <UFormField :label="$t('center.data.name')" name="name">
+      <UInput
+        v-model="state.name"
+        size="xl"
+        class="w-full items-center justify-center"
+      />
+    </UFormField>
+
+    <UFormField :label="$t('center.link.url')" name="url">
+      <UInput
+        v-model="state.url"
+        size="xl"
+        class="w-full items-center justify-center"
+      />
+    </UFormField>
+
+    <UFormField :label="$t('center.receiver.method')" name="method">
+      <USelect
+        v-model="state.method"
+        :items="['POST', 'PUT']"
+        :placeholder="$t('common.select')"
+        size="xl"
+        class="w-full"
+      />
+    </UFormField>
+
+    <UFormField :label="$t('center.receiver.authorization')" name="authorization">
+      <UInput
+        v-model="state.authorization"
+        size="xl"
+        class="w-full items-center justify-center"
+      />
+    </UFormField>
+
+    <div class="mt-3 flex flex-row gap-3">
+      <UButton
+        type="submit"
+        variant="solid"
+        color="primary"
+        size="xl"
+        block
+      >
+        {{ $t('center.update.title') }}
+      </UButton>
+
+      <UButton
+        variant="soft"
+        color="error"
+        size="xl"
+        icon="i-lucide-trash-2"
+        class="aspect-square justify-center"
+        @click="onDelete"
+      />
+    </div>
+  </UForm>
+</template>
+
+<script setup lang="ts">
+import type { ReceiverUpdateSchema } from '@nextorders/core/shared/services/receiver'
+import type { FormSubmitEvent } from '@nuxt/ui'
+import { receiverUpdateSchema } from '@nextorders/core/shared/services/receiver'
+
+const { id } = defineProps<{ id: string }>()
+const emit = defineEmits(['success', 'submitted'])
+
+const { t } = useI18n()
+const actionToast = useActionToast()
+const channel = useChannelStore()
+const receiver = computed(() => channel.receivers.find((receiver) => receiver.id === id))
+
+const state = ref<Partial<ReceiverUpdateSchema>>({
+  name: receiver.value?.name,
+  url: receiver.value?.url,
+  method: receiver.value?.method,
+  authorization: receiver.value?.authorization,
+})
+
+async function onSubmit(event: FormSubmitEvent<ReceiverUpdateSchema>) {
+  actionToast.start()
+  emit('submitted')
+
+  try {
+    await $fetch(`/api/checkout/receiver/${id}`, {
+      method: 'PATCH',
+      body: event.data,
+    })
+
+    await channel.update()
+    actionToast.success(t('toast.receiver-updated'))
+    emit('success')
+  } catch (error) {
+    console.error(error)
+    actionToast.error()
+  }
+}
+
+async function onDelete() {
+  actionToast.start()
+  emit('submitted')
+
+  try {
+    await $fetch(`/api/checkout/receiver/${id}`, {
+      method: 'DELETE',
+    })
+
+    await channel.update()
+    actionToast.success(t('toast.receiver-deleted'))
+    emit('success')
+  } catch (error) {
+    console.error(error)
+    actionToast.error()
+  }
+}
+</script>
diff --git a/apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue b/apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue
new file mode 100644
index 00000000..f7a07f1d
--- /dev/null
+++ b/apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue
@@ -0,0 +1,17 @@
+<template>
+  <UModal :title="$t('center.create.checkout-receiver')">
+    <template #body>
+      <FormCreateCheckoutReceiver @submitted="closeAll" @success="closeAll" />
+    </template>
+  </UModal>
+</template>
+
+<script setup lang="ts">
+const overlay = useOverlay()
+
+function closeAll() {
+  for (const o of overlay.overlays) {
+    overlay.close(o.id)
+  }
+}
+</script>
diff --git a/apps/web-app/app/components/Modal/CreateMenu.vue b/apps/web-app/app/components/Modal/CreateMenu.vue
index b0a67bef..9ca3168a 100644
--- a/apps/web-app/app/components/Modal/CreateMenu.vue
+++ b/apps/web-app/app/components/Modal/CreateMenu.vue
@@ -1,7 +1,7 @@
 <template>
   <UModal :title="$t('center.create.menu')">
     <template #body>
-      <FormCreateMenu @success="closeAll" />
+      <FormCreateMenu @submitted="closeAll" @success="closeAll" />
     </template>
   </UModal>
 </template>
diff --git a/apps/web-app/app/components/Modal/UpdateCheckoutReceiver.vue b/apps/web-app/app/components/Modal/UpdateCheckoutReceiver.vue
new file mode 100644
index 00000000..db935084
--- /dev/null
+++ b/apps/web-app/app/components/Modal/UpdateCheckoutReceiver.vue
@@ -0,0 +1,23 @@
+<template>
+  <UModal :title="$t('center.update.checkout-receiver')">
+    <template #body>
+      <FormUpdateCheckoutReceiver
+        :id="id"
+        @submitted="closeAll"
+        @success="closeAll"
+      />
+    </template>
+  </UModal>
+</template>
+
+<script setup lang="ts">
+defineProps<{ id: string }>()
+
+const overlay = useOverlay()
+
+function closeAll() {
+  for (const o of overlay.overlays) {
+    overlay.close(o.id)
+  }
+}
+</script>
diff --git a/apps/web-app/app/pages/command-center/settings.vue b/apps/web-app/app/pages/command-center/settings.vue
index 3fefab8f..1fac1bc2 100644
--- a/apps/web-app/app/pages/command-center/settings.vue
+++ b/apps/web-app/app/pages/command-center/settings.vue
@@ -33,6 +33,10 @@ const items = ref([
     label: t('center.menu.navigation'),
     to: `/command-center/settings/navigation`,
     icon: 'i-lucide-menu',
+  }, {
+    label: t('center.menu.checkout-receivers'),
+    to: `/command-center/settings/receivers`,
+    icon: 'i-lucide-send',
   },
 ])
 </script>
diff --git a/apps/web-app/app/pages/command-center/settings/receivers.vue b/apps/web-app/app/pages/command-center/settings/receivers.vue
new file mode 100644
index 00000000..029e1108
--- /dev/null
+++ b/apps/web-app/app/pages/command-center/settings/receivers.vue
@@ -0,0 +1,27 @@
+<template>
+  <CommandCenterContent>
+    <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 gap-4">
+      <CommandCenterCheckoutReceiverCard
+        v-for="receiver in channel.receivers"
+        :key="receiver.id"
+        :name="receiver.name"
+        @click="modalUpdateCheckoutReceiver.open({ id: receiver.id })"
+      />
+      <CommandCenterCreateCard
+        icon="i-lucide-square-plus"
+        :label="t('center.create.checkout-receiver')"
+        @click="modalCreateCheckoutReceiver.open()"
+      />
+    </div>
+  </CommandCenterContent>
+</template>
+
+<script setup lang="ts">
+import { ModalCreateCheckoutReceiver, ModalUpdateCheckoutReceiver } from '#components'
+
+const { t } = useI18n()
+const overlay = useOverlay()
+const modalCreateCheckoutReceiver = overlay.create(ModalCreateCheckoutReceiver)
+const modalUpdateCheckoutReceiver = overlay.create(ModalUpdateCheckoutReceiver)
+const channel = useChannelStore()
+</script>
diff --git a/apps/web-app/i18n/locales/en-US.json b/apps/web-app/i18n/locales/en-US.json
index e07f0de8..d055a2f7 100644
--- a/apps/web-app/i18n/locales/en-US.json
+++ b/apps/web-app/i18n/locales/en-US.json
@@ -68,6 +68,8 @@
     "slug": "Human-friendly URL",
     "photo": "Photo",
     "icon": "Icon",
+    "key": "Key",
+    "value": "Value",
     "footer": {
       "copyright": "Copyright",
       "copyright-part-one": "Created with ❤️ on",
@@ -230,6 +232,11 @@
       "title": "Links",
       "url": "URL"
     },
+    "receiver": {
+      "method": "HTTP method",
+      "authorization": "Authorization header",
+      "body": "Body"
+    },
     "checkout": {
       "name": "Name",
       "phone": "Phone",
@@ -250,6 +257,7 @@
       "category": "Category",
       "menu-page": "Menu Page",
       "checkouts": "Checkouts",
+      "checkout-receivers": "Checkout receivers",
       "clients": "Clients",
       "client-page": "Client Page",
       "settings": "Settings",
@@ -268,14 +276,15 @@
     "save": "Save",
     "create": {
       "title": "Create",
-      "product": "Create new Product",
-      "product-variant": "Create new Product Variant",
-      "menu": "Create new Menu",
-      "menu-category": "Create new Menu Category",
-      "warehouse": "Create new Warehouse",
-      "payment-method": "Create new Payment Method",
-      "link": "Create new Link",
-      "page": "Create new Page"
+      "product": "Create Product",
+      "product-variant": "Create Product Variant",
+      "menu": "Create Menu",
+      "menu-category": "Create Menu Category",
+      "warehouse": "Create Warehouse",
+      "payment-method": "Create Payment Method",
+      "link": "Create Link",
+      "page": "Create Page",
+      "checkout-receiver": "Create Checkouts Receiver"
     },
     "attach": {
       "title": "Attach",
@@ -299,7 +308,8 @@
       "online-ordering-time": "Update Online ordering time",
       "payment-method": "Update Payment Method",
       "link": "Update Link",
-      "page": "Update Page"
+      "page": "Update Page",
+      "checkout-receiver": "Update Checkouts Receiver"
     },
     "delete": {
       "title": "Delete",
@@ -339,6 +349,9 @@
     "payment-method-updated": "Payment Method updated!",
     "payment-method-deleted": "Payment Method deleted!",
     "receiving-method-updated": "Method updated!",
+    "receiver-created": "Receiver created!",
+    "receiver-updated": "Receiver updated!",
+    "receiver-deleted": "Receiver deleted!",
     "menu-created": "Menu created!",
     "menu-updated": "Menu updated!",
     "category-created": "Category created!",
diff --git a/apps/web-app/i18n/locales/ka-GE.json b/apps/web-app/i18n/locales/ka-GE.json
index 07d3b4ed..50f011f7 100644
--- a/apps/web-app/i18n/locales/ka-GE.json
+++ b/apps/web-app/i18n/locales/ka-GE.json
@@ -68,6 +68,8 @@
     "slug": "ადამიანის მიერ წაკითხვადი URL",
     "photo": "ფოტო",
     "icon": "Icon",
+    "key": "Key",
+    "value": "Value",
     "footer": {
       "copyright": "Copyright",
       "copyright-part-one": "შექმნილია ❤️-ით",
@@ -230,6 +232,11 @@
       "title": "Links",
       "url": "URL"
     },
+    "receiver": {
+      "method": "HTTP method",
+      "authorization": "Authorization header",
+      "body": "Body"
+    },
     "checkout": {
       "name": "სახელი",
       "phone": "ტელეფონი",
@@ -250,6 +257,7 @@
       "category": "Category",
       "menu-page": "მენიუს გვერდი",
       "checkouts": "შეკვეთები",
+      "checkout-receivers": "Checkout receivers",
       "clients": "კლიენტები",
       "client-page": "კლიენტის გვერდი",
       "settings": "პარამეტრების",
@@ -275,7 +283,8 @@
       "warehouse": "შექმენით ახალი საწყობი",
       "payment-method": "შექმენით ახალი გადახდის მეთოდი",
       "link": "Create new Link",
-      "page": "Create new Page"
+      "page": "Create new Page",
+      "checkout-receiver": "Create Checkout Receiver"
     },
     "attach": {
       "title": "მიმაგრება",
@@ -299,7 +308,8 @@
       "online-ordering-time": "განაახლეთ ონლაინ შეკვეთის დრო",
       "payment-method": "განაახლეთ გადახდის მეთოდი",
       "link": "Update Link",
-      "page": "Update Page"
+      "page": "Update Page",
+      "checkout-receiver": "Update Checkouts Receiver"
     },
     "delete": {
       "title": "წაშლა",
@@ -339,6 +349,9 @@
     "payment-method-updated": "გადახდის მეთოდი განახლდა!",
     "payment-method-deleted": "გადახდის მეთოდი წაიშალა!",
     "receiving-method-updated": "მეთოდი განახლებულია!",
+    "receiver-created": "Receiver created!",
+    "receiver-updated": "Receiver updated!",
+    "receiver-deleted": "Receiver deleted!",
     "menu-created": "მენიუ შეიქმნა!",
     "menu-updated": "მენიუ განახლდა!",
     "category-created": "კატეგორია შეიქმნა!",
diff --git a/apps/web-app/i18n/locales/ru-RU.json b/apps/web-app/i18n/locales/ru-RU.json
index ec8c4667..13fe932e 100644
--- a/apps/web-app/i18n/locales/ru-RU.json
+++ b/apps/web-app/i18n/locales/ru-RU.json
@@ -68,6 +68,8 @@
     "slug": "Человекопонятный URL",
     "photo": "Фото",
     "icon": "Иконка",
+    "key": "Ключ",
+    "value": "Значение",
     "footer": {
       "copyright": "Копирайт",
       "copyright-part-one": "Создано с ❤️ на",
@@ -230,6 +232,11 @@
       "title": "Ссылки",
       "url": "URL"
     },
+    "receiver": {
+      "method": "HTTP метод",
+      "authorization": "Authorization заголовок",
+      "body": "Body"
+    },
     "checkout": {
       "name": "Имя",
       "phone": "Телефон",
@@ -250,6 +257,7 @@
       "category": "Категория",
       "menu-page": "Страница меню",
       "checkouts": "Заявки",
+      "checkout-receivers": "Получатели заявок",
       "clients": "Клиенты",
       "client-page": "Страница клиента",
       "settings": "Настройки",
@@ -268,14 +276,15 @@
     "save": "Сохранить",
     "create": {
       "title": "Создать",
-      "product": "Создать новый продукт",
+      "product": "Создать продукт",
       "product-variant": "Создать вариацию продукта",
       "menu": "Создать новое меню",
-      "menu-category": "Создать новую категорию",
-      "warehouse": "Создать новый склад",
+      "menu-category": "Создать категорию",
+      "warehouse": "Создать склад",
       "payment-method": "Создать метод оплаты",
-      "link": "Создать новую ссылку",
-      "page": "Создать новую страницу"
+      "link": "Создать ссылку",
+      "page": "Создать страницу",
+      "checkout-receiver": "Создать получателя заявок"
     },
     "attach": {
       "title": "Добавить",
@@ -299,7 +308,8 @@
       "online-ordering-time": "Обновить время оформления заказов",
       "payment-method": "Обновить метод оплаты",
       "link": "Обновить ссылку",
-      "page": "Обновить страницу"
+      "page": "Обновить страницу",
+      "checkout-receiver": "Обновить получателя заявок"
     },
     "delete": {
       "title": "Удалить",
@@ -339,6 +349,9 @@
     "payment-method-updated": "Метод оплаты обновлен!",
     "payment-method-deleted": "Метод оплаты удален!",
     "receiving-method-updated": "Метод обновлен!",
+    "receiver-created": "Получатель создан!",
+    "receiver-updated": "Получатель обновлен!",
+    "receiver-deleted": "Получатель удален!",
     "menu-created": "Меню создано!",
     "menu-updated": "Меню обновлено!",
     "category-created": "Категория создана!",
diff --git a/apps/web-app/stores/channel.ts b/apps/web-app/stores/channel.ts
index 02d364a4..420bdd22 100644
--- a/apps/web-app/stores/channel.ts
+++ b/apps/web-app/stores/channel.ts
@@ -24,6 +24,7 @@ export const useChannelStore = defineStore('channel', () => {
   const copyright = ref<LocaleValue[]>([])
   const links = ref<Link[]>([])
   const pages = ref<Page[]>([])
+  const receivers = ref<CheckoutReceiver[]>([])
   const minAmountForDelivery = ref<number | null>(null)
   const masterAccountExists = ref(false)
   const workingDay = ref<WorkingDay | undefined>(undefined)
@@ -69,6 +70,7 @@ export const useChannelStore = defineStore('channel', () => {
       copyright.value = data.copyright
       links.value = data.links
       pages.value = data.pages
+      receivers.value = data.receivers
       minAmountForDelivery.value = data.minAmountForDelivery
       masterAccountExists.value = data.masterAccountExists
       menus.value = data.menus
@@ -159,6 +161,7 @@ export const useChannelStore = defineStore('channel', () => {
     copyright,
     links,
     pages,
+    receivers,
     minAmountForDelivery,
     masterAccountExists,
     workingDay,
diff --git a/packages/core/server/api/channel/index.get.ts b/packages/core/server/api/channel/index.get.ts
index 4ce45cd0..728a0a99 100644
--- a/packages/core/server/api/channel/index.get.ts
+++ b/packages/core/server/api/channel/index.get.ts
@@ -6,6 +6,7 @@ import { getMenus } from '../../../server/services/db/menu'
 import { getPages } from '../../../server/services/db/page'
 import { getPaymentMethods } from '../../../server/services/db/payment'
 import { getProducts } from '../../../server/services/db/product'
+import { getCheckoutReceivers } from '../../../server/services/db/receiver'
 import { getMaster } from '../../../server/services/db/user'
 import { getWarehouses } from '../../../server/services/db/warehouse'
 import { getWorkingDays } from '../../../server/services/db/work'
@@ -61,7 +62,14 @@ export default defineCachedEventHandler(async () => {
       })
     }
 
-    const { menuKeys, productKeys, warehouseKeys, paymentMethodKeys, linkKeys, pageKeys } = await getKeys()
+    const {
+      menuKeys,
+      productKeys,
+      warehouseKeys,
+      paymentMethodKeys,
+      linkKeys,
+      pageKeys,
+    } = await getKeys()
 
     const menus = await getMenus(menuKeys)
     const products = await getProducts(productKeys)
@@ -69,6 +77,7 @@ export default defineCachedEventHandler(async () => {
     const paymentMethods = await getPaymentMethods(paymentMethodKeys)
     const links = await getLinks(linkKeys)
     const pages = await getPages(pageKeys)
+    const receivers = await getCheckoutReceivers()
 
     const workingDays = await getWorkingDays()
 
@@ -89,6 +98,7 @@ export default defineCachedEventHandler(async () => {
       paymentMethods,
       links,
       pages,
+      receivers,
     }
   } catch (error) {
     throw errorResolver(error)
diff --git a/packages/core/server/api/checkout/index.patch.ts b/packages/core/server/api/checkout/index.patch.ts
index 5fd25cc2..beaf7ef8 100644
--- a/packages/core/server/api/checkout/index.patch.ts
+++ b/packages/core/server/api/checkout/index.patch.ts
@@ -1,11 +1,6 @@
-import { TZDate } from '@date-fns/tz'
-import { getKeys } from '../../../server/services/db'
 import { getChannel } from '../../../server/services/db/channel'
 import { getCheckout, patchCheckout, recalculateCheckout, setCheckoutAsFinished } from '../../../server/services/db/checkout'
-import { getPaymentMethods } from '../../../server/services/db/payment'
-import { getProduct, getProductVariant } from '../../../server/services/db/product'
-import { getCheckoutReceivers } from '../../../server/services/db/receiver'
-import { getWarehouses } from '../../../server/services/db/warehouse'
+import { sendToReceivers } from '../../../server/services/receiver'
 import { checkoutUpdateSchema } from './../../../shared/services/checkout'
 
 export default defineEventHandler(async (event) => {
@@ -72,77 +67,3 @@ export default defineEventHandler(async (event) => {
     throw errorResolver(error)
   }
 })
-
-async function sendToReceivers(checkoutId: string) {
-  const { locale } = useRuntimeConfig()
-
-  const checkout = await getCheckout(checkoutId)
-  if (!checkout?.id) {
-    return
-  }
-
-  const channel = await getChannel(checkout.channelId)
-  if (!channel?.id) {
-    return
-  }
-
-  const { paymentMethodKeys, warehouseKeys } = await getKeys()
-
-  const paymentMethods = await getPaymentMethods(paymentMethodKeys)
-  const paymentMethodName = paymentMethods.find((p) => p.id === checkout?.paymentMethodId)?.name.find((name) => name.locale === locale)?.value ?? ''
-
-  const warehouses = await getWarehouses(warehouseKeys)
-  const warehouseAddress = warehouses.find((w) => w.id === checkout?.warehouseId)?.address
-  const address = checkout.street
-    ? {
-        street: checkout.street,
-        flat: checkout.flat ?? undefined,
-        doorphone: checkout.doorphone ?? undefined,
-        entrance: checkout.entrance ?? undefined,
-        floor: checkout.floor ?? undefined,
-        addressNote: checkout.addressNote ?? undefined,
-      }
-    : undefined
-  const time = new TZDate(checkout.time, channel.timeZone).toLocaleString(locale, { year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false })
-
-  const receivers = await getCheckoutReceivers()
-
-  // Prepare lines
-  const lines = []
-  for (const line of checkout.lines) {
-    const variant = await getProductVariant(line.productVariantId)
-    const product = await getProduct(variant?.productId ?? '')
-
-    lines.push({
-      ...line,
-      name: product?.name?.find((name) => name.locale === locale)?.value ?? '',
-      variant: variant?.name.find((name) => name.locale === locale)?.value ?? '',
-    })
-  }
-
-  for (const receiver of receivers) {
-    const data: NewCheckoutTemplate = {
-      id: checkout.id,
-      deliveryMethod: checkout.deliveryMethod,
-      time,
-      timeType: checkout.timeType,
-      paymentMethodName,
-      change: checkout.change ?? undefined,
-      name: checkout.name,
-      phone: checkout.phone,
-      note: checkout.note ?? undefined,
-      totalPrice: checkout.totalPrice,
-      warehouseAddress,
-      address,
-      lines,
-    }
-
-    if (receiver.type === 'EMAIL' && receiver.data.template === 'NEW_CHECKOUT') {
-      await sendEmail<NewCheckoutTemplate>(receiver, data)
-    }
-
-    if (receiver.type === 'HTTP') {
-      await sendHttp<NewCheckoutTemplate>(receiver, data)
-    }
-  }
-}
diff --git a/packages/core/server/api/checkout/receiver/[id].delete.ts b/packages/core/server/api/checkout/receiver/[id].delete.ts
new file mode 100644
index 00000000..03eb3c2c
--- /dev/null
+++ b/packages/core/server/api/checkout/receiver/[id].delete.ts
@@ -0,0 +1,27 @@
+import { deleteReceiver, getCheckoutReceiver } from '../../../../server/services/db/receiver'
+
+export default defineEventHandler(async (event) => {
+  try {
+    const id = getRouterParam(event, 'id')
+    if (!id) {
+      throw createError({
+        statusCode: 400,
+        statusMessage: 'Missing id',
+      })
+    }
+
+    const receiver = await getCheckoutReceiver(id)
+    if (!receiver?.id) {
+      throw createError({
+        statusCode: 404,
+        statusMessage: 'No receiver',
+      })
+    }
+
+    await deleteReceiver(id)
+
+    return { ok: true }
+  } catch (error) {
+    throw errorResolver(error)
+  }
+})
diff --git a/packages/core/server/api/checkout/receiver/[id].patch.ts b/packages/core/server/api/checkout/receiver/[id].patch.ts
new file mode 100644
index 00000000..55b77798
--- /dev/null
+++ b/packages/core/server/api/checkout/receiver/[id].patch.ts
@@ -0,0 +1,33 @@
+import { getCheckoutReceiver, patchReceiver } from '../../../../server/services/db/receiver'
+import { receiverUpdateSchema } from '../../../../shared/services/receiver'
+
+export default defineEventHandler(async (event) => {
+  try {
+    const id = getRouterParam(event, 'id')
+    if (!id) {
+      throw createError({
+        statusCode: 400,
+        statusMessage: 'Missing id',
+      })
+    }
+
+    const body = await readBody(event)
+    const data = receiverUpdateSchema.parse(body)
+
+    const receiver = await getCheckoutReceiver(id)
+    if (!receiver?.id) {
+      throw createError({
+        statusCode: 404,
+        statusMessage: 'No receiver',
+      })
+    }
+
+    await patchReceiver(id, {
+      ...data,
+    })
+
+    return { ok: true }
+  } catch (error) {
+    throw errorResolver(error)
+  }
+})
diff --git a/packages/core/server/api/checkout/receiver/index.post.ts b/packages/core/server/api/checkout/receiver/index.post.ts
new file mode 100644
index 00000000..07b57710
--- /dev/null
+++ b/packages/core/server/api/checkout/receiver/index.post.ts
@@ -0,0 +1,22 @@
+import { createId } from '@paralleldrive/cuid2'
+import { createReceiver } from '../../../../server/services/db/receiver'
+import { receiverCreateSchema } from './../../../../shared/services/receiver'
+
+export default defineEventHandler(async (event) => {
+  try {
+    const body = await readBody(event)
+    const data = receiverCreateSchema.parse(body)
+
+    const receiver = await createReceiver({
+      ...data,
+      id: createId(),
+    })
+
+    return {
+      ok: true,
+      result: receiver,
+    }
+  } catch (error) {
+    throw errorResolver(error)
+  }
+})
diff --git a/packages/core/server/middleware/01.permissions.ts b/packages/core/server/middleware/01.permissions.ts
index 853863fb..b1dd407e 100644
--- a/packages/core/server/middleware/01.permissions.ts
+++ b/packages/core/server/middleware/01.permissions.ts
@@ -20,6 +20,11 @@ const protectedRoutes: ProtectedRoute[] = [
     methods: ['GET'],
     requiredPermissions: ['MANAGE_CHECKOUTS', 'MASTER'],
   },
+  {
+    path: '/api/checkout/receiver',
+    methods: ['POST', 'PATCH', 'DELETE'],
+    requiredPermissions: ['MANAGE_OPTIONS', 'MASTER'],
+  },
   {
     path: '/api/link',
     methods: ['POST', 'PATCH', 'DELETE'],
diff --git a/packages/core/server/services/db/receiver.ts b/packages/core/server/services/db/receiver.ts
index 14eee00d..e1be02a1 100644
--- a/packages/core/server/services/db/receiver.ts
+++ b/packages/core/server/services/db/receiver.ts
@@ -1,5 +1,9 @@
 import { getKeys } from '.'
 
+export async function getCheckoutReceiver(id: string): Promise<CheckoutReceiver | null> {
+  return useStorage('db').getItem<CheckoutReceiver>(`receiver:${id}`)
+}
+
 export async function getCheckoutReceivers(): Promise<CheckoutReceiver[]> {
   const { receiverKeys } = await getKeys()
 
@@ -16,3 +20,33 @@ export async function getCheckoutReceivers(): Promise<CheckoutReceiver[]> {
 
   return receivers
 }
+
+export async function createReceiver(receiver: Omit<CheckoutReceiver, 'createdAt' | 'updatedAt'>): Promise<CheckoutReceiver | null> {
+  await useStorage('db').setItem(`receiver:${receiver.id}`, {
+    ...receiver,
+    createdAt: new Date().toISOString(),
+    updatedAt: new Date().toISOString(),
+  })
+  return getCheckoutReceiver(receiver.id)
+}
+
+export async function patchReceiver(id: string, data: Partial<CheckoutReceiver>): Promise<CheckoutReceiver | null> {
+  const receiver = await getCheckoutReceiver(id)
+  if (!receiver) {
+    return null
+  }
+
+  const newReceiver = {
+    ...receiver,
+    ...data,
+    updatedAt: new Date().toISOString(),
+  }
+
+  await useStorage('db').setItem(`receiver:${id}`, newReceiver)
+
+  return getCheckoutReceiver(id)
+}
+
+export async function deleteReceiver(id: string): Promise<void> {
+  await useStorage('db').removeItem(`receiver:${id}`)
+}
diff --git a/packages/core/server/services/receiver.ts b/packages/core/server/services/receiver.ts
new file mode 100644
index 00000000..f892e085
--- /dev/null
+++ b/packages/core/server/services/receiver.ts
@@ -0,0 +1,113 @@
+import type { CheckoutForReceiver } from '../../types/receiver'
+import { TZDate } from '@date-fns/tz'
+import { getKeys } from './db'
+import { getChannel } from './db/channel'
+import { getCheckout } from './db/checkout'
+import { getPaymentMethods } from './db/payment'
+import { getProduct, getProductVariant } from './db/product'
+import { getCheckoutReceivers } from './db/receiver'
+import { getWarehouses } from './db/warehouse'
+
+async function prepareCheckout(checkoutId: string): Promise<CheckoutForReceiver | null> {
+  const checkout = await getCheckout(checkoutId)
+  if (!checkout?.id) {
+    return null
+  }
+
+  const channel = await getChannel(checkout.channelId)
+  if (!channel?.id) {
+    return null
+  }
+
+  const locale = channel.defaultLocale
+
+  const { paymentMethodKeys, warehouseKeys } = await getKeys()
+
+  const paymentMethods = await getPaymentMethods(paymentMethodKeys)
+  const paymentMethodName = paymentMethods.find((p) => p.id === checkout?.paymentMethodId)?.name.find((name) => name.locale === locale)?.value ?? ''
+
+  const warehouses = await getWarehouses(warehouseKeys)
+  const warehouseAddress = warehouses.find((w) => w.id === checkout?.warehouseId)?.address
+  const address = checkout.street
+    ? {
+        street: checkout.street,
+        flat: checkout.flat ?? undefined,
+        doorphone: checkout.doorphone ?? undefined,
+        entrance: checkout.entrance ?? undefined,
+        floor: checkout.floor ?? undefined,
+        addressNote: checkout.addressNote ?? undefined,
+      }
+    : undefined
+  const time = new TZDate(checkout.time, channel.timeZone).toLocaleString(locale, { year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false })
+
+  const items = []
+  for (const line of checkout.lines) {
+    const variant = await getProductVariant(line.productVariantId)
+    const product = await getProduct(variant?.productId ?? '')
+
+    items.push({
+      ...line,
+      name: product?.name?.find((name) => name.locale === locale)?.value ?? '',
+      variant: variant?.name.find((name) => name.locale === locale)?.value ?? '',
+    })
+  }
+
+  return {
+    id: checkout.id,
+    deliveryMethod: checkout.deliveryMethod,
+    time,
+    timeType: checkout.timeType,
+    paymentMethodName,
+    change: checkout.change ?? undefined,
+    name: checkout.name,
+    phone: checkout.phone,
+    note: checkout.note ?? undefined,
+    totalPrice: checkout.totalPrice,
+    warehouseAddress,
+    address,
+    items,
+  }
+}
+
+async function sendHttp<T>(receiver: CheckoutReceiver, data: T) {
+  const logger = useLogger('sendHttp')
+
+  try {
+    await $fetch(receiver.url, {
+      method: receiver.method,
+      body: {
+        ...receiver.body,
+        checkout: data,
+      },
+      headers: receiver?.authorization
+        ? {
+            Authorization: receiver.authorization,
+          }
+        : undefined,
+    })
+
+    return true
+  } catch (error) {
+    logger.error(error)
+    return false
+  }
+}
+
+export async function sendToReceivers(checkoutId: string): Promise<void> {
+  const logger = useLogger('sendToReceivers')
+
+  try {
+    const checkout = await prepareCheckout(checkoutId)
+    if (!checkout) {
+      return
+    }
+
+    const receivers = await getCheckoutReceivers()
+
+    for (const receiver of receivers) {
+      await sendHttp<CheckoutForReceiver>(receiver, checkout)
+    }
+  } catch (error) {
+    logger.error(error)
+  }
+}
diff --git a/packages/core/server/utils/receiver.ts b/packages/core/server/utils/receiver.ts
deleted file mode 100644
index d988fe01..00000000
--- a/packages/core/server/utils/receiver.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-export async function sendEmail<T>(receiver: CheckoutReceiverTypeEmail, data: T) {
-  const logger = useLogger('sendEmail')
-
-  try {
-    await $fetch(receiver.data.url, {
-      method: receiver.data.method,
-      body: {
-        to: receiver.data.to,
-        template: receiver.data.template,
-        data,
-      },
-      headers: {
-        Authorization: `Bearer ${receiver.data.token}`,
-      },
-    })
-
-    return true
-  } catch (error) {
-    logger.error(error)
-
-    return false
-  }
-}
-
-export async function sendHttp<T>(receiver: CheckoutReceiverTypeHttp, data: T) {
-  const logger = useLogger('sendHttp')
-
-  try {
-    await $fetch(receiver.data.url, {
-      method: receiver.data.method,
-      body: {
-        data,
-      },
-      headers: {
-        Authorization: `Bearer ${receiver.data.token}`,
-      },
-    })
-
-    return true
-  } catch (error) {
-    logger.error(error)
-
-    return false
-  }
-}
diff --git a/packages/core/shared/services/receiver.ts b/packages/core/shared/services/receiver.ts
new file mode 100644
index 00000000..4125a279
--- /dev/null
+++ b/packages/core/shared/services/receiver.ts
@@ -0,0 +1,20 @@
+import { z } from 'zod'
+
+export const receiverCreateSchema = z.object({
+  name: z.string().min(2).max(50),
+  url: z.string().min(2).max(300),
+  method: z.enum(['POST', 'PUT']),
+  authorization: z.string().optional(),
+  body: z.record(z.string()).optional(),
+})
+
+export type ReceiverCreateSchema = z.output<typeof receiverCreateSchema>
+
+export const receiverUpdateSchema = z.object({
+  name: z.string().min(2).max(50).optional(),
+  url: z.string().min(2).max(300).optional(),
+  method: z.enum(['POST', 'PUT']).optional(),
+  authorization: z.string().optional(),
+})
+
+export type ReceiverUpdateSchema = z.output<typeof receiverUpdateSchema>
diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts
index 12b24d47..e84552e4 100644
--- a/packages/core/types/index.d.ts
+++ b/packages/core/types/index.d.ts
@@ -174,32 +174,15 @@ declare global {
     productVariantId: string
   }
 
-  type CheckoutReceiver = {
+  interface CheckoutReceiver {
     id: string
     createdAt: string
     updatedAt: string
-  } & CheckoutReceiverTypes
-
-  type CheckoutReceiverTypes = CheckoutReceiverTypeEmail | CheckoutReceiverTypeHttp
-
-  interface CheckoutReceiverTypeEmail {
-    type: 'EMAIL'
-    data: {
-      url: string
-      method: 'POST'
-      token: string
-      to: string
-      template: 'NEW_CHECKOUT'
-    }
-  }
-
-  interface CheckoutReceiverTypeHttp {
-    type: 'HTTP'
-    data: {
-      url: string
-      method: 'POST'
-      token: string
-    }
+    name: string
+    url: string
+    method: 'POST' | 'PUT'
+    authorization?: string
+    body?: Record<string, string>
   }
 
   interface CheckoutRegistryItem {
diff --git a/packages/core/types/receiver.ts b/packages/core/types/receiver.ts
new file mode 100644
index 00000000..522d5916
--- /dev/null
+++ b/packages/core/types/receiver.ts
@@ -0,0 +1,28 @@
+export interface CheckoutForReceiver {
+  id: string
+  deliveryMethod: CheckoutDeliveryMethod
+  time: string
+  timeType: Checkout['timeType']
+  paymentMethodName: string
+  change?: string
+  name: string
+  phone: string
+  note?: string
+  totalPrice: number
+  items: {
+    id: string
+    name: string
+    variant: string
+    quantity: number
+    totalPrice: number
+  }[]
+  warehouseAddress?: string
+  address?: {
+    street: string
+    flat?: string
+    doorphone?: string
+    entrance?: string
+    floor?: string
+    addressNote?: string
+  }
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (7)</summary><blockquote>

<details>
<summary>packages/core/types/receiver.ts (1)</summary><blockquote>

`1-28`: **Well-structured interface for checkout receiver data.**

The `CheckoutForReceiver` interface provides a comprehensive structure for representing checkout data when sending it to external receivers. The organization of fields is logical and the use of optional properties is appropriate.


Consider adding explicit imports for the referenced types (`CheckoutDeliveryMethod` and `Checkout`) to make the dependencies clearer:

```diff
+import type { Checkout, CheckoutDeliveryMethod } from './checkout'  // Adjust the path as needed
+
export interface CheckoutForReceiver {
  // ...existing code...
}
```

</blockquote></details>
<details>
<summary>packages/core/types/index.d.ts (1)</summary><blockquote>

`177-186`: **Well-defined CheckoutReceiver interface.**

The interface definition is complete with appropriate types for each property. The optional fields are correctly marked with the `?` operator.

Consider adding JSDoc comments to describe the purpose of each field, especially for fields like `authorization` and `body` where their usage may not be immediately obvious to other developers.

</blockquote></details>
<details>
<summary>packages/core/shared/services/receiver.ts (2)</summary><blockquote>

`3-9`: **Well-structured creation schema with appropriate constraints.**

The validation schema for receiver creation includes reasonable constraints:
- Name limited to 2-50 characters
- URL limited to 2-300 characters
- Method restricted to POST or PUT

Consider adding URL format validation to ensure the URL property is well-formed:

```diff
-  url: z.string().min(2).max(300),
+  url: z.string().min(2).max(300).url(),
```

---

`13-19`: **Update schema correctly makes all fields optional.**

The update schema appropriately makes all fields optional while maintaining the same validation constraints as the creation schema.

Apply the same URL validation improvement here if adopted for the creation schema:

```diff
-  url: z.string().min(2).max(300).optional(),
+  url: z.string().min(2).max(300).url().optional(),
```

</blockquote></details>
<details>
<summary>packages/core/server/services/db/receiver.ts (3)</summary><blockquote>

`24-31`: **Receiver creation function looks good.**

The function properly:
1. Creates a new receiver with the provided data
2. Sets timestamps for creation and update
3. Returns the newly created receiver

Consider adding error handling to catch potential storage operation failures:

```diff
 export async function createReceiver(receiver: Omit<CheckoutReceiver, 'createdAt' | 'updatedAt'>): Promise<CheckoutReceiver | null> {
+  try {
     await useStorage('db').setItem(`receiver:${receiver.id}`, {
       ...receiver,
       createdAt: new Date().toISOString(),
       updatedAt: new Date().toISOString(),
     })
     return getCheckoutReceiver(receiver.id)
+  } catch (error) {
+    console.error('Failed to create receiver:', error)
+    return null
+  }
 }
```

---

`33-48`: **Well-implemented receiver update function.**

The function correctly handles:
1. Checking if the receiver exists
2. Merging the update data with existing data
3. Updating the timestamp
4. Returning the updated receiver

Consider implementing the same error handling pattern suggested for the creation function.

---

`50-52`: **Simple deletion implementation looks good.**

The function correctly removes a receiver from storage by ID.

Consider adding error handling:

```diff
 export async function deleteReceiver(id: string): Promise<void> {
+  try {
     await useStorage('db').removeItem(`receiver:${id}`)
+  } catch (error) {
+    console.error('Failed to delete receiver:', error)
+    throw error
+  }
 }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7eeeec7a694d44a3294efca427f8e7c7eec61046 and c3061b3c17771327de61126ba2e6013f6910189d.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `apps/web-app/app/components/Modal/CreateMenu.vue` (1 hunks)
* `apps/web-app/stores/channel.ts` (3 hunks)
* `packages/core/server/api/channel/index.get.ts` (3 hunks)
* `packages/core/server/middleware/01.permissions.ts` (1 hunks)
* `packages/core/server/services/db/receiver.ts` (2 hunks)
* `packages/core/shared/services/receiver.ts` (1 hunks)
* `packages/core/types/index.d.ts` (1 hunks)
* `packages/core/types/receiver.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>packages/core/server/api/channel/index.get.ts (2)</summary><blockquote>

<details>
<summary>packages/core/server/services/db/index.ts (1)</summary>

* `getKeys` (13-35)

</details>
<details>
<summary>packages/core/server/services/db/receiver.ts (1)</summary>

* `getCheckoutReceivers` (7-22)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>apps/web-app/app/components/Modal/CreateMenu.vue (1)</summary>

`4-4`: **Improved user experience with immediate form feedback.**

The addition of the `@submitted` event handler closes the modal immediately when the form is submitted, making the UI feel more responsive rather than waiting for the success response, which might take time.

</details>
<details>
<summary>packages/core/server/middleware/01.permissions.ts (1)</summary>

`23-27`: **Appropriate permission protection added for checkout receiver endpoints.**

This change correctly restricts POST, PATCH, and DELETE operations on the checkout receiver endpoint to users with MANAGE_OPTIONS or MASTER permissions, consistent with the permission pattern used for similar resource types in the application.

</details>
<details>
<summary>packages/core/server/api/channel/index.get.ts (4)</summary>

`9-9`: **Added import for getCheckoutReceivers function.**

Correctly imports the necessary function to fetch checkout receivers.

---

`65-72`: **Improved readability of destructuring assignment.**

Breaking this destructuring assignment into multiple lines improves code readability and makes future additions easier.

---

`80-80`: **Fetching checkout receivers for channel data.**

This line fetches all checkout receivers to include in the channel data response, following the same pattern used for other entity types.

---

`101-101`: **Including receivers in the channel API response.**

Adding receivers to the returned object makes this data available to the client, completing the implementation of the checkout receiver feature.

</details>
<details>
<summary>apps/web-app/stores/channel.ts (3)</summary>

`27-27`: **Addition of receivers state looks good.**

The reactive reference for checkout receivers is properly typed and initialized with an empty array, following the established pattern for other store properties.

---

`73-73`: **Correct implementation of receivers data assignment.**

The new receivers data from the API is properly assigned to the reactive reference, maintaining consistency with the update pattern of other properties.

---

`164-164`: **Proper export of receivers state.**

The receivers reference is correctly included in the store's returned object, making it accessible to components that use this store.

</details>
<details>
<summary>packages/core/server/services/db/receiver.ts (1)</summary>

`3-5`: **Proper implementation of single receiver retrieval.**

This function correctly fetches a checkout receiver by ID with an appropriate return type of `Promise<CheckoutReceiver | null>`.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-04T17:15:01Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>🧹 Nitpick comments (21)</summary><blockquote>

<details>
<summary>apps/web-app/app/components/CommandCenter/CheckoutReceiverCard.vue (3)</summary><blockquote>

`1-9`: **Card layout looks good but consider adding more information**

The card component has a clean implementation with proper structure. However, it only displays the name of the receiver. Consider enhancing the UI by showing additional information such as the URL or method to improve usability.

---

`3-3`: **Icon inconsistency noted**

The icon used here (`i-lucide-combine`) is different from the one used in the navigation menu (`i-lucide-send`). Consider using the same icon for consistency across the UI.


```diff
- <UIcon name="i-lucide-combine" class="size-8 text-(--ui-text-dimmed)" />
+ <UIcon name="i-lucide-send" class="size-8 text-(--ui-text-dimmed)" />
```

---

`1-9`: **Add click handler for better component encapsulation**

According to the AI summary, this card triggers a modal when clicked, but there's no click handler defined in this component. Consider adding the click event handling here for better component encapsulation.


```diff
- <CommandCenterActiveCard class="text-center">
+ <CommandCenterActiveCard class="text-center" @click="$emit('click')">
```

And update the script section:

```diff
- defineProps<{ name: string }>()
+ defineProps<{ name: string }>()
+ defineEmits<{
+   (e: 'click'): void
+ }>()
```

</blockquote></details>
<details>
<summary>packages/core/server/api/checkout/receiver/index.post.ts (2)</summary><blockquote>

`1-4`: **Inconsistent import paths**

The import paths use different styles - some with multiple levels of parent directory references and some with more absolute-like paths. Consider standardizing these for better maintainability.


```diff
import { createId } from '@paralleldrive/cuid2'
- import { createReceiver } from '../../../../server/services/db/receiver'
- import { receiverCreateSchema } from './../../../../shared/services/receiver'
+ import { createReceiver } from '~~/server/services/db/receiver'
+ import { receiverCreateSchema } from '~~/shared/services/receiver'
```

---

`7-8`: **Add validation for unique receiver names**

The endpoint doesn't check if a receiver with the same name already exists. Consider adding this validation to prevent duplicate names.


```diff
  try {
    const body = await readBody(event)
    const data = receiverCreateSchema.parse(body)

+   // Check if receiver with same name already exists
+   const existingReceiver = await findReceiverByName(data.name)
+   if (existingReceiver) {
+     throw createError({
+       statusCode: 400,
+       message: 'A receiver with this name already exists'
+     })
+   }

    const receiver = await createReceiver({
```

</blockquote></details>
<details>
<summary>apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue (1)</summary><blockquote>

`4-4`: **Add error handling for form submission**

The component handles the `submitted` and `success` events, but there's no handling for potential submission errors. Consider adding error handling to provide feedback to users when submissions fail.


```diff
- <FormCreateCheckoutReceiver @submitted="closeAll" @success="closeAll" />
+ <FormCreateCheckoutReceiver 
+   @submitted="closeAll" 
+   @success="closeAll" 
+   @error="handleError" 
+ />
```

And add the error handling function to the script:

```javascript
function handleError(error) {
  // Display error notification to user
  useToast().add({
    title: $t('errors.submission_failed'),
    description: error.message || $t('errors.try_again'),
    color: 'red'
  })
}
```

</blockquote></details>
<details>
<summary>apps/web-app/app/components/Modal/UpdateCheckoutReceiver.vue (1)</summary><blockquote>

`18-22`: **Consider the implications of closing all overlays.**

The `closeAll` function closes every overlay in the application when the form is submitted or successful. This might cause unintended UX issues if you have multiple overlays open that the user may want to keep.


Consider a more targeted approach by closing only this specific modal:

```diff
function closeAll() {
-  for (const o of overlay.overlays) {
-    overlay.close(o.id)
-  }
+  // Close only the current modal
+  const currentOverlay = overlay.overlays.find(o => 
+    o.component?.name === 'UpdateCheckoutReceiver')
+  if (currentOverlay) {
+    overlay.close(currentOverlay.id)
+  }
}
```

Alternatively, if closing all overlays is the intended behavior (for example, if this is part of a wizard-like flow), consider renaming the function to something more descriptive like `closeAllOverlays`.

</blockquote></details>
<details>
<summary>packages/core/server/api/checkout/index.patch.ts (1)</summary><blockquote>

`48-50`: **Consider robust error handling for `sendToReceivers`**

If `sendToReceivers` fails or times out, consider how you want to handle finalizing the checkout. You could add a retry mechanism, a background job, or stronger error handling to ensure data consistency and a good user experience.

</blockquote></details>
<details>
<summary>apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue (2)</summary><blockquote>

`77-83`: **Consider adding a null check for receiver.value**

The computed property might return undefined if no receiver is found with the provided ID. This could cause runtime errors.

```diff
const state = ref<Partial<ReceiverUpdateSchema>>({
-  name: receiver.value?.name,
-  url: receiver.value?.url,
-  method: receiver.value?.method,
-  authorization: receiver.value?.authorization,
+  name: receiver.value?.name || '',
+  url: receiver.value?.url || '',
+  method: receiver.value?.method || 'POST',
+  authorization: receiver.value?.authorization || '',
})
```

---

`104-120`: **Add confirmation dialog before deletion**

For destructive operations like deletion, it's a good practice to add a confirmation dialog to prevent accidental deletions.

```diff
async function onDelete() {
+  if (!window.confirm(t('common.confirm-delete'))) {
+    return
+  }
  actionToast.start()
  emit('submitted')

  try {
    await $fetch(`/api/checkout/receiver/${id}`, {
      method: 'DELETE',
    })

    await channel.update()
    actionToast.success(t('toast.receiver-deleted'))
    emit('success')
  } catch (error) {
    console.error(error)
    actionToast.error()
  }
}
```

</blockquote></details>
<details>
<summary>apps/web-app/app/components/Form/CreateCheckoutReceiver.vue (3)</summary><blockquote>

`46-75`: **Consider adding ability to remove key-value pairs from body**

The UI allows adding new key-value pairs but doesn't provide a way to remove them if the user makes a mistake.

```diff
<div
  v-for="obj in bodyState"
  :key="obj.id"
  class="flex flex-row gap-3"
>
  <UInput
    v-model="obj.key"
    size="xl"
    :placeholder="$t('common.key')"
    class="w-full"
  />
  <UInput
    v-model="obj.value"
    size="xl"
    :placeholder="$t('common.value')"
    class="w-full"
  />
+  <UButton
+    v-if="bodyState.length > 1"
+    variant="subtle"
+    color="error"
+    size="xl"
+    icon="i-lucide-trash-2"
+    @click="removeKeyFromBody(obj.id)"
+  />
</div>
```

And add the corresponding method:

```diff
function addNewKeyToBody() {
  bodyState.value.push({ id: useId(), key: '', value: '' })
}

+function removeKeyFromBody(id: string) {
+  bodyState.value = bodyState.value.filter(obj => obj.id !== id)
+}
```

---

`107-113`: **Improve initial state types and values**

The type definition includes `undefined` for URL, but the API likely expects a string. Consider improving type consistency.

```diff
const state = ref<Partial<ReceiverCreateSchema>>({
  name: '',
-  url: undefined,
+  url: '',
  method: 'POST',
-  authorization: undefined,
+  authorization: '',
-  body: undefined,
+  body: {},
})
```

---

`120-121`: **Handle body preparation more robustly**

The current implementation modifies the state directly and assumes all key-value pairs are valid after filtering. Consider adding more validation and handling potential edge cases.

```diff
// Prepare body
bodyState.value = bodyState.value.filter((obj) => obj.key && obj.value)
-state.value.body = Object.fromEntries(bodyState.value.map((obj) => [obj.key, obj.value]))
+const bodyEntries = bodyState.value.map((obj) => [obj.key.trim(), obj.value.trim()])
+if (bodyEntries.length > 0) {
+  state.value.body = Object.fromEntries(bodyEntries)
+} else {
+  state.value.body = {}
+}
```

</blockquote></details>
<details>
<summary>packages/core/server/services/receiver.ts (2)</summary><blockquote>

`72-94`: **Add retry mechanism for HTTP requests**

For improved reliability when communicating with external systems, consider implementing a retry mechanism with exponential backoff.

```diff
async function sendHttp<T>(receiver: CheckoutReceiver, data: T) {
  const logger = useLogger('sendHttp')

+  const maxRetries = 3
+  let retries = 0
+  
  try {
-    await $fetch(receiver.url, {
-      method: receiver.method,
-      body: {
-        ...receiver.body,
-        checkout: data,
-      },
-      headers: receiver?.authorization
-        ? {
-            Authorization: receiver.authorization,
-          }
-        : undefined,
-    })
+    while (retries < maxRetries) {
+      try {
+        await $fetch(receiver.url, {
+          method: receiver.method,
+          body: {
+            ...receiver.body,
+            checkout: data,
+          },
+          headers: receiver?.authorization
+            ? {
+                Authorization: receiver.authorization,
+              }
+            : undefined,
+        })
+        return true
+      } catch (error) {
+        retries++
+        if (retries >= maxRetries) throw error
+        // Exponential backoff - wait longer between each retry
+        await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, retries)))
+      }
+    }

    return true
  } catch (error) {
    logger.error(error)
    return false
  }
}
```

---

`96-113`: **Consider adding batch processing capability**

For efficiency with multiple receivers, consider adding a concurrency limit to avoid overwhelming the system when many receivers are configured.

```diff
export async function sendToReceivers(checkoutId: string): Promise<void> {
  const logger = useLogger('sendToReceivers')

  try {
    const checkout = await prepareCheckout(checkoutId)
    if (!checkout) {
      return
    }

    const receivers = await getCheckoutReceivers()
+    
+    // Process in batches of 3 receivers at a time
+    const concurrencyLimit = 3
+    for (let i = 0; i < receivers.length; i += concurrencyLimit) {
+      const batch = receivers.slice(i, i + concurrencyLimit)
+      await Promise.all(
+        batch.map(receiver => sendHttp<CheckoutForReceiver>(receiver, checkout))
+      )
+    }
-    for (const receiver of receivers) {
-      await sendHttp<CheckoutForReceiver>(receiver, checkout)
-    }
  } catch (error) {
    logger.error(error)
  }
}
```

</blockquote></details>
<details>
<summary>apps/web-app/i18n/locales/en-US.json (3)</summary><blockquote>

`71-72`: **Add localization keys for common key-value pair.**

The new keys `"key": "Key"` and `"value": "Value"` have been added under the `common` section. Please verify that these keys match the terminology used across the application and are consistent in style with other locales.

---

`279-287`: **Extend create section with checkout receiver key.**

New keys have been added in the `"create"` block—including `"checkout-receiver": "Create Checkouts Receiver"`—to support the creation UI for checkout receivers. Please check that the phrasing is consistent with other create messages and follows the application's style guidelines.

---

`311-312`: **Extend update section with checkout receiver key.**

A new update entry `"checkout-receiver": "Update Checkouts Receiver"` has been introduced. Verify that its wording is consistent with the corresponding create key and that related components reference this key correctly.

</blockquote></details>
<details>
<summary>apps/web-app/i18n/locales/ru-RU.json (3)</summary><blockquote>

`71-72`: **Добавление локализационных ключей для пары «ключ-значение».**

Новые ключи `"key": "Ключ"` и `"value": "Значение"` добавлены в секцию `common`. Проверьте, что терминология соответствует остальным частям приложения и совпадает с используемыми в других локализациях.

---

`279-287`: **Расширение раздела создания с ключом получателя заявок.**

Новые элементы в блоке `"create"`, включая `"checkout-receiver": "Создать получателя заявок"`, добавлены для поддержки функциональности создания получателей заявок. Убедитесь, что формулировка согласована с английской локализацией и понятна для конечных пользователей.

---

`311-312`: **Расширение раздела обновления с ключом получателя заявок.**

Новый ключ `"checkout-receiver": "Обновить получателя заявок"` добавлен в раздел `"update"`. Проверьте, что перевод совпадает со стилистикой остальных разделов и что компоненты корректно его используют.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c3061b3c17771327de61126ba2e6013f6910189d and 305ad8920831fcf0cd5f44a3086f2c2e893244aa.

</details>

<details>
<summary>📒 Files selected for processing (18)</summary>

* `apps/web-app/app/components/CommandCenter/CheckoutReceiverCard.vue` (1 hunks)
* `apps/web-app/app/components/Form/CreateCheckoutReceiver.vue` (1 hunks)
* `apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue` (1 hunks)
* `apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue` (1 hunks)
* `apps/web-app/app/components/Modal/UpdateCheckoutReceiver.vue` (1 hunks)
* `apps/web-app/app/pages/command-center/settings.vue` (1 hunks)
* `apps/web-app/app/pages/command-center/settings/receivers.vue` (1 hunks)
* `apps/web-app/i18n/locales/en-US.json` (6 hunks)
* `apps/web-app/i18n/locales/ka-GE.json` (6 hunks)
* `apps/web-app/i18n/locales/ru-RU.json` (6 hunks)
* `packages/core/server/api/checkout/index.patch.ts` (1 hunks)
* `packages/core/server/api/checkout/receiver/[id].delete.ts` (1 hunks)
* `packages/core/server/api/checkout/receiver/[id].patch.ts` (1 hunks)
* `packages/core/server/api/checkout/receiver/index.post.ts` (1 hunks)
* `packages/core/server/services/receiver.ts` (1 hunks)
* `packages/core/server/utils/receiver.ts` (0 hunks)
* `packages/core/shared/services/receiver.ts` (1 hunks)
* `packages/core/types/receiver.ts` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* packages/core/server/utils/receiver.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (3)</summary>

<details>
<summary>packages/core/server/api/checkout/receiver/index.post.ts (1)</summary><blockquote>

<details>
<summary>packages/core/shared/services/receiver.ts (1)</summary>

* `receiverCreateSchema` (3-9)

</details>

</blockquote></details>
<details>
<summary>packages/core/server/services/receiver.ts (1)</summary><blockquote>

<details>
<summary>packages/core/types/receiver.ts (1)</summary>

* `CheckoutForReceiver` (1-28)

</details>

</blockquote></details>
<details>
<summary>packages/core/server/api/checkout/receiver/[id].patch.ts (1)</summary><blockquote>

<details>
<summary>packages/core/shared/services/receiver.ts (1)</summary>

* `receiverUpdateSchema` (13-18)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (22)</summary><blockquote>

<details>
<summary>apps/web-app/app/pages/command-center/settings.vue (1)</summary>

`36-39`: **Navigation menu entry successfully added**

The new navigation item for checkout receivers has been properly implemented, following the same structure as the existing items. This addition will allow users to access the receivers management page from the settings menu.

</details>
<details>
<summary>packages/core/server/api/checkout/receiver/[id].patch.ts (1)</summary>

`1-33`: **API endpoint implementation looks robust.**

The handler properly validates the ID parameter, parses the request body against the schema, checks the receiver's existence before updating, and includes error handling. This follows RESTful API best practices.

However, there are some missing imports that might be assumed to be globally available:
- `defineEventHandler`
- `getRouterParam`
- `createError`
- `readBody`
- `errorResolver`

</details>
<details>
<summary>packages/core/server/api/checkout/receiver/[id].delete.ts (1)</summary>

`1-27`: **Delete endpoint implementation looks good.**

The handler properly validates the ID parameter, checks the receiver's existence before deletion, and includes error handling. This follows RESTful API best practices.

As with the patch endpoint, there are several implicit imports not declared:
- `defineEventHandler`
- `getRouterParam`
- `createError`
- `errorResolver`

</details>
<details>
<summary>packages/core/types/receiver.ts (1)</summary>

`1-28`: **The interface definition is well-structured.**

The `CheckoutForReceiver` interface provides a comprehensive data structure for checkout data, properly marking optional fields with `?` and using appropriate type references.

Note that the interface depends on types that aren't imported in this file:
- `CheckoutDeliveryMethod`
- `Checkout`

Ensure these types are properly exported and available to prevent compilation errors.

</details>
<details>
<summary>apps/web-app/app/pages/command-center/settings/receivers.vue (2)</summary>

`19-27`: **Clean composition**

The script setup with modal creation and store usage is straightforward and maintainable. No issues found.

---

`4-9`: <details>
<summary>✅ Verification successful</summary>

**Ensure a guaranteed unique key**

Using `:key="receiver.id"` is normally recommended, but ensure that `receiver.id` always remains unique. If there's any chance the ID might be missing or become duplicated, consider using a fallback or generating a short UUID.

---

🏁 Script executed:

```shell
#!/bin/bash
# Checking references to `receiver.id` in the codebase to ensure uniqueness
rg -A 5 "receiver\.id"
```

Length of output: 2313

---



</details>

**Unique Key Verification in Receiver Cards**

- The `receiver.id` is consistently used across the codebase (in both the Vue component and related database operations) as the unique identifier.
- Our search didn’t reveal any issues with missing or duplicate IDs, suggesting that the uniqueness is maintained as expected.
- As long as the backend guarantees unique `receiver.id` values, no fallback is necessary. However, remain cautious if the data source might ever provide non-unique or missing IDs.

</details>
<details>
<summary>packages/core/shared/services/receiver.ts (1)</summary>

`3-9`: **Schema looks well-defined**

The validation constraints for `name`, `url`, and `method` appear appropriate. This is a good approach.

</details>
<details>
<summary>packages/core/server/api/checkout/index.patch.ts (1)</summary>

`3-3`: **Discrepancy with the AI summary**

The AI summary mentioned removing `sendToReceivers`, but it's still imported here. This discrepancy is likely an inconsistency in the summary.



> Likely an incorrect or invalid review comment.

</details>
<details>
<summary>apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue (1)</summary>

`1-63`: **Form layout and structure look good**

The form includes all necessary fields for receiver updates with proper localization and styling. The UForm component with schema validation helps ensure data integrity.

</details>
<details>
<summary>packages/core/server/services/receiver.ts (1)</summary>

`11-70`: **Well-structured checkout preparation function**

The `prepareCheckout` function properly handles all the necessary data retrieval and transformation to construct a comprehensive checkout object for receivers. Good error handling with null checks at critical points.

</details>
<details>
<summary>apps/web-app/i18n/locales/ka-GE.json (6)</summary>

`71-72`: **Key-value translations added correctly**

The "key" and "value" translations are added correctly for use in the receiver body form.

---

`235-239`: **Receiver method translations added correctly**

The translations for HTTP method, authorization header, and body are correctly implemented.

---

`260-260`: **Menu translation for checkout receivers added**

The translation for the checkout receivers menu item is properly added.

---

`286-287`: **Create translations added correctly**

Translations for creating pages and checkout receivers are properly added.

---

`311-312`: **Update translations added correctly**

Translations for updating pages and checkout receivers are properly added.

---

`352-354`: **Toast message translations for receiver operations**

The toast message translations for receiver operations are properly added, ensuring users receive appropriate feedback in the Georgian language.

</details>
<details>
<summary>apps/web-app/i18n/locales/en-US.json (3)</summary>

`235-239`: **Introduce receiver localization object.**

A new `receiver` object with keys `"method"`, `"authorization"`, and `"body"` has been added. This will support receiver-related UI components. Ensure that the wording aligns with your component props and that any corresponding documentation is updated.

---

`260-260`: **Add checkout receivers menu entry.**

The `"checkout-receivers": "Checkout receivers"` key has been added in the menu section. Confirm that any components referencing this key use the correct identifier.

---

`352-354`: **Add new toast messages for receiver actions.**

The toast notifications `"receiver-created"`, `"receiver-updated"`, and `"receiver-deleted"` have been added to provide feedback for receiver operations. Their messages appear consistent with other toast messages.

</details>
<details>
<summary>apps/web-app/i18n/locales/ru-RU.json (3)</summary>

`235-239`: **Введение объекта локализации для получателя.**

Новый объект `receiver` с ключами `"method"`, `"authorization"` и `"body"` добавлен для поддержки Receiver-related интерфейсных компонентов. Убедитесь, что терминология подходит для русскоязычных пользователей и задокументирована.

---

`260-260`: **Добавление ключа для получателей заявок в меню.**

Новый ключ `"checkout-receivers": "Получатели заявок"` добавлен в раздел меню. Проверьте, что компоненты, обращающиеся к этому ключу, используют корректный идентификатор.

---

`352-354`: **Добавление новых сообщений для уведомлений о действиях с получателем.**

Добавлены новые toast-сообщения `"receiver-created"`, `"receiver-updated"` и `"receiver-deleted"`, информирующие пользователя о создании, обновлении и удалении получателя соответственно. Сообщения выглядят согласованными с другими уведомлениями.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-04T17:39:45Z

Review comments:
diff_hunk: @@ -0,0 +1,22 @@
+import { createId } from '@paralleldrive/cuid2'
+import { createReceiver } from '../../../../server/services/db/receiver'
+import { receiverCreateSchema } from './../../../../shared/services/receiver'
+
+export default defineEventHandler(async (event) => {
+  try {
+    const body = await readBody(event)
+    const data = receiverCreateSchema.parse(body)
+
+    const receiver = await createReceiver({
+      ...data,
+      id: createId(),
+    })
+
+    return {
+      ok: true,
+      result: receiver,
+    }
+  } catch (error) {
+    throw errorResolver(error)
+  }
+})
path: packages/core/server/api/checkout/receiver/index.post.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Add authentication and authorization checks**

The endpoint is missing authentication and authorization checks to ensure only authorized users can create receivers. Consider adding middleware or inline checks to verify user permissions.


```diff
export default defineEventHandler(async (event) => {
+  // Ensure user is authenticated and has permissions to manage receivers
+  const user = event.context.user
+  if (!user || !user.hasPermission('manage:receivers')) {
+    throw createError({
+      statusCode: 403,
+      message: 'Unauthorized to manage receivers'
+    })
+  }
+
  try {
    const body = await readBody(event)
    const data = receiverCreateSchema.parse(body)
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export default defineEventHandler(async (event) => {
  // Ensure user is authenticated and has permissions to manage receivers
  const user = event.context.user
  if (!user || !user.hasPermission('manage:receivers')) {
    throw createError({
      statusCode: 403,
      message: 'Unauthorized to manage receivers'
    })
  }

  try {
    const body = await readBody(event)
    const data = receiverCreateSchema.parse(body)

    const receiver = await createReceiver({
      ...data,
      id: createId(),
    })

    return {
      ok: true,
      result: receiver,
    }
  } catch (error) {
    throw errorResolver(error)
  }
})
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-04T17:39:44Z
author_association: NONE
diff_hunk: @@ -0,0 +1,22 @@
+import { createId } from '@paralleldrive/cuid2'
+import { createReceiver } from '../../../../server/services/db/receiver'
+import { receiverCreateSchema } from './../../../../shared/services/receiver'
+
+export default defineEventHandler(async (event) => {
+  try {
+    const body = await readBody(event)
+    const data = receiverCreateSchema.parse(body)
+
+    const receiver = await createReceiver({
+      ...data,
+      id: createId(),
+    })
+
+    return {
+      ok: true,
+      result: receiver,
+    }
+  } catch (error) {
+    throw errorResolver(error)
+  }
path: packages/core/server/api/checkout/receiver/index.post.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing import for errorResolver**

The `errorResolver` function is used but not imported. Make sure to add the import.


```diff
import { createId } from '@paralleldrive/cuid2'
import { createReceiver } from '../../../../server/services/db/receiver'
import { receiverCreateSchema } from './../../../../shared/services/receiver'
+ import { errorResolver } from '~~/server/utils/error'
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import { createId } from '@paralleldrive/cuid2'
import { createReceiver } from '../../../../server/services/db/receiver'
import { receiverCreateSchema } from './../../../../shared/services/receiver'
import { errorResolver } from '~~/server/utils/error'

// ... other code in the file ...

  } catch (error) {
    throw errorResolver(error)
  }
  
// ... rest of the file ...
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-04T17:39:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,17 @@
+<template>
+  <UModal :title="$t('center.create.checkout-receiver')">
+    <template #body>
+      <FormCreateCheckoutReceiver @submitted="closeAll" @success="closeAll" />
+    </template>
+  </UModal>
+</template>
+
+<script setup lang="ts">
+const overlay = useOverlay()
+
+function closeAll() {
+  for (const o of overlay.overlays) {
+    overlay.close(o.id)
+  }
+}
path: apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Improve modal closing logic**

The current `closeAll()` function closes all overlays in the application, which could be problematic if multiple modals are open. Consider modifying this to only close the current modal.


```diff
- function closeAll() {
-   for (const o of overlay.overlays) {
-     overlay.close(o.id)
-   }
- }
+ function closeAll() {
+   // Close only this modal instead of all overlays
+   const currentModal = overlay.overlays.find(o => 
+     o.component?.name === 'CreateCheckoutReceiver'
+   )
+   if (currentModal) {
+     overlay.close(currentModal.id)
+   }
+ }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
function closeAll() {
  // Close only this modal instead of all overlays
  const currentModal = overlay.overlays.find(o => 
    o.component?.name === 'CreateCheckoutReceiver'
  )
  if (currentModal) {
    overlay.close(currentModal.id)
  }
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-04T17:39:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,20 @@
+import { z } from 'zod'
+
+export const receiverCreateSchema = z.object({
+  name: z.string().min(2).max(50),
+  url: z.string().min(2).max(300),
+  method: z.enum(['POST', 'PUT']),
+  authorization: z.string().optional(),
+  body: z.record(z.string()).optional(),
+})
+
+export type ReceiverCreateSchema = z.output<typeof receiverCreateSchema>
+
+export const receiverUpdateSchema = z.object({
+  name: z.string().min(2).max(50).optional(),
+  url: z.string().min(2).max(300).optional(),
+  method: z.enum(['POST', 'PUT']).optional(),
+  authorization: z.string().optional(),
+})
+
+export type ReceiverUpdateSchema = z.output<typeof receiverUpdateSchema>
path: packages/core/shared/services/receiver.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider adding `body` to the update schema**

If updating the `body` property is intended, add it to the `receiverUpdateSchema` to maintain consistency with the create schema. Otherwise, verify that excluding it is intentional.

```diff
 export const receiverUpdateSchema = z.object({
   name: z.string().min(2).max(50).optional(),
   url: z.string().min(2).max(300).optional(),
   method: z.enum(['POST', 'PUT']).optional(),
   authorization: z.string().optional(),
+  body: z.record(z.string()).optional(),
 })
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export const receiverUpdateSchema = z.object({
  name: z.string().min(2).max(50).optional(),
  url: z.string().min(2).max(300).optional(),
  method: z.enum(['POST', 'PUT']).optional(),
  authorization: z.string().optional(),
  body: z.record(z.string()).optional(),
})

export type ReceiverUpdateSchema = z.output<typeof receiverUpdateSchema>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-04T17:39:45Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-04T17:10:42Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request introduces several receiver-related enhancements across the application. The changes add an event listener in the CreateMenu component to trigger the closeAll method on both submit and success events. In the web-app store, a reactive `receivers` variable is added and updated from API data. The API layer now imports and calls `getCheckoutReceivers` to include checkout receivers in its response. New middleware protections for receiver-related endpoints have been added, and comprehensive CRUD functions for checkout receivers are implemented in the database service. Additionally, validation schemas and updated type interfaces have been introduced to standardize receiver data.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `apps/web-app/app/components/Modal/CreateMenu.vue` | Added event listener `@submitted="closeAll"` to trigger the `closeAll` method along with the existing `@success` event. |
| `apps/web-app/stores/channel.ts` | Introduced a new reactive reference `receivers` (of type `CheckoutReceiver[]`), updated in the `update` function from API data, and exposed in the store return. |
| `packages/core/server/api/channel/index.get.ts` | Imported and invoked `getCheckoutReceivers`, reformatted destructuring for readability, and included `receivers` in the API response. |
| `packages/core/server/middleware/01.permissions.ts` | Added a new protected route (`/api/checkout/receiver`) with methods POST, PATCH, DELETE and required permissions `MANAGE_OPTIONS` and `MASTER`. |
| `packages/core/server/services/db/receiver.ts` | Added asynchronous functions for CheckoutReceiver CRUD operations: `getCheckoutReceiver`, `createReceiver`, `patchReceiver`, and `deleteReceiver`. |
| `packages/core/shared/services/receiver.ts` | Introduced Zod validation schemas (`receiverCreateSchema` and `receiverUpdateSchema`) along with corresponding exported types for receiver data validation. |
| `packages/core/types/index.d.ts`<br>`packages/core/types/receiver.ts` | Converted the `CheckoutReceiver` type to an interface with added fields (`name`, `url`, `method`, etc.) and added a new `CheckoutForReceiver` interface to define the receiver-specific checkout object structure. |
| `apps/web-app/app/components/CommandCenter/CheckoutReceiverCard.vue` | New component added for displaying checkout receiver information. |
| `apps/web-app/app/components/Form/CreateCheckoutReceiver.vue` | New component added for creating a checkout receiver with a form. |
| `apps/web-app/app/components/Form/UpdateCheckoutReceiver.vue` | New component added for updating a checkout receiver's details with a form. |
| `apps/web-app/app/components/Modal/CreateCheckoutReceiver.vue` | New modal component added for creating a checkout receiver. |
| `apps/web-app/app/components/Modal/UpdateCheckoutReceiver.vue` | New modal component added for updating a checkout receiver. |
| `apps/web-app/app/pages/command-center/settings.vue` | Added navigation item for "checkout-receivers" in settings. |
| `apps/web-app/app/pages/command-center/settings/receivers.vue` | New component added for managing checkout receivers in a grid layout. |
| `apps/web-app/i18n/locales/en-US.json`<br>`apps/web-app/i18n/locales/ka-GE.json`<br>`apps/web-app/i18n/locales/ru-RU.json` | Updated localization files with new keys and modified existing phrases related to receivers. |
| `packages/core/server/api/checkout/index.patch.ts` | Removed the `sendToReceivers` function and related import statements, refactoring checkout notification logic. |
| `packages/core/server/api/checkout/receiver/[id].delete.ts` | New event handler added for deleting a checkout receiver by ID. |
| `packages/core/server/api/checkout/receiver/[id].patch.ts` | New event handler added for updating a checkout receiver by ID. |
| `packages/core/server/api/checkout/receiver/index.post.ts` | New event handler added for creating a checkout receiver. |
| `packages/core/server/services/receiver.ts` | New service functions for preparing checkout data and sending it to receivers. |
| `packages/core/server/utils/receiver.ts` | Removed functions for sending emails and HTTP requests related to receivers. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant API
    participant DBService
    participant Store

    Client->>API: Request channel data
    API->>DBService: call getCheckoutReceivers()
    DBService-->>API: Return checkout receivers
    API-->>Client: Response with receivers and other data
    Client->>Store: Update state with receivers
```

```mermaid
sequenceDiagram
    participant FormCreateMenu
    participant CreateMenu

    FormCreateMenu->>CreateMenu: Emit "submitted" event
    CreateMenu-->>CreateMenu: Invoke closeAll()
    
    FormCreateMenu->>CreateMenu: Emit "success" event
    CreateMenu-->>CreateMenu: Invoke closeAll()
```

## Poem
> Oh, how I hop with glee,  
> Code and changes set us free.  
> New events and data flow in the night,  
> CRUD functions sparkle, shining bright.  
> With every line, a carrot delight!  
> From CodeRabbit, cheers in flight!  
> 🥕🐇

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=nextorders/food&utm_content=368):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-04T17:32:07Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=next-orders_food&pullRequest=368) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=next-orders_food&pullRequest=368&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=next-orders_food&pullRequest=368&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=next-orders_food&pullRequest=368&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=next-orders_food&pullRequest=368&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.7% Duplication on New Code](https://sonarcloud.io/component_measures?id=next-orders_food&pullRequest=368&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=next-orders_food&pullRequest=368)


author_association: NONE