Orders Guide

    This guide is an in-depth walkthrough of using orders and products in Stripe. You can also get a quick intro or check out the Orders API reference. If you need help after reading this, search our documentation or check out answers to common questions. You can even chat live with other developers in #stripe on freenode.

    Managing products and orders

    If you sell products—whether physical or digital—you use Stripe’s Orders API to keep track of inventory, calculate tax and shipping, apply discounts, store tracking information, and handle returns.

    Stripe lets you define products with images and attributes like color and size and SKUs representing specific items to purchase (like a red shirt in size M) along with price and inventory information. You use SKUs to construct orders on behalf of your customers.

    Understanding the order lifecycle

    When examining the response of any API call or webhook notification, use the Order object’s status attribute to verify the current status of the order and respond accordingly.

    • When an order is first requested, the resulting order object has an initial status of created.
    • When an order is paid, status changes to paid. You should now fulfill the order.
    • After fulfilling the order, update the Order object to change status to fulfilled. Only orders in the paid state can be marked as fulfilled.
    • If the customer cancels the order (through your site) before it’s been fulfilled, update status to canceled which will refund the payment.
    • If the order has been fulfilled and the customer returns the purchased items, update status to returned which will refund the payment.

    Put another way:

    • A created order can become paid or canceled.
    • A paid order can become fulfilled or canceled. If the order becomes canceled, Stripe will automatically refund the payment.
    • A fulfilled order is normally the final state, but can be returned in which case the payment is refunded.

    You can update the order status using the API. The API optionally lets you specify additional information when you update the order. For example, you can provide a carrier and tracking number when you fulfill the order:

    curl https://api.stripe.com/v1/orders/or_1AZDJs2eZvKYlo2Cwak51qej \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
       -d status=fulfilled \
       -d shipping[carrier]=USPS \
       -d shipping[tracking_number]=TRACK123
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    order = Stripe::Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej")
    
    order.status = 'fulfilled'
    order.shipping.tracking_number = 'USPS'
    order.shipping.carrier = 'TRACK123'
    
    order.save
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    order = stripe.Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej")
    
    order.status = 'fulfilled'
    order.shipping.carrier = 'USPS'
    order.shipping.tracking_number = 'TRACK123'
    
    order.save()
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    
    $order = StripeOrder::retrieve("or_1AZDJs2eZvKYlo2Cwak51qej");
    
    $order->status = "fulfilled";
    $order->shipping->tracking_number = "TRACK123";
    $order->shipping->carrier = "USPS";
    
    $order->save();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";
    
    Order order = Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej", requestOptions);
    
    Map<String, Object> updateParams = new HashMap<String, Object>();
    updateParams.put("status", "fulfilled");
    
    Map<String, Object> shippingParams = new HashMap<String, Object>();
    updateParams.put("carrier", "USPS");
    updateParams.put("tracking_number", "TRACK123");
    
    updateParams.put("shipping", shippingParams);
    
    order.update(updateParams);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    stripe.orders.update("or_1AZDJs2eZvKYlo2Cwak51qej", {
      status: "fulfilled",
      shipping: {
        carrier: 'USPS',
        tracking_number: 'TRACK123'
      }
    }, function(err, order) {
      // asynchronously called
    });
    

    Handling returns

    Stripe helps you seamlessly handle returns from your customers when they decide to return all or part of an order. We take care of calculating how much tax to return and (partially) refunding the customer’s payment accordingly. For example, suppose your customer has purchased two instances of the same SKU but decided to return one. You can express this cleanly through the Orders API:

    curl https://api.stripe.com/v1/orders/or_1AZDJs2eZvKYlo2Cwak51qej/returns \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
       -d items[][type]=sku \
       -d items[][parent]=sku_Av3QkZHxahG4M4 \
       -d items[][quantity]=1
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    order = Stripe::Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej")
    order.return_order(
      :items => [
        {
          :type => 'sku',
          :parent => 'sku_Av3QkZHxahG4M4',
          :quantity => 1
        }
      ],
    )
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    order = stripe.Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej")
    order.return_order(
      items=[
        {
          "type": 'sku',
          "parent": 'sku_Av3QkZHxahG4M4',
          "quantity": 1
        }
      ]
    )
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    
    $order = StripeOrder::retrieve("or_1AZDJs2eZvKYlo2Cwak51qej");
    $order->returnOrder(array(
      "items" => array(
        array(
          "type" => "sku",
          "parent" => "sku_Av3QkZHxahG4M4"
          "quantity" => 1,
        )
      ),
    ));
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";
    
    List<Object> itemsParams = new LinkedList<Object>();
    
    Map<String, Object> returnItem = new HashMap<String, Object>();
    returnItem.put("type", "sku");
    returnItem.put("parent", "sku_Av3QkZHxahG4M4");
    returnItem.put("quantity", 1);
    itemsParams.add(returnItem);
    
    Map<String, Object> orderParams = new HashMap<String, Object>();
    orderParams.put("items", itemsParams);
    
    order.returnOrder(orderParams);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    stripe.orders.returnOrder("or_1AZDJs2eZvKYlo2Cwak51qej", {
      items: [
        {
          type: 'sku',
          parent: 'sku_Av3QkZHxahG4M4',
          quantity: 1
        }
      ]
    }, function(err, order) {
      // asynchronously called
    });
    

    The Orders API responds with the items you returned and automatically calculates the appropriate amount of taxes to refund using your tax provider. If a refund was created, it will be linked under refund:

    {
      "id": "orret_18Q3IaDAu10Yox5RqlL8JnnS",
      "object": "order_return",
      "amount": 7524,
      "refund": "re_1AZ7l22eZvKYlo2CFri2O1Gy",
      "items": [
        {
          "type": "sku",
          "amount": 6999,
          "description": "Slim Jeans",
          "parent": "sku_Av3QkZHxahG4M4",
          ...
        },
        {
          "type": "tax",
          "amount": 525,
          "description": "CA STATE TAX (P0000000)",
          "parent": null,
          ...
        },
      ],
      ..
    }

    If the customer later returns the remainder on the order, you can return all the remaining line items and refund the remainder of the charge:

    curl https://api.stripe.com/v1/orders/or_1AZDJs2eZvKYlo2Cwak51qej/returns \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
       -X POST
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    order = Stripe::Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej")
    order.return_order()
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    order = stripe.Order.retrieve("or_1AZDJs2eZvKYlo2Cwak51qej")
    order.return_order()
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    
    $order = StripeOrder::retrieve("or_1AZDJs2eZvKYlo2Cwak51qej");
    $order->returnOrder();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";
    
    Map<String, Object> orderParams = new HashMap<String, Object>();
    order.returnOrder(orderParams);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    stripe.orders.returnOrder("or_1AZDJs2eZvKYlo2Cwak51qej", function(err, order) {
      // asynchronously called
    });
    

    All individual returns will be aggregated under returns on the order object. Note that a partially returned order will remain in its old state until it has been fully returned. For example, a fulfilled order with some return items remains fulfilled until all items have been returned. Once all items are returned, the order automatically transitions to returned.

    Order returns are quite flexible. You can return one or more SKUs or an amount of a particular SKU to offer a partial refund. While you could generate these refunds by partially refunding the underlying charge object, we highly recommend that you only interact with the order object. As you have seen above, interacting with the order helps you return the appropriate amount of taxes to the customer. It also lets you maintain the right order status for your internal bookkeeping. Stripe cannot help you calculate with either of these if you work directly with the charge object, so we recommend against refunding it directly.

    Using webhooks

    Receive real-time updates on the state of your products, SKUs, and orders using webhooks. The applicable Orders API events are:

    • product.created
    • product.updated
    • sku.created
    • sku.updated: e.g., inventory is decreased after a successful order
    • order.created
    • order.updated: the details of an order (e.g., shipping information) are updated
    • order.payment_succeeded: payment is made on an order
    • order.payment_failed: the payment attempt did not work

    Your webhook endpoint can take specific actions when these events occur. You can expect to receive an order.created event whenever someone has begun the checkout flow for one of your products. An order.payment_succeeded event represents a purchase. In other words, once you have received an order.payment_succeeded webhook, it’s time to fulfill whatever product has been purchased.

    Automatically importing products

    If you already maintain a product catalog, perhaps through an OMS or an existing integration, Stripe can import your products to help you get started with the Orders API. Google and LinkShare product feeds are fully compatible with Stripe’s product objects and either type of feed can be imported daily or weekly into Stripe to maintain up to date inventory information.

    To get started, visit the Products page in the Dashboard. In the top right corner, click Add feed and input the location of your feed. We support downloading feeds over HTTP, HTTPS, FTP, and SFTP, and the URL you specify should include the scheme (like https://example.com/my_feed.tsv). After specifying your feed location and credentials (if any), click Test feed to make sure the information is correct. Stripe will attempt to download and parse the feed to make sure your feed location, credentials, and format are valid. If they are, choose how often you would like to import the feed and click Save feed to start importing.

    You can check on previous imports by clicking Manage feed on the products page. Here you can see how many products Stripe has imported and inspect any errors that occurred while parsing. If the contents of your feed changed and you want to import the new feed immediately, you can click Import now to import right away.

    Best practices

    This section contains some recommendations to help you define and manage products effectively.

    To make the most of products, SKUs, and orders, it’ll help to understand where the various objects and attributes come into play.

    Defining products and SKUs

    The first decision to make in organizing a catalog is to identify your products and SKUs. This can be tricky to grasp, but is important to get right.

    Working backwards, a SKU is an individual product: the specific item the customer is actually buying. Anything you’d put into a shopping cart—real or online—is represented by a SKU.

    Each product is an umbrella term for a family of SKUs. These should still be identifying, not too generic. “Book”, “coat”, “software”, and “coffee” are all too broad as products. Instead, you’d have:

    • A specific book title as a product, with formats—hardcover, softcover—and translations differentiated as SKUs.
    • An identifiable label and style of coat as a product, with sizes and colors defined as SKUs.
    • A named application as a product, with operating system versions and releases as SKUs.
    • A line of coffee—such as Jamaican Blue Mountain— as a product, with sizes, roasts, and ground establishing SKUs.

    As a rule of thumb, customers would be unlikely to purchase two different SKUs from the same product family at the same time (unless they really wanted, for example, the same coat in two different colors).

    Detailing products details for customers

    Your goal is to provide as much useful, appealing information as you can about your products and SKUs. We recommend you do the following to take full advantage of the Orders API, especially if you plan on selling through Relay apps:

    • Use clear, descriptive names
    • Provide at least one product image
    • Use a dedicated web page, linked through the url field, to provide more detailed and alternative information (e.g., a video demonstrating the product)

    Next steps

    Congrats! You've gone through how to use the orders API's products, SKUs and orders features. Some things you might want to see next: