---
title: "Real-time έσοδα με GTM, Flask και MySQL"
url: "https://www.omnicliq.com/el/blog/real-time-revenue-tracking-gtm-flask-mysql/"
description: "Σε αυτόν τον οδηγό δείχνουμε βήμα βήμα πώς στήνεις καταγραφή εσόδων σε πραγματικό χρόνο με Google Tag Manager και Server-Side GTM, ώστε κάθε αγορά να γράφεται σε MySQL.

Στόχος δεν είναι ένα ακόμα report, αλλά ένα δικό σου αρχείο αγορών, ώστε τα πραγματικά έσοδα να φαίνονται ακόμα κι όταν το GA4, το Google Ads ή το Meta καθυστερούν ή έχουν πρόβλημα."
category: "data-bi"
datePublished: "2025-11-24"
readTime: "6 λεπτά ανάγνωσης"
author: "Dimitris Bachtsevanis"
locale: "el"
---

# Real-time έσοδα με GTM, Flask και MySQL

> Σε αυτόν τον οδηγό δείχνουμε βήμα βήμα πώς στήνεις καταγραφή εσόδων σε πραγματικό χρόνο με Google Tag Manager και Server-Side GTM, ώστε κάθε αγορά να γράφεται σε MySQL.

Στόχος δεν είναι ένα ακόμα report, αλλά ένα δικό σου αρχείο αγορών, ώστε τα πραγματικά έσοδα να φαίνονται ακόμα κι όταν το GA4, το Google Ads ή το Meta καθυστερούν ή έχουν πρόβλημα.

**TL;DR.** Βήμα-βήμα στήσιμο ενός pipeline για έσοδα σε πραγματικό χρόνο, που δεν εξαρτάται από το GA4, τη Meta ή άλλη διαφημιστική πλατφόρμα: Web GTM → Server-Side GTM → Flask API → MySQL. Κάθε αγορά γράφεται στη δική σου βάση σε κλάσματα δευτερολέπτου και τροφοδοτεί live dashboards και αποφάσεις για το budget, όταν δεν μπορείς να περιμένεις τις καθυστερήσεις των πλατφορμών.

## Γιατί MySQL και όχι GA4 ή BigQuery;

Το [GA4](/el/labs/ga4/) είναι εξαιρετικό για analytics, αλλά έχει ένα δομικό μειονέκτημα που σε peak περιόδους γίνεται επικίνδυνο: δεν είναι real-time revenue system. Σε events υψηλού όγκου (π.χ. Black Friday) το reporting lag οδηγεί σε λάθος αποφάσεις. Όταν το ad spend πρέπει να αλλάξει άμεσα, ένα ανεξάρτητο logging layer από nice-to-have γίνεται must.

Το Web [GTM](/el/labs/gtm/) → [Server-Side GTM](/el/business-intelligence/data-collection/) → MySQL pipeline:
- πιάνει κάθε purchase τη στιγμή που συμβαίνει
- το αποθηκεύει σε δικό σου server / database
- λειτουργεί ανεξάρτητα από analytics/ad platforms
- είναι ιδανικό για real-time dashboards και άμεσες budget αποφάσεις

Με MySQL για order logging κερδίζεις:
- **Real revenue σε milliseconds** από τη στιγμή της αγοράς.
- **Μηδενική εξάρτηση** από GA4 latency / processing / sampling.
- **Δικά σου dashboards** σε Power BI, Looker Studio ή Metabase.
- **Εύκολη διασύνδεση** με custom εργαλεία, scripts, alerts.
- **Reconciliation** με ERP ή site orders για να βρίσκεις αποκλίσεις.
- **Free** σε επίπεδο software — open-source stack + δική σου υποδομή.

Αποτέλεσμα: marketing και performance ομάδα βλέπουν revenue όπως πραγματικά τρέχει, χωρίς καθυστερήσεις και χωρίς να «περιμένουν» τρίτους.

## Η αρχιτεκτονική με μια ματιά

Το σύστημα το στήνουμε σε τρία καθαρά layers, με ξεκάθαρους ρόλους:

1. **Browser / dataLayer:** κάνει emit το purchase event με όλα τα order details.
2. **GTM Web Container:** διαβάζει το dataLayer και προωθεί τα δεδομένα στο Server-Side GTM.
3. **Server-Side GTM → MySQL API:** κάνει processing/enrichment όπου χρειάζεται και γράφει το purchase στη βάση.

Data flow:

```
Browser (dataLayer.purchase)
   ↓
GTM Web (Tag: Send to Server)
   ↓
Server-Side GTM (Client + Tag)
   ↓
REST API Endpoint (Flask / Node / PHP)
   ↓
MySQL (orders table)
```

Κάθε layer κάνει μόνο αυτό που πρέπει. Όταν η αρχιτεκτονική είναι καθαρή, το maintenance, το debugging και η επέκταση (π.χ. alerts, deduplication, joins με ERP) γίνονται σημαντικά πιο εύκολα.

## Το dataLayer είναι η βάση όλων

Πριν πιάσεις GTM (web ή server-side), το e-shop πρέπει να κάνει emit ένα σωστά δομημένο purchase event στο dataLayer. Αν το event είναι ελλιπές ή «πειραγμένο», όλο το pipeline πατάει σε μισές αλήθειες — και αυτό καταλήγει σε λάθος revenue νούμερα και bugs που δεν εντοπίζονται εύκολα.

Recommended purchase event structure (GA4 Enhanced E-commerce format):

```javascript
dataLayer.push({
  event: "purchase",
  ecommerce: {
    transaction_id: "ORDER12345",
    value: 74.90,
    currency: "EUR",
    tax: 0,
    shipping: 3.90,
    discount: 5.00,
    coupon: "BF2025",
    items: [
      {
        item_id: "SKU-00123",
        item_name: "Oversized Hoodie",
        item_brand: "BrandX",
        item_category: "Apparel > Women > Hoodies",
        item_variant: "Black / Medium",
        price: 39.95,
        quantity: 1
      }
    ]
  }
});
```

Συνηθισμένα dataLayer προβλήματα που πρέπει να λυθούν πρώτα «upstream» (στο e-shop):
- λείπει `order_id`
- λείπει `coupon`
- δεν υπάρχει διαχωρισμός σε `subtotal`, `discount`, `tax`
- λείπουν item-level fields (brand, category, variant)
- το event πυροδοτείται λάθος στιγμή (refresh, back navigation, SPA transitions)

Διόρθωσέ τα στην πηγή. Το dataLayer είναι το «data contract» που τροφοδοτεί όλα τα downstream layers.

## Από το Web GTM στο Server-Side GTM

Στο Web GTM αποφεύγουμε επίτηδες να στείλουμε όλο το dataLayer προς Server-Side GTM — αυξάνει processing/transfer κόστος χωρίς ουσιαστικό όφελος. Φτιάχνουμε Data Layer Variables μόνο για τα πεδία που χρειαζόμαστε (`transaction_id`, `value`, `currency`, `shipping`, `tax`, `discount`, `items`) και στέλνουμε μόνο αυτά μέσω Stape Data Tag.

Το tag κάνει trigger αποκλειστικά στο `purchase` event, ώστε κάθε order να στέλνεται μία φορά προς Server-Side GTM — χωρίς duplication και χωρίς άσκοπα calls.

Στο server side, ο Data Client παραλαμβάνει τις incoming variables και τις μετατρέπει σε event μέσα στο Server-Side GTM container. Σε Debug Mode βλέπεις ένα νέο event ανά purchase, με ακριβώς τα fields που προώθησες — `transaction_id`, `value`, `currency`, `shipping`, `tax`, `discount` και όλο το items list. Δεν χρειάζονται transforms· οι τιμές έρχονται όπως είναι.

## Από το Server-Side GTM στο Flask

Το Server-Side GTM προωθεί το purchase στο δικό μας backend μέσω JSON HTTP Request tag που δείχνει σε κάτι σαν `https://api.mydomain.gr/gtm/purchase-log`. Το payload κουβαλά μόνο ό,τι χρειαζόμαστε — transaction_id, value, currency, shipping, tax, discount, items list — και λίγο βασικό context (timestamp, domain).

Minimal Flask app που παραλαμβάνει και γράφει σε MySQL:

```python
from flask import Flask, request, jsonify
import mysql.connector
from datetime import datetime
import json

app = Flask(__name__)

db_config = {
  "host": "localhost",
  "user": "my_user",
  "password": "my_password",
  "database": "my_database",
  "charset": "utf8mb4"
}

@app.route("/gtm/purchase-log", methods=["POST"])
def purchase_log():
  data = request.get_json(silent=True) or {}
  order_id = data.get("transaction_id")
  value    = data.get("value")

  if not order_id or value is None:
    return jsonify({"status": "error", "message": "Missing order_id or value"}), 400

  conn = mysql.connector.connect(**db_config)
  cursor = conn.cursor()
  cursor.execute("""
    INSERT INTO orders_log
    (order_id, order_value, currency, shipping, tax, discount, items_json, created_at, source)
    VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
  """, (
    order_id, value,
    data.get("currency", "EUR"),
    data.get("shipping", 0), data.get("tax", 0), data.get("discount", 0),
    json.dumps(data.get("items", []), ensure_ascii=False),
    datetime.utcnow(),
    "gtm-ss"
  ))
  conn.commit()
  cursor.close()
  conn.close()
  return jsonify({"status": "ok"}), 200
```

MySQL table schema:

```
orders_log
----------
id             AUTO_INCREMENT PRIMARY KEY
order_id       VARCHAR
order_value    DECIMAL
currency       VARCHAR
shipping       DECIMAL
tax            DECIMAL
discount       DECIMAL
items_json     JSON or TEXT
created_at     DATETIME
source         VARCHAR  -- e.g. "gtm-ss"
```

Για πρώτη υλοποίηση, το να αποθηκεύσεις τα items ως JSON είναι μια χαρά. Normalization σε `order_items` table μπορεί να μπει μετά, όταν το χρειαστείς για πιο βαριά BI queries.

## Apache reverse proxy και HTTPS

Το Server-Side GTM χρειάζεται public HTTPS URL για να κάνει call, αλλά το Flask συνήθως τρέχει σε `127.0.0.1:5000`. Ένα Apache reverse proxy αναλαμβάνει SSL termination σε public host και προωθεί το request εσωτερικά προς το Flask.

Ενεργοποιείς τα απαραίτητα modules και στήνεις Virtual Host στο 443 με Let's Encrypt certificates, `ProxyPass` προς το Flask instance, και `Header always set` directives για CORS αν το Server-Side GTM domain διαφέρει από το API domain. Με αυτά στη θέση τους, το Server-Side GTM μπορεί να κάνει POST purchases στο `https://api.mysite.gr/gtm/purchase-log` — και κάθε order γράφεται στη MySQL μέσα σε milliseconds από το click στο «confirm».

## FAQ

### Αν το στήσω αυτό, αντικαθιστά το GA4;
Όχι. Το GA4 συνεχίζει να τρέχει παράλληλα για attribution, audiences και behavioural analytics. Αυτό το pipeline προσθέτει μια ανεξάρτητη real-time πηγή revenue που δεν εξαρτάται από το αν το GA4 είναι «healthy» τη δεδομένη στιγμή.

### Τι γίνεται με privacy και GDPR compliance;
Το MySQL log κρατά order-level δεδομένα, όχι personal identifiers. Επειδή ελέγχουμε end-to-end το backend, μπορούμε να εφαρμόσουμε δικούς μας κανόνες retention, access controls και anonymisation logic — συνήθως πιο αυστηρούς και πιο ξεκάθαρους από third-party analytics.

### Γίνεται να τρέξει χωρίς Server-Side GTM;
Ναι, μπορείς να κάνεις POST απευθείας από Web GTM στο Flask endpoint. Όμως το Server-Side GTM σου δίνει observability, debug εργαλεία, καλύτερο έλεγχο, και τη δυνατότητα να enrich-άρεις events με server-side data — σε production στήσιμο συνήθως αξίζει.
