-->
How Taocarts Turns a Storefront Order Into an Automated 1688 Purchase
The part of a reverse-Taobao system people underestimate is the gap between "a customer paid on the overseas storefront" and "a supplier in China actually has the item reserved." That gap is where manual work hides, and manual work is where mistakes and delays live. At Taocarts we automated it end to end, and the interesting engineering isn't the 1688 API call — it's everything around resolving what to buy and proving it happened.
The order arrives with intent, not a link
When a buyer checks out, the storefront order doesn't carry a raw 1688 URL. It carries our internal product id and the chosen variant (color, size). We resolve that to a source item through a mapping table built during catalog sync:
const source = await db.query(
`SELECT source_item_id, spec_map
FROM product_source_map
WHERE product_id = $1`,
[order.product_id]
);
const specId = source.spec_map[order.variant]; // e.g. "红色 / L" -> 1688 spec id
Keeping the mapping separate from the order means a supplier changing their listing doesn't break historical orders — we look up by product, not by scraping a URL at purchase time.
Snapshot the price before you commit
1688 prices move. If we read the live price at purchase and it jumped, the customer's already-paid amount no longer covers it. So the purchase task stores a price snapshot taken at checkout:
const task = await db.query(
`INSERT INTO purchase_task
(order_id, source_item_id, spec_id, qty, price_snapshot, status)
VALUES ($1,$2,$3,$4,$5,'pending')
RETURNING id`,
[order.id, source.source_item_id, specId, order.qty, order.paid_unit]
);
The snapshot is what reconciliation later compares against. If the live price is higher than the snapshot by more than our tolerance, the task is held for a human instead of silently over-charging or under-buying.
Calling the source and writing back tracking
The actual procurement is a single call to the 1688 order endpoint with the resolved item, spec, and quantity. What matters more is what we do after: we persist the domestic tracking number the moment the supplier confirms, and that number is what the buyer's shipment timeline is built from later.
const res = await place1688Order({
itemId: source.source_item_id,
specId,
qty: order.qty,
outerId: `TC-${task.id}`,
});
await db.query(
`UPDATE purchase_task SET status='purchased',
domestic_tracking=$2 WHERE id=$1`,
[task.id, res.domesticTrackingNo]
);
The outerId we send is our task id, so when the supplier's system pushes status back, we can match it without guessing.
Why this shape holds up
A few things made this reliable in production. First, purchase is a task, not a side effect buried in checkout — if the 1688 call fails, the order stays "paid, not purchased" and a worker retries the task, the buyer is never charged twice because charging already happened upstream. Second, variant resolution is a lookup, so adding a new marketplace source means adding a row, not rewriting the flow. Third, the price snapshot turns "why did this cost more" into a literal number we can show.
The lesson: the hard part of auto-purchasing isn't clicking "buy" on a supplier's site. It's making sure that when a customer pays, the exact right item is reserved, the cost is frozen, and the proof comes back where the rest of the system can see it. Get those three right and the procurement step becomes boring — which is exactly what you want.
Top comments (0)