docs: Document Rajaongkir integration issue and add session support

## Discovery 

Rajaongkir plugin uses a completely different approach:
- Removes standard WooCommerce city/state fields
- Adds custom destination dropdown with Select2 search
- Stores destination in WooCommerce session (not address fields)
- Reads from session during shipping calculation

## Root Cause of Issues:

### 1. Same rates for different provinces
- OrderForm sends: city="Bandung", state="Jawa Barat"
- Rajaongkir ignores these fields
- Rajaongkir reads: WC()->session->get("selected_destination_id")
- Session empty → Uses cached/default rates

### 2. No Rajaongkir API hits
- No destination_id in session
- Rajaongkir can't calculate without destination
- Returns empty or cached rates

## Backend Fix ( DONE):

Added Rajaongkir session support in calculate_shipping:
```php
// Support for Rajaongkir plugin
if ( $country === 'ID' && ! empty( $shipping['destination_id'] ) ) {
    WC()->session->set( 'selected_destination_id', $shipping['destination_id'] );
    WC()->session->set( 'selected_destination_label', $shipping['destination_label'] );
}
```

## Frontend Fix (TODO):

Need to add Rajaongkir destination field:
1. Add destination search component (Select2/Combobox)
2. Search Rajaongkir API for locations
3. Pass destination_id to backend
4. Backend sets session before calculate_shipping()

## Documentation:

Created RAJAONGKIR_INTEGRATION.md with:
- How Rajaongkir works
- Why our implementation fails
- Complete solution steps
- Testing checklist

## Next Steps:

1. Add Rajaongkir search endpoint to OrdersController
2. Create destination search component in OrderForm
3. Pass destination_id in shipping data
4. Test with real Rajaongkir API
This commit is contained in:
dwindown
2025-11-10 18:56:41 +07:00
parent a499b6ad0b
commit 03ef9e3f24
2 changed files with 240 additions and 0 deletions

229
RAJAONGKIR_INTEGRATION.md Normal file
View File

@@ -0,0 +1,229 @@
# Rajaongkir Integration Issue
## Problem Discovery
Rajaongkir plugin **doesn't use standard WooCommerce address fields** for Indonesian shipping calculation.
### How Rajaongkir Works:
1. **Removes Standard Fields:**
```php
// class-cekongkir.php line 645
public function customize_checkout_fields($fields) {
unset($fields['billing']['billing_state']);
unset($fields['billing']['billing_city']);
unset($fields['shipping']['shipping_state']);
unset($fields['shipping']['shipping_city']);
return $fields;
}
```
2. **Adds Custom Destination Dropdown:**
```php
// Adds Select2 dropdown for searching locations
<select id="cart-destination" name="cart_destination">
<option>Search and select location...</option>
</select>
```
3. **Stores in Session:**
```php
// When user selects destination via AJAX
WC()->session->set('selected_destination_id', $destination_id);
WC()->session->set('selected_destination_label', $destination_label);
```
4. **Triggers Shipping Calculation:**
```php
// After destination selected
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
```
### Why Our Implementation Fails:
**OrderForm.tsx:**
- Uses standard fields: `city`, `state`, `postcode`
- Rajaongkir ignores these fields
- Rajaongkir only reads from session: `selected_destination_id`
**Backend API:**
- Sets `WC()->customer->set_shipping_city($city)`
- Rajaongkir doesn't use this
- Rajaongkir reads: `WC()->session->get('selected_destination_id')`
**Result:**
- Same rates for all provinces ❌
- No Rajaongkir API hits ❌
- Shipping calculation fails ❌
---
## Solution
### Backend (✅ DONE):
```php
// OrdersController.php - calculate_shipping method
if ( $country === 'ID' && ! empty( $shipping['destination_id'] ) ) {
WC()->session->set( 'selected_destination_id', $shipping['destination_id'] );
WC()->session->set( 'selected_destination_label', $shipping['destination_label'] );
}
```
### Frontend (TODO):
Need to add Rajaongkir destination field to OrderForm.tsx:
1. **Add Destination Search Field:**
```tsx
// For Indonesia only
{bCountry === 'ID' && (
<div>
<Label>Destination</Label>
<DestinationSearch
value={destinationId}
onChange={(id, label) => {
setDestinationId(id);
setDestinationLabel(label);
}}
/>
</div>
)}
```
2. **Pass to API:**
```tsx
shipping: {
country: bCountry,
state: bState,
city: bCity,
destination_id: destinationId, // For Rajaongkir
destination_label: destinationLabel // For Rajaongkir
}
```
3. **API Endpoint:**
```tsx
// Add search endpoint
GET /woonoow/v1/rajaongkir/search?query=bandung
// Proxy to Rajaongkir API
POST /wp-admin/admin-ajax.php
action=cart_search_destination
query=bandung
```
---
## Rajaongkir Destination Format
### Destination ID Examples:
- `city:23` - City ID 23 (Bandung)
- `subdistrict:456` - Subdistrict ID 456
- `province:9` - Province ID 9 (Jawa Barat)
### API Response:
```json
{
"success": true,
"data": [
{
"id": "city:23",
"text": "Bandung, Jawa Barat"
},
{
"id": "subdistrict:456",
"text": "Bandung Wetan, Bandung, Jawa Barat"
}
]
}
```
---
## Implementation Steps
### Step 1: Add Rajaongkir Search Endpoint (Backend)
```php
// OrdersController.php
public static function search_rajaongkir_destination( WP_REST_Request $req ) {
$query = sanitize_text_field( $req->get_param( 'query' ) );
// Call Rajaongkir API
$api = Cekongkir_API::get_instance();
$results = $api->search_destination_api( $query );
return new \WP_REST_Response( $results, 200 );
}
```
### Step 2: Add Destination Field (Frontend)
```tsx
// OrderForm.tsx
const [destinationId, setDestinationId] = useState('');
const [destinationLabel, setDestinationLabel] = useState('');
// Add to shipping data
const effectiveShippingAddress = useMemo(() => {
return {
country: bCountry,
state: bState,
city: bCity,
destination_id: destinationId,
destination_label: destinationLabel,
};
}, [bCountry, bState, bCity, destinationId, destinationLabel]);
```
### Step 3: Create Destination Search Component
```tsx
// components/RajaongkirDestinationSearch.tsx
export function RajaongkirDestinationSearch({ value, onChange }) {
const [query, setQuery] = useState('');
const { data: results } = useQuery({
queryKey: ['rajaongkir-search', query],
queryFn: () => api.get(`/rajaongkir/search?query=${query}`),
enabled: query.length >= 3,
});
return (
<Combobox value={value} onChange={onChange}>
<ComboboxInput onChange={(e) => setQuery(e.target.value)} />
<ComboboxOptions>
{results?.map(r => (
<ComboboxOption key={r.id} value={r.id}>
{r.text}
</ComboboxOption>
))}
</ComboboxOptions>
</Combobox>
);
}
```
---
## Testing
### Before Fix:
1. Select "Jawa Barat" → JNE REG Rp31,000
2. Select "Bali" → JNE REG Rp31,000 (wrong! cached)
3. Rajaongkir dashboard → 0 API hits
### After Fix:
1. Search "Bandung" → Select "Bandung, Jawa Barat"
2. ✅ Rajaongkir API hit
3. ✅ Returns: JNE REG Rp31,000, JNE YES Rp42,000
4. Search "Denpasar" → Select "Denpasar, Bali"
5. ✅ Rajaongkir API hit
6. ✅ Returns: JNE REG Rp45,000, JNE YES Rp58,000 (different!)
---
## Notes
- Rajaongkir is Indonesia-specific (country === 'ID')
- For other countries, use standard WooCommerce fields
- Destination ID format: `type:id` (e.g., `city:23`, `subdistrict:456`)
- Session data is critical - must be set before `calculate_shipping()`
- Frontend needs autocomplete/search component (Select2 or similar)

View File

@@ -1312,6 +1312,17 @@ class OrdersController {
WC()->customer->set_billing_state( $state );
WC()->customer->set_billing_postcode( $postcode );
WC()->customer->set_billing_city( $city );
// Support for Rajaongkir plugin - set destination in session
// Rajaongkir uses session-based destination instead of standard address fields
if ( $country === 'ID' && ! empty( $shipping['destination_id'] ) ) {
WC()->session->set( 'selected_destination_id', $shipping['destination_id'] );
WC()->session->set( 'selected_destination_label', $shipping['destination_label'] ?? $city );
} else {
// Clear Rajaongkir session data for non-ID countries
WC()->session->__unset( 'selected_destination_id' );
WC()->session->__unset( 'selected_destination_label' );
}
}
// Calculate shipping