Bug Fixes

This is due to when you disable access permission on the localisation/country class, it also disables the country function which is used in the ajax calls to obtain the county zones.
This is of course a design flaw, a bug if you will.
solutions:
1) Add localization/country to the ignore list in admin/controller/startup/permission.php
2) Move the country function out of that class.
3) Leave the access permission granted, you can still refuse permission on modify
I would suggest option 1.
Default OC does not call the gc() function and if it did, it compares timestamp with datetime which will never match.
Unknown why a datetime is used and not a timestamp in the database, now it is like talking via an interpreter when you speak eachother's language.
Replace the system/library/session/db.php file with this:
catalog/controller/common/language.php

catalog/view/theme/default/template/common/language.twig
1) get rid of the cart merging logic in the construct function in system/library/cart/cart.php

remove this part:

2) add these functions which do the same but will be called only when needed:

3) call these functions in register and login controllers
2 function calls for login, giving the current session id to possible old customer items and merging the current guest items
1 function call for register as there cannot be any old customer items so just merging the current guest items

in catalog/controller/checkout/login.php and catalog/controller/account/login.php
just after:

add:

in catalog/controller/checkout/register.php and catalog/controller/account/register.php
just after:

add:

That is all.

Improvements

1) create a static html page with a nice error message.
2) add to top of your index.php:
This can only happen when you share a session_id.
Under normal, proper site operations (^), that can only happen when you are issueing/using the same session_id while that session_id is already given.

Chances of that are very very small but not zero.

The change is increased when:
1) you have a huge backlog of used session records in your database (which is common in default OC) or file system. Remember, bots get a new session id on every request so that db table can fill pretty fast if not cleaned properly.
2) hackers who send requests with random/edited session cookies in the hope one is active, very rare, with virtually no chance but very difficult to counter (*).
3) Users who never close their browser and thus retain their session_id indefinitely which now may have been issued to someone else or some "remember me" implementation where the session cookie is no longer a session cookie but has a defined lifetime (*).

(*) many sites (incl OC) simply use the session_id presented in the cookie even if that session_id was not issued by the server nor whether that session is actually active on the server. As long as it is a valid format, that session_id is used.
(^) excludes unstable/experimental one-page checkout implementations

Item 1. you can counter by:
a) making sure your session garbage collection works properly which is the preferred method with the least performance impact.
b) checking whether a new session_id is already active before issueing/using it which adds extra effort, be it a minute one.

a)
For those sites we use the database for session storage better replace the file system/library/session/db.php


That should take better care of the session garbage collection, the default gc() function is crap as it compares a timestamp with a date-time value apart from the fact that it is never called. The file storage class already has a __destruct() function and works fine.

b)
we changed the start function in system/library/session.php to:

We added to system/library/session/db.php

We added to system/library/session/file.php

These function will check whether a newly generated session_id is already active server-side and generate a new one if it is.
Both for db and file stored sessions. You can toggle that check with the $no_clash parameter.

In addition, as stated (*), OC will use any session_id given to it via the cookie as long as it has the right format, which is not good.
We check whether the given session_id actually is active server-side or that given session_id is ignored.

catalog/controller/startup/session.php
change:

to:

system/framework.php
change:

to:

change:

to:

to be able to access the database from here.
(Turning verify on in framework and session controller makes the format check redundant as an invalid formatted session id would never be found in the database or file system)

define the switches in config
config.php

As said, under proper operations, the chance of clashing sessions is very small but not zero and when it does happen, the impact/embarassment is rather large.
So make your own choice on whether the yield is worth the extra effort.

as mentioned, bots do not retain sessions and as such they invoke a new session generation on each request.
That is why the vast majority of your sessions in your session table or file system are for bots (you thought you had a lot of customers).

Since bots have no use for them beyond the lifetime of the script, those sessions can be removed immediately after script end.
(in the olden days it was common practice to not issue sessions to bots in the first place but that is not a good practice as the script itself and the content it produces relies on variables stored and used in the session during execution)

We do this by identifying public bots (those identifying themselves as such via the user agent, assuming everybody knows how to do that) and direct the session class to delete the session upon final session close (script end).

In catalog/controller/startup/startup.php we identify if the request comes from a public bot.
If it is, we set a session variable:

in system/library/session.php we change the shutdown function.
change:

to:

That way any session given to a bot is deleted after the script for that request ends and that will reduce the session table/directory to a large extend not waiting for the normal session garbage collection to kick in.

you can of course also just increase the character count of the session id from 32 to say 50.
That also reduces the chance of clashes but it still is not zero (just like the lottery) and as such it is more like an arms-race.
When you resize images, as Opencart does, they become more blurry.
Therefore, you need to sharpen them before saving.

catalog/model/tool/image.php
change:
to:
system/library/image.php
add: