## 1. Created BITESHIP_ADDON_SPEC.md ✅ - Complete plugin specification - Database schema, API endpoints - WooCommerce integration - React components - Implementation timeline ## 2. Merged Addon Documentation ✅ Created ADDON_DEVELOPMENT_GUIDE.md (single source of truth): - Merged ADDON_INJECTION_GUIDE.md + ADDON_HOOK_SYSTEM.md - Two addon types: Route Injection + Hook System - Clear examples for each type - Best practices and troubleshooting - Deleted old documents ## 3. Tax Settings ✅ Frontend (admin-spa/src/routes/Settings/Tax.tsx): - Enable/disable tax calculation toggle - Display standard/reduced/zero tax rates - Show tax options (prices include tax, based on, display) - Link to WooCommerce for advanced config - Clean, simple UI Backend (includes/Api/TaxController.php): - GET /settings/tax - Fetch tax settings - POST /settings/tax/toggle - Enable/disable taxes - Fetches rates from woocommerce_tax_rates table - Clears WooCommerce cache on update ## 4. Advanced Local Pickup - TODO Will be simple: Admin adds multiple pickup locations ## Key Decisions: ✅ Hook system = No hardcoding, zero coupling ✅ Tax settings = Simple toggle + view, advanced in WC ✅ Single addon guide = One source of truth Next: Advanced Local Pickup locations
7.3 KiB
7.3 KiB
WooNooW Indonesia Shipping (Biteship Integration)
Plugin Specification
Plugin Name: WooNooW Indonesia Shipping
Description: Simple Indonesian shipping integration using Biteship Rate API
Version: 1.0.0
Requires: WooNooW 1.0.0+, WooCommerce 8.0+
License: GPL v2 or later
Overview
A lightweight shipping plugin that integrates Biteship's Rate API with WooNooW SPA, providing:
- ✅ Indonesian address fields (Province, City, District, Subdistrict)
- ✅ Real-time shipping rate calculation
- ✅ Multiple courier support (JNE, SiCepat, J&T, AnterAja, etc.)
- ✅ Works in both frontend checkout AND admin order form
- ✅ No subscription required (uses free Biteship Rate API)
Features Roadmap
Phase 1: Core Functionality
- WooCommerce Shipping Method integration
- Biteship Rate API integration
- Indonesian address database (Province → Subdistrict)
- Frontend checkout integration
- Admin settings page
Phase 2: SPA Integration
- REST API endpoints for address data
- REST API for rate calculation
- React components (SubdistrictSelector, CourierSelector)
- Hook integration with WooNooW OrderForm
- Admin order form support
Phase 3: Advanced Features
- Rate caching (reduce API calls)
- Custom rate markup
- Free shipping threshold
- Multi-origin support
- Shipping label generation (optional, requires paid Biteship plan)
Plugin Structure
woonoow-indonesia-shipping/
├── woonoow-indonesia-shipping.php # Main plugin file
├── includes/
│ ├── class-shipping-method.php # WooCommerce shipping method
│ ├── class-biteship-api.php # Biteship API client
│ ├── class-address-database.php # Indonesian address data
│ ├── class-addon-integration.php # WooNooW addon integration
│ └── Api/
│ └── AddressController.php # REST API endpoints
├── admin/
│ ├── class-settings.php # Admin settings page
│ └── views/
│ └── settings-page.php # Settings UI
├── admin-spa/
│ ├── src/
│ │ ├── components/
│ │ │ ├── SubdistrictSelector.tsx # Address selector
│ │ │ └── CourierSelector.tsx # Courier selection
│ │ ├── hooks/
│ │ │ ├── useAddressData.ts # Fetch address data
│ │ │ └── useRateCalculation.ts # Calculate rates
│ │ └── index.ts # Addon registration
│ ├── package.json
│ └── vite.config.ts
├── data/
│ └── indonesia-areas.sql # Address database dump
└── README.md
Database Schema
CREATE TABLE `wp_woonoow_indonesia_areas` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`biteship_area_id` varchar(50) NOT NULL,
`name` varchar(255) NOT NULL,
`type` enum('province','city','district','subdistrict') NOT NULL,
`parent_id` bigint(20) DEFAULT NULL,
`postal_code` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `biteship_area_id` (`biteship_area_id`),
KEY `parent_id` (`parent_id`),
KEY `type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
WooCommerce Shipping Method
<?php
// includes/class-shipping-method.php
class WooNooW_Indonesia_Shipping_Method extends WC_Shipping_Method {
public function __construct($instance_id = 0) {
$this->id = 'woonoow_indonesia_shipping';
$this->instance_id = absint($instance_id);
$this->method_title = __('Indonesia Shipping', 'woonoow-indonesia-shipping');
$this->supports = array('shipping-zones', 'instance-settings');
$this->init();
}
public function init_form_fields() {
$this->instance_form_fields = array(
'api_key' => array(
'title' => 'Biteship API Key',
'type' => 'text'
),
'origin_subdistrict_id' => array(
'title' => 'Origin Subdistrict',
'type' => 'select',
'options' => $this->get_subdistrict_options()
),
'couriers' => array(
'title' => 'Available Couriers',
'type' => 'multiselect',
'options' => array(
'jne' => 'JNE',
'sicepat' => 'SiCepat',
'jnt' => 'J&T Express'
)
)
);
}
public function calculate_shipping($package = array()) {
$origin = $this->get_option('origin_subdistrict_id');
$destination = $package['destination']['subdistrict_id'] ?? null;
if (!$origin || !$destination) return;
$api = new WooNooW_Biteship_API($this->get_option('api_key'));
$rates = $api->get_rates($origin, $destination, $package);
foreach ($rates as $rate) {
$this->add_rate(array(
'id' => $this->id . ':' . $rate['courier_code'],
'label' => $rate['courier_name'] . ' - ' . $rate['service_name'],
'cost' => $rate['price']
));
}
}
}
REST API Endpoints
<?php
// includes/Api/AddressController.php
register_rest_route('woonoow/v1', '/indonesia-shipping/provinces', array(
'methods' => 'GET',
'callback' => 'get_provinces'
));
register_rest_route('woonoow/v1', '/indonesia-shipping/calculate-rates', array(
'methods' => 'POST',
'callback' => 'calculate_rates'
));
React Components
// admin-spa/src/components/SubdistrictSelector.tsx
export function SubdistrictSelector({ value, onChange }) {
const [provinceId, setProvinceId] = useState('');
const [cityId, setCityId] = useState('');
const { data: provinces } = useQuery({
queryKey: ['provinces'],
queryFn: () => api.get('/indonesia-shipping/provinces')
});
return (
<div className="space-y-3">
<Select label="Province" options={provinces} />
<Select label="City" options={cities} />
<Select label="Subdistrict" onChange={onChange} />
</div>
);
}
WooNooW Hook Integration
// admin-spa/src/index.ts
import { addonLoader, addFilter } from '@woonoow/hooks';
addonLoader.register({
id: 'indonesia-shipping',
name: 'Indonesia Shipping',
version: '1.0.0',
init: () => {
// Add subdistrict selector in order form
addFilter('woonoow_order_form_after_shipping', (content, formData, setFormData) => {
return (
<>
{content}
<SubdistrictSelector
value={formData.shipping?.subdistrict_id}
onChange={(id) => setFormData({
...formData,
shipping: { ...formData.shipping, subdistrict_id: id }
})}
/>
</>
);
});
}
});
Implementation Timeline
Week 1: Backend
- Day 1-2: Database schema + address data import
- Day 3-4: WooCommerce shipping method class
- Day 5: Biteship API integration
Week 2: Frontend
- Day 1-2: REST API endpoints
- Day 3-4: React components
- Day 5: Hook integration + testing
Week 3: Polish
- Day 1-2: Error handling + loading states
- Day 3: Rate caching
- Day 4-5: Documentation + testing
Status: Specification Complete - Ready for Implementation