E-Commerce Concurrency



  • Alright, this part has been throwing me for a loop every time I've sat down to work on it.

    I can add and remove items from a cart easily enough. The problem comes when I have to estimate shipping or execute a charge. Both are dependent on the contents of said cart.

    How do I stop somebody from adding one item to the cart, launching the shipping estimator, adding a dozen more items, and checking out with the low shipping charge? I have a very similar scenario with doing the same thing except only charging their card for a portion of the cart.

    I think I can get around the shipping problem by setting a flag upon recieving a shipping estimate and resetting it every time they alter the cart. They can't check out without the flag so that should keep the shipping in sync.

    How do I do the same with the card charging?

    If this were a Windows program where I had strict control of windows and navigation, it wouldn't be a problem. But in the browser(s), there's nothing to stop somebody from opening a dozen windows and stomping all over the same session.

    Any suggestions?


  • I survived the hour long Uno hand

    @Zenith
    Can you make the “check out” process a hard refresh to a new page that then rechecks their cart server side and updates shipping & total before doing the money?

    Seems if someone has a bunch of stuff in the cart from different tabs and doesn’t refresh the cart to verify what they have, the mis-total is on them.

    And/or you could basically have the “check out” button go to a confirm page that gives them either a “press yes to accept” button or a “hey wait” button that pulls the checkout back out of the queue within a few seconds.



  • @izzion A confirmation page just shifts the problem forward. They go to the confirmation page, screw up the cart in another window, and confirm the wrong total.

    It's the same with Square's dopey payment form. It opens up client side and hangs out there waiting for credit card details. In the meantime, you can open other windows and screw up the order that charge will be applied against. Every other part of their API works server side and it's just the GoogleScript charge form that throws a monkey wrench into it.

    I wouldn't even be back on this project if I hadn't found two other processors that don't have that stupid limitation.



  • Isn't this the kind of situation that callbacks or reactive is basically made for? When there is a state change in the cart, restart the checkout.
    Or just make a copy of the cart and send that to the checkout flow, so that no changes to the cast will change the order in checkout.
    I prefer the second way, because there is less opportunity to fuck up.


  • Discourse touched me in a no-no place

    @Zenith said in E-Commerce Concurrency:

    How do I stop somebody from adding one item to the cart, launching the shipping estimator, adding a dozen more items, and checking out with the low shipping charge?

    Calculate it again at checkout.

    @Zenith said in E-Commerce Concurrency:

    How do I do the same with the card charging?

    Lock the order at the point of submitting it to the payment provider. If it's successful, that's it, the order's done and the cart is empty again. If it fails, unlock it.

    edit: Or what Carnage said.


  • 🚽 Regular

    @loopback0 said in E-Commerce Concurrency:

    Lock the order at the point of submitting it to the payment provider. If it's successful, that's it, the order's done and the cart is empty again. If it fails, unlock it.

    The only way to go, IMO. It deals with malicious actors, and not just accidental concurrency (including sending the customer to a payment window twice).



  • @Zecc To add to what you two said, it might make sense to implement a little state machine and have a state enum (open, processing_payment, finalized) in the cart/order data structure in the backend/database instead of a bunch of random flags. From a UI perspective, I'd also automatically re-calculate the shipping charge each time the cart is altered or not show it in the cart at all (only in a calculator pop-up) until the order is confirmed and you proceed to the payment processor.

    Edit: And you probably shouldn't let someone confirm an old version of the order, either, so they're not surprised by a sudden change in the charge. So on the order confirmation page, submit the whole visible contents and check whether that's still the current cart in the backend. If not, send the user back to the confirmation page.


  • Banned

    Another idea: in second-to-last step of the order, where you see the confirmation, save the cart server-side, and when the user clicks "confirm", if the cart content has been altered, refuse to accept and show confirmation page again. Only proceed to payment if it hasn't changed. This way, you don't disrupt the user's workflow (at worst he'd have to confirm twice, but other than that, they can do whatever they were doing before and it'd still work).

    You'd still want something to cover your ass after confirmation, in case the payment goes wrong. But it doesn't have to be locking the cart. After confirmation, you might empty the cart and create an actual order, but in some kind staging phase, where the content cannot change anymore. After payment is done, you make the staged order into an actual actual order. All the while, the user is free to fill up the cart again to their heart's content.



  • @Gąska I think there are two general options: Either keep orders and carts as separate objects and convert on confirmation or use the same model and database table and simply assign a new one as the current cart to the active browser session / user account once the old one is confirmed.



  • @loopback0 said in E-Commerce Concurrency:

    Lock the order at the point of submitting it to the payment provider. If it's successful, that's it, the order's done and the cart is empty again. If it fails, unlock it.

    In the case of these processors that want to host their forms, you can't. You lose control of that window the second it's opened and don't get a closure notification.

    I thought about it some more and the two I'm working on now with the server side code might work with two layers of flags.

    EditCart()
    {
      //whatever
      ShipLock = false;
      PayLock = false;
    }
    EditAddress()
    {
      //whatever
      ShipLock = false;
      PayLock = false;
    }
    SetShipQuote ()
    {
      //whatever
      ShipLock = true;
    }
    ChangeCard()
    {
      if (ShipLock == true)
      {
        PayLock = true;
        if (API.Charge() == false)
        {
          PayLock = false;
          //print errors
        }
        else
        {
          MarkOrderPaidAndDestroyCart();
          ShipLock = false;
          PayLock = false;
        }
      }
      else
      {
        //print errors
      }
    }
    


  • @Zenith said in E-Commerce Concurrency:

    In the case of these processors that want to host their forms, you can't. You lose control of that window the second it's opened and don't get a closure notification.

    Every web shop I've ever visited treats the order as complete and waiting for payment the minute the windows of the payment processor is opened. If you add a "waiting for payment" state, there's no problem.



  • @dfdub said in E-Commerce Concurrency:

    @Zenith said in E-Commerce Concurrency:

    In the case of these processors that want to host their forms, you can't. You lose control of that window the second it's opened and don't get a closure notification.

    Every web shop I've ever visited treats the order as complete and waiting for payment the minute the windows of the payment processor is opened. If you add a "waiting for payment" state, there's no problem.

    What about when they close the payment window? Does the order hang in limbo forever?



  • @Zenith said in E-Commerce Concurrency:

    What about when they close the payment window? Does the order hang in limbo forever?

    I guess they eventually cancel it, but often you're redirected to an intermediary page that states (in other words) that the legal contract was already established. So basically, it's the customer's responsibility not to screw it up.

    You can run into the same problem if you accept wire transfers and the customer forgets to send the money. Which is why every eCommerce solution I've used so far has that "waiting for payment" state.


  • Discourse touched me in a no-no place

    @Zenith said in E-Commerce Concurrency:

    In the case of these processors that want to host their forms, you can't. You lose control of that window the second it's opened and don't get a closure notification.

    How do you know the payment is successful without some sort of callback from the payment provider? Is it expected to be an asynchronous process?



  • @loopback0 said in E-Commerce Concurrency:

    @Zenith said in E-Commerce Concurrency:

    In the case of these processors that want to host their forms, you can't. You lose control of that window the second it's opened and don't get a closure notification.

    How do you know the payment is successful without some sort of callback from the payment provider? Is it expected to be an asynchronous process?

    It depends. If it's asynchronous GoogleScript, you only see a callback on success or failure. If somebody opens that window and closes it, nothing happens. That's the case with Square and others like it. The library I've been looking at lately is server side, so it's a synchronous function call that eventually returns. They assume I can just pass form fields in so I guess the PCI police will kick down my door if I make any real progress that way but at least it's an easier process flow to work with.



  • No locks, retries, state, what ever....

    Part of the final is a calculation, even if you did a calculation (estimate) a millisecond before in another code path. Once past this point the cart is immutable (unless you go back to some point that will force you through this gate again.


  • Banned

    @Zenith said in E-Commerce Concurrency:

    @dfdub said in E-Commerce Concurrency:

    @Zenith said in E-Commerce Concurrency:

    In the case of these processors that want to host their forms, you can't. You lose control of that window the second it's opened and don't get a closure notification.

    Every web shop I've ever visited treats the order as complete and waiting for payment the minute the windows of the payment processor is opened. If you add a "waiting for payment" state, there's no problem.

    What about when they close the payment window? Does the order hang in limbo forever?

    Unless your product owner/specification team says otherwise, then yes, keeping unpaid orders in limbo forever is a perfectly fine thing to do. It only costs a few dozen bytes in the database, and greatly simplifies the workflow, as well as nicely handles the nastiest corner cases, ie. those involving failed payment, especially the kind where the money was taken but confirmation from payment processor hasn't arrived - it's much easier to issue the refund if there's some kind of trace of the order.



  • @Gąska I'm the product owner so I have a vested interest in doing it right.


  • Notification Spam Recipient

    @Zenith said in E-Commerce Concurrency:

    What about when they close the payment window? Does the order hang in limbo forever?

    Yes, and that's totally fine:

    df63da4d-09c7-4bff-8939-01cd618d8d5c-image.png

    In this screenshot you can see the person fucked up and closed the PayPal window without paying, and another attempted to use a method with insufficient funds (or something else wrong).

    So long as you filter out the Incompletes in your sales reports, it's no big deal.

    Now, if you're implementing some kind of "X people have this in their cart, better hurry!" then maybe more logic is needed, but otherwise stale records is kosher.

    And hey, if they do end up paying in the other window, that just means they bought two or whatever, win win!



  • @Tsaukpaetra Until they want a refund and you have to eat the transaction fees. People rail at the 6% sales tax but don't realize there's a 3% Visa tax.


  • Notification Spam Recipient

    @Zenith said in E-Commerce Concurrency:

    Until they want a refund and you have to eat the transaction fees.

    Eh. You can't stop stupid, and if they're doing it intentionally for...reasons, then (I think) you can just report them for fraud or something.

    Now that you've mentioned this One Neat Trick, I want to try it on some of these stores I use and see what happens...


  • Banned

    @Zenith said in E-Commerce Concurrency:

    @Tsaukpaetra Until they want a refund and you have to eat the transaction fees. People rail at the 6% sales tax but don't realize there's a 3% Visa tax.

    Realistically, how often will this happen? And what do you plan to do in case of a legitimate software failure that eats their money but doesn't go forward with the order? You must be prepared for this possibility.



  • @Gąska said in E-Commerce Concurrency:

    You must be prepared for this possibility.

    Fully agree. The "ordered, but not paid" state exists anyway, whether you like it or not, can be entered for a variety of reasons (e.g. fraudulent chargebacks) and must be considered in your bookkeeping. So you might as well explicitly model it in the software and enter it before you redirect to the payment provider.

    How you want to deal with that state is up to you. Both silently canceling the order after xx hours via cronjob or keeping it open until the user cancels or pays it are valid options.


  • Java Dev

    @Tsaukpaetra said in E-Commerce Concurrency:

    Now, if you're implementing some kind of "X people have this in their cart, better hurry!" then maybe more logic is needed, but otherwise stale records is kosher.

    Or if you're tracking limited stock. If you have only 3 of a certain item available, and you're reserving one for a basket which is in limbo, you want to return it to the pool at some point.



  • @dfdub I actually have a working solution. My cart prevents cartjacking by acting like an actual cart and putting items into a state between available and sold. When the PHP session timeout handler swings around, it moves everything that's still in that state back into the pool. It's literally just the checkout logic that's had me hung up.



  • @Zenith
    That's nice, but stock tracking and order tracking are two separate issues. I was talking about the latter. You need an intermediate state in both cases.

    BTW: If you're already using PHP, why not just install Magento and configure it for your needs? I mean, yeah, it's a behemoth that eats a few Gigs of RAM for breakfast, but rolling your own eCommerce solution is a path full of regulatory compliance footguns.



  • @dfdub said in E-Commerce Concurrency:

    @Zenith
    BTW: If you're already using PHP, why not just install Magento and configure it for your needs? I mean, yeah, it's a behemoth that eats a few Gigs of RAM for breakfast, but rolling your own eCommerce solution is a path full of regulatory compliance footguns.

    • integration with my accounting system
    • not impressed with features offered by Shopify, BigCommerce, etc
    • difficulty of kludging desired features into their MVC spaghetti design pattern
    • my bachelor's degree in software engineering

  • Discourse touched me in a no-no place

    @Zenith said in E-Commerce Concurrency:

    my bachelor's degree in software overengineering

    Helping you out here… (and getting it past regulatory checking is why you're in for real pain, even if you actually manage to eventually get everything right).



  • Isn't it only the payment providers that need regulatory inspections? I believe anyone can set up a web shop just fine and there aren't many regulations in that area.

    As for the initial question, any web app with persistent session state should:

    • Always use transactions to update the state
    • If the state changes after a confirmation page has been shown, refuse to continue, and send you back to the confirmation page. So if you confirm an order for 6 onions, and then add a TV, and then try to proceed to payment, you will get sent back to the confirmation page


  • @bobjanova said in E-Commerce Concurrency:

    I believe anyone can set up a web shop just fine and there aren't many regulations in that area.

    Yes, you can set it up without inspections, but you might still violate a bunch of bookkeeping, tax or consumer protection laws accidentally if you just do what seems right in your online shop, which can result in costly fines if discovered.


  • Banned

    @bobjanova said in E-Commerce Concurrency:

    Isn't it only the payment providers that need regulatory inspections? I believe anyone can set up a web shop just fine and there aren't many regulations in that area.

    Depends on local laws. You're the one entering contracts with customers for item sales, so while you don't handle the money, you still have to do all the legally mandated paperwork, such as transaction history, numbered invoices, etc. Also proper handling of personal data. Although IIRC @Zenith lives in USA, and AFAIK they're very light on the mandatory paperwork for business owners, especially compared to EU.



  • @Gąska said in E-Commerce Concurrency:

    @bobjanova said in E-Commerce Concurrency:

    Isn't it only the payment providers that need regulatory inspections? I believe anyone can set up a web shop just fine and there aren't many regulations in that area.

    Depends on local laws. You're the one entering contracts with customers for item sales, so while you don't handle the money, you still have to do all the legally mandated paperwork, such as transaction history, numbered invoices, etc. Also proper handling of personal data. Although IIRC @Zenith lives in USA, and AFAIK they're very light on the mandatory paperwork for business owners, especially compared to EU.

    Proper sales tax accounting is the biggest one AFAIK. Which for online stuff can be particularly annoying with so many jurisdictions.



  • @Benjamin-Hall I already do sales tax in the offline business.

    Some of these answers, I mean, sound like my friend that warned me not to start a business years ago. He had absolutely no ideas on how to run a business because all of his mental energy was focused on being afraid of not having an accountant and a lawyer and a chamber of commerce membership and a lobbyist and HR/payroll services lined up. My business may be marginal at best but at least I can say I had the experience of starting and operating one.



  • @Zenith
    Well, I didn't know you had prior experience with bookeeping and all the sales tax edge cases. If you do, then your plan sounds significantly less insane.

    I'd still suggest taking a look at the established solutions for the edge cases and simply copying what they do - there's probably a good reason.


Log in to reply