Zen Cart custom software development, Zen Cart modules, Zen Cart Expert eCommerce with Zen Cart!

better together

OpenCart Better Together

An OpenCart discounting module allowing vendors to create "buy one, get one" style discounts.

Donate: This is free software. Show your appreciation by supporting my efforts. Donate

Relevance: OpenCart 2.3.x, 3.x

Current Version: 1.5.1
Occasionally, new features are documented prior to being publicly available; please check the version history to ensure the feature you want is available in your version.

Support Thread: Better Together Support Thread

Cost: Free, but donation appreciated

Installed Cost: $90.00 Buy Professional Installation by That Software Guy

Installation Difficulty: Moderate (You must write some PHP to configure this mod, unless you purchase the Better Together Admin panel.)

Installation Instructions: click here

Common Installation Problems: click here

Location: OpenCart Extensions page, under Order Totals

Marketing Text: click here

Download: Better Together On OpenCart Extensions Page


In this video, you can watch me configure Better Together by editing the setup() function in the file

Here are the discounts I add:
        // Samsung Galaxy Tablet - BOGO 
        $this->add_prod_to_prod(49, 49, "%", 100);
        // Phones - buy one, get one half off 
        $this->add_cat_to_cat(24, 24, "%", 50);
        // Cameras - buy the Canon, get the Nikon $40 off
        $this->add_prod_to_prod(30, 31, "$", 40);

If you want to see how easy it is to add the same discounts using the Better Together Admin Panel, just watch the video on OpenCart Better Together Admin.

FAQ: click here

Configuration: The basic configuration of Better Together involves adding PHP code to the setup() function in the module, as shown below. Many examples of basic configuration are shown in this help file. For people who would prefer not to do this, a Better Together Admin panel is available.


The gold standard of online retailing is Amazon.com. OpenCart store operators looking to increase their profitability should constantly be asking, "WWAD?" or "What would Amazon do?"

When you look at an item in Amazon, not only is a cross selling recommendation made, a discount is offered to persuade the customer to accept the recommendation. This mod permits you to offer this type of discounted cross selling in your OpenCart.

You may specify
  • Buy item X, get item Y at a discount
  • Buy item X, get an item from category A at a discount
  • Buy an item from category A, get an item from category B at a discount
  • Buy an item from category A, get item X at a discount

Discounts may be specified as percentages of the latter item's price or as absolute values in the currency your cart uses.
These discount specifications are called "linkages," because they "link" one product or category to another. These linkages are not only used in discount calculations; they are also used to create messages which are automatically displayed on the product page. Details on how to do this are provided in marketing.

In addition, Better Together may be used to facilitate two-for-one offers for identical items. Although it may be argued that this is simply a special case of "buy item X, get item Y at a discount," this capability was added to facilitate two for one offers for an entire category of goods with a single statement. You may specify two-for-one type specials such as
  • Buy item X, get another item X free
  • Buy an item from category A, another identical item free

Please note that Better Together only provides a discount; it does not automatically add items to the cart.

Shopping Cart Page displaying Better Together Discount

Shopping Cart page showing Better Together discount

It shows "Buy the Palm Treo Pro (product 29), get the HTC Touch HD (product 28) for 50% off" with Tax Recalculation set to Standard. This was created with the setup function
   private function setup() { 
      $this->add_prod_to_prod(29,28,"%", 50); 

Detailed Description:

The original model for adding Better Together discounts was that people would modify the setup() function at the bottom of the file upload/catalog/model/extension/total/better_together.php
This still works, but now another available option is the Better Together Admin panel (a separate module which is sold commercially). Better Together Admin is easy to use and fast - and it's a great way to show your support for Better Together. Here's a video that shows the use of Better Together Admin:

If you wish to continue editing the setup function, here are some examples:

Four types of linkages may be performed. The format of each of these is the same:
  • first identifier (product or category)
  • second identifier (product or category)
  • "%" or "$" to indicate how discounting is to be done
  • a number, indicating the discount amount.

The four calls for the four types of discounting are
  • add_prod_to_prod()
  • add_prod_to_cat()
  • add_cat_to_cat()
  • add_cat_to_prod()

If a straight two for one discount is what is desired, the calls are
  • add_twoforone_prod()
  • add_twoforone_cat()

Let's consider two products: product 5 from category 3, and product 2 from category 1.

Suppose you want to offer a 50% discount on product 5 with the purchase of product 2. Make the setup() function look like this:
   private function setup() { 
      $this->add_prod_to_prod(2,5,"%", 50); 

Want to make it buy product 2, get product 5 free?
   private function setup() { 
      $this->add_prod_to_prod(2,5,"%", 100); 

How about buy one product 2, get one free?
   private function setup() { 
      $this->add_prod_to_prod(2,2,"%", 100); 

Remember product 5 is in category 3. If instead of specifying product 5 in particular, you want to discount any item in category 3 by 20% with the purchase of a product 2 item, use
   private function setup() { 
      $this->add_prod_to_cat(2,3,"%", 20); 

Discount can be done in currencies as well. To offer $7 off (or 7 off of whatever currency your cart uses), use
   private function setup() { 
      $this->add_prod_to_cat(2,3,"$", 7); 

(The "$" is just used to specify currency; your cart's currency settings will be respected when computing the discount.)

Remember product 2 is in category 1. If you want to widen the discount to provide a discount of 20% off any item in category 3 when an item from category 1 is purchased, use
   private function setup() { 
      $this->add_cat_to_cat(1,3,"%", 20); 

Any number of these discounts may be offered; discount computation will be done in the order in which your discounts are specified in setup(), and items will be processed in the order in which they appear in the cart.

Using the examples above, suppose these items are in your cart:

1 - Product 2, category 1
2 - Product 10, category 1
2 - Product 20, category 3
2 - Product 5, category 3

and suppose you have coded these discounts:
   private function setup() { 
      $this->add_prod_to_prod(2,5,"$", 7); 
      $this->add_cat_to_cat(1,3,"%", 25); 

The following discounts will be computed:
  • $7 off ONE product 5 because of ONE product 2 (rule 1)
  • 25% each off TWO product 20 because of TWO product 10 (rule 2)
To get $7 off the second product 5, the customer would need to add a second product 2 to the cart.

With the same cart, coding
   private function setup() { 
      $this->add_cat_to_cat(1,3,"%", 25); 
      $this->add_prod_to_prod(2,5,"$", 7); 

Would compute the following discount:
  • 25% off ONE product 20 because of ONE product 2 (rule 1)
  • 25% off ONE product 20 because of ONE product 10 (rule 1)
  • 25% off ONE product 5 because of ONE product 10 (rule 1)
Obviously these could be very different discounts!

To create a two for one discount for product 5, simply code
   private function setup() { 
And to create two for one discount for all products in category 3, code
   private function setup() { 

Note the difference between
   private function setup() { 
   private function setup() { 
      $this->add_cat_to_cat(3,3,"%", 100); 
The latter says, "buy any item from category three, and get 100% off any other item from category three." The former says, "all items in category three are buy one, get an identical item free." So if a customer bought items 20 and 30 from category three, a discount would only be given in the latter case.

To make these discounts visible on your product page, follow the directions in marketing below. This step will create text like this:

Buy this item, get a Microsoft Intellimouse Explorer at 50% off
Buy this item, get an item from Memory at 50% off

The link is created to facilitate the cross sell. This step is optional; if you prefer, you can add your own cross selling text.

Upgrading to Better Together Admin makes this process even easier. In this video, you can see how Better Together Admin handles the different linkage types by adjusting the screen to capture only the necessary data:

Category Handling

Note that the "category" in add_cat_to_cat(), add_prod_to_cat(), add_cat_to_prod() and add_twoforone_cat() is the "parent category," which is determined using the product_to_category table. If subcategories are being used, the parent category will not be the same as the top level category.

Men's Clothing  (Category 7)
     ---->  Shirts (Category 12)
            -------> shirt A 
                     shirt B
                     shirt C 
     ---->  Discounted Shirts (Category 13)
            -------> shirt A 
                     shirt D
                     shirt E 
In this example, the parent category of "shirt A" can be category 12 (Shirts) or category 13 (Discounted Shirts). Note that category 7 (Men's Clothing) would not be the parent; it would be considered the top level category.

If your store uses subcategories and you require the ability to reference categories at different levels, you may wish to consider purchasing Discount Chooser.

How Discounts are Selected

Better Together sorts items by price in descending order, then looks for the condition (the first part of the linkage) from the top going down. Once it has found a match, it looks for the discount (the second part of the linkage) from the bottom going up. So if the following items are in your cart,
Product ID Category ID Price

Better Together sorts them by price like this:
Product ID Category ID Price

It then matches conditions from most expensive items to least (top down), and matches discounts from least expensive items to most (bottom up).
If your discount is
   function setup() { 
      $this->add_cat_to_cat(12,12,"%", 100); 
then this means that product 9 will be the condition, and product 1 will be the item which is discounted 100%.

But if your discount is
   function setup() { 
      $this->add_cat_to_cat(12,18,"%", 100); 
then this means that product 9 will be the condition, and product 5 will be the item which is discounted 100%. Since product 5 is more expensive than product 9, this may not be what you want.

As items are used in linkages, they are no longer available for subsequent in linkages. So if your cart contents are this:
Quantity Product ID Price
and your discount is
   function setup() { 
      $this->add_prod_to_prod(5,8,"%", 100); 
you will only get one of product 8 for free. The second product 8 has no matching product 5. If you need to ensure that the item which is discounted is less than or equal in price to the condition, you have the following options:
  • Use the same category id in your add_cat_to_cat discounts. If the condition and the discount have the same category, then the price based sorting that Better Together does will ensure the lower priced item will be discounted.
  • Select high value categories (with only high priced items) as your first (condition) category, and lower value categories (with lower priced items) as your second (discount) category.

Installation Instructions:

  1. Back up everything! Try this in a test environment prior to installing it on a live shop.
  2. Unzip the file provided. Go into the directory corresponding to your OpenCart version (2.3 or 3.0), and copy the contents of the unzipped folder to the root directory of your shop.
  3. Login to admin and in Extensions > Order Totals you will see 'Better Together' listed along with all the other modules available.
  4. Click the 'Install' link on the 'Better Together' row. Then click Edit and set the status to Enabled and set the Sort Order to something less than the sort order for Total (or less that the sort order for Taxes if your store has Sales Tax). If you want to to recalculate taxes after the deduction, set Tax Recalculation to Standard.
  5. Decide on the discounts you wish to offer, and add them to catalog/model/extension/total/better_together.php as indicated in the notes above this.
  6. There is a separate Better Together Marketing plugin which shows your discounts on the product page. You may install it if you wish.
  7. Donate! Show your appreciation by supporting my efforts.

Installation Problems:

The most common installation problems for this module are as follows:
  1. During the installation of Discount Chooser, performing the step Extensions > Extensions > Order Totals > Better Together > Install, and then setting Status to Enabled.


What good is having cross selling and upselling specials if you don't advertise them?

Better Together Discounts may be automatically displayed on the product info page with the Better Together Marketing extension. It changes your product page so that discounts available for the current item are show like this:

Product page showing Better Together discount
Better Together Marketing is a separate extension.

Major Versions

  • 1.5.1 09/15/2019 Fixing sort order issue
  • 1.5 01/13/2017 Updates for OpenCart 3.0
  • 1.4a 07/16/2017 Bug fix in Marketing Text
  • 1.4 07/01/2017 Added Marketing text
  • 1.3 06/13/2017 Interoperability with/without admin panel
  • 1.2 04/08/2017 Updates for OpenCart 2.3
  • 1.1 02/20/2015 Updates for OpenCart 2.0
  • 1.0 08/03/2014 First Release


Q: What's the difference between Better Together and Discount Chooser?
A: Better Together only permits pairs of items to be discounted together, such as two products, or one product and one item from a specific category. Discount Chooser permits the creation of "Buy 2, get 1", "Buy 3 get 1," or "Buy some number, get a choice of some number." So Better Together can be thought of as a subset of Discount Chooser.

Q: How do I set up Better Together discounts?
A: Decide on the linkages you wish to use, and add them to the setup() function in upload/catalog/model/total/better_together.php ("Linkages" are what add_prod_to_prod, etc. are called.) Don't want to edit code? Get the Better Together Admin panel.

Q: Can I start and stop my Better Together discounts on certain dates in the future?
A: This feature is included in the Better Together Admin panel.

Q: Why do you have to add PHP code to setup()? Why didn't you put this in the Admin panel?
A: Creating the Better Together Admin panel was a significant amount of work and represents my main revenue stream for Better Together; please show your support for my software by purchasing it.

Q: What would my setup() look like if I wanted to give 2 for 1 on all items in category 1?
A: You have two options. This provides a two for one discount, allowing you to mix and match items from category 1:
  private function setup() { 
      $this->add_cat_to_cat(1,1,"%", 100); 

This provides a two for one discount on identical items in category 1:
  private function setup() { 

Q: What would my setup() look like if I wanted to give 2 for 1 on all items 5, 8, 12, 17 and 31? What about buy one get one half off?
A: Here's what you would do for 2 for 1:
  private function setup() { 
      $this->add_prod_to_prod(5,5,"%", 100); 
      $this->add_prod_to_prod(8,8,"%", 100); 
      $this->add_prod_to_prod(12,12,"%", 100); 
      $this->add_prod_to_prod(17,17,"%", 100); 
      $this->add_prod_to_prod(31,31,"%", 100); 
Here's buy one get one half off:
  private function setup() { 
      $this->add_prod_to_prod(5,5,"%", 50); 
      $this->add_prod_to_prod(8,8,"%", 50); 
      $this->add_prod_to_prod(12,12,"%", 50); 
      $this->add_prod_to_prod(17,17,"%", 50); 
      $this->add_prod_to_prod(31,31,"%", 50); 

As in the previous question, mixing and matching is allowed with these discounts - but since these are specific products, what are the semantics of "mixing and matching?"

Well, for add_prod_to_prod() and add_twoforone_prod(), mixing and matching means "having different attributes." If you wish to give a buy one get one free for only precisely the same item for items 5 and 8, and not permit mixing and matching, you would use:
  private function setup() { 

For example, if you sell sweaters with the attribute "color," then buying a red sweater and a blue sweater would not produce a discount if add_twoforone_prod() were used, but it would if add_prod_to_prod() were used.

Q: What would my setup() look like if I wanted to do buy one of item 5, get one from category 2, 7 or 9 at $5 off?
A: Here's what you would do:
  private function setup() { 
      $this->add_prod_to_cat(5,2,"$", 5); 
      $this->add_prod_to_cat(5,7,"$", 5); 
      $this->add_prod_to_cat(5,9,"$", 5); 

Q: Can I do three for the price of two? Four for the price of three?
A: Not with Better Together. But you may with Discount Chooser.

Q: What if I want to do offers on all my products?
A: The setup() function is in a PHP file, so you can write software to configure your discounts. For instance, if you have 600 products, and you want to do "buy one get one 50% off" on all of them, you can do:
private function setup() {
   for ($i = 1; $i <= 600; $i++) {
      $this->add_prod_to_prod($i,$i,"%", 50);
Obviously you could do this for add_twoforone_prod, etc. just as well. And a more sophisticated implementation could walk the product table instead of hardcoding a count, etc.