Sambung ToyyibPay ke Lucee: cfhttp dan REST API (contoh penuh)
Panduan sambung ToyyibPay ke Lucee guna cfhttp: cipta bil createBill, redirect halaman bayar, sahkan hash MD5 callback dan semak status getBillTransactions.

Kandungan
- Sambung ToyyibPay ke Lucee: cfhttp dan REST API (contoh penuh)
- Apa itu ToyyibPay dan mengapa sesuai untuk Lucee
- Dapatkan User Secret Key dan sediakan kategori
- Cipta bil dengan cfhttp (contoh penuh createBill)
- Redirect pelanggan ke halaman pembayaran
- Terima callback dan sahkan hash MD5
- Semak status dengan getBillTransactions
- Nyahaktif bil dan kendali ralat
- Ujian sandbox di dev.toyyibpay.com
- Kesilapan lazim
- Soalan Lazim (FAQ)
- Sumber
Sambung ToyyibPay ke Lucee: cfhttp dan REST API (contoh penuh)
ToyyibPay ialah gerbang pembayaran Malaysia yang boleh anda sambungkan ke Lucee menggunakan tag cfhttp sahaja, tanpa SDK khas. Anda hantar User Secret Key sebagai medan borang, cipta bil melalui createBill, bawa pelanggan ke halaman pembayaran, kemudian sahkan callback dan silang-semak status melalui getBillTransactions. Artikel ini menunjukkan aliran penuh dengan contoh kod CFML yang boleh disalin terus.
Apa itu ToyyibPay dan mengapa sesuai untuk Lucee
ToyyibPay menyediakan API berasaskan HTTP yang ringkas dan dipanggil dengan cfhttp. Pengesahan ialah User Secret Key yang dihantar sebagai medan POST dalam badan permintaan, bukan pengepala, jadi tiada persediaan header khas diperlukan. Setiap endpoint dinyatakan penuh, contohnya https://toyyibpay.com/index.php/api/createBill. Oleh sebab ia hanya borang POST yang membalas JSON, gerbang ini paling mudah untuk projek CFML berbanding gerbang yang memerlukan tandatangan kompleks. Asas memanggil API luar dibincangkan dalam panduan cfhttp API luar.
Dapatkan User Secret Key dan sediakan kategori
Log masuk ke akaun toyyibPay untuk mendapatkan User Secret Key. Kunci ini dihantar sebagai medan userSecretKey dalam setiap permintaan. Simpan kunci dalam pemboleh ubah persekitaran atau fail konfigurasi luar dan bukannya dikod terus dalam sumber; rujuk panduan menyimpan rahsia dalam CFML.
Setiap bil perlu tergolong dalam satu kategori. Cipta kategori melalui endpoint createCategory dengan parameter catname, catdescription dan userSecretKey:
local.resp = "";
http url="https://toyyibpay.com/index.php/api/createCategory" method="post" result="local.resp" charset="utf-8" {
httpparam type="formfield" name="catname" value="Pesanan Kedai";
httpparam type="formfield" name="catdescription" value="Bayaran pesanan dalam talian";
httpparam type="formfield" name="userSecretKey" value=variables.userSecretKey;
}
local.kategori = deserializeJSON(local.resp.fileContent);
variables.categoryCode = local.kategori[1].CategoryCode;
Balasan ialah array JSON seperti [{"CategoryCode":"j0tzqhka"}]. Simpan CategoryCode ini kerana ia diperlukan semasa mencipta bil. Anda juga boleh menyemak butiran kategori melalui getCategoryDetails dengan parameter userSecretKey dan categoryCode.
Cipta bil dengan cfhttp (contoh penuh createBill)
Bil dicipta dengan POST ke https://toyyibpay.com/index.php/api/createBill. Medan wajib ialah userSecretKey, categoryCode, billName (maks 30 aksara alfanumerik, ruang atau garis bawah), billDescription (maks 100 aksara), billPriceSetting, billPayorInfo, billAmount, billReturnUrl dan billCallbackUrl. Amaun dinyatakan dalam sen, iaitu 100 bersamaan RM1. Contoh penuh:
local.resp = "";
http url="https://toyyibpay.com/index.php/api/createBill" method="post" result="local.resp" charset="utf-8" {
httpparam type="formfield" name="userSecretKey" value=variables.userSecretKey;
httpparam type="formfield" name="categoryCode" value=variables.categoryCode;
httpparam type="formfield" name="billName" value="Pesanan_1001";
httpparam type="formfield" name="billDescription" value="Bayaran untuk pesanan 1001";
httpparam type="formfield" name="billPriceSetting" value="1";
httpparam type="formfield" name="billPayorInfo" value="0";
httpparam type="formfield" name="billAmount" value="100";
httpparam type="formfield" name="billReturnUrl" value="https://anda.my/terima-kasih";
httpparam type="formfield" name="billCallbackUrl" value="https://anda.my/callback-toyyibpay";
httpparam type="formfield" name="billExternalReferenceNo" value="Pesanan-1001";
httpparam type="formfield" name="billPaymentChannel" value="2";
}
local.billCode = "";
if (isJSON(local.resp.fileContent)) {
local.data = deserializeJSON(local.resp.fileContent);
if (arrayLen(local.data) > 0) {
local.billCode = local.data[1].BillCode;
}
}
Nilai 1 pada billPriceSetting bermakna amaun tetap, manakala 0 membenarkan pelanggan menentukan amaun sendiri, dan dalam kes itu billAmount ditetapkan 0. Nilai 0 pada billPayorInfo bermakna bil terbuka tanpa maklumat pembayar wajib. Medan billPaymentChannel menetapkan saluran yang diterima: 0 untuk FPX, 1 untuk kad kredit, dan 2 untuk kedua-duanya. Medan pilihan lain termasuk billTo, billEmail, billPhone, billContentEmail (maks 1000 aksara), billChargeToCustomer, enableDuitNowQR dan chargeDuitNowQR.
Perkara paling penting: createBill mengembalikan HTTP 200 untuk kedua-dua kejayaan dan kegagalan. Anda mesti membaca isi balasan dan bukannya kod status. Ralat pengesahan parameter berbentuk JSON seperti {"status":"error","msg":"..."}, manakala ralat akaun atau bil berbentuk teks kosong seperti [KEY-DID-NOT-EXIST], [CATEGORY-NOT-MATCH] atau [INVALID-EMAIL-FORMAT]. Oleh itu semak isJSON terlebih dahulu sebelum deserializeJSON, seperti dalam contoh di atas.
Redirect pelanggan ke halaman pembayaran
Setelah mendapat BillCode, bina URL pembayaran dan alihkan pelanggan kepadanya. URL pelanggan ialah https://toyyibpay.com/ diikuti BillCode:
location("https://toyyibpay.com/" & local.billCode, false);
Simpan BillCode dan rujukan luaran anda dalam pangkalan data supaya anda boleh memadankannya semula apabila callback tiba. Gunakan billExternalReferenceNo untuk membawa rujukan sistem anda sendiri, kerana ia dikembalikan semula dalam data transaksi apabila anda menyemak status.
Terima callback dan sahkan hash MD5
Selepas pelanggan membayar, toyyibPay menghantar kemas kini server-side sebagai POST borang ke billCallbackUrl dengan medan refno, status (1 berjaya, 2 tertunda, 3 gagal), reason, billcode, order_id, amount, transaction_time dan hash. Ia juga mengalihkan pelanggan ke billReturnUrl melalui GET dengan parameter status_id, billcode dan order_id.
Untuk mengesahkan callback itu benar daripada toyyibPay, bandingkan hash MD5 yang dihantar dengan nilai yang anda kira semula. Formula rasminya ialah MD5(userSecretKey + status + order_id + refno + "ok"):
local.expected = lcase(hash(
variables.userSecretKey & form.status & form.order_id & form.refno & "ok",
"MD5"
));
if (local.expected != lcase(form.hash)) {
cfheader(statuscode = 403);
writeOutput("signature tidak sah");
abort;
}
Jangan menutup pesanan berdasarkan callback semata-mata. Callback ialah isyarat awal; status akhir mesti disilang-semak melalui getBillTransactions seperti bahagian seterusnya. Return URL pula menerima parameter GET iaitu status_id (1, 2 atau 3), billcode dan order_id, jadi anda boleh memaparkan hasil pembayaran kepada pelanggan di halaman terima kasih. Bagi pembayaran DuitNow QR, callback menambah medan status_id, msg, transaction_id dan dnqr_transaction_id tetapi tidak menyertakan transaction_time atau fpx_transaction_id, jadi jangan harapkan medan tersebut wujud untuk setiap saluran.
Hash MD5 ini tidak mengandungi masa atau nonce, jadi ia hanya membuktikan integriti data dan bukannya kesegaran permintaan. Simpan refno yang sudah diproses untuk menolak duplikat, dan sentiasa silang-semak status. Perhatikan juga bahawa callback tidak boleh diarahkan ke localhost, jadi semasa pembangunan anda memerlukan tunnel. Rujuk panduan webhook tempatan dan panduan keselamatan aplikasi web untuk langkah melindungi endpoint ini.
Semak status dengan getBillTransactions
Sahkan status akhir melalui endpoint getBillTransactions. Parameter ialah billCode dan userSecretKey (wajib jika anda mahu menerima nama, e-mel dan telefon pembayar), serta billpaymentStatus yang pilihan:
local.resp = "";
http url="https://toyyibpay.com/index.php/api/getBillTransactions" method="post" result="local.resp" charset="utf-8" {
httpparam type="formfield" name="billCode" value=variables.billCode;
httpparam type="formfield" name="userSecretKey" value=variables.userSecretKey;
}
local.transactions = deserializeJSON(local.resp.fileContent);
local.bayarBerjaya = false;
if (arrayLen(local.transactions) > 0) {
local.bayarBerjaya = (local.transactions[1].billpaymentStatus == "1");
}
Balasan ialah array JSON. Kod status bil ialah 1 berjaya, 2 tertunda, 3 tidak berjaya, dan 4 tertunda. Medan billpaymentStatus membawa status pembayaran, dan medan billpaymentInvoiceNo membawa nombor invois rasmi toyyibPay. Tanpa userSecretKey pemilik bil, medan billTo, billEmail dan billPhone dikembalikan kosong atas sebab privasi.
Nyahaktif bil dan kendali ralat
Untuk membatalkan atau menyahaktifkan bil yang belum dibayar, panggil endpoint inactiveBill dengan parameter secretKey dan billCode:
local.resp = "";
http url="https://toyyibpay.com/index.php/api/inactiveBill" method="post" result="local.resp" charset="utf-8" {
httpparam type="formfield" name="secretKey" value=variables.userSecretKey;
httpparam type="formfield" name="billCode" value=variables.billCode;
}
Balasan boleh jadi {"status":"success","result":"Bill status changed to inactive"}, atau {"status":"failed","result":"Bill has pending transaction process"}, atau {"status":"failed","result":"Bill is inactive"}. Baca isi balasan untuk membezakan tiga keadaan ini.
Ujian sandbox di dev.toyyibpay.com
ToyyibPay menyediakan persekitaran sandbox yang boleh didaftarkan sendiri di https://dev.toyyibpay.com tanpa memerlukan akaun perniagaan sebenar. Untuk ujian, gantikan domain toyyibpay.com dengan dev.toyyibpay.com dan gunakan kelayakan akaun sandbox. Di sandbox anda boleh mencipta bil dummy dan membayarnya menggunakan Bank Simulators, jadi anda boleh menguji keseluruhan aliran daripada cipta bil hingga callback tanpa transaksi wang sebenar.
Kesilapan lazim
Satu kesilapan besar ialah mempercayai kod status HTTP: createBill mengembalikan 200 walaupun gagal, jadi sentiasa baca isi balasan. Kedua, callback tidak boleh diarahkan ke localhost. Ketiga, amaun mesti dalam sen dan mesti melebihi 1.00, dan billName serta billDescription mempunyai had panjang masing-masing. Keempat, tempoh bil ditetapkan melalui billExpiryDays antara 1 hingga 100 hari atau billExpiryDate dengan format "17-12-2020 17:00:00"; jika kedua-duanya dihantar, billExpiryDate diutamakan, dan bil tamat pada 23:59:59 pada hari akhir.
Soalan Lazim (FAQ)
Kenapa createBill balas 200 tetapi bil tidak menjadi?
ToyyibPay mengembalikan HTTP 200 untuk kedua-dua kejayaan dan kegagalan. Baca isi balasan: kejayaan berbentuk array JSON dengan BillCode, manakala ralat akaun berbentuk teks kosong seperti [KEY-DID-NOT-EXIST] atau [CATEGORY-NOT-MATCH].
Bagaimana saya tahu bayaran benar-benar berjaya?
Jangan harapkan redirect sahaja. Sahkan form.status bernilai 1 pada callback, kemudian silang-semak melalui getBillTransactions dan pastikan billpaymentStatus bernilai 1 sebelum menandakan pesanan selesai.
Bagaimana menguji tanpa akaun perniagaan sebenar?
Daftar akaun sandbox di dev.toyyibpay.com dan gantikan domain kepada dev.toyyibpay.com bersama kelayakan sandbox. Anda boleh membayar bil dummy menggunakan Bank Simulators.
Bolehkah saya gunakan localhost untuk callback?
Tidak. Dokumen rasmi menyatakan URL callback tidak boleh diterima di localhost. Gunakan tunnel seperti ngrok atau cloudflared semasa pembangunan untuk mendedahkan endpoint tempatan kepada internet.
Apa yang berlaku apabila bil tamat tempoh?
Tempoh ditetapkan melalui billExpiryDays atau billExpiryDate. Dokumen API menyatakan bil akan ditetapkan sebagai tidak aktif (inactive) apabila tamat, tetapi tidak memperincikan sebarang pembayaran balik automatik. Sahkan tingkah laku tepat di portal toyyibPay sebelum bergantung padanya.