Files
WooNooW/RAJAONGKIR_INTEGRATION.md
Dwindi Ramadhana 6694d9e0c4 feat(checkout): dynamic checkout fields with PHP filter support
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.
2026-01-08 11:48:53 +07:00

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:

  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
/**
 * 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

  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?

// 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);