> ## Documentation Index
> Fetch the complete documentation index at: https://docs.craveup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout Flow

> Collect customer details, set order time, and process payment.

This guide walks through the checkout process — from collecting customer information to creating a payment intent and completing the order.

## Checkout steps

<Steps>
  <Step title="Set fulfillment method">
    Choose takeout, delivery, table-side, or room service.
  </Step>

  <Step title="Set order time">
    ASAP or scheduled for a specific date and time.
  </Step>

  <Step title="Collect customer details">
    Name, email, and optionally phone number.
  </Step>

  <Step title="Set gratuity (optional)">
    Apply a tip percentage or custom amount.
  </Step>

  <Step title="Create payment intent">
    Get a Stripe client secret for secure payment collection.
  </Step>

  <Step title="Confirm payment">
    Use Stripe.js to complete the charge on the client.
  </Step>
</Steps>

## 1. Set fulfillment method

Update the cart with the customer's preferred fulfillment method.

<CodeGroup>
  ```ts SDK theme={null}
  await storefront.cart.update('loc_123', cartId, {
    fulfillmentMethod: 'takeout', // 'takeout' | 'delivery' | 'table_side' | 'room_service'
  });
  ```

  ```bash REST theme={null}
  curl -X PATCH "https://api.craveup.com/api/v1/locations/loc_123/carts/{cartId}" \
    -H "X-API-Key: $CRAVEUP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"fulfillmentMethod": "takeout"}'
  ```
</CodeGroup>

For delivery, table-side, or room service, see the [Fulfillment Methods guide](/guides/fulfillment-methods).

## 2. Set order time

<CodeGroup>
  ```ts SDK theme={null}
  // ASAP order
  await storefront.cart.updateOrderTime('loc_123', cartId, {
    pickupType: 'ASAP',
  });

  // Scheduled order
  await storefront.cart.updateOrderTime('loc_123', cartId, {
    pickupType: 'LATER',
    orderDate: '2025-03-15',
    orderTime: '18:30',
  });
  ```

  ```bash REST theme={null}
  curl -X PUT "https://api.craveup.com/api/v1/locations/loc_123/carts/{cartId}/update-order-time" \
    -H "X-API-Key: $CRAVEUP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"pickupType": "LATER", "orderDate": "2025-03-15", "orderTime": "18:30"}'
  ```
</CodeGroup>

<Tip>
  Call `storefront.locations.getOrderTimes(locationId)` to get the available time slots before showing the picker.
</Tip>

## 3. Collect customer details

Validate and save the customer's name and contact information.

<CodeGroup>
  ```ts SDK theme={null}
  await storefront.cart.validateAndUpdateCustomer('loc_123', cartId, {
    customerName: 'Alex Johnson',
    emailAddress: 'alex@example.com',
    phoneNumber: '+1234567890',
  });
  ```

  ```bash REST theme={null}
  curl -X PUT "https://api.craveup.com/api/v1/locations/loc_123/cart/{cartId}/validate-and-update" \
    -H "X-API-Key: $CRAVEUP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "customerName": "Alex Johnson",
      "emailAddress": "alex@example.com",
      "phoneNumber": "+1234567890"
    }'
  ```
</CodeGroup>

## 4. Set gratuity (optional)

If the location has tipping enabled, set a tip amount or percentage.

```ts theme={null}
// First, check if tipping is enabled
const tipConfig = await storefront.locations.getGratuity('loc_123');

if (tipConfig.enabled) {
  // Apply a percentage-based tip
  await storefront.cart.updateGratuity('loc_123', cartId, {
    percentage: '18',
  });

  // Or a fixed amount
  await storefront.cart.updateGratuity('loc_123', cartId, {
    amount: '5.00',
  });
}
```

The `WaiterTipConfigResponse` tells you:

| Field                  | Description                                                 |
| ---------------------- | ----------------------------------------------------------- |
| `enabled`              | Whether tipping is turned on for this location              |
| `tipPercentage`        | Array of suggested percentages (e.g., `["15", "18", "20"]`) |
| `defaultTipPercentage` | The pre-selected percentage                                 |
| `shouldAllowCustomTip` | Whether to show a custom amount input                       |

## 5. Create a payment intent

Create a Stripe PaymentIntent to securely collect payment on the client.

<CodeGroup>
  ```ts SDK theme={null}
  const payment = await storefront.payments.createIntent('loc_123', cartId);

  // payment.clientSecret — pass to Stripe.js
  // payment.stripeAccountId — the connected Stripe account
  ```

  ```bash REST theme={null}
  curl -X GET "https://api.craveup.com/api/v1/stripe/payment-intent?locationId=loc_123&cartId=cart_456" \
    -H "X-API-Key: $CRAVEUP_API_KEY"
  ```
</CodeGroup>

## 6. Confirm payment with Stripe.js

Use the `clientSecret` to complete the payment on the frontend.

```tsx filename="components/PaymentForm.tsx" theme={null}
import { useStripe, useElements, PaymentElement } from '@stripe/react-stripe-js';

function PaymentForm({ clientSecret }: { clientSecret: string }) {
  const stripe = useStripe();
  const elements = useElements();

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!stripe || !elements) return;

    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/order/confirmation`,
      },
    });

    if (error) {
      console.error('Payment failed:', error.message);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button type="submit" className="mt-4 w-full py-3 bg-teal-600 text-white rounded-lg">
        Pay now
      </button>
    </form>
  );
}
```

<Warning>
  Always use Stripe.js or Stripe Elements for payment collection. Never handle raw card numbers in your own code.
</Warning>

## Track analytics events

Fire analytics events at key checkout milestones.

```ts theme={null}
// When the user views the checkout page
await storefront.analyticsEvents.track('loc_123', {
  cartId,
  eventType: 'CHECKOUT_VIEW',
});

// After order is placed
await storefront.analyticsEvents.track('loc_123', {
  cartId,
  eventType: 'ORDER_PLACED',
  metadata: { total: cart.orderTotalWithServiceFee },
});
```

## Complete checkout example

```ts filename="lib/checkout.ts" theme={null}
import { storefront } from '@/lib/storefront';

export async function checkout(locationId: string, cartId: string, customer: {
  name: string;
  email: string;
  phone?: string;
}) {
  // 1. Set customer details
  await storefront.cart.validateAndUpdateCustomer(locationId, cartId, {
    customerName: customer.name,
    emailAddress: customer.email,
    phoneNumber: customer.phone,
  });

  // 2. Set order time
  await storefront.cart.updateOrderTime(locationId, cartId, {
    pickupType: 'ASAP',
  });

  // 3. Create payment intent
  const { clientSecret, stripeAccountId } = await storefront.payments.createIntent(
    locationId,
    cartId
  );

  return { clientSecret, stripeAccountId };
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="Fulfillment Methods" icon="truck" href="/guides/fulfillment-methods">
    Configure delivery, table-side, and room service.
  </Card>

  <Card title="Accept Payments" icon="money-bill" href="/guides/accept-payments">
    In-depth Stripe setup and testing guide.
  </Card>

  <Card title="Order Tracking" icon="location-dot" href="/guides/order-tracking">
    Poll order status after checkout.
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/guides/error-codes">
    Handle checkout errors gracefully.
  </Card>
</CardGroup>
