Loading...
Loading...
Compare original and translation side by side
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Local Delivery by Zapiet + DoorDash Drive or Uber Direct | Zapiet handles time-slot booking at checkout; DoorDash Drive / Uber Direct dispatch drivers automatically |
| WooCommerce | WooCommerce Local Pickup Plus + Onfleet or your own drivers | Local Pickup Plus handles zones and time slots; Onfleet provides driver dispatch and tracking |
| BigCommerce | Zapiet Delivery + Onfleet or Dispatch Science | Zapiet and similar apps add delivery scheduling; Dispatch Science optimizes routes |
| Custom / Headless | Build time-slot booking + integrate Onfleet/DoorDash Drive API for dispatch | Full control over zone management, slot capacity, and driver routing |
| 平台 | 推荐工具 | 原因 |
|---|---|---|
| Shopify | Local Delivery by Zapiet + DoorDash Drive 或 Uber Direct | Zapiet负责结账时的时段预订;DoorDash Drive / Uber Direct自动调度司机 |
| WooCommerce | WooCommerce Local Pickup Plus + Onfleet 或自有司机团队 | Local Pickup Plus负责区域和时段管理;Onfleet提供司机调度和追踪功能 |
| BigCommerce | Zapiet Delivery + Onfleet 或 Dispatch Science | Zapiet及同类应用添加配送排期功能;Dispatch Science优化路线 |
| 自定义/无头电商 | 搭建时段预订功能 + 集成Onfleet/DoorDash Drive API实现调度 | 完全掌控区域管理、时段运力和司机路线规划 |
// Check if a customer address is in a delivery zone
async function checkDeliveryEligibility(params: {
customerZip: string;
deliveryZones: { name: string; zipCodes: string[]; deliveryFeeCents: number }[];
}): Promise<{ eligible: boolean; zone?: string; feeCents?: number }> {
const zone = params.deliveryZones.find(z => z.zipCodes.includes(params.customerZip));
if (!zone) return { eligible: false };
return { eligible: true, zone: zone.name, feeCents: zone.deliveryFeeCents };
}
// Get available time slots for today (slots with remaining capacity)
async function getAvailableSlots(params: {
date: Date;
zone: string;
slots: { id: string; label: string; capacity: number; booked: number; cutoffTime: Date }[];
}): Promise<{ id: string; label: string; spotsRemaining: number }[]> {
const now = new Date();
return params.slots
.filter(slot => slot.cutoffTime > now && slot.booked < slot.capacity)
.map(slot => ({
id: slot.id,
label: slot.label,
spotsRemaining: slot.capacity - slot.booked,
}));
}
// Dispatch a delivery via DoorDash Drive API
async function dispatchDoorDashDelivery(params: {
externalDeliveryId: string;
pickupAddress: Address;
dropoffAddress: Address;
customerPhone: string;
pickupWindow: { startTime: string; endTime: string }; // ISO 8601
}): Promise<{ trackingUrl: string; fee: number }> {
const response = await fetch('https://openapi.doordash.com/drive/v2/deliveries', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DOORDASH_DRIVE_JWT}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_delivery_id: params.externalDeliveryId,
pickup_address: `${params.pickupAddress.street1}, ${params.pickupAddress.city}, ${params.pickupAddress.state} ${params.pickupAddress.zip}`,
dropoff_address: `${params.dropoffAddress.street1}, ${params.dropoffAddress.city}, ${params.dropoffAddress.state} ${params.dropoffAddress.zip}`,
dropoff_phone_number: params.customerPhone,
pickup_time: params.pickupWindow.startTime,
}),
});
const data = await response.json();
return { trackingUrl: data.tracking_url, fee: data.fee };
}// Check if a customer address is in a delivery zone
async function checkDeliveryEligibility(params: {
customerZip: string;
deliveryZones: { name: string; zipCodes: string[]; deliveryFeeCents: number }[];
}): Promise<{ eligible: boolean; zone?: string; feeCents?: number }> {
const zone = params.deliveryZones.find(z => z.zipCodes.includes(params.customerZip));
if (!zone) return { eligible: false };
return { eligible: true, zone: zone.name, feeCents: zone.deliveryFeeCents };
}
// Get available time slots for today (slots with remaining capacity)
async function getAvailableSlots(params: {
date: Date;
zone: string;
slots: { id: string; label: string; capacity: number; booked: number; cutoffTime: Date }[];
}): Promise<{ id: string; label: string; spotsRemaining: number }[]> {
const now = new Date();
return params.slots
.filter(slot => slot.cutoffTime > now && slot.booked < slot.capacity)
.map(slot => ({
id: slot.id,
label: slot.label,
spotsRemaining: slot.capacity - slot.booked,
}));
}
// Dispatch a delivery via DoorDash Drive API
async function dispatchDoorDashDelivery(params: {
externalDeliveryId: string;
pickupAddress: Address;
dropoffAddress: Address;
customerPhone: string;
pickupWindow: { startTime: string; endTime: string }; // ISO 8601
}): Promise<{ trackingUrl: string; fee: number }> {
const response = await fetch('https://openapi.doordash.com/drive/v2/deliveries', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DOORDASH_DRIVE_JWT}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_delivery_id: params.externalDeliveryId,
pickup_address: `${params.pickupAddress.street1}, ${params.pickupAddress.city}, ${params.pickupAddress.state} ${params.pickupAddress.zip}`,
dropoff_address: `${params.dropoffAddress.street1}, ${params.dropoffAddress.city}, ${params.dropoffAddress.state} ${params.dropoffAddress.zip}`,
dropoff_phone_number: params.customerPhone,
pickup_time: params.pickupWindow.startTime,
}),
});
const data = await response.json();
return { trackingUrl: data.tracking_url, fee: data.fee };
}| Problem | Solution |
|---|---|
| Two customers book the last spot in a slot simultaneously | Use atomic slot decrement with capacity check — Zapiet handles this; for custom builds use database-level locks or atomic updates |
| Customer enters an address outside the delivery zone but sees time slots | Validate zone eligibility server-side at checkout, not just client-side; Zapiet enforces this automatically |
| Cutoff time displayed in wrong timezone for customer | Always store and compare times in UTC; display to customers in their local timezone using the browser's |
| Driver assigned to more orders than they can fulfill in the window | Cap orders per driver per slot and enable route optimization in Onfleet; set realistic capacity limits when building your slot schedule |
| 问题 | 解决方案 |
|---|---|
| 两名客户同时预订时段的最后一个名额 | 使用原子性的时段运力递减和检查——Zapiet会处理此问题;对于自定义构建,请使用数据库级锁或原子更新 |
| 客户输入超出配送区域的地址但仍能看到时段 | 在结账时进行服务器端区域资格验证,而不仅仅是客户端;Zapiet会自动强制执行此规则 |
| 客户看到的截止时间时区错误 | 始终以UTC存储和比较时间;使用浏览器的 |
| 司机被分配的订单超出其在时段内可完成的数量 | 限制每个司机每个时段的订单数量,并在Onfleet中启用路线优化;构建时段计划时设置合理的运力限制 |