ROAS.my Developers

Outgoing webhooks

ROAS.my menghantar event jualan ke endpoint HTTPS milik anda. Ini ialah kontrak webhook keluar, bukan inbound API. Setiap endpoint memilih event yang diperlukan dan menerima payload lengkap versi 1.

Event catalog

order.created

Order COD/manual yang sah, atau FPX selepas gerbang mengesahkan bayaran.

order.updated

Status payment atau fulfillment bagi order sah berubah.

checkout.abandoned

Checkout belum selesai selepas kira-kira 5 minit tanpa aktiviti form dan mempunyai nombor telefon. Ini bukan order.

order.payment_failed

Gerbang mengesahkan kegagalan atau order FPX tamat melalui expiry generic/CHIP.

Recovery daripada bayaran gagal kepada paid masih menghasilkan order.created. Gunakan eventId sebagai idempotency key; event failure dan abandoned menggunakan ID deterministik.

Request dan headers

HeaderNilai
Content-Typeapplication/json
X-Roas-EventNama event payload
X-Roas-DeliveryID cubaan delivery ROAS.my
X-Roas-SignatureHex HMAC-SHA256 bagi raw request body

Kira signature sebelum parse atau format semula JSON. Signing secret berbeza bagi setiap endpoint dan boleh disalin dari Settings → Integrasi.

Verify HMAC-SHA256

Node.js / Express
import crypto from "node:crypto";

const rawBody = await readRawRequestBody(request);
const received = request.headers["x-roas-signature"];
const expected = crypto
  .createHmac("sha256", process.env.ROAS_WEBHOOK_SECRET)
  .update(rawBody)
  .digest("hex");

const valid = typeof received === "string" &&
  received.length === expected.length &&
  crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return response.status(401).end();

const payload = JSON.parse(rawBody.toString("utf8"));
await processIdempotently(payload.eventId, payload);
return response.status(204).end();
PHP
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_ROAS_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, getenv('ROAS_WEBHOOK_SECRET'));

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

$payload = json_decode($rawBody, true, flags: JSON_THROW_ON_ERROR);
process_idempotently($payload['eventId'], $payload);
http_response_code(204);

Delivery, retry dan respons

  • Pulangkan mana-mana HTTP 2xx selepas payload disimpan atau diproses secara idempotent.
  • Non-2xx, timeout dan network error dicuba maksimum tiga kali melalui queue ROAS.my.
  • Timeout setiap request ialah 10 saat dan HTTP redirect tidak diikuti.
  • URL mesti HTTPS, tanpa credentials dalam URL, dan DNS hanya boleh resolve ke public IP.
  • ROAS.my menyimpan payload snapshot ketika event berlaku; retry tidak berubah apabila Checkout diedit kemudian.

Payload examples

order.package.sku ialah identifier Checkout/package dan bukan line inventori tambahan. SKU fizikal serta kuantitinya berada dalam order.package.variant,order.package.bundleItems dan order.orderBumps. Nilai SKU boleh sama antara metadata package dan item fizikal; jangan jumlahkan kedua-duanya.

order.created
{
  "schemaVersion": 1,
  "event": "order.created",
  "eventId": "order.created:ord_01",
  "occurredAt": "2026-08-22T04:10:00.000Z",
  "test": false,
  "order": {
    "code": "AH-ABC123-1001",
    "status": "confirmed",
    "paymentStatus": "cod",
    "fulfillmentStatus": "unfulfilled",
    "paymentMethod": "cod",
    "customer": {
      "name": "Nur Aisyah",
      "phone": "60123456789",
      "email": "[email protected]",
      "address1": "12 Jalan Melur",
      "address2": "Taman Indah",
      "postcode": "40000",
      "city": "Shah Alam",
      "state": "Selangor",
      "customFields": {}
    },
    "package": {
      "slug": "pakej-kombo",
      "name": "Pakej Kombo",
      "sku": "CHECKOUT-KOMBO",
      "quantity": 1,
      "variant": null,
      "bundleItems": [
        {
          "componentId": "sku_01",
          "name": "Produk A",
          "sku": "PROD-A",
          "quantity": 2,
          "role": "choice"
        }
      ]
    },
    "orderBumps": [],
    "orderBump": null,
    "amount": {
      "subtotalSen": 12900,
      "shippingSen": 1000,
      "codFeeSen": 0,
      "totalSen": 13900,
      "currency": "MYR"
    },
    "tracking": {
      "courier": "",
      "number": "",
      "url": ""
    },
    "utm": {
      "source": "facebook",
      "medium": "paid",
      "campaign": "launch",
      "content": null,
      "term": null,
      "fbclid": null
    },
    "createdAt": "2026-08-22T04:10:00.000Z",
    "updatedAt": "2026-08-22T04:10:00.000Z"
  }
}
order.updated
{
  "schemaVersion": 1,
  "event": "order.updated",
  "eventId": "evt_update_01",
  "occurredAt": "2026-08-22T06:00:00.000Z",
  "test": false,
  "order": {
    "code": "AH-ABC123-1001",
    "status": "shipped",
    "paymentStatus": "cod",
    "fulfillmentStatus": "shipped",
    "paymentMethod": "cod",
    "customer": {
      "name": "Nur Aisyah",
      "phone": "60123456789",
      "email": "[email protected]",
      "address1": "12 Jalan Melur",
      "address2": "Taman Indah",
      "postcode": "40000",
      "city": "Shah Alam",
      "state": "Selangor",
      "customFields": {}
    },
    "package": {
      "slug": "pakej-kombo",
      "name": "Pakej Kombo",
      "sku": "CHECKOUT-KOMBO",
      "quantity": 1,
      "variant": null,
      "bundleItems": []
    },
    "orderBumps": [],
    "orderBump": null,
    "amount": {
      "subtotalSen": 12900,
      "shippingSen": 1000,
      "codFeeSen": 0,
      "totalSen": 13900,
      "currency": "MYR"
    },
    "tracking": {
      "courier": "Ninja Van",
      "number": "NVMY123",
      "url": "https://example.com/track/NVMY123"
    },
    "utm": {
      "source": "facebook",
      "medium": "paid",
      "campaign": "launch",
      "content": null,
      "term": null,
      "fbclid": null
    },
    "createdAt": "2026-08-22T04:10:00.000Z",
    "updatedAt": "2026-08-22T06:00:00.000Z"
  }
}
order.payment_failed
{
  "schemaVersion": 1,
  "event": "order.payment_failed",
  "eventId": "order.payment_failed:ord_02",
  "occurredAt": "2026-08-22T05:15:00.000Z",
  "test": false,
  "order": {
    "code": "AH-DEF456-1002",
    "status": "failed",
    "paymentStatus": "failed",
    "fulfillmentStatus": "unfulfilled",
    "paymentMethod": "fpx",
    "customer": {
      "name": "Muhammad Ali",
      "phone": "60198765432",
      "email": "[email protected]",
      "address1": "8 Jalan Damai",
      "address2": "",
      "postcode": "75450",
      "city": "Melaka",
      "state": "Melaka",
      "customFields": {}
    },
    "package": {
      "slug": "pakej-asas",
      "name": "Pakej Asas",
      "sku": "CHECKOUT-ASAS",
      "quantity": 1,
      "variant": null,
      "bundleItems": []
    },
    "orderBumps": [],
    "orderBump": null,
    "amount": {
      "subtotalSen": 7900,
      "shippingSen": 800,
      "codFeeSen": 0,
      "totalSen": 8700,
      "currency": "MYR"
    },
    "tracking": {
      "courier": "",
      "number": "",
      "url": ""
    },
    "utm": {
      "source": null,
      "medium": null,
      "campaign": null,
      "content": null,
      "term": null,
      "fbclid": null
    },
    "createdAt": "2026-08-22T04:15:00.000Z",
    "updatedAt": "2026-08-22T05:15:00.000Z"
  }
}
checkout.abandoned
{
  "schemaVersion": 1,
  "event": "checkout.abandoned",
  "eventId": "checkout.abandoned:cs_01",
  "occurredAt": "2026-08-22T04:40:00.000Z",
  "test": false,
  "checkout": {
    "customer": {
      "name": "Siti Hajar",
      "phone": "60111222333",
      "email": "",
      "address1": "",
      "address2": "",
      "postcode": "",
      "city": "",
      "state": "",
      "customFields": {}
    },
    "package": {
      "slug": "pakej-kombo",
      "name": "Pakej Kombo",
      "sku": "CHECKOUT-KOMBO",
      "variantSelections": {},
      "bundleSelections": {
        "sku_01": 2
      },
      "bundleSlotSelections": {}
    },
    "paymentMethod": "cod",
    "source": {
      "landingPage": {
        "id": "lp_01",
        "slug": "promo-ogos",
        "title": "Promo Ogos"
      },
      "sessionId": "sid_01",
      "utm": {
        "source": "facebook",
        "medium": "paid",
        "campaign": "launch",
        "content": null,
        "term": null,
        "fbclid": null
      }
    },
    "createdAt": "2026-08-22T04:00:00.000Z",
    "lastActivityAt": "2026-08-22T04:10:00.000Z",
    "resumeUrl": "https://shop.example.com/checkout/pakej-kombo?rt=resume_token"
  }
}

Testing, PII dan operasi

Test webhook

Settings menghantar payload sintetik dengan test: true dan kod TEST-WEBHOOK. Jangan jadikannya order sebenar.

Data pelanggan

Payload membawa PII yang diperlukan oleh sistem order. Gunakan endpoint terkawal, encrypt at rest dan hadkan retention/access.

Tiada secrets

Payload tidak membawa API key, payment token, tracking secret atau signing secret. Jangan log raw headers/PII tanpa kawalan.

Versioning dan changelog

schemaVersion: 1 ialah kontrak semasa. Field optional baharu boleh ditambah tanpa menaikkan versi; perubahan breaking akan mendapat schemaVersion baharu dan tempoh migrasi. Parser anda patut mengabaikan field yang tidak dikenali.

22 Ogos 2026 — schemaVersion 1

Tambah catalog empat event, Checkout SKU, payload snapshot, synthetic testing dan JSON Schema awam.

Outgoing Webhooks | ROAS.my Developers