Overview

This guide explains the order processing flow from a storefront perspective. Important: Storefronts don’t directly create or manage orders - this is handled by Crave’s backend via webhooks after successful payments.

Order Flow for Storefronts

From a storefront developer’s perspective, the order flow is:
  1. Customer places order → Payment succeeds
  2. Crave receives webhook → Order is created automatically
  3. Merchant receives notification → Order appears in Crave Business Manager
  4. Customer receives receipt → Via Stripe (handled automatically)

What Storefronts Handle

1. Pre-Order (Your Responsibility)

  • Menu Display - Show available products
  • Cart Management - Add/remove items, calculate totals
  • Checkout Flow - Collect customer info, process payment
  • Payment Success - Show confirmation to customer

2. Post-Payment (Handled by Crave)

  • Order Creation - Crave creates order from successful payment
  • Merchant Notification - Order appears in Crave Business Manager
  • Order Fulfillment - Merchant handles preparation and delivery
  • Customer Updates - Merchant handles status updates

Cart to Order Flow

Here’s what happens when a customer completes checkout:
// 1. Customer completes Stripe payment
const paymentResult = await stripe.confirmPayment({
  elements,
  confirmParams: {
    return_url: `${window.location.origin}/order-confirmation`
  }
});

// 2. Payment succeeds
if (paymentResult.paymentIntent.status === 'succeeded') {
  // 3. Show confirmation to customer
  showOrderConfirmation(paymentResult.paymentIntent);
  
  // 4. Crave backend receives Stripe webhook
  // 5. Order is created automatically (you don't handle this)
  // 6. Merchant receives order notification
}

Order Confirmation Page

Create a confirmation page to show after successful payment:
// pages/order-confirmation.js
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

export default function OrderConfirmation() {
  const router = useRouter();
  const { payment_intent } = router.query;
  const [paymentDetails, setPaymentDetails] = useState(null);

  useEffect(() => {
    if (payment_intent) {
      // Get payment details from Stripe
      fetchPaymentDetails(payment_intent);
    }
  }, [payment_intent]);

  const fetchPaymentDetails = async (paymentIntentId) => {
    try {
      const response = await fetch(`/api/payment-details?payment_intent=${paymentIntentId}`);
      const data = await response.json();
      setPaymentDetails(data);
    } catch (error) {
      console.error('Failed to fetch payment details:', error);
    }
  };

  if (!paymentDetails) {
    return <div>Loading order confirmation...</div>;
  }

  return (
    <div className="order-confirmation">
      <h1>Order Confirmed!</h1>
      
      <div className="confirmation-details">
        <h2>Thank you for your order</h2>
        <p>Your payment has been processed successfully.</p>
        
        <div className="order-summary">
          <h3>Order Details</h3>
          <p><strong>Payment ID:</strong> {paymentDetails.id}</p>
          <p><strong>Amount:</strong> ${(paymentDetails.amount / 100).toFixed(2)}</p>
          <p><strong>Status:</strong> {paymentDetails.status}</p>
          
          {paymentDetails.receipt_url && (
            <a href={paymentDetails.receipt_url} target="_blank" rel="noopener noreferrer">
              View Receipt
            </a>
          )}
        </div>
        
        <div className="next-steps">
          <h3>What happens next?</h3>
          <ul>
            <li>Your order has been sent to the restaurant</li>
            <li>The restaurant will begin preparing your order</li>
            <li>You'll receive updates about your order status</li>
            <li>Estimated preparation time: 15-30 minutes</li>
          </ul>
        </div>
      </div>
    </div>
  );
}

API Route for Payment Details

// pages/api/payment-details.js
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export default async function handler(req, res) {
  if (req.method !== 'GET') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { payment_intent } = req.query;

  if (!payment_intent) {
    return res.status(400).json({ error: 'Payment intent ID required' });
  }

  try {
    const paymentIntent = await stripe.paymentIntents.retrieve(payment_intent);
    
    // Return only safe details for confirmation page
    res.json({
      id: paymentIntent.id,
      amount: paymentIntent.amount,
      currency: paymentIntent.currency,
      status: paymentIntent.status,
      receipt_url: paymentIntent.charges.data[0]?.receipt_url
    });
  } catch (error) {
    console.error('Payment details error:', error);
    res.status(500).json({ error: 'Failed to fetch payment details' });
  }
}

Customer Communication

What You Can Do

// Show immediate confirmation
function showOrderConfirmation(paymentIntent) {
  return (
    <div className="order-success">
      <h2>Payment Successful!</h2>
      <p>Your order has been placed and sent to the restaurant.</p>
      <p>Order ID: {paymentIntent.id}</p>
      <p>Amount: ${(paymentIntent.amount / 100).toFixed(2)}</p>
      
      <div className="next-steps">
        <h3>What's next?</h3>
        <ul>
          <li>✅ Payment processed</li>
          <li>🔄 Order sent to restaurant</li>
          <li>⏳ Restaurant will confirm your order</li>
          <li>👨‍🍳 Food preparation begins</li>
        </ul>
      </div>
    </div>
  );
}

What You Cannot Do

Don’t implement these (merchant-side features managed through Crave Business Manager):
  • Order status tracking
  • Order modifications
  • Delivery tracking
  • Customer accounts with order history
  • Real-time order updates

Error Handling

Handle payment-related errors that might occur:
function handleOrderError(error) {
  switch (error.type) {
    case 'payment_failed':
      return 'Payment failed. Please try again with a different payment method.';
    case 'payment_cancelled':
      return 'Payment was cancelled. Your order was not placed.';
    case 'network_error':
      return 'Network error. Please check your connection and try again.';
    default:
      return 'An error occurred. Please try again or contact support.';
  }
}

// In your checkout component
const handlePaymentError = (error) => {
  const errorMessage = handleOrderError(error);
  setError(errorMessage);
  
  // Log error for debugging
  console.error('Payment error:', error);
};

Order Information Storage

If you need to store order information locally:
// Store basic order info for customer reference
function storeOrderInfo(paymentIntent, customerInfo) {
  const orderInfo = {
    id: paymentIntent.id,
    amount: paymentIntent.amount,
    currency: paymentIntent.currency,
    status: paymentIntent.status,
    timestamp: new Date().toISOString(),
    customer: customerInfo,
    receipt_url: paymentIntent.charges.data[0]?.receipt_url
  };
  
  // Store in localStorage for customer reference
  localStorage.setItem(`order_${paymentIntent.id}`, JSON.stringify(orderInfo));
  
  return orderInfo;
}

// Retrieve order info
function getOrderInfo(paymentIntentId) {
  const stored = localStorage.getItem(`order_${paymentIntentId}`);
  return stored ? JSON.parse(stored) : null;
}

Best Practices

1. Clear Communication

  • Always explain that the order is being sent to the restaurant
  • Set proper expectations about timing
  • Provide clear next steps

2. Error Recovery

  • Handle payment failures gracefully
  • Provide clear error messages
  • Offer retry options

3. Confirmation Details

  • Show payment confirmation immediately
  • Include order reference (payment intent ID)
  • Provide receipt link when available

4. Don’t Over-Promise

  • Don’t promise real-time order tracking
  • Don’t claim to control restaurant operations
  • Focus on what you can deliver

Integration with Merchant Systems

The order flow continues on the merchant side:
[Storefront] → [Crave API] → [Stripe] → [Webhook] → [Merchant Dashboard]
  1. Storefront processes payment
  2. Crave API handles payment intent
  3. Stripe processes payment
  4. Webhook notifies Crave of success
  5. Crave Business Manager receives new order

Styling Example

.order-confirmation {
  max-width: 600px;
  margin: 2rem auto;
  padding: 2rem;
  text-align: center;
}

.order-success {
  background: #d4edda;
  border: 1px solid #c3e6cb;
  color: #155724;
  padding: 2rem;
  border-radius: 8px;
  margin-bottom: 2rem;
}

.confirmation-details {
  background: white;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 2rem;
  margin-top: 2rem;
}

.order-summary {
  background: #f8f9fa;
  padding: 1rem;
  border-radius: 4px;
  margin: 1rem 0;
}

.next-steps {
  text-align: left;
  margin-top: 2rem;
}

.next-steps ul {
  list-style: none;
  padding: 0;
}

.next-steps li {
  padding: 0.5rem 0;
  border-bottom: 1px solid #eee;
}

.next-steps li:last-child {
  border-bottom: none;
}

Summary

As a storefront developer, your responsibility ends when the payment succeeds. The order creation and management is handled by Crave’s backend and the merchant. Focus on:
  1. Smooth checkout experience
  2. Clear payment confirmation
  3. Proper error handling
  4. Setting correct expectations
The merchant handles everything after payment success through the Crave Business Manager.