docs(rajaongkir): update integration guide with correct code snippet
Based on deep analysis of Rajaongkir plugin: - Destinations searched via RajaOngkir API (no local DB table) - Uses Cekongkir_API::search_destination() for search - Session 'selected_destination_id' required for rates - Added SPA-aware checkout field injection Code snippet includes: 1. REST endpoint /woonoow/v1/rajaongkir/destinations 2. Checkout field filter with REST context detection 3. Session bridge via woonoow/shipping/before_calculate
This commit is contained in:
@@ -1,40 +1,133 @@
|
|||||||
# 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
|
## How It Works
|
||||||
|
|
||||||
WooNooW now supports **dynamic checkout fields**:
|
```
|
||||||
|
┌─────────────────┐ ┌───────────────────┐ ┌──────────────────┐
|
||||||
1. You add fields via the standard `woocommerce_checkout_fields` filter
|
│ Customer types │ → │ /rajaongkir/ │ → │ SearchableSelect │
|
||||||
2. WooNooW's `/checkout/fields` API returns these fields
|
│ "Bandung" │ │ destinations API │ │ shows results │
|
||||||
3. Customer-spa renders them automatically (including `searchable_select` type)
|
└─────────────────┘ └───────────────────┘ └──────────────────┘
|
||||||
4. Field data is included in checkout submission
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐ ┌───────────────────┐ ┌──────────────────┐
|
||||||
|
│ 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
|
```php
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Rajaongkir Destination Field for WooNooW
|
* Rajaongkir Bridge for WooNooW SPA Checkout
|
||||||
*
|
*
|
||||||
* Add this snippet to Code Snippets plugin or WPCodebox.
|
* Enables searchable destination field in WooNooW checkout
|
||||||
* Works with WooNooW's dynamic checkout field system.
|
* and bridges data to Rajaongkir plugin.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 1. Add destination_id field to shipping checkout fields
|
// ============================================================
|
||||||
|
// 1. REST API Endpoint: Search destinations via Rajaongkir API
|
||||||
|
// ============================================================
|
||||||
|
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',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
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) {
|
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 = '';
|
$country = '';
|
||||||
if (function_exists('WC') && WC()->customer) {
|
|
||||||
|
// From WC customer object
|
||||||
|
if (WC()->customer) {
|
||||||
$country = WC()->customer->get_shipping_country();
|
$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;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,107 +139,58 @@ add_filter('woocommerce_checkout_fields', function($fields) {
|
|||||||
'priority' => 85, // After country/state, before postcode
|
'priority' => 85, // After country/state, before postcode
|
||||||
'class' => ['form-row-wide'],
|
'class' => ['form-row-wide'],
|
||||||
'placeholder' => __('Search destination...', 'woonoow'),
|
'placeholder' => __('Search destination...', 'woonoow'),
|
||||||
// WooNooW-specific: API endpoint for search
|
// WooNooW-specific: API endpoint configuration
|
||||||
'search_endpoint' => '/woonoow/v1/rajaongkir/destinations',
|
'search_endpoint' => '/woonoow/v1/rajaongkir/destinations',
|
||||||
'search_param' => 'search',
|
'search_param' => 'search',
|
||||||
'min_chars' => 2,
|
'min_chars' => 2,
|
||||||
];
|
];
|
||||||
|
|
||||||
return $fields;
|
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() {
|
// 3. Bridge WooNooW shipping data to Rajaongkir session
|
||||||
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) {
|
add_action('woonoow/shipping/before_calculate', function($shipping, $items) {
|
||||||
// Check for destination_id in shipping data
|
// Set country in WC customer
|
||||||
$destination_id = $shipping['destination_id'] ?? ($shipping['shipping_destination_id'] ?? '');
|
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_id');
|
||||||
WC()->session->__unset('selected_destination_label');
|
WC()->session->__unset('selected_destination_label');
|
||||||
return;
|
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_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);
|
}, 10, 2);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -154,84 +198,89 @@ add_action('woonoow/shipping/before_calculate', function($shipping, $items) {
|
|||||||
|
|
||||||
## Field Configuration Options
|
## Field Configuration Options
|
||||||
|
|
||||||
WooNooW's `searchable_select` type supports these options:
|
The `searchable_select` field type supports:
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `type` | `'searchable_select'` for API-backed search |
|
| `type` | `'searchable_select'` for API-backed search |
|
||||||
| `search_endpoint` | REST API endpoint for searching |
|
| `search_endpoint` | REST API endpoint path |
|
||||||
| `search_param` | Query param name (default: 'search') |
|
| `search_param` | Query parameter name (default: 'search') |
|
||||||
| `min_chars` | Minimum characters before searching (default: 2) |
|
| `min_chars` | Minimum characters before search (default: 2) |
|
||||||
| `placeholder` | Input placeholder text |
|
| `placeholder` | Input placeholder text |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
1. Add the code snippet via Code Snippets plugin
|
### 1. Test the API Endpoint
|
||||||
2. Go to WooNooW checkout with a product in cart
|
|
||||||
|
```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
|
3. Set country to Indonesia
|
||||||
4. A "Destination" field should appear
|
4. "Destination" field should appear
|
||||||
5. Type at least 2 characters to search
|
5. Type 2+ characters to search
|
||||||
6. Select a destination from dropdown
|
6. Select a destination
|
||||||
7. Complete checkout - shipping rates should use selected destination
|
7. Click "Calculate Shipping" or proceed
|
||||||
|
8. Rajaongkir rates should appear
|
||||||
---
|
|
||||||
|
|
||||||
## 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
|
## 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
|
```php
|
||||||
// Debug: Check if field is being added
|
add_action('woocommerce_checkout_init', function() {
|
||||||
add_action('wp_footer', function() {
|
|
||||||
$fields = WC()->checkout()->get_checkout_fields();
|
$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?
|
### Search returns empty?
|
||||||
```
|
|
||||||
GET /wp-json/woonoow/v1/rajaongkir/destinations?search=jakarta
|
```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
|
```php
|
||||||
add_action('woonoow/shipping/before_calculate', function($shipping) {
|
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);
|
}, 5);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
2. Check Rajaongkir debug:
|
||||||
|
- Enable debug in Rajaongkir settings
|
||||||
|
- Check `debug.log` for Rajaongkir messages
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- [SHIPPING_INTEGRATION.md](SHIPPING_INTEGRATION.md) - General shipping patterns
|
- [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
|
||||||
|
|||||||
Reference in New Issue
Block a user