算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
ecommerce-checkout-cart【设计与创意】Use when the user asks about shopping cart, checkout flow, cart management, order management, discount/coupon system, tax calculation, shipping logic, or e-commerce backend patterns. Do NOT use for: payment processing (ecommerce-payment-processing), or general backend API design (backend-api-design).
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Design shopping cart and checkout systems: cart management, order lifecycle, discount/promotion engine, tax calculation, shipping integration, and checkout optimization.
Are users required to log in before adding items to cart?
Do you need cross-device cart persistence for guest users?
Is cart value typically high or low?
Do you need server-side cart features (abandoned cart emails, analytics)?
Does the discount apply to individual items or the entire order?
Is the discount automatic or requires a coupon code?
Can discounts be combined with other discounts?
Does the discount have usage limits?
Is this a returning customer?
Is the cart value below free shipping threshold?
Are there regulatory requirements for checkout?
Is this a high-risk order (new customer, high value, unusual shipping)?
Add Item -> Cart Service -> Database
+-- Validate stock, price, eligibility
Update Qty -> Recalculate totals
+-- Subtotal, discount, tax, shipping, total
Apply Coupon -> Validate and apply discount
Checkout -> Convert cart to order
+-- Lock cart, create order, clear cart
cart:
id: UUID (primary key)
user_id: UUID (nullable for guest)
session_token: string (for guest carts)
status: active | abandoned | converting | checkout_complete
created_at: timestamp
updated_at: timestamp
expires_at: timestamp
items:
- id: UUID
product_id: UUID
variant_id: UUID (nullable)
sku: string
name: string
quantity: integer
unit_price: decimal (price at time of add)
discount_amount: decimal
discount_percentage: decimal
applied_promotions: [promotion_id]
image_url: string
is_gift: boolean
totals:
subtotal: decimal (sum of item prices x quantities)
discount_total: decimal (sum of all discounts)
shipping_total: decimal (calculated by shipping service)
tax_total: decimal (calculated by tax service)
grand_total: decimal (subtotal - discount + shipping + tax)
currency: string (ISO 4217)
coupon_code: string (nullable)
coupon_id: UUID (nullable)
applied_promotions: [promotion_id]
shipping_address_id: UUID (nullable)
billing_address_id: UUID (nullable)
shipping_method: string (nullable)
notes: string (nullable)
metadata: map (extensible, store any additional data)
guest_cart:
storage: "Local storage (client-side) or temporary server-side key"
merge: "Merge into user cart on login -- resolve conflicts by keeping newest"
expiry: "30 days since last activity"
limitation: "Cannot save for later, limited to 50 items"
registered_cart:
storage: "Server-side persistent storage"
merge_strategy:
- "If guest cart has items before login: merge into server cart"
- "Conflict resolution: newest quantity wins"
- "Keep guest cart items that don't exist in server cart"
persistence: "Unlimited duration, cross-device sync"
features: ["Save for later", "Wishlist conversion", "Multi-device sync"]
cart_states:
active:
description: "User is actively adding/removing items"
ttl: "30 days inactivity -> abandoned"
allowed_transitions: ["abandoned", "converting"]
abandoned:
description: "Cart expired or user left without checkout"
actions: ["Send recovery email", "Save items for 7 days"]
allowed_transitions: ["active", "converting"]
converting:
description: "User entered checkout flow"
actions: ["Lock cart quantities", "Reserve inventory", "Calculate final totals"]
allowed_transitions: ["active", "checkout_complete", "expired"]
checkout_complete:
description: "Cart successfully converted to order"
actions: ["Clear cart", "Create order record", "Send confirmation"]
final: true
scalability_patterns:
high_traffic_events:
problem: "Flash sales, drops, Black Friday spike"
solutions:
- "Queue cart write operations -- accept writes, process asynchronously"
- "Pre-calculate price snapshots -- avoid real-time price lookups"
- "Inventory reservation with timeout -- release unconfirmed after 15min"
- "Read-through cache for cart load -- write-back cache for updates"
multi_tenant_cart:
b2b:
features: ["Quote-based pricing", "Approval workflows", "Budgets", "Multiple shipping addresses"]
data_model: "Cart belongs to organization, items priced per negotiated contract"
marketplace:
features: ["Multi-vendor cart", "Split shipments", "Seller-specific promotions"]
data_model: "Cart items grouped by vendor, each vendor calculates their own totals"
Cart -> Pending -> Confirmed -> Processing -> Shipped -> Delivered
+ +
Payment Failed Return Requested
+ +
Cancelled Returned/Refunded
order_states:
pending:
entry: "Create order record, reserve inventory"
exit: "Release inventory reservation"
timeout: "30min -> auto-cancel if payment not confirmed"
confirmed:
entry: "Capture payment, send confirmation email"
actions: ["Fulfillment queue assignment", "Tax recording", "Accounting entry"]
processing:
entry: "Picking/packing handoff"
sub_states: ["awaiting_pickup", "picked", "packed", "label_created"]
shipped:
entry: "Tracking number registered, notification sent"
data: ["Carrier", "Tracking number", "Estimated delivery date"]
delivered:
entry: "Delivery confirmation, review request"
follow_up: ["Return window tracking", "Review NPS survey"]
cancelled:
allowed_before: "confirmed -- auto-refund"
after_confirmed: "Manual refund processing required"
returned:
states: ["return_requested", "return_approved", "item_received", "refund_issued"]
sla: "Approve within 48h, refund within 5 business days of receipt"
| Discount Type | Logic | Example |
|---|---|---|
| Percentage | % off total or category | 20% off all shoes |
| Fixed amount | Flat discount | $10 off orders over $100 |
| BOGO | Buy one get one | Buy 2, get 1 free |
| Tiered | Volume-based pricing | 10% off 5+ items |
| Bundle | Fixed price for set | $50 for 3 selected items |
stacking_rules:
order: "Apply in order: item discounts -> cart discounts -> shipping discounts -> loyalty"
exclusivity:
exclusive: "Cannot combine with any other promotion"
stackable: "Can combine with other stackable promotions"
best_of: "Auto-select best applicable promotion from set"
limits:
usage: "Max N uses per customer"
budget: "Total discount cap: $X"
stack: "Max N promotions per order"
validation_pipeline:
step_1_syntax: "Is the coupon code format valid? Alphanumeric, correct length."
step_2_existence: "Does the coupon code exist in the database and is it active?"
step_3_ timeframe: "Is the current date within the valid_from and valid_until range?"
step_4_usage_limits: "Has the coupon exceeded max uses (total or per customer)?"
step_5_eligibility: "Does the cart meet minimum purchase amount, specific items, or category requirements?"
step_6_exclusivity: "Is the coupon compatible with already-applied promotions?"
step_7_apply: "Calculate discount amount, update cart totals, record promotion usage."
| Strategy | Tool | Best For |
|---|---|---|
| Manual rules | Custom logic | Single jurisdiction |
| TaxJar | API integration | US multi-state |
| Avalara | API integration | Global multi-jurisdiction |
| Stripe Tax | Built-in | Stripe users |
| Carrier | Integration Method | Key Feature |
|---|---|---|
| FedEx | REST API | Rate quotes, tracking, labels |
| UPS | SOAP/REST API | Rate quotes, time-in-transit |
| USPS | Web Tools API | Domestic rates, tracking |
| DHL | REST API | International shipping, customs |
| ShipEngine | Unified API | Multi-carrier, label generation |
recovery_flow:
trigger: "Cart abandoned for 1 hour (status: active -> abandoned)"
step_1_immediate: "Send email: 'You left something behind' with cart summary and CTA"
step_2_24h: "Send email: 'Your cart is still waiting' with product recommendations"
step_3_72h: "Send email: 'Last chance' with limited-time discount offer (if margin allows)"
step_4_7d: "Send email: 'We saved your cart' with note that cart will expire"
step_5_30d: "Mark cart as expired, release any inventory reservations"
optimization:
timing: "Send first email within 1 hour for best conversion"
personalization: "Include images of abandoned items, similar products"
incentives: "Free shipping or 10% discount on abandoned cart items"
exit_intent: "Capture email before abandonment (exit-intent popup)"
optimization_checklist:
performance:
- "Cart summary loads in <500ms from any action"
- "Price calculations cached and invalidated on change"
- "Async inventory check -- don't block checkout on stock verification"
ux:
- "Guest checkout available -- no forced account creation"
- "Progress indicator showing step N of 4"
- "Save address autocomplete (Google Places, Loqate)"
- "Inline validation -- errors shown per field on blur"
conversion:
- "Abandoned cart email within 1 hour"
- "Cart persistence across sessions (logged-in users)"
- "Multiple payment methods displayed upfront"
- "Estimated total shown before checkout entry"
trust:
- "Security badges (SSL, PCI compliant) visible"
- "Clear return policy link near checkout button"
- "Shipping cost shown before payment entry"
- "Stock availability indicator per item"
Pitfall 1: Cart not persisted across sessions for logged-in users. Users return to find empty cart. Frustration and lost sales. Mitigation: server-side cart persistence for authenticated users. Sync across devices.
Pitfall 2: Price changes between cart creation and checkout. Product price increases after item added. Customer sees higher price at checkout. Mitigation: snapshot price when item added to cart. Honor snapshot price for N hours.
Pitfall 3: Discount stacking causing negative totals. Multiple promotions combined result in order total below zero. Mitigation: cap discount at subtotal amount. Minimum cart value for promotion eligibility.
Pitfall 4: Inventory overallocation during high-traffic events. More inventory reserved than available because of race conditions. Mitigation: atomic inventory operations (Redis DECR, database row lock). Short reservation timeout (15 minutes).
Pitfall 5: Tax calculation incorrect for multi-jurisdiction orders. Items shipping to different locations with different tax rates. Mitigation: line-item level tax calculation. Determine tax jurisdiction per shipping destination per line item.
Pitfall 6: Order state machine missing failure transitions. Payment fails, inventory release not triggered. Mitigation: all failure states mapped to inventory release. Timeout transitions on payment pending status.
Pitfall 7: Guest cart merge conflicts. User adds items as guest, logs in, items from server cart are lost. Mitigation: merge strategy preserves both sets of items. Conflict resolution: newest quantity wins.
Practice 1: Always snapshot prices at cart-add time. Product prices change. Cart should honor the price shown to the customer when they added the item. Price snapshot stored in cart item record.
Practice 2: Implement cart-level idempotency keys. Prevent duplicate item additions from retry logic. Deduplicate on product_id + variant_id + user_id within same session.
Practice 3: Reserve inventory at checkout entry, not cart add. Early reservation blocks inventory unnecessarily. Reserve when user enters checkout (converting state). Release after timeout or payment completion.
Practice 4: Use event-driven architecture for order processing. Order created event triggers: payment capture, inventory decrement, fulfillment assignment, notification sending. Decoupled services handle each concern.
Practice 5: Log all price changes with before/after values. Audit trail for price discrepancy investigation. Critical for tax audits and chargeback disputes.
Practice 6: Pre-calculate shipping and tax estimates. Show estimated total before checkout entry. Avoid surprise costs at final step. Recalculate after address change.
Practice 7: Support split payments, partial payments, and multi-currency. Not all customers pay with a single method. Cart system should support partial payments (gift card + credit card).
Practice 8: Implement cart expiry and cleanup. Abandoned carts consume storage. Schedule cleanup job for expired carts. Follow data retention policy for abandoned cart data.
async function mergeCartsOnLogin(guestToken: string, userId: string): Promise<Cart> {
const guestCart = await Cart.findOne({ sessionToken: guestToken, status: 'active' });
const userCart = await Cart.findOne({ userId, status: 'active' });
if (!guestCart) return userCart;
if (!userCart) {
guestCart.userId = userId;
guestCart.sessionToken = null;
return guestCart.save();
}
// Merge guest items into user cart
for (const guestItem of guestCart.items) {
const existing = userCart.items.find(i =>
i.productId === guestItem.productId && i.variantId === guestItem.variantId
);
if (existing) {
existing.quantity = Math.max(existing.quantity, guestItem.quantity);
} else {
userCart.items.push(guestItem);
}
}
// Recalculate totals
userCart.totals = calculateTotals(userCart.items, userCart.couponCode);
await userCart.save();
await Cart.deleteOne({ _id: guestCart._id });
return userCart;
}
interface DiscountRule {
type: 'percentage' | 'fixed' | 'bogo' | 'tiered' | 'bundle';
priority: number;
condition: (cart: Cart) => boolean;
apply: (cart: Cart) => DiscountResult;
}
class DiscountEngine {
private rules: DiscountRule[];
applyPromotions(cart: Cart, couponCode?: string): Cart {
let workingCart = this.snapshotCart(cart);
const applicable = this.rules
.filter(r => r.condition(workingCart))
.sort((a, b) => a.priority - b.priority);
for (const rule of applicable) {
const result = rule.apply(workingCart);
workingCart.discountTotal += result.amount;
workingCart.appliedPromotions.push(result.promotionId);
// Cap discount at subtotal
if (workingCart.discountTotal > workingCart.subtotal) {
workingCart.discountTotal = workingCart.subtotal;
}
}
workingCart.grandTotal = workingCart.subtotal
- workingCart.discountTotal
+ workingCart.shippingTotal
+ workingCart.taxTotal;
return workingCart;
}
private snapshotCart(cart: Cart): Cart {
return JSON.parse(JSON.stringify(cart));
}
}
| Anti-Pattern | Why It Hurts | Fix |
|---|---|---|
| Real-time pricing for every cart load | 30+ DB calls per cart view. Load spike on sales. | Snapshot price at add-time. Periodically re-verify. |
| Blocking inventory check | Cart page stuck loading if inventory slow. | Async inventory check. Show optimistic availability. |
| No cart expiry cleanup | Millions of abandoned cart rows. Query slowdown. | TTL index. Batch delete expired carts daily. |
| Coupon code case-sensitivity | Users confused when "SAVE20" ≠ "save20". | Normalize to uppercase on input. Store and compare uppercase. |
| Cart merge destroys guest items | User logs in, guest items disappear. Lost revenue. | Merge strategy: union both sets, newest quantity wins. |
productRepository.findByIds(ids) instead of N individual queries.DECR on Redis for flash sales. Row-level lock for normal flow.