4

I have a shopping cart with a checkout page. On the checkout page is a payment button which takes them to a stripe payment form.

I've read some books on building eCommerce shopping carts in which the order is created when the user clicks the payment button and the order id is passed to the payment gateway. In this scenario if the user abandons the payment form then they are left with an unpaid order linked to their account.

So my question is what is preferable. Do you create the order only after a successful payment charge is made or would you create the order before the payment is made?

I suppose the only benefit to creating an order before the payment is made is that the site becomes more flexible in the sense that offline orders could be made as well i.e. and invoiced order.

* UPDATE *

An example of how an order can be created after payment charge has been made.

 public function checkout(Request $request)
{
    $token = $request->input('stripeToken');

    //Retriieve cart information
    $cart = Cart::where('user_id',Auth::user()->id)->first();
    $items = $cart->cartItems;
    $total=0;
    foreach($items as $item){
        $total+=$item->product->price;
    }
    if(
        Auth::user()->charge($total*100, [
        'source' => $token,
        'receipt_email' => Auth::user()->email,
    ])){

        $order = new Order();
        $order->total_paid= $total;
        $order->user_id=Auth::user()->id;
        $order->save();

        foreach($items as $item){
            $orderItem = new OrderItem();
            $orderItem->order_id=$order->id;
            $orderItem->product_id=$item->product->id;
            $orderItem->file_id=$item->product->file->id;
            $orderItem->save();

            CartItem::destroy($item->id);
    }
        return redirect('/order/'.$order->id);

    }else{
        return redirect('/cart');
    }

}
7
  • A "Cart" object and a "save for later" option ala Amazon could easily replace the need for creating an "Order" object before the customer attempts to checkout.
    – Ranger
    May 27, 2016 at 20:49
  • 2
    The answer to the question "which is preferable" depends entirely on what sort of behavior you want from the application. Some websites store incomplete orders; you can't do that if the order only gets created when the payment is submitted. May 27, 2016 at 20:53
  • Almost all payment processing providers require a unique identifier from you so that they can tell the buyer what the payment is for. In that case, even if you don't actually create an order in advance, you'll still have to create its unique identifier and make sure it's never reused. At that point, it is often simpler to just go ahead and create the order already than to keep straight which IDs have been used or not. May 27, 2016 at 21:45
  • @KilianFoth not necessarily - see above code of how an order can be created after payment is made.
    – adam78
    May 27, 2016 at 22:56
  • If you create the order Before pay. How it affect the stock management? Is there any sort of units that may end being reserved? If you lock stock. Are you punishing possible orders that would be paid at the moment (instead of an uncertain future) bc stock is locked/reserved?
    – Laiv
    May 28, 2016 at 9:39

8 Answers 8

5

Yes, create it!

You absolutely must create the Order (or some other kind of record) before taking payment, not afterward. I have never seen an e-commerce system not do this. Here are just a few reasons why, off the top of my head.

Very bad edge cases. The payment could go through on the payment processor's server, but a network problem could prevent the response from reaching your system. If this were to happen, there would be a mysterious debit on the customer's ledger with no record of how it got there or why.

Matching entries. You need the OrderID from that record in order to pass into the "Memo" field in the payment transaction. That way you have matching entries in case of dispute. If you haven't generated the Order record yet, you probably don't have that ID.

Customer support. If a payment fails, the customer might call to find out why. If you don't have the Order record, that call becomes very awkward, as the customer service rep won't have a clue what the customer is talking about, and reconstructing the order becomes a chore.

Fault tolerance. If your payment processor goes down for any reason, you want may want to keep your web site open and allow customers to continue to place orders. With the order records created you will just need to run payment processing offline. In theory this could even be done manually.

Sale Leads. If a payment fails, that does not mean the customer no longer wants the product. If you have the Order on record, your application has many more options for helping the customer complete his purchase, e.g. by trying again with a different payment method, by emailing them a month later to see if they can now afford it, etc.

Business intelligence. If people are ordering certain products, your marketing folks will want to know which products and how many. This will help them gauge demand, pricing, and product offerings.

Compliance. The Order provides an audit trail, which is required by PCI-DSS. You must be able to account for the source of every credit card transaction or you will fail PCI audits possibly lose your merchant status.

What is the down side of creating it first? Oh no, you've got an extra record in the table. Who cares? Set up a job to clean up unfulfilled orders in 90 days; this is incredibly easy, compared to the problems that are solved.

Here is an example of a high level flow that is common:

  1. Customer completes order
  2. Order record is created
  3. Payment is authorized; order record is updated with the authorization code.
  4. Shipping order and pick lists are created; warehouse worker attempts to fill order
  5. If out of stock, go to alternative flow where you either cancel the order or email the customer.
  6. If fully in stock, capture payment using the authorization code created in step 3. Update the order to indicate payment has been captured.
  7. Ship
1
  • very well explained.
    – adam78
    Oct 5, 2021 at 17:23
4

Could there be a way to dodge this issue by turning "order" from a possibly transient object into an action performed by an object whose state you have more direct control over?

Let's assume four object types (classes or prototypes, depending on what you're working with): Customer, Product, Cart, and Receipt.

For clarity's sake, instance objects are lower_snake_case and classes are in StudlyCaps. I'm coming at this from a Ruby and JavaScript perspective, so translate as needed...

  • A customer is a user shopping on your site.
  • A product is an item for sale in your store.
  • A cart is an object representing all the products in a customer's shopping cart. Each customer has only one cart (for now: see below). That single cart per customer may contain many products, and has an order_id property that defaults to null. A cart's methods could include content manipulations like empty, add([skus]), remove([skus]), increment(sku), and decrement(sku). And then there's checkout, which populates order_id with an identifier that doesn't collide with other extant order IDs (perhaps generated based on the current cart state, customer ID, and time) and sends that order ID and other relevant info to the payment processor. Upon confirmation from the payment processor of a successful transaction, the pay method converts that cart's data into a Receipt instance and empties the shopping cart.
  • A receipt is like a cart preserved in its final paid state. A list of receipts is what a customer sees when looking at her order history. Each one has some new properties like tracking_number, and methods like refund([skus]).

Here's how I see this working in practice:

  • If the payment processor responds that the transaction failed, inform the user and revert order_id to null.
  • If the user abandons the payment process, the presence of a non-null order_id on their cart will indicate an incomplete transaction. Inform the user in the UI, along with links to complete checkout or view their cart.
  • If the user abandons payment, returns to the site, and alters the contents of their cart then nullify order_id -- this is effectively a different order.

The nice things about this approach:

  • No orphan orders hanging around.
  • You now have a structure that you can use to add additional functionality. For example, you could save a cart to turn it into a "shopping list" or "wishlist." This would empty the current active cart that the user thinks of as "their shopping cart," but preserve the data for display elsewhere in the UI. Those saved carts could then have a "Buy This List" button that jumps straight to checkout with those items, or an "Add to Cart" button that merges the list's contents with the active cart.
2
  • So for every item row in the cart the same order_id would have to be repeated?
    – adam78
    May 27, 2016 at 23:24
  • If you mean in the database, no. The way I imagine it, order_id would be a field in the carts table for each cart. Tracking which items are in which carts could be handled by a join table (say, carts_orders) that has, for each row, a quantity field and foreign keys for cart and product. Disclaimer: I'm recently arrived on the back-end after over a decade in front-end design & development. At this point my back-end architecture ideas my still be half-baked, and I'm happy to get feedback if they are. :) May 28, 2016 at 1:14
2

one way of approaching this problem is to consider the various states of an order as separate entities. so, you might have (in a simple example) something like OrderUnpaid, OrderPaid, OrderPaymentFailed, OrderCancelled, OrderApproved, OrderUnfulfilled , OrderFulfilled, OrderComplete. this can be useful because it allows intuitive and clear separation of responsibilities based on the immediate needs of the order process.

1
  • Why not an enumerate column Order.state_id fk -> OrderState? Dec 2, 2016 at 18:36
1

I'll answer your question with two questions:

  1. Is it a bad thing to have to delete an order object?
  2. Is an order object invalid if it's supposed to be paid for (i.e., not merely waiting to be paid and picked up) but the payment hasn't come through?

If the answer to both questions is "yes," then you don't have a choice. A failed payment will leave you in a state that requires you to delete the order object, which your architecture discourages.

It seems like good practice to refrain from creating objects that you don't know you're going to use (thus restriction 0) and it seems almost axiomatic that you don't want bogus orders in your system that will never be completed (thus restriction 1). So the best answer is that you should create an order after executing payment.

0

Customarily in business, an order could exist in any one of several states:

  • Now being constructed.
  • Abandoned.
  • Parked by the user who someday plans to return to it and finish it.
  • Submitted but not yet paid for.
  • Paid for.
  • Awaiting purchase-order; purchase-order accepted; purchase-order refused.
  • Fulfilled.

Orders are sometimes accepted, fulfilled, and delivered based on a customer's credit with the company, or the presence of a purchase-order which serves to legally promise payment at a future date, and so on. Commonly, the financial transactions associated with an order are handled entirely separately from the order itself. The decision to put an order into active-fulfillment state is sometimes performed by software; sometimes only by human staff.

0

I think another way of doing this will be to enumerate the order. So the order can be uncompleted, paid or what event sates you would want if your architecture supports it.

1
  • 2
    We don't know what "enumerize" means. Can you edit this answer to use a different word instead? Nov 5, 2018 at 15:44
0

Yes but no, it depends if you are going to invoice them.

I usually walk through this from an accounting side scenario, remembering that there are slightly different workflows depending if purchase orders or credit accounts and such are involved.

A "Cart" is a Sales Estimate (Quote) - it accumulates what the customer wants to buy and the negotiated price and terms

The Sales Estimate is changed to a Sales Order, which is more like the contract that if the customer pays the store will deliver. When payment is posted then the fulfillment process can be kicked off.

However, for online retail level e-commerce you may or may not get payment quite frequently and will gather this build-up of sales orders that will never move forward. So the Sales Order gets linked to the payment.

A more traditional way to do this would be to put an expiry date on the unpaid order. then if they don't pay you are not going to reserve the goods.

However, by Sales Order, typically its really a pro-forma invoice, but that term is not used in retail consumer transactions much.

https://bizfluent.com/about-5047536-difference-between-sales-order-invoice.html has a good explanation of the document flow.

0

I would have order objects that can be in different states. The list of items in the order may be (yet) incomplete. The order may not yet be legally binding for the customer. You may have the customer information, payment information, the customer may have paid, the order may be delivered, partially delivered, the order may be stored for historical reasons, and say 6 years later it is deleted.

That should make things more clear. The order can be created when the user presses a checkout button on his shopping basket. At that point the order is very incomplete. Then it is filled with info from the checkout, with everything you have. Then your website will try to get any missing information. On the other side your company will make a delivery when enough info is there. None of your business. You just try to get whatever info the company wants and add it to the order object.

Not the answer you're looking for? Browse other questions tagged or ask your own question.