Skip to content

Payment form webhook payload

Updated

A webhook lets BCL tell your own system about a sale the moment it happens. When a customer submits a payment form, or their payment succeeds or fails, BCL sends an HTTP POST with a JSON body to each URL you set. This page lists every field in that body.

For the full BCL API, see the API reference.

You set webhooks per form. To turn them on:

  1. Go to Payments > Forms and open your form.
  2. Click Advanced at the top, then open the Webhook Settings tab.
  3. Turn on Enable Webhook.
  4. Under Webhook URLs, enter your endpoint. Click Add Webhook URL to add more, up to 10. Every URL must start with https://, and every URL receives the same events.
  5. Under Webhook Events, tick the events you want.
  6. Click Save Advanced Settings.

To try your endpoint without a real sale, click Send Test in the Webhook Configuration section. Choose an Event Type, check the Webhook URL and the Payload Preview, then click Send Test Webhook. Test payloads use random sample values.

Event and booking forms have the same Webhook Settings tab under Advanced, with the same payment payload described here.

Each request body has two keys: event (the event name) and data (the payload). These events use the payload on this page:

Event When BCL sends it
form-submit The customer submits the form, before payment.
payment-success Bayarcash confirms the payment.
payment-failed The payment fails or is cancelled.

For Direct Debit forms, see Direct Debit webhook payload. Event forms can also send ticket-checked-in, and booking forms booking-confirmed and booking-cancelled. Those use a different payload.

This is a payment-success request. The values are made up.

{
"event": "payment-success",
"data": {
"formable_type": "payment_form",
"formable_id": 210,
"form_title": "Online Cooking Class",
"form_url": "https://shop.example.com/form/online-cooking-class",
"form_featured_image": "https://cdn.example.com/banner.jpg",
"record_type": "Transaction",
"record_id": "LINK-02046",
"main_data": {
"id": "01jfps609t08vjb8d5exq27bz6",
"payer_name": "Ali bin Abu",
"payer_email": "ali@example.com",
"payer_telephone_number": "+60123456789",
"order_number": "LINK-02046",
"transaction_type": "live",
"currency": "MYR",
"subtotal_amount": "100.00",
"fee_amount": "1.00",
"amount": "101.00",
"payment_channel": "FPX",
"status": 3,
"status_description": "Approved",
"created_at": "27/09/2026 14:05:09",
"customer_id": "01jfps5zq1m2k8c4x7v9b3n6td",
"is_paid": 1,
"retry_count": 0,
"source_type": "payment_form",
"items": [
{
"id": 5501,
"index": 101,
"productable_type": "product_item",
"productable_id": 101,
"item": "Cooking Class Recording",
"sku": "CC-001",
"option": "",
"quantity": 1,
"unit_amount": "100.00",
"normal_price": "120.00",
"sale_price": "100.00",
"amount": "100.00",
"access_url": null,
"weight": "0.00",
"total_weight": "0.00",
"metadata": null,
"product_image": "https://cdn.example.com/class.jpg",
"protected_content": [
{
"title": "Module 1",
"url": "https://shop.example.com/content/abc123token",
"access_finder_url": "https://shop.example.com/content/7/access"
}
]
}
]
},
"tracking_params": {
"utm_source": "facebook",
"utm_campaign": "september-promo"
},
"receipt_url": "https://bcl.my/storage/pdf/LINK-02046.pdf",
"custom_fields_data": {
"address_line1": "1 Jalan Contoh",
"city": "Kajang",
"state": "Selangor",
"postal_code": "43000",
"country": "MY"
},
"affiliate_data": {
"referral_id": 57,
"affiliate_id": 12,
"affiliate_username": "siti",
"affiliate_name": "Siti binti Ahmad",
"affiliate_email": "siti@example.com",
"affiliate_phone": "+60198765432",
"commission_type": "percentage",
"commission_rate": "10.00",
"commission_amount": "10.00",
"commission_breakdown": null,
"status": "approved",
"referral_source": "direct"
}
}
}

The data object contains these keys:

Field Type Meaning
formable_type string Form type: payment_form, event_form or booking_form.
formable_id number The form’s ID. Replaces the old form_id.
form_title string Form title. For event forms, the event name.
form_url string Public link to the form. Empty if the form has no slug or domain.
form_featured_image string Featured image URL, or empty.
record_type string Always Transaction on this page.
record_id string The BCL order number, for example LINK-02046. Use it to match records.
main_data object The order. See the next section.
tracking_params object or null UTM and click IDs captured when the customer opened the form: utm_source, utm_medium, utm_campaign, utm_content, utm_term, utm_id, fbclid, ttclid, gclid. Only the ones present are sent.
receipt_url string payment-success only. Link to the PDF receipt. Anyone opening it is asked to verify with a one-time code first.
custom_fields_data object The form’s custom fields and address fields, keyed by field name. Absent if there are none.
affiliate_data object Present only when an affiliate referred the sale.

BCL sends the order record and drops empty values. A field with no value (null, empty, or 0.00 for most amounts) is left out, so do not rely on every field being present.

Field Type Meaning
id string Internal transaction ID.
payer_name, payer_email, payer_telephone_number string The buyer’s details.
order_number string Same as record_id.
transaction_type string live or test.
currency string Usually MYR.
subtotal_amount string Item total before fees. Always sent.
discount_amount string Coupon discount.
coupon_code, coupon_usage_id string, number The coupon used.
fee_amount string Fee charged to the buyer. Always sent.
additional_fee_amount string Extra fee set on the form.
rounding_adjustment string Rounding applied to the total.
tax_amount object Tax details, such as the tax name, rate and amount.
shipping object Shipping method name, cost and total_weight.
bank_transfer_details object Bank details, for manual transfer orders.
amount string Total the buyer pays.
payment_channel string Channel name, for example FPX, DuitNow QR, Credit Card, Cash on Delivery (COD) or Manual Bank Transfer.
status number Payment status code. 3 means successful and 2 means failed. It may be absent on form-submit.
status_description string Status text from Bayarcash. payment-success and payment-failed only.
is_paid number 1 once paid, otherwise 0.
retry_count number How many times the buyer retried payment.
created_at string Order time, DD/MM/YYYY HH:MM:SS, Malaysia time.
customer_id string BCL customer ID.
classification_code string e-invoice classification code.
affiliate_id, cookie_id string Affiliate attribution, when present.
source_type string Same as formable_type.
items array Items bought. See the next section.

Each entry in items has these keys:

Field Type Meaning
id number Line item ID.
index, productable_id number Product ID. index is kept for older integrations.
productable_type string product_item, event_ticket or booking_service, for example.
item, sku, option string Product name, SKU and chosen variation.
quantity number Quantity.
unit_amount, normal_price, sale_price string Price paid per unit, normal price and sale price.
amount string Line total.
access_url string or null Download link for a digital product.
weight, total_weight string Weight per unit and for the line.
metadata object or null Extra item data.
product_image string Product image URL, or empty.
protected_content array payment-success only: each item has title, url (the buyer’s personal access link) and access_finder_url (the page where a buyer who lost the link can find it again). Empty on other events.

The affiliate_data object has referral_id, affiliate_id, affiliate_username, affiliate_name, affiliate_email and affiliate_phone. It also has commission_type (flat, percentage or per_item), commission_rate and commission_amount. For per-item commission, commission_rate is null and commission_breakdown lists each item. Finally, status (for example pending or approved) and referral_source.

If you built your integration from the old sample, check these changes:

  • form_id is gone. Use formable_id, and formable_type for the form type.
  • New top-level fields: formable_type, form_url, form_featured_image and tracking_params.
  • New order fields: subtotal_amount, created_at, customer_id, is_paid, retry_count, source_type and the discount, coupon, tax and shipping fields.
  • New item fields: id, productable_type, productable_id, sku, normal_price, sale_price, weight, total_weight, metadata, product_image and protected_content.
  • New affiliate field: commission_breakdown.
  • New event: payment-failed.

BCL does not sign webhooks. There is no signature header or shared secret, so anyone who knows your URL could post to it. To stay safe:

  • Before you ship an order or grant access, look up the order by record_id through the BCL API and confirm the status there.
  • Use a URL that is hard to guess, for example with a long random token in the path.
  • Handle the same event more than once safely. Use record_id and event together to spot repeats.

BCL sends each event once to each URL, one request per URL. Any 2xx response counts as delivered. Reply quickly and do the heavy work afterwards.

Every attempt is recorded under Tools > Webhook Logs, with the payload, your response and any error. To send one again, open the row’s menu and click Resend.

Common issues

I added my webhook URL but my system receives nothing. How do I test it?

Open the form's Webhook Settings tab and click Send Test. Pick an Event Type, check the Webhook URL and send. If the test arrives but real payments do not, check that you ticked Payment Success and not only Form Submit. If the test does not arrive, make sure your URL is public, starts with https://, accepts POST and replies with a 2xx status.

How can my own system know when a payment comes in?

Turn on the webhook for the form and tick Payment Success. BCL posts the order, buyer, items and custom fields to your URL as soon as the payment is confirmed. Match it to your records with record_id, which is the BCL order number.

Will importing old records fire my webhook?

Only if you tick Run webhooks and automations on the import page. Each imported row then triggers your webhooks as if it were a real payment, so leave it off if you only want the records in BCL.

Was this article helpful?

Cookie settings

We use Google Analytics to see which guides help and where readers get stuck. It is on by default; you can turn it off. Your choice is saved on this device.