# Payload webhook Direct Debit

> JSON yang BCL hantar ke URL webhook anda untuk pendaftaran Direct Debit, kelulusan mandate dan setiap potongan bulanan, ruangan demi ruangan.
>
> Source: https://docs.bcl.my/ms/webhook-direct-debit/

Payment form Direct Debit menghantar webhook pada tiga peringkat: semasa pelanggan mendaftar, semasa bank meluluskan atau menolak mandate, dan untuk setiap potongan selepas itu. Setiap satu ialah HTTP `POST` dengan body JSON yang mengandungi `event` dan `data`. Page ini menyenaraikan setiap ruangan.

Untuk API BCL yang penuh, lihat [rujukan API](https://bcl.my/docs/api).

## Tetapkan URL webhook anda

Anda menetapkan webhook pada payment form Direct Debit itu sendiri:

1. Pergi ke **Payments** → **Forms** dan buka payment form Direct Debit anda.
2. Klik **Advanced** di bahagian atas, kemudian buka tab **Webhook Settings**.
3. On-kan **Enable Webhook** dan masukkan satu atau lebih **Webhook URLs** (sehingga 10, setiap satu bermula dengan `https://`). Jika tiada kotak dipaparkan, klik **Add Webhook URL** dahulu.
4. Di bawah **Webhook Events**, tandakan event yang anda perlukan. Lihat bahagian seterusnya.

   *(Screenshot: Senarai Webhook Events dengan keempat-empat event ditandakan dan Direct Debit ditanda)*

5. Klik **Save Advanced Settings**.

Untuk mencuba endpoint anda, klik **Send Test** dan pilih **Direct Debit - When direct debit transactions occur** dalam **Event Type**. BCL menghantar tiga contoh request, satu untuk setiap peringkat: pendaftaran, kelulusan dan potongan.

*(Screenshot: Panel Send Test Webhook dengan event type Direct Debit ditanda dan tiga contoh payload dalam preview)*

## Event mana dihantar bila

Checkbox **Direct Debit** sahaja tidak meliputi pendaftaran. Empat event ini dipadankan dengan peringkat mandate seperti berikut:

| Peringkat | `event` | Tandakan event ini | `record_type` |
| --- | --- | --- | --- |
| Pelanggan menghantar payment form | `form-submit` | **Form Submit** | `DirectDebit` |
| Bank meluluskan pendaftaran atau perubahan | `payment-success` | **Payment Success** | `DirectDebit` |
| Bank menolaknya, atau ia gagal | `payment-failed` | **Payment Failed** | `DirectDebit` |
| Setiap potongan | `direct-debit` | **Direct Debit** | `DirectDebitDeduction` |

BCL tidak menghantar webhook apabila mandate ditamatkan.

## Payload pendaftaran & kelulusan

Event `form-submit`, `payment-success` dan `payment-failed` berkongsi satu bentuk payload. Ini ialah `payment-success` untuk pendaftaran yang diluluskan. Nilai-nilainya rekaan.

```json
{
  "event": "payment-success",
  "data": {
    "formable_type": "payment_form",
    "formable_id": 34,
    "form_title": "Monthly Fees",
    "form_url": "https://shop.example.com/form/monthly-fees",
    "form_featured_image": "",
    "record_type": "DirectDebit",
    "record_id": "LINK-00041",
    "main_data": {
      "id": "01jfmnrycszgmdhdk78nmtr3gs",
      "payer_name": "Ali bin Abu",
      "payer_email": "ali@example.com",
      "payer_telephone_number": "+60123456789",
      "order_number": "LINK-00041",
      "transaction_type": "live",
      "currency": "MYR",
      "subtotal_amount": "10.00",
      "fee_amount": "1.50",
      "amount": "11.50",
      "application_type": "Enrolment",
      "approval_status": "Approved",
      "bayarcash_mandate_id": "md_AbC123",
      "mandate_reference_number": "E-20261727400000",
      "effective_date": "2026-10-01",
      "frequency_mode": "MT",
      "created_at": "27/09/2026 10:15:00"
    },
    "tracking_params": null,
    "custom_fields_data": {
      "company_name": "Contoh Trading"
    }
  }
}
```

Objek `data` ada key berikut:

| Ruangan | Jenis | Maksud |
| --- | --- | --- |
| `formable_type` | string | Sentiasa `payment_form`. Menggantikan `form_id` yang lama. |
| `formable_id` | number | ID payment form. |
| `form_title` | string | Tajuk payment form. |
| `form_url` | string | Link awam ke payment form, atau kosong. |
| `form_featured_image` | string | URL featured image, atau kosong. |
| `record_type` | string | `DirectDebit`. |
| `record_id` | string | Nombor pesanan BCL bagi mandate itu. Nombor ini kekal sama untuk setiap potongan. |
| `main_data` | object | Mandate itu. Lihat di bawah. |
| `tracking_params` | null | Tidak direkodkan untuk Direct Debit. |
| `custom_fields_data` | object | Custom field payment form, dengan nama ruangan sebagai key. Tiada jika payment form itu tidak ada custom field. |

Tiada `receipt_url` untuk Direct Debit.

Dalam `main_data`, BCL tidak menghantar ruangan yang kosong:

| Ruangan | Jenis | Maksud |
| --- | --- | --- |
| `id` | string | ID mandate dalaman. |
| `payer_name`, `payer_email`, `payer_telephone_number` | string | Butiran pelanggan. |
| `order_number` | string | Sama seperti `record_id`. |
| `transaction_type` | string | `live` atau `test`. |
| `currency` | string | Biasanya `MYR`. |
| `subtotal_amount`, `fee_amount` | string | Amaun sebelum caj, dan caj itu. Sentiasa dihantar. |
| `amount` | string | Amaun yang dipotong setiap kitaran. |
| `discount_amount`, `coupon_code`, `coupon_usage_id`, `additional_fee_amount`, `tax_amount`, `shipping`, `classification_code` | pelbagai | Dihantar hanya jika payment form menggunakannya. Maksudnya sama seperti dalam [payload payment form](/ms/webhook-payment-form/). |
| `application_type` | string | Event kelulusan sahaja: `Enrolment` atau `Maintenance` (perubahan pada mandate). |
| `approval_status` | string | Pada event kelulusan, salah satu daripada `New`, `Waiting Approval`, `Failed Bank Verification`, `Active`, `Terminated`, `Approved`, `Rejected`, `Cancelled` atau `Error`. Pada `form-submit`, ia ialah kod status mentah, atau tiada. |
| `bayarcash_mandate_id` | string | ID mandate Bayarcash, selepas bank membalas. |
| `mandate_reference_number` | string | Rujukan mandate daripada bank. |
| `effective_date`, `expiry_date` | string | Tarikh mula dan tarikh tamat mandate. |
| `frequency_mode` | string | Kekerapan potongan. `MT` bermaksud bulanan. |
| `created_at` | string | Masa pendaftaran, `DD/MM/YYYY HH:MM:SS`, waktu Malaysia. |
| `items` | array | Produk pada payment form, jika ada: `index`, `item`, `option`, `quantity`, `unit_amount`, `amount`, `access_url`, `product_image` dan `protected_content`. |

## Payload potongan

BCL menghantar `direct-debit` untuk setiap keputusan potongan yang Bayarcash laporkan, sama ada berjaya atau tidak. Payload ini masih menggunakan `form_id`.

```json
{
  "event": "direct-debit",
  "data": {
    "form_id": 34,
    "form_title": "Monthly Fees",
    "record_type": "DirectDebitDeduction",
    "record_id": "LINK-00041",
    "main_data": {
      "order_number": "1-727-400-448-141498",
      "payer_name": "Ali bin Abu",
      "payer_email": "ali@example.com",
      "payer_telephone_number": "+60123456789",
      "mandate_id": "md_AbC123",
      "mandate_reference_number": "E-20261727400000",
      "bayarcash_transaction_id": "trx_3qngpY",
      "batch_number": "HLB1727400000",
      "amount": "11.50",
      "status": 3,
      "status_description": "Successful",
      "datetime": "2026-10-01 00:00:00",
      "cycle": 1
    },
    "custom_fields_data": {
      "company_name": "Contoh Trading"
    }
  }
}
```

Maksud ruangan potongan:

| Ruangan | Jenis | Maksud |
| --- | --- | --- |
| `form_id` | number | ID payment form. |
| `form_title` | string | Tajuk payment form. |
| `record_type` | string | `DirectDebitDeduction`. |
| `record_id` | string | Nombor pesanan BCL bagi mandate itu. |
| `main_data.order_number` | string | Nombor rujukan potongan ini. |
| `payer_name`, `payer_email`, `payer_telephone_number` | string | Butiran pelanggan daripada mandate. |
| `mandate_id` | string | ID mandate Bayarcash. |
| `mandate_reference_number` | string | Rujukan mandate daripada Bayarcash. |
| `bayarcash_transaction_id` | string | ID transaksi Bayarcash untuk potongan ini. |
| `batch_number` | string atau null | Nombor batch bank. |
| `amount` | string | Amaun yang dipotong. |
| `status` | number | Kod status potongan. `3` bermaksud berjaya. |
| `status_description` | string atau null | Teks status dari Bayarcash. |
| `datetime` | string | Tarikh dan masa potongan. |
| `cycle` | number atau null | Potongan yang keberapa, dikira dari 1. |
| `custom_fields_data` | object | Custom field payment form. Tiada jika payment form itu tidak ada custom field. |

## Perubahan daripada contoh lama

Jika anda membina integrasi anda berdasarkan contoh lama, semak perubahan ini:

- Payload pendaftaran dan kelulusan tidak lagi ada `form_id`. Guna `formable_id`.
- Ruangan baru pada pendaftaran dan kelulusan: `formable_type`, `form_url`, `form_featured_image`, `tracking_params`, `subtotal_amount`, `created_at`, dan `items` jika payment form ada produk.
- Kelulusan yang ditolak atau gagal kini dihantar sebagai `payment-failed`.
- Payload potongan tidak berubah.

## Semak sama ada webhook itu tulen

BCL tidak menandatangani webhook, jadi tiada signature untuk disemak. Sebelum anda bertindak atas sesuatu webhook, cari mandate atau pesanan itu melalui [API BCL](https://bcl.my/docs/api), guna URL yang sukar diteka, dan pastikan sistem anda selamat menerima event yang sama dua kali. Setiap cubaan disenaraikan di bawah **Tools** → **Webhook Logs**. BCL tidak menghantar semula secara automatik jika penghantaran gagal: buka menu baris itu dan klik **Resend**. Dalam Test Mode, cari pesanan dengan `GET /transaction/{order_number}`, dan hantar header `User-Agent` anda sendiri.

## Isu biasa

### Kenapa saya tidak menerima webhook apabila pelanggan mendaftar, walaupun saya sudah tandakan Direct Debit?

Event Direct Debit hanya meliputi potongan. Pendaftaran menggunakan Form Submit, dan kelulusan atau penolakan oleh bank menggunakan Payment Success atau Payment Failed. Tandakan keempat-empat event untuk mengikuti sesuatu mandate dari mula hingga akhir.

### Bagaimana saya memadankan potongan bulanan dengan mandate pelanggan?

Dalam payload direct-debit, record_id ialah nombor pesanan BCL bagi mandate itu, iaitu record_id yang sama yang anda terima semasa pendaftaran. main_data.mandate_id ialah ID mandate Bayarcash, yang sama dengan bayarcash_mandate_id dalam webhook kelulusan.

### Kenapa webhook test berbeza daripada webhook sebenar?

Contoh Send Test untuk Direct Debit masih menggunakan form_id dalam payload pendaftaran dan kelulusan. Webhook pendaftaran dan kelulusan yang sebenar menghantar formable_type dan formable_id. Webhook potongan, sama ada test atau sebenar, menggunakan form_id.
