# Affiliate webhook的payload

> 有人加入你的affiliate计划或推荐获批准时，BCL发送到你webhook URL的JSON，逐个栏位说明。
>
> Source: https://docs.bcl.my/zh/webhook-affiliate/

Affiliate webhook会在有人加入你的affiliate（推广伙伴）计划，或推荐获批准时通知你自己的系统。BCL会向你为整个计划设置的一个URL发送带JSON body的HTTP `POST`。本页列出每个栏位。

完整的BCL API，请看[API参考文档](https://bcl.my/docs/api)。

## 设置你的webhook URL

Affiliate webhook是为整个计划设置一次，不是每个表单分开设置：

1. 前往**Affiliate** → **Settings**。
2. 打开**Webhook Settings**标签页。

   *(Screenshot: Manage Affiliate Commission Settings页面，已标示Webhook Settings标签页)*

3. 开启**Enable Webhook**。
4. 输入你的**Webhook URL**。
5. 勾选你要的事件：**Affiliate Joined**、**Referral Approved**或**Payout Request**。
6. 点击**Save Webhook Settings**。

   *(Screenshot: Enable Webhook已开启，填好webhook URL，已勾选Affiliate Joined和Referral Approved，已标示Save Webhook Settings按钮)*

> **Caution**
> **Payout Request**虽然出现在列表里，但目前版本的BCL不会发送它。暂时不要创建依赖它的流程。

## 事件

Body是一个扁平的JSON对象，没有`data`外层。`event`键告诉你是哪个事件：

| `event` | BCL什么时候发送 |
| --- | --- |
| `affiliate_joined` | 有人加入你的affiliate计划，包括你还需要批准的时候。 |
| `referral_approved` | 一个推荐获批准，佣金也已确认。 |
| `payout_request` | 在列表里，但目前不会发送。 |

每个payload都有`event`、`team_id`、`team_name`、一个`affiliate`对象和一个`timestamp`。

## Affiliate加入

这是一个`affiliate_joined`请求。里面的值都是虚构的。

```json
{
  "event": "affiliate_joined",
  "team_id": "9ce445a9-21f4-4e2a-b6fa-226dbb2a14f3",
  "team_name": "Contoh Trading",
  "affiliate": {
    "id": 12,
    "name": "Siti binti Ahmad",
    "email": "siti@example.com",
    "phone": "+60198765432"
  },
  "timestamp": "2026-09-27 12:46:53 +08"
}
```

各栏位的意思：

| 栏位 | 类型 | 意思 |
| --- | --- | --- |
| `event` | string | `affiliate_joined`。 |
| `team_id` | string | 你的BCL账号ID。 |
| `team_name` | string | 你在BCL的商家名称。 |
| `affiliate.id` | number | Affiliate的ID。和付款表单webhook里的`affiliate_id`相同。 |
| `affiliate.name`、`affiliate.email`、`affiliate.phone` | string | Affiliate的资料。 |
| `timestamp` | string | BCL发送的时间，格式`YYYY-MM-DD HH:MM:SS +08`（马来西亚时间）。 |

## 推荐获批准

`referral_approved`请求会多一个`referral`对象：

```json
{
  "event": "referral_approved",
  "team_id": "9ce445a9-21f4-4e2a-b6fa-226dbb2a14f3",
  "team_name": "Contoh Trading",
  "affiliate": {
    "id": 12,
    "name": "Siti binti Ahmad",
    "email": "siti@example.com",
    "phone": "+60198765432"
  },
  "referral": {
    "id": 82,
    "form_title": "Online Cooking Class",
    "transaction_id": "01jnszhdng1z4mxqwsfqd27k5w",
    "order_number": "LINK-05912",
    "amount": "100.00",
    "commission_amount": "10.00",
    "status": "approved",
    "referral_source": "direct",
    "ip_address": "203.0.113.10",
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
  },
  "timestamp": "2026-09-27 12:18:27 +08"
}
```

`referral`各栏位的意思：

| 栏位 | 类型 | 意思 |
| --- | --- | --- |
| `id` | number | 推荐ID。和付款表单webhook里的`affiliate_data.referral_id`相同。 |
| `form_title` | string或null | 这笔销售来自的付款表单标题。如果推荐没有连接到付款表单，就是null。 |
| `transaction_id` | string | 这笔销售的内部ID。 |
| `order_number` | string | 这笔销售的BCL订单号码。 |
| `amount` | string | 计算佣金所根据的销售金额。 |
| `commission_amount` | string | 赚到的佣金。 |
| `status` | string | 推荐状态，`approved`。 |
| `referral_source` | string | 买家怎样来到，例如`direct`。 |
| `ip_address`、`user_agent` | string | 记录推荐时买家的IP地址和浏览器。 |

## 提款申请

供参考：BCL已经做好但还没有发送的`payout_request` payload，会多一个`payout_request`对象，里面有`id`、`total_amount`、`status`、`requested_at`、`referral_count`和一个`referrals`列表。每个推荐都有`id`、`order_number`、`commission_amount`、`status`和`transaction_id`。

## 和旧范例的不同

Payload的结构和旧范例一样。不同的是，就算你勾选了`payout_request`，BCL目前也不会发送它。

## 确认webhook是真的

BCL不会为webhook签名。没有signature header，也没有共享的secret。为了安全，请使用难以猜到的URL，在发放佣金前通过[BCL API](https://bcl.my/docs/api)确认这笔销售，并让你的处理程序即使运行两次也不会出问题。

BCL每个事件只发送一次，发送失败也不会自动重试。任何2xx响应都算送达。每次发送都会列在**Tools** → **Webhook Logs**，你可以在那里点击**Resend**重新发送。

## 常见问题

### 我可以把affiliate webhook发送到多个URL吗？

不可以。整个affiliate计划只有一个Webhook URL。如果有几个系统需要这些资料，就把它指向一个会转发资料的endpoint。

### 我怎样知道一个referral_approved webhook属于哪一笔销售？

用referral.order_number。它是BCL订单号码，和付款表单webhook (/zh/webhook-payment-form/)里的record_id是同一个值。

### 有affiliate加入时，我的endpoint刚好停机。我可以再收到那个webhook吗？

可以。前往Tools → Webhook Logs，在All标签页找到失败的那一行，然后在它的菜单点击Resend。Affiliate webhook没有表单，所以不会出现在各个表单类型的标签页下。
