Files
WooNooW/RAJAONGKIR_INTEGRATION.md
dwindown 03ef9e3f24 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
2025-11-10 18:56:41 +07:00

5.7 KiB

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:

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

    // Adds Select2 dropdown for searching locations
    <select id="cart-destination" name="cart_destination">
        <option>Search and select location...</option>
    </select>
    
  3. Stores in Session:

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

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

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

    // For Indonesia only
    {bCountry === 'ID' && (
      <div>
        <Label>Destination</Label>
        <DestinationSearch 
          value={destinationId}
          onChange={(id, label) => {
            setDestinationId(id);
            setDestinationLabel(label);
          }}
        />
      </div>
    )}
    
  2. Pass to API:

    shipping: {
      country: bCountry,
      state: bState,
      city: bCity,
      destination_id: destinationId,      // For Rajaongkir
      destination_label: destinationLabel // For Rajaongkir
    }
    
  3. API Endpoint:

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

{
  "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)

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

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

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