diff --git a/backend/app/DomainObjects/EventSettingDomainObject.php b/backend/app/DomainObjects/EventSettingDomainObject.php
index baae6217..df26b17c 100644
--- a/backend/app/DomainObjects/EventSettingDomainObject.php
+++ b/backend/app/DomainObjects/EventSettingDomainObject.php
@@ -4,6 +4,8 @@
use HiEvents\DataTransferObjects\AddressDTO;
use HiEvents\Helper\AddressHelper;
+use Liquid\Liquid;
+use Liquid\Template;
class EventSettingDomainObject extends Generated\EventSettingDomainObjectAbstract
{
@@ -41,4 +43,31 @@ public function getAddress(): AddressDTO
country: $this->getLocationDetails()['country'] ?? null,
);
}
+
+ /**
+ * Get the offline payment instructions with Liquid template variables processed
+ *
+ * @param array $variables Variables to use in template processing
+ * @return string|null Processed instructions or null if no instructions set
+ */
+ public function getProcessedOfflinePaymentInstructions(array $variables = []): ?string
+ {
+ $instructions = $this->getOfflinePaymentInstructions();
+ if (!$instructions) {
+ return null;
+ }
+
+ try {
+ $template = new Template();
+ $template->parse($instructions);
+ return $template->render($variables);
+ } catch (\Throwable $e) {
+ // If template processing fails, return original instructions
+ \Log::error('Error processing Liquid template for offline payment instructions', [
+ 'error' => $e->getMessage(),
+ 'instructions' => $instructions
+ ]);
+ return $instructions;
+ }
+ }
}
diff --git a/backend/composer.json b/backend/composer.json
index b510c419..30cf05ac 100644
--- a/backend/composer.json
+++ b/backend/composer.json
@@ -19,6 +19,7 @@
"laravel/tinker": "^2.8",
"laravel/vapor-core": "^2.37",
"league/flysystem-aws-s3-v3": "^3.0",
+ "liquid/liquid": "^1.4",
"maatwebsite/excel": "^3.1",
"nette/php-generator": "^4.0",
"php-open-source-saver/jwt-auth": "^2.1",
diff --git a/backend/composer.lock b/backend/composer.lock
index 3fbda137..f1e7668d 100644
--- a/backend/composer.lock
+++ b/backend/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "62f38e147541b5968f5fe0e08abb6c57",
+ "content-hash": "617d2e62c958866b961b8ab4dd605039",
"packages": [
{
"name": "amphp/amp",
@@ -4358,6 +4358,75 @@
],
"time": "2024-12-08T08:18:47+00:00"
},
+ {
+ "name": "liquid/liquid",
+ "version": "1.4.43",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kalimatas/php-liquid.git",
+ "reference": "3a472d5a87fd40c075eea2c4afec5028564e00c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kalimatas/php-liquid/zipball/3a472d5a87fd40c075eea2c4afec5028564e00c1",
+ "reference": "3a472d5a87fd40c075eea2c4afec5028564e00c1",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "ergebnis/composer-normalize": ">=2.8",
+ "friendsofphp/php-cs-fixer": "^3.22",
+ "infection/infection": ">=0.17.6",
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^9.2.6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Liquid\\": "src/Liquid"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guz Alexander",
+ "email": "kalimatas@gmail.com",
+ "homepage": "http://guzalexander.com"
+ },
+ {
+ "name": "Harald Hanek"
+ },
+ {
+ "name": "Mateo Murphy"
+ },
+ {
+ "name": "Alexey Kopytko",
+ "email": "alexey@kopytko.com",
+ "homepage": "https://www.alexeykopytko.com/"
+ }
+ ],
+ "description": "Liquid template engine for PHP",
+ "homepage": "https://github.com/kalimatas/php-liquid",
+ "keywords": [
+ "liquid",
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/kalimatas/php-liquid/issues",
+ "source": "https://github.com/kalimatas/php-liquid/tree/1.4.43"
+ },
+ "time": "2024-10-04T06:14:41+00:00"
+ },
{
"name": "maatwebsite/excel",
"version": "3.1.55",
@@ -13040,13 +13109,13 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": "^8.2",
"ext-intl": "*"
},
- "platform-dev": [],
- "plugin-api-version": "2.2.0"
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
}
diff --git a/backend/resources/views/emails/orders/summary.blade.php b/backend/resources/views/emails/orders/summary.blade.php
index e0b426b7..b1b6bba7 100644
--- a/backend/resources/views/emails/orders/summary.blade.php
+++ b/backend/resources/views/emails/orders/summary.blade.php
@@ -21,7 +21,17 @@
{{ __('Payment Instructions') }}
{{ __('Please follow the instructions below to complete your payment.') }}
-{!! $eventSettings->getOfflinePaymentInstructions() !!}
+{!! $eventSettings->getProcessedOfflinePaymentInstructions([
+ 'order_short_id' => $order->getShortId(),
+ 'order_public_id' => $order->getPublicId(),
+ 'order_first_name' => $order->getFirstName(),
+ 'order_last_name' => $order->getLastName(),
+ 'order_email' => $order->getEmail(),
+ 'order_total_gross' => $order->getTotalGross(),
+ 'order_currency' => $event->getCurrency(),
+ 'order_items' => $order->getOrderItems(),
+ 'client_language' => app()->getLocale()
+]) !!}
@endif
diff --git a/frontend/package.json b/frontend/package.json
index 579dce4e..84c660e9 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -56,6 +56,7 @@
"cross-env": "^7.0.3",
"dayjs": "^1.11.8",
"express": "^4.19.2",
+ "liquidjs": "^10.21.0",
"qr-scanner": "^1.4.2",
"query-string": "^8.1.0",
"react": "^18.2.0",
diff --git a/frontend/src/components/layouts/EventHomepage/EventInformation/index.tsx b/frontend/src/components/layouts/EventHomepage/EventInformation/index.tsx
index ef21671e..14fa426e 100644
--- a/frontend/src/components/layouts/EventHomepage/EventInformation/index.tsx
+++ b/frontend/src/components/layouts/EventHomepage/EventInformation/index.tsx
@@ -6,9 +6,11 @@ import {Button} from "@mantine/core";
import {LoadingMask} from "../../../common/LoadingMask";
import {ShareComponent} from "../../../common/ShareIcon";
import {eventCoverImageUrl, eventHomepageUrl} from "../../../../utilites/urlHelper.ts";
-import {FC} from "react";
+import {FC, useMemo} from "react";
import {Event} from "../../../../types.ts";
import {EventDateRange} from "../../../common/EventDateRange";
+import { getClientLocale } from "../../../../locales.ts";
+import { Liquid } from "liquidjs";
export const EventInformation: FC<{
event: Event
@@ -17,6 +19,21 @@ export const EventInformation: FC<{
if (!event) {
return ;
}
+
+ const processedDescription = useMemo(() => {
+ if (!event.description) return '';
+
+ const engine = new Liquid();
+ try {
+ const clientLocale = getClientLocale();
+ return engine.parseAndRenderSync(event.description, {
+ client_language: clientLocale
+ });
+ } catch (error) {
+ console.error("Error processing liquid template:", error);
+ return event.description;
+ }
+ }, [event.description]);
return (
<>
@@ -75,7 +92,7 @@ export const EventInformation: FC<{
)}
diff --git a/frontend/src/components/routes/product-widget/OrderSummaryAndProducts/index.tsx b/frontend/src/components/routes/product-widget/OrderSummaryAndProducts/index.tsx
index a8c75ff4..7e863ddd 100644
--- a/frontend/src/components/routes/product-widget/OrderSummaryAndProducts/index.tsx
+++ b/frontend/src/components/routes/product-widget/OrderSummaryAndProducts/index.tsx
@@ -15,6 +15,7 @@ import {
IconPrinter,
IconUser
} from "@tabler/icons-react";
+import { OfflinePaymentMethod } from "../Payment/PaymentMethods/Offline";
import {useGetOrderPublic} from "../../../../queries/useGetOrderPublic.ts";
import {eventCheckoutPath} from "../../../../utilites/urlHelper.ts";
@@ -223,16 +224,9 @@ const PostCheckoutMessage = ({ message }: { message: string }) => (
);
-const OfflinePaymentInstructions = ({ event }: { event: Event }) => (
+const OfflinePaymentInstructions = ({ event, order }: { event: Event, order: Order }) => (
-
{t`Payment Instructions`}
-
-
-
+
);
@@ -260,7 +254,7 @@ export const OrderSummaryAndProducts = () => {
- {order?.status === 'AWAITING_OFFLINE_PAYMENT' && }
+ {order?.status === 'AWAITING_OFFLINE_PAYMENT' && }
{t`Order Details`}
diff --git a/frontend/src/components/routes/product-widget/Payment/PaymentMethods/Offline/index.tsx b/frontend/src/components/routes/product-widget/Payment/PaymentMethods/Offline/index.tsx
index cd3c5b00..5114871b 100644
--- a/frontend/src/components/routes/product-widget/Payment/PaymentMethods/Offline/index.tsx
+++ b/frontend/src/components/routes/product-widget/Payment/PaymentMethods/Offline/index.tsx
@@ -1,13 +1,52 @@
-import {Event} from "../../../../../../types.ts";
-import {Card} from "../../../../../common/Card";
-import {t} from "@lingui/macro";
+import { Event, Order } from "../../../../../../types.ts";
+import { Card } from "../../../../../common/Card";
+import {getClientLocale} from "../../../../../../locales";
+import { t } from "@lingui/macro";
+import { Liquid } from 'liquidjs';
+import React from 'react';
interface OfflinePaymentMethodProps {
event: Event;
+ order?: Order;
}
-export const OfflinePaymentMethod = ({event}: OfflinePaymentMethodProps) => {
+export const OfflinePaymentMethod = ({ event, order }: OfflinePaymentMethodProps) => {
const eventSettings = event?.settings;
+ // Initialize Liquid engine with default settings
+ const engine = new Liquid();
+
+ const replaceVariables = async (text: string) => {
+ if (!text || !order) return text;
+
+ const variables = {
+ order_short_id: order.short_id,
+ order_public_id: order.public_id,
+ order_first_name: order.first_name,
+ order_last_name: order.last_name,
+ order_email: order.email,
+ order_total_gross: order.total_gross,
+ order_currency: order.currency,
+ order_items: order.order_items,
+ client_language: getClientLocale()
+ };
+
+ try {
+ return await engine.parseAndRender(text, variables);
+ } catch (error) {
+ console.error('Error processing Liquid template:', error);
+ return text;
+ }
+ };
+
+ const [processedInstructions, setProcessedInstructions] = React.useState(eventSettings?.offline_payment_instructions || "");
+
+ React.useEffect(() => {
+ const processInstructions = async () => {
+ const processed = await replaceVariables(eventSettings?.offline_payment_instructions || "");
+ setProcessedInstructions(processed);
+ };
+ processInstructions();
+ }, [eventSettings?.offline_payment_instructions, order]);
return (
@@ -15,7 +54,7 @@ export const OfflinePaymentMethod = ({event}: OfflinePaymentMethodProps) => {
diff --git a/frontend/src/components/routes/product-widget/Payment/index.tsx b/frontend/src/components/routes/product-widget/Payment/index.tsx
index 44ceb77a..3d6729f6 100644
--- a/frontend/src/components/routes/product-widget/Payment/index.tsx
+++ b/frontend/src/components/routes/product-widget/Payment/index.tsx
@@ -90,7 +90,7 @@ const Payment = () => {
{isOfflineEnabled && (
-
+
)}