Squashed commit message
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
This commit is contained in:
26
backend/src/app/Handlers/BlocksHandler.php
Executable file
26
backend/src/app/Handlers/BlocksHandler.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Services\BlocksService;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
|
||||
class BlocksHandler
|
||||
{
|
||||
private BlocksService $blocksService;
|
||||
|
||||
public function __construct(BlocksService $blocksService)
|
||||
{
|
||||
$this->blocksService = $blocksService;
|
||||
}
|
||||
|
||||
public function processBlock(Request $request): JsonResponse
|
||||
{
|
||||
$block = $request->json();
|
||||
|
||||
$data = $this->blocksService->process($block);
|
||||
|
||||
return new JsonResponse(compact('data'));
|
||||
}
|
||||
}
|
||||
54
backend/src/app/Handlers/CartHandler.php
Executable file
54
backend/src/app/Handlers/CartHandler.php
Executable file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Services\CartService;
|
||||
use Cart\Cart;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
|
||||
class CartHandler
|
||||
{
|
||||
private Cart $cart;
|
||||
private CartService $cartService;
|
||||
|
||||
public function __construct(Cart $cart, CartService $cartService)
|
||||
{
|
||||
$this->cart = $cart;
|
||||
$this->cartService = $cartService;
|
||||
}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$items = $this->cartService->getCart();
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $items,
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkout(Request $request): JsonResponse
|
||||
{
|
||||
$items = $request->json();
|
||||
|
||||
foreach ($items as $item) {
|
||||
$options = [];
|
||||
|
||||
foreach ($item['options'] as $option) {
|
||||
if (! empty($option['value']) && ! empty($option['value']['product_option_value_id'])) {
|
||||
$options[$option['product_option_id']] = $option['value']['product_option_value_id'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->cart->add(
|
||||
$item['productId'],
|
||||
$item['quantity'],
|
||||
$options,
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $items,
|
||||
]);
|
||||
}
|
||||
}
|
||||
127
backend/src/app/Handlers/CategoriesHandler.php
Executable file
127
backend/src/app/Handlers/CategoriesHandler.php
Executable file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Services\SettingsService;
|
||||
use App\Support\Utils;
|
||||
use Acme\ECommerceFramework\Cache\CacheInterface;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Acme\ECommerceFramework\ImageTool\ImageFactory;
|
||||
use Acme\ECommerceFramework\QueryBuilder\Builder;
|
||||
use Acme\ECommerceFramework\QueryBuilder\JoinClause;
|
||||
use Acme\ECommerceFramework\QueryBuilder\Table;
|
||||
use Acme\ECommerceFramework\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class CategoriesHandler
|
||||
{
|
||||
private const THUMB_SIZE = 150;
|
||||
|
||||
private Builder $queryBuilder;
|
||||
private ImageFactory $image;
|
||||
private SettingsService $settings;
|
||||
private CacheInterface $cache;
|
||||
|
||||
public function __construct(
|
||||
Builder $queryBuilder,
|
||||
ImageFactory $ocImageTool,
|
||||
SettingsService $settings,
|
||||
CacheInterface $cache
|
||||
) {
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->image = $ocImageTool;
|
||||
$this->settings = $settings;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$cacheKey = 'categories.index';
|
||||
|
||||
$categories = $this->cache->get($cacheKey);
|
||||
|
||||
if ($categories === null) {
|
||||
$languageId = $this->settings->config()->getApp()->getLanguageId();
|
||||
$storeId = $this->settings->get('store.oc_store_id', 0);
|
||||
|
||||
$perPage = $request->get('perPage', 100);
|
||||
|
||||
$categoriesFlat = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'categories.category_id' => 'id',
|
||||
'categories.parent_id' => 'parent_id',
|
||||
'categories.image' => 'image',
|
||||
'descriptions.name' => 'name',
|
||||
'descriptions.description' => 'description',
|
||||
])
|
||||
->from(db_table('category'), 'categories')
|
||||
->join(
|
||||
db_table('category_description') . ' AS descriptions',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('categories.category_id', '=', 'descriptions.category_id')
|
||||
->where('descriptions.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->join(
|
||||
new Table(db_table('category_to_store'), 'category_to_store'),
|
||||
function (JoinClause $join) use ($storeId) {
|
||||
$join->on('category_to_store.category_id', '=', 'categories.category_id')
|
||||
->where('category_to_store.store_id', '=', $storeId);
|
||||
}
|
||||
)
|
||||
->where('categories.status', '=', 1)
|
||||
->orderBy('parent_id')
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
|
||||
$categories = $this->buildCategoryTree($categoriesFlat);
|
||||
|
||||
$categories = array_slice($categories, 0, $perPage);
|
||||
|
||||
$this->cache->set($cacheKey, $categories, 60 * 60 * 24);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => array_map(static function ($category) {
|
||||
return [
|
||||
'id' => (int) $category['id'],
|
||||
'image' => $category['image'] ?? '',
|
||||
'name' => Str::htmlEntityEncode($category['name']),
|
||||
'description' => $category['description'],
|
||||
'children' => $category['children'],
|
||||
];
|
||||
}, $categories),
|
||||
]);
|
||||
}
|
||||
|
||||
public function buildCategoryTree(array $flat, $parentId = 0): array
|
||||
{
|
||||
$branch = [];
|
||||
|
||||
foreach ($flat as $category) {
|
||||
if ((int) $category['parent_id'] === (int) $parentId) {
|
||||
$children = $this->buildCategoryTree($flat, $category['id']);
|
||||
if ($children) {
|
||||
$category['children'] = $children;
|
||||
}
|
||||
|
||||
$image = $this->image
|
||||
->make($category['image'])
|
||||
->resize(self::THUMB_SIZE, self::THUMB_SIZE)
|
||||
->url();
|
||||
|
||||
$branch[] = [
|
||||
'id' => (int) $category['id'],
|
||||
'image' => $image,
|
||||
'name' => Utils::htmlEntityEncode($category['name']),
|
||||
'description' => $category['description'],
|
||||
'children' => $category['children'] ?? [],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $branch;
|
||||
}
|
||||
}
|
||||
58
backend/src/app/Handlers/CronHandler.php
Normal file
58
backend/src/app/Handlers/CronHandler.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use Acme\ECommerceFramework\Config\Settings;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Acme\ECommerceFramework\Scheduler\SchedulerService;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CronHandler
|
||||
{
|
||||
private Settings $settings;
|
||||
private SchedulerService $schedulerService;
|
||||
|
||||
public function __construct(Settings $settings, SchedulerService $schedulerService)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$this->schedulerService = $schedulerService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запуск планировщика по HTTP (для cron-job.org и аналогов).
|
||||
* Требует api_key в query, совпадающий с настройкой cron.api_key.
|
||||
*/
|
||||
public function runSchedule(Request $request): JsonResponse
|
||||
{
|
||||
$mode = $this->settings->get('cron.mode', 'disabled');
|
||||
if ($mode !== 'cron_job_org') {
|
||||
return new JsonResponse(['error' => 'Scheduler is not in cron-job.org mode'], Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
$apiKey = $request->get('api_key', '');
|
||||
$expectedKey = $this->settings->get('cron.api_key', '');
|
||||
if ($expectedKey === '' || $apiKey === '' || !hash_equals($expectedKey, $apiKey)) {
|
||||
return new JsonResponse(['error' => 'Invalid or missing API key'], Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// Увеличиваем лимит времени выполнения при запуске по HTTP, чтобы снизить риск timeout
|
||||
$limit = 300; // 5 минут
|
||||
if (function_exists('set_time_limit')) {
|
||||
@set_time_limit($limit);
|
||||
}
|
||||
if (function_exists('ini_set')) {
|
||||
@ini_set('max_execution_time', (string) $limit);
|
||||
}
|
||||
|
||||
$result = $this->schedulerService->run(true);
|
||||
$data = [
|
||||
'success' => true,
|
||||
'executed' => count($result->executed),
|
||||
'failed' => count($result->failed),
|
||||
'skipped' => count($result->skipped),
|
||||
];
|
||||
|
||||
return new JsonResponse($data);
|
||||
}
|
||||
}
|
||||
187
backend/src/app/Handlers/ETLHandler.php
Executable file
187
backend/src/app/Handlers/ETLHandler.php
Executable file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Acme\ECommerceFramework\Config\Settings;
|
||||
use Acme\ECommerceFramework\Exceptions\InvalidApiTokenException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Acme\ECommerceFramework\QueryBuilder\Builder;
|
||||
use Acme\ECommerceFramework\QueryBuilder\RawExpression;
|
||||
use Acme\ECommerceFramework\Support\DateUtils;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class ETLHandler
|
||||
{
|
||||
private Builder $builder;
|
||||
private Settings $settings;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(Builder $builder, Settings $settings, LoggerInterface $logger)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
$this->settings = $settings;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
private function getLastUpdatedAtSql(): string
|
||||
{
|
||||
return '
|
||||
GREATEST(
|
||||
COALESCE((
|
||||
SELECT MAX(date_modified)
|
||||
FROM oc_order as o
|
||||
where o.customer_id = acmeshop_customers.oc_customer_id
|
||||
), 0),
|
||||
acmeshop_customers.updated_at
|
||||
)
|
||||
';
|
||||
}
|
||||
|
||||
private function getCustomerQuery(?Carbon $updatedAt = null): Builder
|
||||
{
|
||||
$lastUpdatedAtSql = $this->getLastUpdatedAtSql();
|
||||
|
||||
return $this->builder->newQuery()
|
||||
->from('acmeshop_customers')
|
||||
->where('allows_write_to_pm', '=', 1)
|
||||
->when($updatedAt !== null, function (Builder $builder) use ($lastUpdatedAtSql, $updatedAt) {
|
||||
$builder->where(new RawExpression($lastUpdatedAtSql), '>=', $updatedAt);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidApiTokenException
|
||||
*/
|
||||
public function getCustomersMeta(Request $request): JsonResponse
|
||||
{
|
||||
$this->validateApiKey($request);
|
||||
|
||||
$updatedAt = $request->get('updated_at');
|
||||
if ($updatedAt) {
|
||||
$updatedAt = DateUtils::toSystemTimezone($updatedAt);
|
||||
}
|
||||
$query = $this->getCustomerQuery($updatedAt);
|
||||
$total = $query->count();
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'total' => $total,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidApiTokenException
|
||||
*/
|
||||
public function customers(Request $request): JsonResponse
|
||||
{
|
||||
$this->validateApiKey($request);
|
||||
|
||||
$this->logger->debug('Get customers for ETL');
|
||||
|
||||
$page = (int)$request->get('page', 1);
|
||||
$perPage = (int)$request->get('perPage', 10000);
|
||||
$successOrderStatusIds = '5,3';
|
||||
$updatedAt = $request->get('updated_at');
|
||||
if ($updatedAt) {
|
||||
$updatedAt = DateUtils::toSystemTimezone($updatedAt);
|
||||
}
|
||||
|
||||
$lastUpdatedAtSql = $this->getLastUpdatedAtSql();
|
||||
$query = $this->getCustomerQuery($updatedAt);
|
||||
|
||||
$query->orderBy('telegram_user_id');
|
||||
$query->forPage($page, $perPage);
|
||||
|
||||
$query
|
||||
->select([
|
||||
'tracking_id',
|
||||
'username',
|
||||
'photo_url',
|
||||
'telegram_user_id' => 'tg_user_id',
|
||||
'acmeshop_customers.oc_customer_id',
|
||||
'is_premium',
|
||||
'last_seen_at',
|
||||
'orders_count' => 'orders_count_total',
|
||||
'created_at' => 'registered_at',
|
||||
new RawExpression(
|
||||
'(
|
||||
SELECT MIN(date_added)
|
||||
FROM oc_order
|
||||
WHERE oc_order.customer_id = acmeshop_customers.oc_customer_id
|
||||
) AS first_order_date'
|
||||
),
|
||||
new RawExpression(
|
||||
'(
|
||||
SELECT MAX(date_added)
|
||||
FROM oc_order
|
||||
WHERE oc_order.customer_id = acmeshop_customers.oc_customer_id
|
||||
) AS last_order_date'
|
||||
),
|
||||
new RawExpression(
|
||||
"COALESCE((
|
||||
SELECT
|
||||
SUM(total)
|
||||
FROM
|
||||
oc_order
|
||||
WHERE
|
||||
oc_order.customer_id = acmeshop_customers.oc_customer_id
|
||||
AND oc_order.order_status_id IN ($successOrderStatusIds)
|
||||
), 0) AS total_spent"
|
||||
),
|
||||
new RawExpression(
|
||||
"COALESCE((
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
oc_order
|
||||
WHERE
|
||||
oc_order.customer_id = acmeshop_customers.oc_customer_id
|
||||
AND oc_order.order_status_id IN ($successOrderStatusIds)
|
||||
), 0) AS orders_count_success"
|
||||
),
|
||||
new RawExpression("$lastUpdatedAtSql AS updated_at"),
|
||||
]);
|
||||
|
||||
$items = $query->get();
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => array_map(static function ($item) {
|
||||
return [
|
||||
'tracking_id' => $item['tracking_id'],
|
||||
'username' => $item['username'],
|
||||
'photo_url' => $item['photo_url'],
|
||||
'tg_user_id' => filter_var($item['tg_user_id'], FILTER_VALIDATE_INT),
|
||||
'oc_customer_id' => filter_var($item['oc_customer_id'], FILTER_VALIDATE_INT),
|
||||
'is_premium' => filter_var($item['is_premium'], FILTER_VALIDATE_BOOLEAN),
|
||||
'last_seen_at' => DateUtils::toUTC($item['last_seen_at']),
|
||||
'orders_count_total' => filter_var($item['orders_count_total'], FILTER_VALIDATE_INT),
|
||||
'registered_at' => DateUtils::toUTC($item['registered_at']),
|
||||
'first_order_date' => DateUtils::toUTC($item['first_order_date']),
|
||||
'last_order_date' => DateUtils::toUTC($item['last_order_date']),
|
||||
'total_spent' => (float)$item['total_spent'],
|
||||
'orders_count_success' => filter_var($item['orders_count_success'], FILTER_VALIDATE_INT),
|
||||
'updated_at' => DateUtils::toUTC($item['updated_at']),
|
||||
];
|
||||
}, $items),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidApiTokenException
|
||||
*/
|
||||
private function validateApiKey(Request $request): void
|
||||
{
|
||||
$token = $request->getApiKey();
|
||||
|
||||
if (empty($token)) {
|
||||
throw new InvalidApiTokenException('Invalid API Key.');
|
||||
}
|
||||
|
||||
if (strcmp($token, $this->settings->get('pulse.api_key')) !== 0) {
|
||||
throw new InvalidApiTokenException('Invalid API Key');
|
||||
}
|
||||
}
|
||||
}
|
||||
49
backend/src/app/Handlers/FiltersHandler.php
Executable file
49
backend/src/app/Handlers/FiltersHandler.php
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Filters\ProductCategory;
|
||||
use App\Filters\ProductPrice;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class FiltersHandler
|
||||
{
|
||||
public function getFiltersForMainPage(): JsonResponse
|
||||
{
|
||||
$filters = [
|
||||
'operand' => 'AND',
|
||||
'rules' => [
|
||||
ProductPrice::NAME => [
|
||||
'criteria' => [
|
||||
'product_price' => [
|
||||
'type' => 'number',
|
||||
'params' => [
|
||||
'operator' => 'between',
|
||||
'value' => [
|
||||
'from' => 0,
|
||||
'to' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
ProductCategory::NAME => [
|
||||
'criteria' => [
|
||||
'product_category_id' => [
|
||||
'type' => 'product_category',
|
||||
'params' => [
|
||||
'operator' => 'contains',
|
||||
'value' => null,
|
||||
],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $filters,
|
||||
]);
|
||||
}
|
||||
}
|
||||
51
backend/src/app/Handlers/FormsHandler.php
Executable file
51
backend/src/app/Handlers/FormsHandler.php
Executable file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use JsonException;
|
||||
use Acme\ECommerceFramework\Exceptions\EntityNotFoundException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Acme\ECommerceFramework\QueryBuilder\Builder;
|
||||
|
||||
class FormsHandler
|
||||
{
|
||||
private Builder $builder;
|
||||
|
||||
public function __construct(Builder $builder)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EntityNotFoundException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function getForm(Request $request): JsonResponse
|
||||
{
|
||||
$alias = $request->json('alias');
|
||||
if (! $alias) {
|
||||
return new JsonResponse([
|
||||
'error' => 'Form alias is required',
|
||||
], Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$form = $this->builder->newQuery()
|
||||
->from('acmeshop_forms')
|
||||
->where('alias', '=', $alias)
|
||||
->firstOrNull();
|
||||
|
||||
if (! $form) {
|
||||
throw new EntityNotFoundException("Form with alias `{$alias}` not found");
|
||||
}
|
||||
|
||||
$schema = json_decode($form['schema'], true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'schema' => $schema,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
16
backend/src/app/Handlers/HealthCheckHandler.php
Executable file
16
backend/src/app/Handlers/HealthCheckHandler.php
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class HealthCheckHandler
|
||||
{
|
||||
public function handle(): JsonResponse
|
||||
{
|
||||
return new JsonResponse([
|
||||
'status' => 'ok',
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
}
|
||||
37
backend/src/app/Handlers/OrderHandler.php
Executable file
37
backend/src/app/Handlers/OrderHandler.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Exceptions\OrderValidationFailedException;
|
||||
use App\Services\OrderCreateService;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class OrderHandler
|
||||
{
|
||||
private OrderCreateService $orderCreateService;
|
||||
|
||||
public function __construct(OrderCreateService $orderCreateService)
|
||||
{
|
||||
$this->orderCreateService = $orderCreateService;
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$order = $this->orderCreateService->create($request->json(), [
|
||||
'ip' => $request->getClientIp(),
|
||||
'user_agent' => $request->getUserAgent(),
|
||||
]);
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $order,
|
||||
], Response::HTTP_CREATED);
|
||||
} catch (OrderValidationFailedException $exception) {
|
||||
return new JsonResponse([
|
||||
'data' => $exception->getErrorBag()->firstOfAll(),
|
||||
], Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
backend/src/app/Handlers/PrivacyPolicyHandler.php
Executable file
73
backend/src/app/Handlers/PrivacyPolicyHandler.php
Executable file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Models\TelegramCustomer;
|
||||
use Carbon\Carbon;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Acme\ECommerceFramework\Support\Arr;
|
||||
use Acme\ECommerceFramework\Telegram\Enums\TelegramHeader;
|
||||
use Acme\ECommerceFramework\Telegram\TelegramService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class PrivacyPolicyHandler
|
||||
{
|
||||
private TelegramService $telegramService;
|
||||
private TelegramCustomer $telegramCustomer;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
TelegramService $telegramService,
|
||||
TelegramCustomer $telegramCustomer,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->telegramService = $telegramService;
|
||||
$this->telegramCustomer = $telegramCustomer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function checkIsUserPrivacyConsented(Request $request): JsonResponse
|
||||
{
|
||||
$isPrivacyConsented = false;
|
||||
$telegramUserId = $this->telegramService->userId($request->header(TelegramHeader::INIT_DATA));
|
||||
|
||||
if (! $telegramUserId) {
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'is_privacy_consented' => false,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$customer = $this->telegramCustomer->findByTelegramUserId($telegramUserId);
|
||||
|
||||
if ($customer) {
|
||||
$isPrivacyConsented = Arr::get($customer, 'privacy_consented_at') !== null;
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'is_privacy_consented' => $isPrivacyConsented,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function userPrivacyConsent(Request $request): JsonResponse
|
||||
{
|
||||
$telegramUserId = $this->telegramService->userId($request->header(TelegramHeader::INIT_DATA));
|
||||
|
||||
if ($telegramUserId) {
|
||||
$this->telegramCustomer->updateByTelegramUserId($telegramUserId, [
|
||||
'privacy_consented_at' => Carbon::now()->toDateTimeString(),
|
||||
]);
|
||||
} else {
|
||||
$this->logger->warning(
|
||||
'Could not find customer with telegram user_id: ' . $telegramUserId . ' to give privacy consent.'
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
125
backend/src/app/Handlers/ProductsHandler.php
Executable file
125
backend/src/app/Handlers/ProductsHandler.php
Executable file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Services\ProductsService;
|
||||
use App\Services\SettingsService;
|
||||
use Exception;
|
||||
use Acme\ECommerceFramework\Cache\CacheInterface;
|
||||
use Acme\ECommerceFramework\Exceptions\EntityNotFoundException;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ProductsHandler
|
||||
{
|
||||
private SettingsService $settings;
|
||||
private ProductsService $productsService;
|
||||
private LoggerInterface $logger;
|
||||
private CacheInterface $cache;
|
||||
|
||||
public function __construct(
|
||||
SettingsService $settings,
|
||||
ProductsService $productsService,
|
||||
LoggerInterface $logger,
|
||||
CacheInterface $cache
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->productsService = $productsService;
|
||||
$this->logger = $logger;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$page = (int) $request->json('page', 1);
|
||||
$perPage = min((int) $request->json('perPage', 20), 20);
|
||||
$maxPages = (int) $request->json('maxPages', 10);
|
||||
$search = trim($request->json('search', ''));
|
||||
$filters = $request->json('filters');
|
||||
$storeId = $this->settings->get('store.oc_store_id', 0);
|
||||
$languageId = $this->settings->config()->getApp()->getLanguageId();
|
||||
|
||||
$response = $this->productsService->getProductsResponse(
|
||||
compact('page', 'perPage', 'search', 'filters', 'maxPages'),
|
||||
$languageId,
|
||||
$storeId,
|
||||
);
|
||||
|
||||
return new JsonResponse($response);
|
||||
}
|
||||
|
||||
public function show(Request $request): JsonResponse
|
||||
{
|
||||
$productId = (int) $request->get('id');
|
||||
|
||||
try {
|
||||
$product = $this->productsService->getProductById($productId);
|
||||
} catch (EntityNotFoundException $exception) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Product with id ' . $productId . ' not found',
|
||||
], Response::HTTP_NOT_FOUND);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error($exception->getMessage(), ['exception' => $exception]);
|
||||
throw new RuntimeException('Error get product with id ' . $productId, 500);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $product,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getProductImages(Request $request): JsonResponse
|
||||
{
|
||||
$productId = (int) $request->get('id');
|
||||
|
||||
try {
|
||||
$images = $this->productsService->getProductImages($productId);
|
||||
} catch (EntityNotFoundException $exception) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Product with id ' . $productId . ' not found',
|
||||
], Response::HTTP_NOT_FOUND);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error('Could not load images for product ' . $productId, ['exception' => $exception]);
|
||||
$images = [];
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $images,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSearchPlaceholder(Request $request): JsonResponse
|
||||
{
|
||||
$storeId = $this->settings->get('store.oc_store_id', 0);
|
||||
$languageId = $this->settings->config()->getApp()->getLanguageId();
|
||||
$cacheKey = "products.search_placeholder.{$storeId}.{$languageId}";
|
||||
|
||||
$cached = $this->cache->get($cacheKey);
|
||||
|
||||
if ($cached !== null) {
|
||||
return new JsonResponse($cached);
|
||||
}
|
||||
|
||||
$response = $this->productsService->getProductsResponse(
|
||||
[
|
||||
'page' => 1,
|
||||
'perPage' => 3,
|
||||
'search' => '',
|
||||
'filters' => [],
|
||||
'maxPages' => 1,
|
||||
],
|
||||
$languageId,
|
||||
$storeId,
|
||||
);
|
||||
|
||||
// Кешируем на 24 часа
|
||||
$this->cache->set($cacheKey, $response, 60 * 60 * 24);
|
||||
|
||||
return new JsonResponse($response);
|
||||
}
|
||||
}
|
||||
115
backend/src/app/Handlers/SettingsHandler.php
Executable file
115
backend/src/app/Handlers/SettingsHandler.php
Executable file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Services\SettingsService;
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Acme\ECommerceFramework\ImageTool\ImageFactory;
|
||||
use Acme\ECommerceFramework\Telegram\TelegramService;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class SettingsHandler
|
||||
{
|
||||
private SettingsService $settings;
|
||||
private ImageFactory $image;
|
||||
private TelegramService $telegramService;
|
||||
|
||||
public function __construct(
|
||||
SettingsService $settings,
|
||||
ImageFactory $image,
|
||||
TelegramService $telegramService
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->image = $image;
|
||||
$this->telegramService = $telegramService;
|
||||
}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$appConfig = $this->settings->config()->getApp();
|
||||
|
||||
$appIcon = $appConfig->getAppIcon();
|
||||
$hash = $this->settings->getHash();
|
||||
|
||||
if ($appIcon) {
|
||||
$appIcon = $this->image->make($appIcon)->resize(null, 64)->url('png') . '?_v=' . $hash;
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'app_name' => $appConfig->getAppName(),
|
||||
'app_debug' => $appConfig->isAppDebug(),
|
||||
'app_icon' => $appIcon,
|
||||
'theme_light' => $appConfig->getThemeLight(),
|
||||
'theme_dark' => $appConfig->getThemeDark(),
|
||||
'ya_metrika_enabled' => $this->settings->config()->getMetrics()->isYandexMetrikaEnabled(),
|
||||
'app_enabled' => $appConfig->isAppEnabled(),
|
||||
'product_interaction_mode' => $this->settings->config()->getStore()->getProductInteractionMode(),
|
||||
'manager_username' => $this->settings->config()->getStore()->getManagerUsername(),
|
||||
'feature_coupons' => $this->settings->config()->getStore()->isFeatureCoupons(),
|
||||
'feature_vouchers' => $this->settings->config()->getStore()->isFeatureVouchers(),
|
||||
'show_category_products_button' => $this->settings->config()->getStore()->isShowCategoryProductsButton(),
|
||||
'currency_code' => $this->settings->config()->getStore()->getOcDefaultCurrency(),
|
||||
'texts' => $this->settings->config()->getTexts()->toArray(),
|
||||
'mainpage_blocks' => $this->settings->get('mainpage_blocks', []),
|
||||
'privacy_policy_link' => $this->settings->get('app.privacy_policy_link'),
|
||||
'image_aspect_ratio' => $this->settings->get('app.image_aspect_ratio', '1:1'),
|
||||
'haptic_enabled' => $appConfig->isHapticEnabled(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function testTgMessage(Request $request): JsonResponse
|
||||
{
|
||||
$template = $request->json('template', 'Нет шаблона');
|
||||
$token = $request->json('token');
|
||||
$chatId = $request->json('chat_id');
|
||||
|
||||
if (! $token) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Не задан Telegram BotToken',
|
||||
]);
|
||||
}
|
||||
|
||||
if (! $chatId) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Не задан ChatID.',
|
||||
]);
|
||||
}
|
||||
|
||||
$variables = [
|
||||
'{store_name}' => $this->settings->config()->getApp()->getAppName(),
|
||||
'{order_id}' => 777,
|
||||
'{customer}' => 'Иван Васильевич',
|
||||
'{email}' => 'telegram@ecommerce.com',
|
||||
'{phone}' => '+79999999999',
|
||||
'{comment}' => 'Это тестовый заказ',
|
||||
'{address}' => 'г. Москва',
|
||||
'{total}' => 100000,
|
||||
'{ip}' => '127.0.0.1',
|
||||
'{created_at}' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$message = $this->telegramService->prepareMessage($template, $variables);
|
||||
|
||||
try {
|
||||
$this->telegramService
|
||||
->setBotToken($token)
|
||||
->sendMessage($chatId, $message);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => 'Сообщение отправлено. Проверьте Telegram.',
|
||||
]);
|
||||
} catch (ClientException $exception) {
|
||||
$json = json_decode($exception->getResponse()->getBody(), true);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => $json['description'],
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return new JsonResponse([
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
134
backend/src/app/Handlers/TelegramCustomerHandler.php
Executable file
134
backend/src/app/Handlers/TelegramCustomerHandler.php
Executable file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Services\MegapayCustomerService;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Acme\ECommerceFramework\Support\Arr;
|
||||
use Acme\ECommerceFramework\AcmeShopPulse\TrackingIdGenerator;
|
||||
use Acme\ECommerceFramework\Telegram\Enums\TelegramHeader;
|
||||
use Acme\ECommerceFramework\Telegram\Exceptions\DecodeTelegramInitDataException;
|
||||
use Acme\ECommerceFramework\Telegram\TelegramInitDataDecoder;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class TelegramCustomerHandler
|
||||
{
|
||||
private MegapayCustomerService $telegramCustomerService;
|
||||
private LoggerInterface $logger;
|
||||
private TelegramInitDataDecoder $initDataDecoder;
|
||||
|
||||
public function __construct(
|
||||
MegapayCustomerService $telegramCustomerService,
|
||||
LoggerInterface $logger,
|
||||
TelegramInitDataDecoder $initDataDecoder
|
||||
) {
|
||||
$this->telegramCustomerService = $telegramCustomerService;
|
||||
$this->logger = $logger;
|
||||
$this->initDataDecoder = $initDataDecoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить или обновить Telegram-пользователя
|
||||
*
|
||||
* @param Request $request HTTP запрос с данными пользователя
|
||||
* @return JsonResponse JSON ответ с результатом операции
|
||||
*/
|
||||
public function saveOrUpdate(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$customer = $this->telegramCustomerService->saveOrUpdate(
|
||||
$this->extractTelegramUserData($request)
|
||||
);
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'tracking_id' => Arr::get($customer, 'tracking_id'),
|
||||
'created_at' => Arr::get($customer, 'created_at'),
|
||||
],
|
||||
], Response::HTTP_OK);
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->error('Could not save telegram customer data', ['exception' => $e]);
|
||||
|
||||
return new JsonResponse([], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить данные текущего пользователя
|
||||
*
|
||||
* @param Request $request HTTP запрос
|
||||
* @return JsonResponse JSON ответ с данными пользователя
|
||||
*/
|
||||
public function getCurrent(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$telegramUserData = $this->extractUserDataFromInitData($request);
|
||||
$telegramUserId = (int)Arr::get($telegramUserData, 'id');
|
||||
|
||||
if ($telegramUserId <= 0) {
|
||||
return new JsonResponse([
|
||||
'data' => null,
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
$customer = $this->telegramCustomerService->getByTelegramUserId($telegramUserId);
|
||||
|
||||
if (!$customer) {
|
||||
return new JsonResponse([
|
||||
'data' => null,
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'created_at' => Arr::get($customer, 'created_at'),
|
||||
],
|
||||
], Response::HTTP_OK);
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->error('Could not get current telegram customer data', ['exception' => $e]);
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => null,
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлечь данные Telegram пользователя из запроса
|
||||
*
|
||||
* @param Request $request HTTP запрос
|
||||
* @return array Данные пользователя
|
||||
* @throws RuntimeException|DecodeTelegramInitDataException невозможно извлечь данные пользователя из Request
|
||||
*/
|
||||
private function extractTelegramUserData(Request $request): array
|
||||
{
|
||||
$telegramUserData = $request->json('user');
|
||||
|
||||
if (! $telegramUserData) {
|
||||
$telegramUserData = $this->extractUserDataFromInitData($request);
|
||||
}
|
||||
|
||||
return $telegramUserData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DecodeTelegramInitDataException
|
||||
*/
|
||||
private function extractUserDataFromInitData(Request $request): array
|
||||
{
|
||||
$raw = $request->header(TelegramHeader::INIT_DATA);
|
||||
if (! $raw) {
|
||||
throw new RuntimeException('No init data found in http request header');
|
||||
}
|
||||
|
||||
$initData = $this->initDataDecoder->decode($raw);
|
||||
|
||||
return Arr::get($initData, 'user');
|
||||
}
|
||||
}
|
||||
99
backend/src/app/Handlers/TelegramHandler.php
Executable file
99
backend/src/app/Handlers/TelegramHandler.php
Executable file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Mockery\Exception;
|
||||
use Acme\ECommerceFramework\Cache\CacheInterface;
|
||||
use Acme\ECommerceFramework\Container\Container;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Acme\ECommerceFramework\Support\Arr;
|
||||
use Acme\ECommerceFramework\Telegram\Contracts\TelegramCommandInterface;
|
||||
use Acme\ECommerceFramework\Telegram\Exceptions\TelegramCommandNotFoundException;
|
||||
use Acme\ECommerceFramework\Telegram\TelegramBotStateManager;
|
||||
use Acme\ECommerceFramework\Telegram\TelegramCommandsRegistry;
|
||||
use Acme\ECommerceFramework\Telegram\TelegramService;
|
||||
|
||||
class TelegramHandler
|
||||
{
|
||||
private CacheInterface $cache;
|
||||
private TelegramCommandsRegistry $telegramCommandsRegistry;
|
||||
private Container $container;
|
||||
private TelegramBotStateManager $telegramBotStateManager;
|
||||
private LoggerInterface $logger;
|
||||
private TelegramService $telegramService;
|
||||
|
||||
public function __construct(
|
||||
CacheInterface $cache,
|
||||
TelegramCommandsRegistry $telegramCommandsRegistry,
|
||||
Container $container,
|
||||
TelegramBotStateManager $telegramBotStateManager,
|
||||
LoggerInterface $logger,
|
||||
TelegramService $telegramService
|
||||
) {
|
||||
$this->cache = $cache;
|
||||
$this->telegramCommandsRegistry = $telegramCommandsRegistry;
|
||||
$this->container = $container;
|
||||
$this->telegramBotStateManager = $telegramBotStateManager;
|
||||
$this->logger = $logger;
|
||||
$this->telegramService = $telegramService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws GuzzleException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function webhook(Request $request): JsonResponse
|
||||
{
|
||||
$this->logger->debug('Webhook received');
|
||||
|
||||
$update = $request->json();
|
||||
$message = $update['message'] ?? null;
|
||||
if (! $message) {
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
$userId = $update['message']['from']['id'];
|
||||
$chatId = $update['message']['chat']['id'];
|
||||
|
||||
try {
|
||||
$message = Arr::get($update, 'message', []);
|
||||
|
||||
$this->cache->set('tg_latest_msg', $message, 60);
|
||||
|
||||
$text = Arr::get($message, 'text', '');
|
||||
|
||||
// command starts from "/"
|
||||
if (strpos($text, '/') === 0) {
|
||||
$this->telegramBotStateManager->clearState($userId, $chatId);
|
||||
$command = substr($text, 1);
|
||||
$handler = $this->telegramCommandsRegistry->resolve($command);
|
||||
|
||||
/** @var TelegramCommandInterface $concrete */
|
||||
$concrete = $this->container->get($handler);
|
||||
$concrete->handle($update);
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
// Continue state
|
||||
$hasState = $this->telegramBotStateManager->hasState($userId, $chatId);
|
||||
if ($hasState) {
|
||||
$handler = $this->telegramBotStateManager->getCurrentStateCommandHandler($userId, $chatId);
|
||||
/** @var TelegramCommandInterface $concrete */
|
||||
$concrete = $this->container->get($handler);
|
||||
$concrete->handle($update);
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
} catch (TelegramCommandNotFoundException $exception) {
|
||||
$this->telegramService->sendMessage($chatId, 'Неверная команда');
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error($exception->getMessage(), ['exception' => $exception]);
|
||||
}
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
}
|
||||
48
backend/src/app/Handlers/TelemetryHandler.php
Executable file
48
backend/src/app/Handlers/TelemetryHandler.php
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use Acme\ECommerceFramework\Http\Request;
|
||||
use Acme\ECommerceFramework\AcmeShopPulse\PulseIngestException;
|
||||
use Acme\ECommerceFramework\AcmeShopPulse\AcmeShopPulseService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
class TelemetryHandler
|
||||
{
|
||||
private AcmeShopPulseService $megaPayPulseService;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
AcmeShopPulseService $megaPayPulseService,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->megaPayPulseService = $megaPayPulseService;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PulseIngestException
|
||||
*/
|
||||
public function ingest(Request $request): JsonResponse
|
||||
{
|
||||
$this->megaPayPulseService->handleIngest($request->json());
|
||||
|
||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
public function heartbeat(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->megaPayPulseService->handleHeartbeat();
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->warning('AcmeShop Pulse Heartbeat failed: ' . $e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
|
||||
return new JsonResponse(['status' => 'ok']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user