Update: The code in this article is now available as a ready-to-use example in the Order Example in the Commerce Examples project.
As a flexible e-commerce framework, Drupal Commerce allows you to customize the checkout process on your site to suit your desired user experience. This is usually determined by what you're selling. A checkout process for physical items will likely include shipping and tax calculations, which often require information to be supplied by the customer.
What's it gonna take?
In Drupal Commerce, the Checkout module is not dependent on the Cart module. This means you don't have to have a shopping cart to expose a checkout form to your customers. You'll just be responsible for creating the order that will be completed via the checkout form some other way.
Additionally, the Product module lets you create products without mandating that they be displayed anywhere to your users. However, you still must define the product and place it in the order you've created to make sure the customer pays the proper amount and that that payment can be tied to the particular "thing" they're payment for. A donation site might use a separate product per fundraising campaign. An event registration site might have a single product representing a ticket or multiple products for different levels or types of admissions.
Once you've created your product, you should know its product ID so you can add it to the order before redirecting the customer to the checkout form or embedding the checkout form somewhere else. There's a bit of a "chicken or the egg" problem here in that you need an order ID to properly associate a line item to the order, and you need a line item ID to properly reference the line item from an order. Because of this bi-directional reference, you have to end up saving one of these entities twice. You'll see in my example below that I chose the order.
Code example: creating an order using the Drupal Commerce API
This code example demonstrates creating a new order for the current user, adding a product to it, and then redirecting to the checkout form for the new order. You might use this as part of an existing form's submission process (e.g. pay to publish a node) or perhaps for standalone "Buy it now" buttons on products.
$product_id = 1;
// Create the new order in checkout; you might also check first to
// see if your user already has an order to use instead of a new one.
$order = commerce_order_new($user->uid, 'checkout_checkout'); // Save the order to get its ID.
// Load whatever product represents the item the customer will be
// paying for and create a line item for it.
$product = commerce_product_load($product_id);
$line_item = commerce_product_line_item_new($product, 1, $order->order_id); // Save the line item to get its ID.
// Add the line item to the order using fago's rockin' wrapper.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_wrapper->commerce_line_items = $line_item; // Save the order again to update its line item reference field.
commerce_order_save($order); // Redirect to the order's checkout form. Obviously, if this were a
// form submit handler, you'd just set $form_state['redirect'].
drupal_goto('checkout/' . $order->order_id);
Notice that we're redirecting to the URL
/checkout/[order-id]. The URL
/checkout is defined by the Cart module and reroutes to a checkout URL for the current user's active shopping cart. Without the Cart module enabled, we must send the customer directly to the checkout URL for their new order.
This process can be used in any number of places, and unless someone beats me to it (be my guest ; ), I'll see what I can do to turn it into a simple product reference field display formatter / product Views field handler.