Manova Developers

Order lookup

The signed request Manova sends your store when a customer asks where their order is.

When a customer asks about an order ("سفارشم کجاست؟", or the پیگیری سفارش button), the assistant asks for the order number and the mobile number they bought with. Manova then asks your store, and replies with the status, the items and the tracking code. Both numbers must match the same order, so nobody can read someone else's order by guessing a number.

Order lookup is on when the store is connected with an apiUrl and پیگیری سفارش is enabled in the panel.

The request

POST {apiUrl}/order
Content-Type: application/json
Accept: application/json
User-Agent: ManovaBot/1.0 (+https://manova.ir)
X-Manova-Timestamp: 1793371200
X-Manova-Signature: 5d1c2a0b9e...f4

{"number":"10457","phone":"09121234567"}
FieldFormat
numberThe order number as the customer typed it, digits only (Persian digits are converted).
phoneAn Iranian mobile number, normalised to 09xxxxxxxxx.

Manova waits up to 10 seconds, does not follow redirects and only calls public addresses.

Verify the signature

The signature is a hex HMAC-SHA256 of timestamp + "." + rawBody, keyed with the secret: the part of your connection key after the last underscore.

Reject the request with 401 unless:

  1. the timestamp is within 5 minutes of your clock, and
  2. the signature matches, compared in constant time.

Always hash the raw body bytes exactly as received, before parsing the JSON.

<?php
$key = getenv('MANOVA_STORE_KEY');                  // mnv_<store>_<secret>
$secret = substr($key, strrpos($key, '_') + 1);

$body = file_get_contents('php://input');
$time = $_SERVER['HTTP_X_MANOVA_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MANOVA_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $time . '.' . $body, $secret);
if (abs(time() - (int) $time) > 300 || !hash_equals($expected, $signature)) {
    http_response_code(401);
    exit(json_encode(['error' => 'unauthorized']));
}

$data = json_decode($body, true);

Find the order

Look the order up by the number customers see (your order number, which may differ from your database id). Then check that phone matches one of the order's phone numbers. Compare the last 10 digits of each, so +989121234567, 00989121234567 and 09121234567 all match.

If there is no such order, or the phone does not match, answer the same way, so a caller cannot tell which one was wrong:

HTTP/1.1 404 Not Found
Content-Type: application/json

{"found": false}

The response

200 OK
{
  "found": true,
  "order": {
    "number": "10457",
    "status": "processing",
    "status_label": "در حال انجام",
    "date": "۸ آبان ۱۴۰۵",
    "date_iso": "2026-10-30T10:24:00+03:30",
    "total": "۴٬۹۰۰٬۰۰۰ تومان",
    "items": [
      { "name": "کفش رانینگ مردانه مدل آریا", "qty": 1 },
      { "name": "جوراب ورزشی", "qty": 2 }
    ],
    "shipping": "پست پیشتاز",
    "tracking": {
      "code": "123456789012345678901234",
      "carrier": "پست",
      "url": "https://tracking.post.ir/?id=123456789012345678901234"
    },
    "url": "https://shop.example.com/account/orders/10457"
  }
}

Everything inside order is optional except number and status. HTML is stripped from every field.

Prop

Type

statusMeaning shown to the customer
pendingWaiting for payment
on-holdPlaced, waiting for payment confirmation or review
processingPaid, being prepared and shipped
completedCompleted and shipped
cancelledCancelled
refundedRefunded
failedPayment failed

Any other status is shown with your status_label.

Failures

A response other than 200 or 404 {"found": false}, a timeout or invalid JSON counts as a failure: the customer is told the store cannot be reached right now, and the error appears in the panel's فروشگاه section.

Each conversation can make up to 6 lookups an hour, which stops number guessing in the chat itself.

On this page