diff --git a/RAJAONGKIR_INTEGRATION.md b/RAJAONGKIR_INTEGRATION.md index 8241cf9..4c6e300 100644 --- a/RAJAONGKIR_INTEGRATION.md +++ b/RAJAONGKIR_INTEGRATION.md @@ -1,43 +1,136 @@ -# Rajaongkir Integration with WooNooW +# Rajaongkir Integration with WooNooW SPA -This guide explains how to add Rajaongkir's destination selector to WooNooW checkout using a **simple code snippet** (no bridge plugin required). +This guide explains how to integrate Rajaongkir's destination selector with WooNooW's customer checkout SPA. + +--- + +## Prerequisites + +Before using this integration: + +1. **Rajaongkir Plugin Installed & Active** +2. **WooCommerce Shipping Zone Configured** + - Go to: WC → Settings → Shipping → Zones + - Add Rajaongkir method to your Indonesia zone +3. **Valid API Key** (Check in Rajaongkir settings) +4. **Couriers Selected** (In Rajaongkir settings) --- ## 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 +``` +┌─────────────────┐ ┌───────────────────┐ ┌──────────────────┐ +│ Customer types │ → │ /rajaongkir/ │ → │ SearchableSelect │ +│ "Bandung" │ │ destinations API │ │ shows results │ +└─────────────────┘ └───────────────────┘ └──────────────────┘ + │ + ▼ +┌─────────────────┐ ┌───────────────────┐ ┌──────────────────┐ +│ Rajaongkir uses │ ← │ Hook sets WC │ ← │ Customer selects │ +│ session to get │ │ session data │ │ destination │ +│ shipping rates │ │ │ │ │ +└─────────────────┘ └───────────────────┘ └──────────────────┘ +``` --- -## Code Snippet (Add to Code Snippets or WPCodebox) +## Code Snippet + +Add this to **Code Snippets** or **WPCodebox**: ```php 'GET', + 'callback' => 'woonoow_rajaongkir_search_destinations', + 'permission_callback' => '__return_true', + 'args' => [ + 'search' => [ + 'required' => false, + 'type' => 'string', + ], + ], + ]); +}); + +function woonoow_rajaongkir_search_destinations($request) { + $search = sanitize_text_field($request->get_param('search') ?? ''); + + if (strlen($search) < 2) { + return []; + } + + // Check if Rajaongkir plugin is active + if (!class_exists('Cekongkir_API')) { + return new WP_Error('rajaongkir_missing', 'Rajaongkir plugin not active', ['status' => 400]); + } + + // Use Rajaongkir's API class for the search + $api = Cekongkir_API::get_instance(); + $results = $api->search_destination(['keyword' => $search]); + + if (is_wp_error($results)) { + return []; + } + + // Format for WooNooW's SearchableSelect component + $formatted = []; + foreach ($results as $r) { + $formatted[] = [ + 'value' => (string) $r['id'], + 'label' => $r['label'], // "Province, City, District" + ]; + } + + // Limit results + return array_slice($formatted, 0, 50); +} + +// ============================================================ +// 2. Add destination field to checkout fields (SPA-aware) +// ============================================================ add_filter('woocommerce_checkout_fields', function($fields) { - // Only add for Indonesia or when country not yet selected + // Check if Rajaongkir is active + if (!class_exists('Cekongkir_API')) { + return $fields; + } + + // Get country from various sources $country = ''; - if (function_exists('WC') && WC()->customer) { + + // From WC customer object + if (WC()->customer) { $country = WC()->customer->get_shipping_country(); } - if ($country !== 'ID' && $country !== '') { + // From REST API request body (for SPA context) + if (defined('REST_REQUEST') && REST_REQUEST) { + $json = file_get_contents('php://input'); + if ($json) { + $data = json_decode($json, true); + if (isset($data['shipping']['country'])) { + $country = $data['shipping']['country']; + } + } + } + + // Only add field for Indonesia or when country not yet determined + if ($country && $country !== 'ID') { return $fields; } - + // Add searchable destination field $fields['shipping']['shipping_destination_id'] = [ 'type' => 'searchable_select', @@ -46,107 +139,58 @@ add_filter('woocommerce_checkout_fields', function($fields) { 'priority' => 85, // After country/state, before postcode 'class' => ['form-row-wide'], 'placeholder' => __('Search destination...', 'woonoow'), - // WooNooW-specific: API endpoint for search + // WooNooW-specific: API endpoint configuration 'search_endpoint' => '/woonoow/v1/rajaongkir/destinations', 'search_param' => 'search', 'min_chars' => 2, ]; - + return $fields; -}); +}, 20); // Priority 20 to run after Rajaongkir's own filter -// 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 +// ============================================================ +// 3. Bridge WooNooW shipping data to 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'] ?? ''); + // Set country in WC customer + if (!empty($shipping['country'])) { + WC()->customer->set_shipping_country($shipping['country']); + WC()->customer->set_billing_country($shipping['country']); + } - if (empty($destination_id)) { + // Only process for Indonesia + $country = $shipping['country'] ?? WC()->customer->get_shipping_country(); + if ($country !== 'ID') { + // Clear destination for non-Indonesia WC()->session->__unset('selected_destination_id'); WC()->session->__unset('selected_destination_label'); return; } - - // Set Rajaongkir session variables + + // Get destination_id from shipping data (various possible keys) + $destination_id = $shipping['destination_id'] + ?? $shipping['shipping_destination_id'] + ?? null; + + if (empty($destination_id)) { + // No destination selected yet + return; + } + + // Set session for Rajaongkir 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'] ?? '' - )); + + // Also set label if provided (for display purposes) + $label = $shipping['destination_label'] + ?? $shipping['shipping_destination_id_label'] + ?? ''; + if ($label) { + WC()->session->set('selected_destination_label', sanitize_text_field($label)); + } + + // Clear shipping cache to force recalculation + WC()->session->set('shipping_for_package_0', false); + }, 10, 2); ``` @@ -154,84 +198,89 @@ add_action('woonoow/shipping/before_calculate', function($shipping, $items) { ## Field Configuration Options -WooNooW's `searchable_select` type supports these options: +The `searchable_select` field type supports: | 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) | +| `search_endpoint` | REST API endpoint path | +| `search_param` | Query parameter name (default: 'search') | +| `min_chars` | Minimum characters before search (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 +### 1. Test the API Endpoint + +```bash +curl "https://yoursite.com/wp-json/woonoow/v1/rajaongkir/destinations?search=bandung" +``` + +Should return: +```json +[ + {"value": "1234", "label": "Jawa Barat, Bandung, Kota"}, + {"value": "1235", "label": "Jawa Barat, Bandung, Kabupaten"} +] +``` + +### 2. Test Checkout Flow + +1. Add product to cart +2. Go to SPA checkout: `/store/checkout` 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 +4. "Destination" field should appear +5. Type 2+ characters to search +6. Select a destination +7. Click "Calculate Shipping" or proceed +8. Rajaongkir rates should appear --- ## Troubleshooting -### Field not appearing? +### Destination field not appearing? + +Check if: +- Country is set to Indonesia +- Rajaongkir plugin is active +- Snippet has no syntax errors + +Debug in PHP: ```php -// Debug: Check if field is being added -add_action('wp_footer', function() { +add_action('woocommerce_checkout_init', function() { $fields = WC()->checkout()->get_checkout_fields(); - error_log('Checkout fields: ' . print_r(array_keys($fields['shipping'] ?? []), true)); + error_log('Shipping fields: ' . print_r(array_keys($fields['shipping'] ?? []), true)); }); ``` -### Search not working? -``` -GET /wp-json/woonoow/v1/rajaongkir/destinations?search=jakarta +### Search returns empty? + +```php +// Add to the search endpoint +error_log('Rajaongkir search: ' . $search); +error_log('Results: ' . print_r($results, true)); ``` -### Session not set? +### Rajaongkir rates not appearing? + +1. Check session is set: ```php add_action('woonoow/shipping/before_calculate', function($shipping) { - error_log('Shipping data: ' . print_r($shipping, true)); + error_log('Destination ID in shipping data: ' . print_r($shipping, true)); + error_log('Session destination: ' . WC()->session->get('selected_destination_id')); }, 5); ``` +2. Check Rajaongkir debug: +- Enable debug in Rajaongkir settings +- Check `debug.log` for Rajaongkir messages + --- ## Related Documentation - [SHIPPING_INTEGRATION.md](SHIPPING_INTEGRATION.md) - General shipping patterns -- [HOOKS_REGISTRY.md](HOOKS_REGISTRY.md) - All WooNooW hooks +- [HOOKS_REGISTRY.md](HOOKS_REGISTRY.md) - WooNooW hooks reference