Adding shipping fee onto a Sale

bobpassarobobpassaro Member Posts: 13

Forgive me if I missed something on this. I imagine it's come up before, but I'm not seeing a straightforward solution.

I work on a website (not eCom, a standalone site) for a store that uses LS Retail in the bricks and mortar store. We can import products form LS to the site and sync inventory, etc. Currently, we are trying to upgrade the integration so that when a sale happens on the website we create an actual "Sale" in LS. Basically, I have this working fine using the API, but the problem has to do with shipping fees.

LS wants the Sale to be "balanced," that is, all the Sale Lines need to add up to exactly match the total of the transaction.

So, let's say somebody on the web buys a product for $100 and shipping ends up being $5.99.

If I POST the Sale with Sale Lines that just include the product ($100) and a SalePayment Amount of $105.99, obviously the Sale won't balance. I could just have a "Shipping" product in LS so I send the Sale Lines as product: $100 and shipping product: $5.99.

That would be fine, but the shipping fee can vary (and/or be changed by the webstore manager at any time) so this needs to get handled dynamically.

A few solutions I've been pondering:

  • Create a separate product for each possible shipping amount (ugh -- janky and fragile)
  • Create a "shipping" product on the fly that is priced at whatever the shipping fee is and then archive it after the Sale is complete (maybe workable, but also feels a little janky)
  • Manipulate the website data before posting it to LS, so we just eliminate the shipping fee entirely, and just have the LS Sale include actual products as Sale Lines and a total that matches those (not idea from a bookkeeping perspective)

Maybe I'm missing something easier? Is it possible to change the price of a product on the fly when posting a Sale (without do a separate PUT beforehand)? There is a PriceLevel "type" property and I see it can be set to "fixed" in some examples, but I don't see any documentation on other PriceLevel types. Is there some way to have a fluid price? Anyone else figured out a good way to deal with this in a dynamic way so that a shipping fee of any amount can be handled?



  • LeahLeah Moderator, Lightspeed Staff Posts: 219 moderator

    Hi @bobpassaro you can create a non-inventory item called 'Shipping' (or whatever you choose) and then update its price for each sale using the Sale SaleLine endpoint and changing the "unitPrice" field to match the shipping charge from the online order. This is likely the best way to achieve what you are looking for.

    API Support
    Lightspeed HQ
  • bobpassarobobpassaro Member Posts: 13

    Thanks. What I ended up doing was my #2 above, but this sounds a little better in that I guess I don't need to worry about inventory -- just price. Thanks, I'll try ti.

  • jtellierjtellier Member Posts: 56

    Is there an example of how you are posting this? I need to do the same.

  • bobpassarobobpassaro Member Posts: 13

    Here is what I did. I never got around to doing it the way Leah suggested, which might be the better approach. What I'm doing is creating a one-off product for shipping on the fly (I use the Order ID to be part of the SKU -- this is all coming from Woocommerce), then I grab the ItemID for that product that LS sends back, then add this ItemID plus the price and qty (always 1) to the order, then post the Sale to LS, then finally one more API call to delete (or "archive," I think, in LS parlance) the shipping product after I'm done with it.

    Here is the method from the class that is creating the Sale, which is creating a new SaleLine that is for the shipping amount:

    public function add_shipping() {

             $shipping_item = new WC_LightSpeed_Retail_POS_Shipping_Item( $this->woo_order );


             $shipping_line['itemID'] = $shipping_item->get_item_id();

             $shipping_line['unitQuantity'] = 1;


             return $shipping_line;



    Here is the entire class that is referenced above that creates the Shipping Item


    class WC_LightSpeed_Retail_POS_Shipping_Item {

       private $order;

       private $path_param;

       private $order_id;

       private $shop_id;

       private $description;

       private $custom_sku;

       private $manufacturer_sku;

       private $price;

       private $price_type;

       private $price_type_id;

       private $inventory_quantity;

       private $item_id;


       * Initialize the class and set its properties.


       * @since   1.0.0


       public function __construct( $order ) {

          $this->set_order( $order);



       private function init() {












       // ============================================================

       //   SETTERS

       // ============================================================

       private function set_order( $order ) {

          $this->order = $order;


       private function set_path_param() {

          $this->path_param = '/Item';


       private function set_order_id() {

          $this->order_id = $this->order->get_id();


       private function set_shop_id() {

          $this->shop_id = get_option( WC_LIGHTSPEED_RETAIL_POS_PREFIX . 'sale_shop' );


       private function set_description() {

          $this->description = 'Web sale shipping: WooCommerce order #' . $this->get_order_id();


       private function set_custom_sku() {

          $this->custom_sku = 'woocommerceshipping-order' . $this->get_order_id();


       private function set_manufacturer_sku() {

          $this->manufacturer_sku = 'woocommerceshipping-order' . $this->get_order_id();


       private function set_price() {

          $this->price = $this->order->get_shipping_total();


       private function set_price_type() {

          $this->price_type = 'Default';


       private function set_price_type_id() {

          $this->price_type_id = 1;


       private function set_inventory_quantity() {

          $this->inventory_quantity = 1;


       public function set_item_id( $id ) {

          $this->item_id = $id;


       // ============================================================

       //   GETTERS

       // ============================================================

       public function get_path_param() {

          return $this->path_param;


       public function get_order_id() {

          return $this->order_id;


       public function get_shop_id() {

          return $this->shop_id;


       public function get_description() {

          return $this->description;


       public function get_custom_sku() {

          return $this->custom_sku;


       public function get_manufacturer_sku() {

          return $this->manufacturer_sku;


       public function get_price() {

          return $this->price;


       public function get_price_type() {

          return $this->price_type;


       public function get_price_type_id() {

          return $this->price_type_id;


       public function get_inventory_quantity() {

          return $this->inventory_quantity;


       public function get_item_id() {

          return $this->item_id;


       // ============================================================


       // ============================================================

       public function build_data() {

          $item = array(

             'description' => $this->get_description(),

             'customSku' => $this->get_custom_sku(),

             'manufacturerSku' => $this->get_manufacturer_sku(),

             'Prices' => array(

                'ItemPrice' => array(

                   'amount' => $this->get_price(),

                   'useTypeID' => $this->get_price_type_id(),

                   'useType' => $this->get_price_type(),



             'ItemShops' => array(

                'ItemShop' => array(

                   'qoh' => $this->get_inventory_quantity(),

                   'shopID' => $this->get_shop_id(),




          return apply_filters( WC_LIGHTSPEED_RETAIL_POS_PREFIX . 'item_request_data', $item );


       public function create() {

          $api = new WC_LightSpeed_Retail_POS_API();

          $endpoint = $api->build_endpoint( $this->get_path_param() . '.json' );

          $response = $api->post( $endpoint, $this->build_data(), true );

          //TODO: handle errors

          $response = json_decode( $response, true );

          $this->set_item_id( $response['Item']['itemID'] );

          return $response;


       public function delete() {

          $api = new WC_LightSpeed_Retail_POS_API();

          $endpoint = $api->build_endpoint( $this->get_path_param() . '/' . $this->get_item_id() . '.json' );

          $response = $api->delete( $endpoint );

          //TODO: handle errors

          return json_decode( $response, true );



Sign In or Register to comment.