Skip to main content

Checkout API

The Checkout API enables you to convert shopping carts into orders, collect customer information, and process payments. It provides a complete checkout flow for headless and traditional WooCommerce implementations.

Overview

The Checkout API allows you to:
  • Retrieve checkout session data
  • Update customer billing and shipping information
  • Select payment methods
  • Add order notes and custom fields
  • Process payment and create orders
  • Handle payment gateway integration
All checkout endpoints require either a Nonce Token or a Cart Token for security. Checkout operations will fail without proper authentication.

Base Endpoint

All checkout endpoints use the base path:
/wp-json/wc/store/v1/checkout

Authentication

Nonce Tokens

Checkout requires valid nonce tokens for all requests:
curl --header "Nonce: 12345" \
  --request GET \
  https://example-store.com/wp-json/wc/store/v1/checkout
Generate nonces using:
$nonce = wp_create_nonce( 'wc_store_api' );
The nonce is updated with each response. Always use the latest nonce from the response headers for your next request.

Cart Tokens

For headless implementations, use Cart Tokens:
curl --header "Cart-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  --request POST \
  https://example-store.com/wp-json/wc/store/v1/checkout
Cart Tokens eliminate the need for nonces in headless environments.

Get Checkout Data

Retrieve the current checkout session, including a draft order created from the cart.
GET /wp-json/wc/store/v1/checkout

Request

curl --header "Nonce: 12345" \
  --request GET \
  "https://example-store.com/wp-json/wc/store/v1/checkout"

Response

{
  "order_id": 146,
  "status": "checkout-draft",
  "order_key": "wc_order_VPffqyvgWVqWL",
  "order_number": "146",
  "customer_note": "",
  "customer_id": 1,
  "billing_address": {
    "first_name": "Peter",
    "last_name": "Venkman",
    "company": "",
    "address_1": "550 Central Park West",
    "address_2": "Corner Penthouse Spook Central",
    "city": "New York",
    "state": "NY",
    "postcode": "10023",
    "country": "US",
    "email": "[email protected]",
    "phone": "555-2368"
  },
  "shipping_address": {
    "first_name": "Peter",
    "last_name": "Venkman",
    "company": "",
    "address_1": "550 Central Park West",
    "address_2": "Corner Penthouse Spook Central",
    "city": "New York",
    "state": "NY",
    "postcode": "10023",
    "country": "US"
  },
  "payment_method": "",
  "payment_result": {
    "payment_status": "",
    "payment_details": [],
    "redirect_url": ""
  },
  "additional_fields": {}
}

Response Fields

order_id
integer
Draft order ID created from the cart
status
string
Order status (typically checkout-draft until payment is processed)
order_key
string
Unique order key for verification
billing_address
object
Customer billing address with email and phone
shipping_address
object
Customer shipping address
payment_method
string
Selected payment method ID (empty until selected)
payment_result
object
Payment processing results (populated after POST request)
additional_fields
object
Custom fields added by plugins or themes

Update Checkout Data

Update checkout information without processing payment. This is useful for persisting checkout fields as the customer fills them out.
PUT /wp-json/wc/store/v1/checkout

Parameters

payment_method
string
The ID of the selected payment method (e.g., “bacs”, “cheque”, “stripe”)
order_notes
string
Customer notes for the order
additional_fields
object
Custom fields with namespace/key structure (e.g., plugin-namespace/field-name)
__experimental_calc_totals
boolean
Set to true to recalculate cart totals (default: false)

Request

curl --header "Nonce: 12345" \
  --request PUT \
  --header "Content-Type: application/json" \
  --data '{
    "payment_method": "bacs",
    "order_notes": "Please leave package on back porch",
    "additional_fields": {
      "plugin-namespace/delivery-instructions": "Call on arrival"
    }
  }' \
  "https://example-store.com/wp-json/wc/store/v1/checkout?__experimental_calc_totals=true"

Response

{
  "order_id": 1486,
  "status": "checkout-draft",
  "order_key": "wc_order_KLpMaJ054PVlb",
  "order_number": "1486",
  "customer_note": "Please leave package on back porch",
  "customer_id": 1,
  "billing_address": {
    "first_name": "Peter",
    "last_name": "Venkman",
    "company": "",
    "address_1": "550 Central Park West",
    "address_2": "Corner Penthouse Spook Central",
    "city": "New York",
    "state": "NY",
    "postcode": "10023",
    "country": "US",
    "email": "[email protected]",
    "phone": "555-2368"
  },
  "shipping_address": {
    "first_name": "Peter",
    "last_name": "Venkman",
    "company": "",
    "address_1": "550 Central Park West",
    "address_2": "Corner Penthouse Spook Central",
    "city": "New York",
    "state": "NY",
    "postcode": "10023",
    "country": "US"
  },
  "payment_method": "bacs",
  "payment_result": null,
  "additional_fields": {
    "plugin-namespace/delivery-instructions": "Call on arrival"
  },
  "__experimentalCart": {
    "items": [ ... ],
    "totals": { ... }
  },
  "extensions": {}
}
The __experimentalCart field is included when __experimental_calc_totals=true, allowing you to update the cart UI based on checkout changes without an additional request.

Process Order and Payment

Submit final checkout data and process payment to create an order.
POST /wp-json/wc/store/v1/checkout

Parameters

billing_address
object
required
Complete billing address object
shipping_address
object
required
Complete shipping address object (same fields as billing except no email)
payment_method
string
required
Payment method ID (e.g., “bacs”, “cheque”, “stripe”, “paypal”)
payment_data
array
Payment-specific data required by the payment gateway (structure varies by gateway)
customer_note
string
Order notes from the customer
customer_password
string
Password for new account creation (if enabled)
create_account
boolean
Whether to create a new customer account
extensions
object
Extension-specific data

Request

curl --header "Nonce: 12345" \
  --request POST \
  --header "Content-Type: application/json" \
  --data '{
    "billing_address": {
      "first_name": "Peter",
      "last_name": "Venkman",
      "company": "",
      "address_1": "550 Central Park West",
      "address_2": "Corner Penthouse Spook Central",
      "city": "New York",
      "state": "NY",
      "postcode": "10023",
      "country": "US",
      "email": "[email protected]",
      "phone": "555-2368"
    },
    "shipping_address": {
      "first_name": "Peter",
      "last_name": "Venkman",
      "company": "",
      "address_1": "550 Central Park West",
      "address_2": "Corner Penthouse Spook Central",
      "city": "New York",
      "state": "NY",
      "postcode": "10023",
      "country": "US"
    },
    "customer_note": "Test notes on order.",
    "payment_method": "cheque",
    "payment_data": []
  }' \
  "https://example-store.com/wp-json/wc/store/v1/checkout"

Response

{
  "order_id": 146,
  "status": "on-hold",
  "order_key": "wc_order_VPffqyvgWVqWL",
  "order_number": "146",
  "customer_note": "Test notes on order.",
  "customer_id": 1,
  "billing_address": {
    "first_name": "Peter",
    "last_name": "Venkman",
    "company": "",
    "address_1": "550 Central Park West",
    "address_2": "Corner Penthouse Spook Central",
    "city": "New York",
    "state": "NY",
    "postcode": "10023",
    "country": "US",
    "email": "[email protected]",
    "phone": "555-2368"
  },
  "shipping_address": {
    "first_name": "Peter",
    "last_name": "Venkman",
    "company": "",
    "address_1": "550 Central Park West",
    "address_2": "Corner Penthouse Spook Central",
    "city": "New York",
    "state": "NY",
    "postcode": "10023",
    "country": "US"
  },
  "payment_method": "cheque",
  "payment_result": {
    "payment_status": "success",
    "payment_details": [],
    "redirect_url": "https://example-store.com/checkout/order-received/146/?key=wc_order_VPffqyvgWVqWL"
  }
}

Payment Result Object

payment_status
string
Payment outcome: “success”, “failure”, “pending”, or “error”
payment_details
array
Additional payment information from the gateway
redirect_url
string
URL to redirect customer after successful checkout (typically order confirmation page)

Checkout Flow

1

Initialize Checkout

Make a GET request to retrieve the checkout session and pre-fill customer data
GET /wc/store/v1/checkout
2

Collect Information

Display checkout form to collect:
  • Billing address
  • Shipping address
  • Payment method selection
  • Order notes
  • Custom fields
3

Validate and Update

Optionally use PUT requests to persist data and validate fields as the customer fills out the form
PUT /wc/store/v1/checkout?__experimental_calc_totals=true
4

Process Payment

Submit final checkout with POST request
POST /wc/store/v1/checkout
5

Handle Response

  • On success: Redirect to payment_result.redirect_url
  • On failure: Display error message and allow retry

Payment Gateway Integration

Different payment gateways require different payment_data structures. Always consult the payment gateway’s documentation for specific requirements.

Example: Stripe Payment Gateway

The WooCommerce Stripe Payment Gateway expects:
{
  "payment_method": "stripe",
  "payment_data": [
    {
      "key": "stripe_source",
      "value": "src_xxxxxxxxxxxxx"
    },
    {
      "key": "billing_email",
      "value": "[email protected]"
    },
    {
      "key": "billing_first_name",
      "value": "Jane"
    },
    {
      "key": "billing_last_name",
      "value": "Doe"
    },
    {
      "key": "paymentMethod",
      "value": "stripe"
    },
    {
      "key": "paymentRequestType",
      "value": "cc"
    },
    {
      "key": "wc-stripe-new-payment-method",
      "value": true
    }
  ]
}
The stripe_source is generated using the Stripe JavaScript SDK:
// Create Stripe source
const { source, error } = await stripe.createSource({
  type: 'card',
  card: cardElement,
  owner: {
    name: billingData.firstName + ' ' + billingData.lastName,
    email: billingData.email
  }
});

if (error) {
  // Handle error
} else {
  // Add source.id to payment_data
  paymentData.push({
    key: 'stripe_source',
    value: source.id
  });
}

Example: PayPal

PayPal integration typically involves:
  1. Generate PayPal order ID client-side
  2. Include PayPal order ID in payment_data
  3. Gateway validates and captures payment
{
  "payment_method": "paypal",
  "payment_data": [
    {
      "key": "paypal_order_id",
      "value": "1AB23456CD789012E"
    }
  ]
}

Standard Payment Methods

WooCommerce includes built-in payment methods that don’t require additional payment_data:
  • bacs - Direct bank transfer
  • cheque - Check payments
  • cod - Cash on delivery
{
  "payment_method": "bacs",
  "payment_data": []
}

Additional Fields

Plugins can add custom checkout fields using the additional_fields parameter:
{
  "additional_fields": {
    "plugin-namespace/gift-message": "Happy Birthday!",
    "plugin-namespace/gift-wrap": true,
    "plugin-namespace/delivery-date": "2024-12-25"
  }
}
Fields must use namespaced keys (plugin-namespace/field-name) to avoid conflicts.

Registering Custom Fields

Plugin developers can register custom checkout fields:
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;

add_action('woocommerce_blocks_loaded', function() {
  $checkout_fields = Package::container()->get(CheckoutFields::class);
  
  $checkout_fields->register_checkout_field([
    'id' => 'plugin-namespace/gift-message',
    'label' => 'Gift message',
    'location' => 'order',
    'type' => 'text',
    'required' => false,
  ]);
});

Error Handling

Common Error Codes

CodeDescription
woocommerce_rest_checkout_invalid_addressBilling or shipping address validation failed
woocommerce_rest_checkout_payment_errorPayment processing failed
woocommerce_rest_checkout_missing_payment_methodNo payment method selected
woocommerce_rest_invalid_nonceNonce token is invalid or expired
woocommerce_rest_checkout_cart_emptyCannot checkout with empty cart
woocommerce_rest_checkout_invalid_cartCart contains invalid items

Handling Validation Errors

Validation errors include field-specific details in data.params:
{
  "code": "woocommerce_rest_checkout_invalid_address",
  "message": "Invalid billing address provided.",
  "data": {
    "status": 400,
    "params": {
      "billing_address.email": "Invalid email address",
      "billing_address.postcode": "Postcode is required"
    }
  }
}
Display these errors next to the relevant form fields:
try {
  const order = await processCheckout(formData);
  window.location.href = order.payment_result.redirect_url;
} catch (error) {
  const errorData = await error.response.json();
  
  if (errorData.data?.params) {
    // Display field-specific errors
    Object.keys(errorData.data.params).forEach(field => {
      const errorMessage = errorData.data.params[field];
      displayFieldError(field, errorMessage);
    });
  } else {
    // Display general error
    displayError(errorData.message);
  }
}

Order Statuses

After successful checkout, orders are assigned a status based on the payment method:
StatusDescription
checkout-draftTemporary status during checkout (not a real order yet)
pendingOrder received, awaiting payment
processingPayment received, order is being prepared
on-holdAwaiting confirmation (e.g., bank transfer)
completedOrder fulfilled and complete
cancelledOrder cancelled by customer or admin
refundedOrder refunded
failedPayment failed or declined
Most payment gateways set orders to processing or on-hold after successful checkout.

Retrieving Order Details

After checkout, customers can retrieve their order using the Order API:
GET /wp-json/wc/store/v1/order/:id
This requires the order ID and key for authorization:
curl --header "Nonce: 12345" \
  "https://example-store.com/wp-json/wc/store/v1/order/146?key=wc_order_VPffqyvgWVqWL"
Customers can only access their own orders. The order key provides authorization without requiring authentication.

Best Practices

Validate Before Submitting

Use PUT requests during checkout to validate data before final submission:
// Validate address when customer enters it
async function validateAddress(address) {
  await fetch('/wp-json/wc/store/v1/checkout', {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'Nonce': nonce
    },
    body: JSON.stringify({ billing_address: address })
  });
}

Handle Payment Redirects

Some payment gateways redirect customers to external sites. Always use payment_result.redirect_url:
const result = await processCheckout(formData);

if (result.payment_result.payment_status === 'success') {
  window.location.href = result.payment_result.redirect_url;
}

Show Loading States

Checkout can take several seconds. Always show loading indicators:
async function handleCheckout(formData) {
  setLoading(true);
  try {
    const order = await processCheckout(formData);
    window.location.href = order.payment_result.redirect_url;
  } catch (error) {
    displayError(error.message);
  } finally {
    setLoading(false);
  }
}

Retry Failed Payments

Allow customers to retry after payment failure:
if (result.payment_result.payment_status === 'failure') {
  displayError('Payment failed. Please check your payment details and try again.');
  // Keep checkout form visible for retry
} else {
  window.location.href = result.payment_result.redirect_url;
}

Security Considerations

Never store payment card details on your server. Use payment gateway SDKs to tokenize payment information client-side.

PCI Compliance

  • Use payment gateway JavaScript SDKs to collect card details
  • Never send raw card numbers to your server
  • Use tokens or source IDs in payment_data

Nonce Expiration

Nonces expire after 12-24 hours. If checkout fails with invalid nonce error:
  1. Refresh the nonce by making a GET request
  2. Retry the checkout with the new nonce

HTTPS Required

Always use HTTPS for checkout operations to protect customer data in transit.

Next Steps

Cart API

Learn about cart management before checkout

Store API Overview

Back to Store API introduction

Payment Gateways

Browse available payment gateway extensions

Extending Checkout

Learn how to extend the checkout experience

Build docs developers (and LLMs) love