diff --git a/frontend/spa/src/stores/Pulse.js b/frontend/spa/src/stores/Pulse.js index 13f7a62..235be72 100644 --- a/frontend/spa/src/stores/Pulse.js +++ b/frontend/spa/src/stores/Pulse.js @@ -37,7 +37,7 @@ export const usePulseStore = defineStore('pulse', { console.debug('[Pulse] Saving Telegram customer data'); saveTelegramCustomer(userData) .then((response) => { - this.tracking_id = this.tracking_id || response?.data?.tracking_id || null; + this.tracking_id = response?.data?.tracking_id || this.tracking_id || null; console.debug( '[Pulse] Telegram customer data saved successfully. Tracking ID: ', toRaw(this.tracking_id) diff --git a/frontend/spa/src/utils/ftch.js b/frontend/spa/src/utils/ftch.js index 55df3ee..5376ad8 100644 --- a/frontend/spa/src/utils/ftch.js +++ b/frontend/spa/src/utils/ftch.js @@ -43,10 +43,7 @@ async function ftchPost(action, json = {}) { } export async function storeOrder(data) { - return await apiFetch(`${BASE_URL}index.php?route=extension/tgshop/handle&api_action=storeOrder`, { - method: 'POST', - body: data, - }); + return ftch('storeOrder', null, data); } export async function getCart() { diff --git a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php index c9e9419..a230477 100755 --- a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php +++ b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php @@ -51,7 +51,7 @@ class ControllerExtensionTgshopHandle extends Controller 'app' => [ 'shop_base_url' => HTTPS_SERVER, // for catalog: HTTPS_SERVER, for admin: HTTPS_CATALOG 'language_id' => (int)$this->config->get('config_language_id'), - 'timezone' => $this->config->get('config_timezone'), + 'oc_timezone' => $this->config->get('config_timezone'), ], 'logs' => [ 'path' => DIR_LOGS, @@ -74,7 +74,7 @@ class ControllerExtensionTgshopHandle extends Controller ], ]); - $appDebug = Arr::get($items, 'app.app_debug'); + $appDebug = Arr::get($items, 'app.app_debug', false); $app = ApplicationFactory::create($items); diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000000_migrate_from_legacy_settings.php b/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000000_migrate_from_legacy_settings.php index 5cd48e1..5846f1e 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000000_migrate_from_legacy_settings.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000000_migrate_from_legacy_settings.php @@ -29,7 +29,6 @@ return new class extends Migration { 'module_tgshop_owner_notification_template' => 'telegram.owner_notification_template', 'module_tgshop_text_order_created_success' => 'texts.text_order_created_success', 'module_tgshop_enable_store' => 'store.enable_store', - 'module_tgshop_yandex_metrika' => 'metrics.yandex_metrika_counter', 'module_tgshop_customer_notification_template' => 'telegram.customer_notification_template', 'module_tgshop_feature_vouchers' => 'store.feature_vouchers', 'module_tgshop_order_default_status_id' => 'orders.order_default_status_id', diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000001_create_telecart_cache_table.php b/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000001_create_telecart_cache_table.php deleted file mode 100755 index 991f96a..0000000 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations/20260101000001_create_telecart_cache_table.php +++ /dev/null @@ -1,11 +0,0 @@ -app->get(DoctrineDbalAdapter::class)->createTable(); - } -}; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php index cefc42a..694e1de 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php @@ -2,6 +2,7 @@ namespace Openguru\OpenCartFramework; +use Carbon\Carbon; use Closure; use Dotenv\Dotenv; use InvalidArgumentException; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Migrations/MigratorService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Migrations/MigratorService.php index 4957de5..2443f5a 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Migrations/MigratorService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Migrations/MigratorService.php @@ -2,6 +2,7 @@ namespace Openguru\OpenCartFramework\Migrations; +use Carbon\Carbon; use Exception; use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface; use Psr\Log\LoggerInterface; @@ -61,7 +62,7 @@ class MigratorService return <<getMigrationsTableName()}` ( migration VARCHAR(191) NOT NULL, - executed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + executed_at DATETIME NOT NULL, PRIMARY KEY (migration) ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB; SQL; @@ -91,6 +92,7 @@ SQL; private function applyMigrations(array $files): int { $count = 0; + sort($files); foreach ($files as $file) { try { @@ -120,7 +122,7 @@ SQL; { $this->connection->insert($this->getMigrationsTableName(), [ 'migration' => $file, - 'executed_at' => date('Y-m-d H:i:s'), + 'executed_at' => Carbon::now(), ]); } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/DateUtils.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/DateUtils.php new file mode 100644 index 0000000..21663eb --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/DateUtils.php @@ -0,0 +1,34 @@ +setTimezone('UTC')->toJSON(); + } + + /** + * @param string|null $date + * @param DateTimeZone|string|null $tz + * @return Carbon|null + */ + public static function toSystemTimezone(?string $date = null, $tz = 'UTC'): ?Carbon + { + if (! $date) { + return null; + } + + $systemTimezone = date_default_timezone_get(); + + return Carbon::parse($date, $tz)->timezone($systemTimezone); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/PayloadSigner.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/PayloadSigner.php index 05cf627..1eaa851 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/PayloadSigner.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/PayloadSigner.php @@ -6,9 +6,9 @@ use JsonException; class PayloadSigner { - private string $secret; + private ?string $secret; - public function __construct(string $secret) + public function __construct(?string $secret = null) { $this->secret = $secret; } @@ -18,6 +18,7 @@ class PayloadSigner */ public function sign(array $payload): string { + $this->ensureSecretExists(); $encoded = $this->encodeJson($payload); return hash_hmac('sha256', $encoded, $this->secret); @@ -28,6 +29,7 @@ class PayloadSigner */ public function verify(string $signature, array $payload): bool { + $this->ensureSecretExists(); $encoded = $this->encodeJson($payload); $expected = hash_hmac('sha256', $encoded, $this->secret); @@ -42,4 +44,11 @@ class PayloadSigner throw new PayloadSignException('Could not encode JSON: ' . $e->getMessage(), 0, $e); } } + + private function ensureSecretExists(): void + { + if (! $this->secret) { + throw new PayloadSignException('No secret provided'); + } + } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/TeleCartPulseService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/TeleCartPulseService.php index ae71a8a..85acc57 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/TeleCartPulseService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/TeleCartPulseService.php @@ -15,10 +15,13 @@ class TeleCartPulseService { private TelegramInitDataDecoder $initDataDecoder; private PayloadSigner $payloadSigner; - private string $apiKey; + private ?string $apiKey; - public function __construct(TelegramInitDataDecoder $initDataDecoder, PayloadSigner $payloadSigner, string $apiKey) - { + public function __construct( + TelegramInitDataDecoder $initDataDecoder, + PayloadSigner $payloadSigner, + ?string $apiKey = null + ) { $this->initDataDecoder = $initDataDecoder; $this->payloadSigner = $payloadSigner; $this->apiKey = $apiKey; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/TrackingIdGenerator.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/TeleCartPulse/TrackingIdGenerator.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ETLHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ETLHandler.php index 0319aef..0461559 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ETLHandler.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ETLHandler.php @@ -9,6 +9,7 @@ use Openguru\OpenCartFramework\Http\JsonResponse; use Openguru\OpenCartFramework\Http\Request; use Openguru\OpenCartFramework\QueryBuilder\Builder; use Openguru\OpenCartFramework\QueryBuilder\RawExpression; +use Openguru\OpenCartFramework\Support\DateUtils; use Psr\Log\LoggerInterface; class ETLHandler @@ -45,7 +46,7 @@ class ETLHandler return $this->builder->newQuery() ->from('telecart_customers') ->where('allows_write_to_pm', '=', 1) - ->when(! empty($updatedAt), function (Builder $builder) use ($lastUpdatedAtSql, $updatedAt) { + ->when($updatedAt !== null, function (Builder $builder) use ($lastUpdatedAtSql, $updatedAt) { $builder->where(new RawExpression($lastUpdatedAtSql), '>=', $updatedAt); }); } @@ -59,7 +60,7 @@ class ETLHandler $updatedAt = $request->get('updated_at'); if ($updatedAt) { - $updatedAt = Carbon::parse($updatedAt); + $updatedAt = DateUtils::toSystemTimezone($updatedAt); } $query = $this->getCustomerQuery($updatedAt); $total = $query->count(); @@ -85,7 +86,7 @@ class ETLHandler $successOrderStatusIds = '5,3'; $updatedAt = $request->get('updated_at'); if ($updatedAt) { - $updatedAt = Carbon::parse($updatedAt); + $updatedAt = DateUtils::toSystemTimezone($updatedAt); } $lastUpdatedAtSql = $this->getLastUpdatedAtSql(); @@ -138,14 +139,20 @@ class ETLHandler return new JsonResponse([ 'data' => array_map(static function ($item) { - $item['is_premium'] = filter_var($item['is_premium'], FILTER_VALIDATE_BOOLEAN); - $item['orders_count_total'] = filter_var($item['orders_count_total'], FILTER_VALIDATE_INT); - $item['oc_customer_id'] = filter_var($item['oc_customer_id'], FILTER_VALIDATE_INT); - $item['tg_user_id'] = filter_var($item['tg_user_id'], FILTER_VALIDATE_INT); - $item['orders_count_success'] = filter_var($item['orders_count_success'], FILTER_VALIDATE_INT); - $item['total_spent'] = (float)$item['total_spent']; - - return $item; + return [ + 'tracking_id' => $item['tracking_id'], + '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), ]); } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php index 0dad496..26fd543 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php @@ -118,7 +118,7 @@ class OrderCreateService ); } - $orderData['customer_id'] = $ocCustomerId; + $orderData['customer_id'] = $ocCustomerId ?? 0; $this->database->insert(db_table('order'), $orderData); $orderId = $this->database->lastInsertId();