Backend (CheckoutController): - Enhanced get_fields() API with custom_attributes, search_endpoint, search_param, min_chars, input_class, default - Supports new 'searchable_select' field type for API-backed search Customer SPA: - Created DynamicCheckoutField component for all field types - Checkout fetches fields from /checkout/fields API - Renders custom fields from PHP filters (billing + shipping) - searchable_select type with live API search - Custom field data included in checkout submission This enables: - Checkout Field Editor Pro compatibility - Rajaongkir destination_id via simple code snippet - Any plugin using woocommerce_checkout_fields filter Updated RAJAONGKIR_INTEGRATION.md with code snippet approach.
8.0 KiB
8.0 KiB
Rajaongkir Integration with WooNooW
This guide explains how to add Rajaongkir's destination selector to WooNooW checkout using a simple code snippet (no bridge plugin required).
How It Works
WooNooW now supports dynamic checkout fields:
- You add fields via the standard
woocommerce_checkout_fieldsfilter - WooNooW's
/checkout/fieldsAPI returns these fields - Customer-spa renders them automatically (including
searchable_selecttype) - Field data is included in checkout submission
Code Snippet (Add to Code Snippets or WPCodebox)
<?php
/**
* Rajaongkir Destination Field for WooNooW
*
* Add this snippet to Code Snippets plugin or WPCodebox.
* Works with WooNooW's dynamic checkout field system.
*/
// 1. Add destination_id field to shipping checkout fields
add_filter('woocommerce_checkout_fields', function($fields) {
// Only add for Indonesia or when country not yet selected
$country = '';
if (function_exists('WC') && WC()->customer) {
$country = WC()->customer->get_shipping_country();
}
if ($country !== 'ID' && $country !== '') {
return $fields;
}
// Add searchable destination field
$fields['shipping']['shipping_destination_id'] = [
'type' => 'searchable_select',
'label' => __('Destination (Province, City, Subdistrict)', 'woonoow'),
'required' => true,
'priority' => 85, // After country/state, before postcode
'class' => ['form-row-wide'],
'placeholder' => __('Search destination...', 'woonoow'),
// WooNooW-specific: API endpoint for search
'search_endpoint' => '/woonoow/v1/rajaongkir/destinations',
'search_param' => 'search',
'min_chars' => 2,
];
return $fields;
});
// 2. Register REST API endpoint for destination search
add_action('rest_api_init', function() {
register_rest_route('woonoow/v1', '/rajaongkir/destinations', [
'methods' => 'GET',
'callback' => 'woonoow_rajaongkir_search_destinations',
'permission_callback' => '__return_true',
'args' => [
'search' => [
'required' => false,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
],
],
]);
});
function woonoow_rajaongkir_search_destinations($request) {
$search = $request->get_param('search') ?? '';
if (strlen($search) < 2) {
return [];
}
// Get cached destinations
$destinations = get_transient('woonoow_rajaongkir_destinations');
if (!$destinations) {
$destinations = woonoow_rajaongkir_build_destinations();
set_transient('woonoow_rajaongkir_destinations', $destinations, DAY_IN_SECONDS);
}
// Filter by search term
$results = array_filter($destinations, function($d) use ($search) {
return stripos($d['label'], $search) !== false;
});
// Return max 50 results
return array_slice(array_values($results), 0, 50);
}
function woonoow_rajaongkir_build_destinations() {
$destinations = [];
// Try to get from Rajaongkir plugin's database
global $wpdb;
$table = $wpdb->prefix . 'cekongkir_subdistrict'; // Adjust table name
if ($wpdb->get_var("SHOW TABLES LIKE '$table'") === $table) {
$rows = $wpdb->get_results("
SELECT id, province, city, subdistrict
FROM $table
ORDER BY province, city, subdistrict
", ARRAY_A);
foreach ($rows as $row) {
$destinations[] = [
'value' => $row['id'],
'label' => $row['province'] . ', ' . $row['city'] . ', ' . $row['subdistrict'],
];
}
}
// Fallback: Try options storage
if (empty($destinations)) {
$stored = get_option('cekongkir_subdistricts', []);
foreach ($stored as $id => $data) {
$destinations[] = [
'value' => (string) $id,
'label' => is_array($data) ? ($data['label'] ?? $data['name'] ?? $id) : $data,
];
}
}
return $destinations;
}
// 3. Hook into shipping calculation to set Rajaongkir session
add_action('woonoow/shipping/before_calculate', function($shipping, $items) {
// Check for destination_id in shipping data
$destination_id = $shipping['destination_id'] ?? ($shipping['shipping_destination_id'] ?? '');
if (empty($destination_id)) {
WC()->session->__unset('selected_destination_id');
WC()->session->__unset('selected_destination_label');
return;
}
// Set Rajaongkir session variables
WC()->session->set('selected_destination_id', intval($destination_id));
WC()->session->set('selected_destination_label', sanitize_text_field(
$shipping['destination_id_label'] ?? $shipping['shipping_destination_id_label'] ?? ''
));
}, 10, 2);
Field Configuration Options
WooNooW's searchable_select type supports these options:
| Option | Description |
|---|---|
type |
'searchable_select' for API-backed search |
search_endpoint |
REST API endpoint for searching |
search_param |
Query param name (default: 'search') |
min_chars |
Minimum characters before searching (default: 2) |
placeholder |
Input placeholder text |
Testing
- Add the code snippet via Code Snippets plugin
- Go to WooNooW checkout with a product in cart
- Set country to Indonesia
- A "Destination" field should appear
- Type at least 2 characters to search
- Select a destination from dropdown
- Complete checkout - shipping rates should use selected destination
How WooNooW Processes This
┌─────────────────┐ ┌───────────────────┐ ┌─────────────────┐
│ PHP Filter adds │ ──▶ │ /checkout/fields │ ──▶ │ Customer-spa │
│ shipping_ │ │ API returns field │ │ renders field │
│ destination_id │ │ with type/config │ │ (searchable) │
└─────────────────┘ └───────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌───────────────────┐ ┌─────────────────┐
│ Rajaongkir uses │ ◀── │ Hook fires with │ ◀── │ User submits │
│ session data │ │ shipping data │ │ checkout │
└─────────────────┘ └───────────────────┘ └─────────────────┘
Compatibility
This works with:
- Standard WooCommerce filters - no WooNooW-specific code needed
- Checkout Field Editor Pro - all its fields will render
- Custom code snippets - just use the filter
- Other shipping plugins - same hook pattern works
Troubleshooting
Field not appearing?
// Debug: Check if field is being added
add_action('wp_footer', function() {
$fields = WC()->checkout()->get_checkout_fields();
error_log('Checkout fields: ' . print_r(array_keys($fields['shipping'] ?? []), true));
});
Search not working?
GET /wp-json/woonoow/v1/rajaongkir/destinations?search=jakarta
Session not set?
add_action('woonoow/shipping/before_calculate', function($shipping) {
error_log('Shipping data: ' . print_r($shipping, true));
}, 5);
Related Documentation
- SHIPPING_INTEGRATION.md - General shipping patterns
- HOOKS_REGISTRY.md - All WooNooW hooks