Prototype · September 2026
Local Distribution Dispatch: from a Shopify checkout to a delivered order, with SAP in between.
A working prototype of the distribution layer a business needs once it sells online but delivers through independent local distributors. Web orders from Shopify become sales orders in SAP S/4HANA, are dispatched to the right distributor, and are closed by drivers on a phone app with the customer’s PIN, a returnables count and a photo. The demo runs a propane cylinder business in Dallas–Fort Worth. I built it to show how I would design the system; the data is fictional and the Shopify and SAP calls are simulated.
The gap
Two good systems and a phone call between them.
A business that adds a web shop but delivers through local distributors usually ends up with Shopify taking the order and the payment, and SAP holding the customer, the material, the price and the stock. What happens between the two is often handled on WhatsApp and in spreadsheets: which distributor takes the order, which driver goes, whether it arrived, whether the returnables came back, whether the cash reached the bank.
The customer’s order page says “processing” until someone remembers to update it. Management sees orders and invoices but not the delivery in between. This prototype is that missing layer, and it does not care what the product is.

Track record
The system is a prototype. Its parts are not.
I have not shipped this exact system for a client, and this page does not pretend otherwise. Every hard part of it, though, is something I have delivered in production, most of it for clients abroad.
Vehicles and drivers in the field
Autom8
Client · live
GPS fleet telematics with live vehicle video: device protocol servers on TCP and UDP, HLS transcoding through FFmpeg, and a deployment runbook onto the client’s own server.
autom8.kakushin.ioIntegration with an enterprise system of record
National electricity utility apps
Europe · regulated · under subcontract
Native iOS and Android customer apps over eight backend services, integrated through Azure API Management onto the utility’s billing platform (Oracle CC&B). Coordinated seven external counterparties and drove penetration-test remediation to production acceptance.
Many independent operators on one platform
BookMi
Client · multi-vendor marketplace
Venues, packages and transport from independent vendors, with escrow and vendor payouts. Production-freeze governance on payment flows and a 233-test Playwright suite against 148 documented use cases.
bookmi.kakushin.ioE-commerce tied to statutory invoicing
Monvasi
Client · Greece · delivered
B2C olive-oil marketplace with myDATA invoicing, so every web order produces the tax document the authority requires: the same order-to-finance discipline as posting a web order into an ERP.
Mobile apps that work without signal
oajax.js
Open source · author · 2016 · MIT
Offline-first request library written for field apps: requests made with no connectivity are held on the device and replayed when it returns. The driver app’s sync model is this idea, a decade on.
github.comFive years building for US companies
PAO · RYZA · SwotKnot
Boulder, CO and Palo Alto, CA · 2018 – 2023
Backend services, deployments and releases for a local-discovery marketplace on the App Store (PAO); full architecture of a real-time co-creation platform (RYZA); lead developer of a learning platform (SwotKnot).
The flow
Eight steps, each one recorded.
01
Checkout
The customer orders on the Shopify store. The orders/create webhook is verified (HMAC-SHA256) and stored idempotently by webhook ID before anything else happens; the customer is texted a four-digit delivery PIN.
02
Sales order
The order is posted to SAP S/4HANA as a sales order. A first-time customer is created as a business partner first; if SAP rejects the post, the order is held as an exception rather than silently lost.
03
Dispatch
Partners are ranked for each order: zone coverage is a hard rule, then stock at their yard, then their 30-day on-time record, then open load. Dispatch can auto-assign the queue or override any single order.
04
Accept
The partner’s driver sees the job on the phone app, and only their own jobs, with the customer’s number masked.
05
Load
Each line is scanned at the yard. The yard’s stock is drawn down, so a partner running short stops being offered orders it cannot fill.
06
Deliver
At the door the driver enters the customer’s PIN, counts any returnables handed back, records money collected and takes a photo. A wrong PIN keeps the job open.
07
Post back
Goods issue and billing are posted in SAP, the Shopify order is marked fulfilled, and the customer’s “My orders” page shows the delivery time.
08
Exceptions
A failed attempt returns to dispatch with its reason and is reassigned; late orders, SAP rejections and failures share one queue.
Order desk
Not every order is a checkout. The phone takes the same road.

The record opens before the hello
The phone system passes the caller’s number in as it rings. The agent sees who it is, where they live, their SAP partner number, how many returnables they hold, today’s orders and any notes, without asking.
One pipeline, two doors
A phoned-in order is written to Shopify as a draft order and completed, so it arrives by the same webhook as a web checkout and posts to SAP the same way. One catalogue, one price list, one audit trail.
The last mile reports back
“Where is my order” is answered from the driver’s live status. A failed delivery becomes a callback, the desk agrees a new time and the order returns to dispatch. A note such as a gate code travels to the driver’s phone.
Two phones
The driver does the paperwork at the door. The customer watches it happen.


A driver sees their own jobs in window order: accept, scan the goods at the yard, deliver. The delivery screen will not close without the customer’s PIN, and it records the returnables handed back, the amount collected at the door and a photo of the delivery in place.
If the delivery cannot be made, the driver picks a reason and the order goes straight back to dispatch with it, instead of sitting in a van until the end of the shift.
The customer’s tracking page reads the same order record, so nobody updates them by hand: it shows the ETA, five plain steps with no system jargon, and the PIN the driver will ask for.
The driver’s phone never shows the customer’s full number, and a driver cannot see another driver’s jobs. The production app works offline and syncs when signal returns.
Integration
What actually crosses the wire.
The three calls that matter, as the production build would make them. Field names follow the Shopify Admin API and the S/4HANA OData services; organisation values and IDs are placeholders.
POST /webhooks/shopify/orders-create
X-Shopify-Topic: orders/create
X-Shopify-Webhook-Id: 7b1c9e2a-… ← idempotency key
X-Shopify-Hmac-Sha256: 3q2+7w… ← verified before parsing
{
"id": 5821390233716,
"name": "#10413",
"financial_status": "pending",
"payment_gateway_names": ["Cash on Delivery (COD)"],
"total_price": "84.48",
"currency": "USD",
"shipping_address": {
"address1": "2210 Cedar Springs Rd", "address2": "Apt 1804",
"city": "Dallas", "province_code": "TX", "zip": "75201"
},
"line_items": [
{ "sku": "LPG-CYL-20X", "quantity": 2, "price": "24.99" },
{ "sku": "LPG-ACC-RHK", "quantity": 1, "price": "34.50" }
]
}POST /sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder
x-csrf-token: <fetched first>
{
"SalesOrderType": "OR",
"SalesOrganization": "1710",
"DistributionChannel": "10",
"OrganizationDivision": "00",
"SoldToParty": "17100042",
"PurchaseOrderByCustomer": "#10413",
"to_Item": [
{ "Material": "LPG-CYL-20X", "RequestedQuantity": "2", "RequestedQuantityUnit": "EA" },
{ "Material": "LPG-ACC-RHK", "RequestedQuantity": "1", "RequestedQuantityUnit": "EA" }
]
}
→ 201 Created SalesOrder "812341"# when the driver's PIN is verified
POST /sap/opu/odata/sap/API_OUTBOUND_DELIVERY_SRV;v=0002/
PostGoodsIssue?DeliveryDocument='80046210'
# then back to the store
POST /admin/api/2024-07/fulfillments.json
{
"fulfillment": {
"line_items_by_fulfillment_order": [{ "fulfillment_order_id": 6934817… }],
"notify_customer": true
}
}| Shopify | SAP | Rule |
|---|---|---|
| name | PurchaseOrderByCustomer | Shopify order number carried into SAP for reconciliation |
| customer / shipping_address | SoldToParty (business partner) | Looked up by phone and email; created through API_BUSINESS_PARTNER if new |
| line_items[].sku | to_Item[].Material | Shopify SKU is the SAP material number, so there is no mapping table to drift |
| line_items[].quantity | RequestedQuantity | Unit of measure fixed per material |
| payment_gateway_names | Payment terms | Card, cash on delivery or on account, set on the sales order |
| X-Shopify-Webhook-Id | Idempotency key | A replayed webhook finds its earlier result instead of creating a second order |
Architecture
What a production build would run on.
- Integration service
- Node.js / TypeScript workers behind a queue (Azure Service Bus or equivalent). A webhook receiver writes to an outbox; workers call SAP over OData (API_BUSINESS_PARTNER, API_SALES_ORDER_SRV, API_OUTBOUND_DELIVERY_SRV) or through SAP Integration Suite where the landscape has standardised on it. Every call carries an idempotency key; failures retry with backoff and then land in a dead-letter queue that the console shows as exceptions.
- Dispatch core
- PostgreSQL with an explicit order state machine (new → assigned → accepted → loaded → delivered, or failed) and an append-only audit trail on every order. Catalogue, geography and partner network are configuration, which is how the same engine runs both scenarios.
- Partner portal
- The same console, scoped by role and tenant with row-level security: a distributor sees its own orders, drivers and stock, and nothing else.
- Driver app
- React Native, offline-first: actions queue on the phone and replay when signal returns, the same idea as oajax.js, the offline request library I published in 2016. GPS, camera and barcode scanning.
- Reporting
- Partner SLA, on-time rate, returnables outstanding and cash to reconcile, fed to the BI layer (SAP Analytics Cloud or Power BI) for management.
- Identity and security
- SSO with MFA for staff (Microsoft Entra ID), separate partner accounts, device-bound tokens for drivers, secrets in a vault, customer data masked by default.
Decisions
The choices I would defend in a design review.
SAP stays the system of record.
Customer, material, price, stock and money live in SAP. Dispatch owns only the delivery workflow, so there is never a second price list to drift.
No dispatch without a sales order.
Nothing leaves a yard without a sales order to post the goods issue against. An order SAP holds back shows up as an exception, not as a gap someone finds at month end.
The PIN is the proof of delivery.
Not the driver’s word. It also protects the partner when a customer disputes a delivery.
Returnables are counted per order.
An unreturned cylinder or crate is lost capital. Counting at the door is what lets the yard, the plant and SAP’s returnable-packaging balance agree.
Least privilege by default.
Drivers see a masked phone number and only their own jobs; partners see only their own data.
Everything is idempotent.
Shopify re-sends webhooks, mobile networks drop and SAP calls time out. A retry must never become a second sales order or a second goods issue.
Scope
What the demo does, and what it doesn’t.
Working in the demo
- The full order flow, from a new Shopify order to SAP goods issue and Shopify fulfilment
- Live mode: orders arrive, dispatch clears the queue and drivers deliver on their own, so the board can be watched as well as worked
- A guided walkthrough that plays one order from checkout to SAP goods issue, hands off, in about forty seconds
- A live map on Google Maps, styled dark, with vehicles moving to the door and an ETA for each
- A call-centre screen: caller recognised by number, customer record with history, returnables and notes, orders taken by phone, callbacks for missed deliveries
- The customer’s tracking page, reading the same order as dispatch and the driver, with the delivery PIN
- An analytics screen: volume and on-time rate over two weeks, and on-time, load and returnables by partner
- Refill and first-time orders with a refundable deposit; standard and express delivery windows
- Partner ranking with reasons, auto-assign and manual override
- A first-time customer rejected by SAP, then retried with a new business partner
- A failed delivery returned to dispatch and reassigned
- Yard stock, returnables and cash at the door reconciled as orders close
- An audit trail per order and an integration log across Shopify, SAP, SMS and the phone system
Simulated or left out
- Shopify and SAP calls are simulated in the browser, using the real endpoint names
- No live GPS or route optimisation; vehicles move in straight lines across a real map
- No telephony; incoming calls are generated in the browser
- No sign-in; the partner and driver views are switchable for the demo
- State is kept in the visitor’s browser, not on a server
Next

