shipping-service is a Quarkus service that owns shipping zone configuration, rate calculation, carrier integration, shipment creation, and tracking for ShopSTAR3. Storage is PostgreSQL with store_id as a discriminator column for multi-tenant isolation. Carrier API credentials are stored in Vault — they are never written to the database.
Carrier Abstraction#
All live-rate carrier integrations implement a common CarrierAdapter interface. This decouples the rate calculation and shipment creation flows from any specific carrier.
interface CarrierAdapter {
List<RateOption> getRates(RateRequest request);
ShipmentResult createShipment(ShipmentRequest request);
LabelResult fetchLabel(String providerShipmentId);
TrackingResult getTracking(String trackingNumber);
}Built-in Adapters#
| Adapter | Notes |
|---|---|
FedExAdapter | Live rates + label generation via FedEx Web Services |
DhlAdapter | Live rates + label generation via DHL Express API |
UpsAdapter | Live rates + label generation via UPS Developer API |
AusPostAdapter | Live rates + label generation via Australia Post eParcel API |
Adding a new carrier requires only a new class implementing CarrierAdapter and the corresponding Vault entries for its credentials.
Rate Engine#
The rate engine is hybrid. Each shipping zone can be served by the built-in rules engine, a live carrier adapter, or both. The store admin configures this per zone.
Built-in Rate Types#
| Type | Behaviour |
|---|---|
FLAT | Fixed price regardless of weight or order value |
WEIGHT_BASED | Price derived from total shipment weight within configured bands |
VALUE_BASED | Price derived from order total within configured bands |
FREE_THRESHOLD | Zero charge when order total meets a minimum value |
Zones#
A shipping zone groups destination regions (country, state, postal-code pattern). A zone matches a destination if any of its regions matches. Overlapping zones are resolved by specificity — more specific region patterns take precedence.
Data Model#
Core tables#
shipping_zones — zone_id UUID PK, store_id, name.
shipping_zone_regions — region_id UUID PK, zone_id FK, country_code CHAR(2), state_code (nullable), postal_pattern (nullable, supports wildcards).
carrier_configs — config_id UUID PK, store_id, carrier_type (FEDEX / DHL / UPS / AUSPOST), display_name, is_active, settings JSONB (non-sensitive: enabled service types, weight unit).
shipping_rates — rate_id UUID PK, zone_id FK, store_id, name, rate_type ENUM, base_amount, currency, min_weight, max_weight, min_value, max_value, position.
Shipments#
shipments — shipment_id UUID PK, store_id, order_id, carrier_type, carrier_config_id FK (nullable for rules-based), provider_shipment_id, tracking_number, label_url (object storage reference), status ENUM (PENDING / LABEL_CREATED / SHIPPED / IN_TRANSIT / OUT_FOR_DELIVERY / DELIVERED / FAILED / RETURNED), estimated_delivery, shipped_at, delivered_at.
shipment_items — item_id UUID PK, shipment_id FK, order_item_id, quantity.
Checkout Integration#
checkout-service calls GetAvailableRates before the shipping selection step. The call is stateless — no shipment record is created at this point. The customer’s selected rate is stored on the order by order-service and passed back to shipping-service when CreateShipment is called post-order.
checkout-service → GetAvailableRates(storeId, shippingAddress, lineItems[weight])
→ rates[{ rateId, name, carrier, amount, currency, estimatedDays }]Fulfillment Flow#
Triggered when order-service publishes order.fulfillment_started (staff initiates fulfillment in admin).
- shipping-service resolves the selected rate from the order
- For carrier-based rates:
CarrierAdapter.createShipment()→CarrierAdapter.fetchLabel()→ label stored in object storage - Shipment record created with tracking number and label URL
shipment.createdpublished to Kafka
A scheduled polling job checks active in-transit shipments against carrier tracking APIs and publishes shipment.tracking.updated for each status change.
gRPC Interface#
| Method | Caller | Purpose |
|---|---|---|
GetAvailableRates(RatesRequest) | checkout-service | Return available rate options for a cart and destination |
CreateShipment(ShipmentRequest) | order-service | Create shipment record and generate label |
GetShipmentStatus(ShipmentIdRequest) | order-service | Return current shipment status and tracking |
Kafka Topics#
Consumed#
| Topic | Action |
|---|---|
order.fulfillment_started | Trigger shipment creation and label generation |
Published#
| Topic | Payload |
|---|---|
shipment.created | shipmentId, orderId, storeId, trackingNumber, carrier |
shipment.shipped | shipmentId, orderId, storeId, shippedAt |
shipment.tracking.updated | shipmentId, orderId, storeId, status, location, updatedAt |
shipment.delivered | shipmentId, orderId, storeId, deliveredAt |
Vault Configuration#
| Key path | Purpose |
|---|---|
ss3/kv/shipping-service/db.url | PostgreSQL JDBC URL |
ss3/kv/shipping-service/db.username | Database username |
ss3/kv/shipping-service/db.password | Database password |
ss3/kv/shipping-service/fedex.api-key | FedEx API key |
ss3/kv/shipping-service/fedex.account-number | FedEx account number |
ss3/kv/shipping-service/dhl.api-key | DHL Express API key |
ss3/kv/shipping-service/ups.client-id | UPS OAuth client ID |
ss3/kv/shipping-service/ups.client-secret | UPS OAuth client secret |
ss3/kv/shipping-service/auspost.api-key | Australia Post eParcel API key |
ss3/kv/shared/ | Shared Kafka bootstrap and OTel config |