Files
WooNooW/BITESHIP_ADDON_SPEC.md
dwindown 603d94b73c feat: Tax settings + unified addon guide + Biteship spec
## 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
2025-11-09 23:13:52 +07:00

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