Notifications, Telegram, CRM
Unpaid-order reminders
REMINDER_AFTER_HOURS after the invoice is created, if the order is still pending: an email (SMTP) and, with SMS_REMINDER=true, an SMS with the same payment link. Count and interval — REMINDER_MAX, REMINDER_GAP_HOURS. Paid another way or cancelled — reminders stop.
SMS
SMS_PROVIDER=turbosms|alphasms. SMS_ON_PAID=true — a “payment received” message to the buyer. Phone numbers are normalised to 380XXXXXXXXX.
Telegram bot
TELEGRAM_BOT_TOKEN + TELEGRAM_ADMIN_IDS. Alerts go to TELEGRAM_CHAT_ID; commands are accepted from admins only.
| Command | Action |
|---|---|
/today, /week | summary: orders, payments, revenue, lost, by gateway, anomalies |
/status #1001, /sync #1001 | payment state; forced sync with the gateway |
/link 1250 Худі чорне M | email | phone | a draft in Shopify + a payment link in 5 seconds |
/refund #1001 300 → /confirm | refund through Shopify (Mistok then sends it to the gateway) |
/cod_off 380… reason, /cod_on, /trusted, /rules | phone rules |
CRM / Zapier / Make
Signed event webhook — the format is on the API page. KeyCRM and SalesDrive accept it via an incoming webhook/ApiX-Drive; your own backend — directly.
Meta CAPI, GA4, Sendpulse
After a successful payment Mistok sends server-side events — independent of ad blockers and the buyer's browser:
| Where | What | Variables |
|---|---|---|
| Meta Conversions API | Purchase (prepayment — InitiateCheckout): value, currency, contents, event_id = mistok-<orderId> for deduplication with the pixel event; email/phone/country/city are SHA-256 hashed, no raw PII leaves the server | META_PIXEL_ID, META_CAPI_TOKEN, META_TEST_EVENT_CODE |
| GA4 Measurement Protocol | purchase (prepayment — begin_checkout) with transaction_id, items, value; client_id is a stable pseudo-id derived from the order (the server never sees the _ga cookie) | GA4_MEASUREMENT_ID, GA4_API_SECRET |
| Sendpulse | an Automation360 event on payment and on every unpaid reminder (with pay_url) — build the email/Viber/SMS chains in Sendpulse; optionally the contact is added to an address book with variables | SENDPULSE_ID, SENDPULSE_SECRET, SENDPULSE_EVENT_PAID, SENDPULSE_EVENT_UNPAID, SENDPULSE_BOOK_ID |
Pixel errors never affect the payment: they are written to the order timeline (stage pixels). If the store already runs Shopify's Facebook & Instagram channel it sends its own Purchase with its own event_id — keep one of the two or rename ours via META_EVENT_NAME.
KeyCRM — native connector
Besides the universal webhook, Mistok can write straight into KeyCRM (OpenAPI, Bearer key from the cabinet). Two schemes:
KEYCRM_MODE | When it fits | What Mistok does |
|---|---|---|
payments (default) | orders already flow into KeyCRM from Shopify via KeyCRM's own integration | finds the order by source_uuid (Shopify order name, e.g. #1001) and adds the payment with POST /order/{id}/payment: amount in UAH, status: paid, method = gateway label, payment date, comment with the invoice id |
orders | no native sync | when the order is missing — creates it (POST /order: source KEYCRM_SOURCE_ID, buyer, products with SKU and prices, shipping with address), then adds the payment; with KEYCRM_PAID_STATUS_ID moves it to that status |
- A prepayment is labelled “Передоплата #1001”, a full payment “Оплата #1001”.
- Refunds: partial — a payment record with status
canceledfor the refunded amount (“Повернення #1001”); once everything is refunded the original payment is set tocanceled. - The fiscal receipt goes into the order's manager comment.
- Duplicate webhooks never create a second payment: the Shopify order → KeyCRM order/payment link is stored in the DB.
- KeyCRM limit is 60 requests/min per key; errors are alerted to the owner and never block the PSP webhook.
Format checked against docs.keycrm.app via the generated client plakidan/key-crm. SalesDrive is next in line.
SalesDrive — native connector
SALESDRIVE_MODE | When it fits | What Mistok does |
|---|---|---|
payments (default) | orders already sync via SalesDrive's own Shopify integration | adds the payment with POST /api/payment/ attached by orderExternalId = Shopify order name, uniqueId = mistok-<invoice> (retries never duplicate); sets paymentDate, payment_method and the SALESDRIVE_PAID_STATUS_ID status |
orders | no native sync | creates the order through POST /handler/ (contact, products with SKU, shipping method, NP branch from order attributes), then attaches the payment by orderId |
- Refunds — an outgoing payment with a negative sum (“Повернення #1001”).
- NP branch picked on the Thank-you page →
shipping_addressof the CRM order. - SalesDrive limits:
/api/order/list/is 10 req/min; Mistok does not use it in the hot path.
Format checked against the open-source client dannychirkov/salesdrive-api and the SalesDrive knowledge base; Swagger — api.salesdrive.me/api/docs. Not yet verified on a live account.
System events in Telegram
Besides alerts, the owner's bot receives system events (TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID; disable with SYS_EVENTS=off, language SYS_EVENTS_LANG=uk|ru):
- 🚀 a new version after deploy — the full list of new features for major releases, a one-liner “upgraded X → Y” for the rest;
- ⚙️ someone changed settings in the admin (who and which keys), 👥 created / disabled a user, 🧪 turned the sandbox on or off;
- 🚫 5 failed logins in a row — the IP is locked for 15 minutes;
- 🔁 a provider did not answer and the failover kicked in; 💾 a missed backup (systemd OnFailure).
The same is visible in the admin: 🧾 Audit log. The admin language switches with the UA | RU button in the header (a cookie, per browser).
Buyer emails
With SMTP_* configured (and EMAIL_ON_PAID not disabled) the buyer receives “Payment received” with the order items, total and the fiscal receipt link (when the PRRO receipt arrives later — a separate receipt email once it is ready), and “Refund” after a refund. Language: EMAIL_LANG=uk|ru|en. Unpaid-order reminders for installment orders include the schedule “N × amount”.
Daily reconciliation
At 07:00 (Kyiv) Mistok compares three sources over the last RECONCILE_DAYS (3) days: Shopify (financial status, gateway), its own database and the PSP statement. Lost webhooks are healed automatically (status re-read at the PSP, payment recorded); the rest goes to Telegram: paid here but not PAID in Shopify; marked paid in Shopify by hand without a payment; old unpaid orders; holds older than 5 days. On demand: the admin button or POST /admin/reconcile/run. Disable with RECONCILE=off, time with RECONCILE_HOUR.
Shopify Markets (currencies)
When the buyer sees prices in PLN or EUR while the shop keeps its books in UAH, Mistok picks the charge currency itself: a PLN/EUR-capable gateway (P24, PayU, Stripe, Fondy) gets the buyer-currency amount, a UAH-only one (mono, LiqPay…) the UAH amount Shopify already converted; the payment is recorded in Shopify in the order currency. Return receipts are built from the real line items, scaled proportionally to the refunded sum (discounts, partial refunds) — no anonymous “Refund” line.
Alerts
Slack/Discord/Mattermost (ALERT_WEBHOOK_URL) and Telegram: amount mismatch, reversed, failed refund, gateway failure with failover, a storm of bad signatures, risky cash on delivery, PRRO error.