Cara Membuat Checkout Order via WhatsApp di WordPress Tanpa WooCommerce
Banyak toko kecil berjualan lewat WhatsApp. Tombol "Order via WA" biasa memang praktis, tetapi pesanannya tidak tercatat, stok tidak berkurang, dan admin harus menghitung total secara manual. Di sisi lain, memasang WooCommerce terasa terlalu berat hanya untuk menerima pesanan.
Di tutorial ini kita membuat jalan tengahnya di WordPress: keranjang belanja yang menyimpan pesanan ke database, mengurangi stok dengan aman, lalu membuka WhatsApp admin dengan rincian pesanan. Contohnya diambil dari plugin Katalog & Order WhatsApp yang bisa kamu download gratis di Papicode (link di akhir artikel).
1. Simpan pesanan di tabel sendiri
Pesanan bukan konten, jadi jangan disimpan sebagai post. Tabel sendiri jauh lebih cepat untuk laporan dan mudah di-query. Buat tabelnya saat plugin diaktifkan dengan dbDelta():
<?php
register_activation_hook(__FILE__, 'kwa_install');
function kwa_install()
{
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$c = $wpdb->get_charset_collate();
dbDelta("CREATE TABLE {$wpdb->prefix}kwa_orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
kode VARCHAR(24) NOT NULL,
nama VARCHAR(100) NOT NULL,
wa VARCHAR(20) NOT NULL,
alamat TEXT NOT NULL,
subtotal INT UNSIGNED NOT NULL DEFAULT 0,
ongkir INT UNSIGNED NOT NULL DEFAULT 0,
total INT UNSIGNED NOT NULL DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'baru',
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY kode (kode)
) $c;");
dbDelta("CREATE TABLE {$wpdb->prefix}kwa_order_items (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_id BIGINT UNSIGNED NOT NULL,
produk_id BIGINT UNSIGNED NOT NULL,
nama VARCHAR(200) NOT NULL,
harga INT UNSIGNED NOT NULL,
qty SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (id),
KEY order_id (order_id)
) $c;");
}
Perhatikan aturan dbDelta: dua spasi setelah PRIMARY KEY, dan setiap kolom di baris sendiri. Kalau formatnya meleset, tabel tidak akan dibuat dan tidak ada pesan error.
Nama dan harga produk ikut disalin ke kwa_order_items. Dengan begitu, kalau harga produk naik bulan depan, pesanan lama tetap mencatat harga saat dibeli.
2. Endpoint checkout dengan admin-ajax
Keranjang disimpan di localStorage browser. Saat pembeli menekan tombol pesan, JavaScript mengirim isi keranjang beserta data pengiriman ke admin-ajax.php.
<?php
add_action('wp_ajax_kwa_checkout', 'kwa_checkout');
add_action('wp_ajax_nopriv_kwa_checkout', 'kwa_checkout'); // pengunjung tanpa login
function kwa_checkout()
{
check_ajax_referer('kwa_checkout', 'nonce');
$nama = sanitize_text_field(wp_unslash($_POST['nama'] ?? ''));
$wa = preg_replace('/\D/', '', $_POST['wa'] ?? '');
if (!preg_match('/^(0|62)8\d{7,12}$/', $wa)) {
wp_send_json_error(['msg' => 'Nomor WhatsApp tidak valid.']);
}
$cart = json_decode(wp_unslash($_POST['cart'] ?? '[]'), true);
// ... buat pesanan (langkah 3)
}
Kirim nonce ke JavaScript dengan wp_localize_script():
wp_localize_script('kwa-front', 'KWA', [
'ajax' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('kwa_checkout'),
]);
3. Jangan percaya harga dari browser
Isi keranjang di localStorage bisa diubah siapa saja lewat DevTools. Karena itu, dari browser cukup kirim ID produk, varian, dan jumlah. Harga selalu diambil ulang dari database:
<?php
$items = [];
foreach ($cart as $c) {
$p = get_post(absint($c['id']));
if (!$p || $p->post_type !== 'kwa_produk' || $p->post_status !== 'publish') {
wp_send_json_error(['msg' => 'Ada produk yang sudah tidak tersedia.']);
}
$items[] = [
'produk_id' => $p->ID,
'nama' => get_the_title($p),
'harga' => (int) get_post_meta($p->ID, '_kwa_harga', true), // dari database!
'qty' => min(99, absint($c['qty'])),
];
}
$subtotal = array_sum(array_map(fn ($i) => $i['harga'] * $i['qty'], $items));
Lakukan hal yang sama untuk ongkir: browser hanya mengirim nama tujuan, sedangkan tarifnya dicari dari pengaturan di server.
4. Kurangi stok secara atomik
Ini bagian yang paling sering salah. Pola berikut tidak aman:
$stok = get_post_meta($id, '_kwa_stok', true);
if ($stok >= $qty) {
update_post_meta($id, '_kwa_stok', $stok - $qty); // dua pembeli bisa lolos bersamaan
}
Kalau dua orang checkout pada detik yang sama, keduanya membaca stok 3, keduanya lolos pengecekan, dan stok berakhir minus.
Solusinya: gabungkan pengecekan dan pengurangan dalam satu query UPDATE ... WHERE stok >= qty. MySQL menjamin hanya satu request yang berhasil:
<?php
global $wpdb;
$wpdb->query('START TRANSACTION');
foreach ($items as $it) {
$berhasil = $wpdb->query($wpdb->prepare(
"UPDATE {$wpdb->postmeta}
SET meta_value = CAST(meta_value AS SIGNED) - %d
WHERE post_id = %d AND meta_key = '_kwa_stok'
AND CAST(meta_value AS SIGNED) >= %d",
$it['qty'], $it['produk_id'], $it['qty']
));
if (!$berhasil) { // 0 baris berubah = stok tidak cukup
$wpdb->query('ROLLBACK'); // batalkan pengurangan stok item sebelumnya
wp_send_json_error(['msg' => 'Stok ' . $it['nama'] . ' tidak cukup.']);
}
}
// simpan pesanan + item di sini, lalu:
$wpdb->query('COMMIT');
foreach ($items as $it) wp_cache_delete($it['produk_id'], 'post_meta');
Satu hal lagi: gabungkan dulu item dengan produk yang sama sebelum mengecek stok. Kalau tidak, pembeli bisa memecah 5 pcs menjadi dua baris varian (2 + 3) dan lolos dari pengecekan per baris.
5. Buat link WhatsApp
Setelah pesanan tersimpan, susun pesan lalu arahkan pembeli ke wa.me. Nomor harus berformat internasional tanpa + dan tanpa angka 0 di depan:
<?php
function kwa_wa_link(string $no, string $text): string
{
$no = preg_replace('/\D/', '', $no);
if (strpos($no, '0') === 0) $no = '62' . substr($no, 1); // 0812... -> 62812...
return 'https://wa.me/' . $no . '?text=' . rawurlencode($text);
}
$pesan = "Halo, saya mau pesan:\n\n";
foreach ($items as $i => $it) {
$pesan .= ($i + 1) . ". {$it['nama']} x{$it['qty']} = Rp" . number_format($it['harga'] * $it['qty'], 0, ',', '.') . "\n";
}
$pesan .= . (, , , ) . ;
([ => ((), )]);
Gunakan rawurlencode(), bukan urlencode(). urlencode() mengubah spasi menjadi +, dan tanda + itu ikut tampil di chat WhatsApp.
Di JavaScript, gunakan window.location.href = res.data.wa alih-alih window.open(). Browser di HP sering memblokir window.open yang dipanggil setelah request AJAX selesai.
6. Kembalikan stok saat pesanan dibatalkan
Kalau admin membatalkan pesanan, stoknya harus kembali. Kunci baris pesanan dengan FOR UPDATE agar pembatalan tidak dijalankan dua kali bersamaan:
<?php
$wpdb->query('START TRANSACTION');
$o = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kwa_orders WHERE id = %d FOR UPDATE", $id));
if ($o->status !== 'batal') {
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT produk_id, SUM(qty) q FROM {$wpdb->prefix}kwa_order_items WHERE order_id = %d GROUP BY produk_id", $id));
foreach ($rows as $r) {
$wpdb->query($wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = CAST(meta_value AS SIGNED) + %d
WHERE post_id = %d AND meta_key = '_kwa_stok' AND meta_value <> ''", $r->q, $r->produk_id));
}
$wpdb->update("{$wpdb->prefix}kwa_orders", ['status' => 'batal'], ['id' => $id]);
}
$wpdb->query('COMMIT');
Pesanan yang sudah batal sebaiknya dikunci dan tidak bisa diaktifkan lagi. Kalau bisa, stoknya akan terhitung dua kali.
Bonus: aset tidak muncul di tema blok?
Pada tema blok seperti Twenty Twenty-Five, isi halaman (termasuk shortcode) dirender sebelum hook wp_enqueue_scripts. Kalau script didaftarkan di hook itu lalu di-wp_localize_script dari dalam shortcode, data lokalisasinya bisa tertimpa dan window.KWA menjadi undefined. Solusinya, daftarkan aset di dalam shortcode bila belum terdaftar:
if (!wp_script_is('kwa-front', 'registered')) {
wp_register_script('kwa-front', plugins_url('assets/js/front.js', __FILE__), [], '1.0', true);
}
Ringkasan
- Simpan pesanan di tabel sendiri dengan
dbDelta()dan salin nama serta harga ke item pesanan. - Dari browser cukup kirim ID dan jumlah. Harga dan ongkir dihitung ulang di server.
- Kurangi stok dengan
UPDATE ... WHERE stok >= qtydi dalam transaksi agar tidak pernah minus. - Buat link
wa.medenganrawurlencode()dan nomor berformat 62. - Pembatalan mengembalikan stok dan mengunci pesanan.
Download plugin lengkapnya
Semua teknik di atas sudah dipakai di plugin Katalog & Order WhatsApp: katalog dengan varian dan filter, keranjang melayang, ongkir per kota, stok atomik, halaman lacak pesanan, dashboard omzet, kelola pesanan dengan tombol chat otomatis, dan ekspor CSV. Gratis dan tanpa WooCommerce.
Source code lengkapnya gratis
Lihat semua yang dibahas di artikel ini langsung berjalan.