# 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**: 1. You add fields via the standard `woocommerce_checkout_fields` filter 2. WooNooW's `/checkout/fields` API returns these fields 3. Customer-spa renders them automatically (including `searchable_select` type) 4. Field data is included in checkout submission --- ## Code Snippet (Add to Code Snippets or WPCodebox) ```php 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 1. Add the code snippet via Code Snippets plugin 2. Go to WooNooW checkout with a product in cart 3. Set country to Indonesia 4. A "Destination" field should appear 5. Type at least 2 characters to search 6. Select a destination from dropdown 7. 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? ```php // 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? ```php add_action('woonoow/shipping/before_calculate', function($shipping) { error_log('Shipping data: ' . print_r($shipping, true)); }, 5); ``` --- ## Related Documentation - [SHIPPING_INTEGRATION.md](SHIPPING_INTEGRATION.md) - General shipping patterns - [HOOKS_REGISTRY.md](HOOKS_REGISTRY.md) - All WooNooW hooks