RAJAONGKIR_INTEGRATION.md: - Hide country/state/city when Indonesia is the only allowed country - Make destination_id required for Indonesia-only stores - Force country to ID in session bridge - Added billing_destination_id fallback SHIPPING_BRIDGE_PATTERN.md: - New generic template for shipping provider integrations - Documents architecture, hooks, and field types - Provides copy-paste template for new providers - Includes checklist for new integrations
8.6 KiB
8.6 KiB
WooNooW Shipping Bridge Pattern
This document describes a generic pattern for integrating any external shipping API with WooNooW's SPA checkout.
Overview
WooNooW provides hooks and endpoints that allow any shipping plugin to:
- Register custom checkout fields (searchable selects, dropdowns, etc.)
- Bridge data to the plugin's session/API before shipping calculation
- Display live rates from external APIs
This pattern is NOT specific to Rajaongkir - it can be used for any shipping provider.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ WooNooW Customer SPA │
├─────────────────────────────────────────────────────────────────┤
│ 1. Checkout loads → calls /checkout/fields │
│ 2. Renders custom fields (e.g., searchable destination) │
│ 3. User fills form → calls /checkout/shipping-rates │
│ 4. Hook triggers → shipping plugin calculates rates │
│ 5. Rates displayed → user selects → order submitted │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Your Bridge Snippet │
├─────────────────────────────────────────────────────────────────┤
│ • woocommerce_checkout_fields → Add custom fields │
│ • register_rest_route → API endpoint for field data │
│ • woonoow/shipping/before_calculate → Set session/data │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Shipping Plugin (Any) │
├─────────────────────────────────────────────────────────────────┤
│ • Reads from WC session or customer data │
│ • Calls external API (Rajaongkir, Sicepat, JNE, etc.) │
│ • Returns rates via get_rates_for_package() │
└─────────────────────────────────────────────────────────────────┘
Generic Bridge Template
<?php
/**
* [PROVIDER_NAME] Bridge for WooNooW SPA Checkout
*
* Replace [PROVIDER_NAME], [PROVIDER_CLASS], and [PROVIDER_SESSION_KEY]
* with your shipping plugin's specifics.
*/
// ============================================================
// 1. REST API Endpoint: Search/fetch data from provider
// ============================================================
add_action('rest_api_init', function() {
register_rest_route('woonoow/v1', '/[provider]/search', [
'methods' => 'GET',
'callback' => 'woonoow_[provider]_search',
'permission_callback' => '__return_true', // Public for customer use
'args' => [
'search' => ['type' => 'string', 'required' => false],
],
]);
});
function woonoow_[provider]_search($request) {
$search = sanitize_text_field($request->get_param('search') ?? '');
if (strlen($search) < 3) {
return [];
}
// Check if plugin is active
if (!class_exists('[PROVIDER_CLASS]')) {
return new WP_Error('[provider]_missing', 'Plugin not active', ['status' => 400]);
}
// Call provider's API
// $results = [PROVIDER_CLASS]::search($search);
// Format for WooNooW's SearchableSelect
$formatted = [];
foreach ($results as $r) {
$formatted[] = [
'value' => (string) $r['id'],
'label' => $r['name'],
];
}
return array_slice($formatted, 0, 50);
}
// ============================================================
// 2. Add custom field(s) to checkout
// ============================================================
add_filter('woocommerce_checkout_fields', function($fields) {
if (!class_exists('[PROVIDER_CLASS]')) {
return $fields;
}
$custom_field = [
'type' => 'searchable_select', // or 'select', 'text'
'label' => __('[Field Label]', 'woonoow'),
'required' => true,
'priority' => 85,
'class' => ['form-row-wide'],
'placeholder' => __('Search...', 'woonoow'),
'search_endpoint' => '/[provider]/search', // Relative to /wp-json/woonoow/v1
'search_param' => 'search',
'min_chars' => 3,
];
$fields['billing']['billing_[provider]_field'] = $custom_field;
$fields['shipping']['shipping_[provider]_field'] = $custom_field;
return $fields;
}, 20);
// ============================================================
// 3. Bridge data to provider before shipping calculation
// ============================================================
add_action('woonoow/shipping/before_calculate', function($shipping, $items) {
if (!class_exists('[PROVIDER_CLASS]')) {
return;
}
// Get custom field value from shipping data
$field_value = $shipping['[provider]_field']
?? $shipping['shipping_[provider]_field']
?? $shipping['billing_[provider]_field']
?? null;
if (empty($field_value)) {
return;
}
// Set in WC session for the shipping plugin to use
WC()->session->set('[PROVIDER_SESSION_KEY]', $field_value);
// Clear shipping cache to force recalculation
WC()->session->set('shipping_for_package_0', false);
}, 10, 2);
Supported Field Types
| Type | Description | Use Case |
|---|---|---|
searchable_select |
Dropdown with API search | Destinations, locations, service points |
select |
Static dropdown | Service types, delivery options |
text |
Free text input | Reference numbers, notes |
hidden |
Hidden field | Default values, auto-set data |
WooNooW Hooks Reference
| Hook | Type | Description |
|---|---|---|
woonoow/shipping/before_calculate |
Action | Called before shipping rates are calculated |
woocommerce_checkout_fields |
Filter | Standard WC filter for checkout fields |
Examples
Example 1: Sicepat Integration
// Endpoint
register_rest_route('woonoow/v1', '/sicepat/destinations', ...);
// Session key
WC()->session->set('sicepat_destination_code', $code);
Example 2: JNE Direct API
// Endpoint for origin selection
register_rest_route('woonoow/v1', '/jne/origins', ...);
register_rest_route('woonoow/v1', '/jne/destinations', ...);
// Multiple session keys
WC()->session->set('jne_origin', $origin);
WC()->session->set('jne_destination', $destination);
Checklist for New Integration
- Identify the shipping plugin's class name
- Find what session/data it reads for API calls
- Create REST endpoint for searchable data
- Add checkout field(s) via filter
- Bridge data via
woonoow/shipping/before_calculate - Test shipping rate calculation
- Document in dedicated
[PROVIDER]_INTEGRATION.md
Related Documentation
- RAJAONGKIR_INTEGRATION.md - Rajaongkir-specific implementation
- SHIPPING_INTEGRATION.md - General shipping patterns
- HOOKS_REGISTRY.md - All WooNooW hooks