Technical

Plentymarkets: the worst shopping system ever!

At my work we deal often with shopping systems to get orders or customer data and sent it to our API. Most of time using the different shopping systems APIs is easy and quick.. Except for Plentymarkets: it is a nightmare!!!

Plentymarkets newsletter image

While I don’t want to spend more time writing about my experience about this shopping system (because I spent already too much time with it trying to fix small problems) I will just make a quick bullet list of bugs/issues I had with them:

  1. The keep changing the API so often: the changes are big that it breaks your code!
  2. Their documentation is shit and most of it is auto generated.
  3. They send you emails with non working links!
  4. In order to change the shop admin password, you need to call them!! They don’t have a reset password link!
  5. In other shopping systems you have access to a demo account where you can test your API calls, with Plentymarkets they only give you a limited 1 month account that you have to pay for afterwards!
  6. They are advertising the use of REST API instead of SOAP but it never happened! The docs are confusing :/
  7. The admin interface UI and design is awful and not user friendly. Serious shop system some some time and money to get a better UX, unfortunately, it seems not the case with Plentymarkets.
  8. While using a PHP framework is a good idea, Plentymarkets is using Laravel to power up (at least) their REST API: Larevl is based on Symfony, which is much more greater.
    However when I was playing with the rest/orders endpoint I got this uncatched error exception:

    {
    "message": "Type error: Argument 1 passed to Illuminate\\Validation\\Factory::make() must be of the type array, integer given, called in /var/www3/plenty/stable7_a/pl/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php on line 219",
    "status_code": 500
    }
  9. TBC..

A little history about Plentymarkets:
It started out as a contract job for a few eBay PowerSellers. The development began in 2001 and initially focused on providing an interface to eBay, the ability to process orders and an online store. Back then, the software was still called plentyShop.
It developed into a shop system very quickly and then it became an all-in-one solution for managing important e-commerce processes such as B2B and B2C, checkout, content management, invoicing, stock management, after sales management, fulfillment and returns.

Hide password on MinTTY and Cygwin

If you want to use SMB folder sharing for vagrant on a windows machine, you will need to run the CLI as administrator and then provide the login and password. The problem is that MinTTY and Cygwin do not support the no-echo TTY mode.

To fix this I use this code snippet:

# Capture username and password for smb synced folders (Windows)
if OS.windows?
   puts "\e[36mDue to MinTTY and Cygwin not supporting the no-echo TTY mode"
   puts "it is necessary to request your account username and password"
   puts "at this stage in order to correctly setup SMB shared folders.\e[0m"
   puts

   if !parameters['smb_username']
      print "\e[35mEnter username:\e[0m "
      STDOUT.flush
      smb_username = STDIN.gets.chomp
   else
      puts "\e[32mUser name is already supplied in parameters.\e[0m"
      puts
      smb_username = parameters['smb_username']
   end

   if !parameters['smb_password']
      # 8m is the control code to hide characters
      print "\e[35mEnter password:\e[0m \e[0;8m"
      STDOUT.flush
      smb_password = STDIN.gets.chomp
      # 0m is the control code to reset formatting attributes
      puts "\e[0m"
      STDOUT.flush
   else
      puts "\e[32mUser password is already supplied in parameters.\e[0m"
      puts
      smb_password = parameters['smb_password']
   end
end

Fix ERR_ICANN_NAME_COLLISION

I have this vagrant setup where I map my shared folder to a folder where I have all my projects. In windows 10 I got this strange problem from Chrome showing

ERR_ICANN_NAME_COLLISION

After many researches I found out the problem was on the hosts file;

I just had to write each entry in a new line and save; that stupidly fixed it!

Add subdomain to a Let’s Encrypt SSL certificate

First of all, add an entry to the DNS zone; you can do this in your host panel.

Pointer records = A

Target = IP of the server

 

Remove existing one with this command:

sudo rm -rf /etc/letsencrypt/live/example.com

sudo rm /etc/letsencrypt/renewal/example.com.conf

sudo rm -rf /etc/letsencrypt/archive/example.com

Stop server:

sudo service nginx stop

Create a new one certificate:

cd /opt/letsencrypt
./letsencrypt-auto certonly --standalone

Then add all domaines there separated by comma;

example.com www.example.com subdomain1.example.com sub2.example.com

Restart server:

sudo service nginx restart

Update nginx sites

sudo rm /etc/nginx/sites-enabled/example.com

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

From China to Berlin; the flight of a MacBook Air

Ordered a custom MacBook Air, 13″ from Apple Germany on the 16. August 2016 and followed its travel plane 🙂

Berlin, Germany 08/22/2016 6:35 A.M. Out For Delivery
08/22/2016 5:00 A.M. Arrival Scan
Koeln, Germany 08/21/2016 11:53 P.M. Departure Scan
08/21/2016 10:36 P.M. Import Scan
Dubai, United Arab Emirates 08/21/2016 5:20 P.M. Departure Scan
08/21/2016 9:35 A.M. Arrival Scan
Chek Lap Kok, Hong Kong 08/21/2016 6:00 A.M. Departure Scan
Chek Lap Kok, Hong Kong 08/19/2016 7:19 P.M. Arrival Scan
Shenzhen, China 08/19/2016 5:20 P.M. Departure Scan
08/19/2016 5:00 P.M. Export Scan
08/19/2016 5:00 P.M. Origin Scan
China 08/19/2016 4:04 A.M. Order Processed: Ready for UPS

Disable Xdebug when installing Composer packages

Find  the folder where composer is installed:

which composer

Open the composer file and replace:

php "${dir}/composer.phar" $*

with

php -n -d extension=C:/wamp/bin/php/php7.0.7/ext/php_openssl.dll -d extension=C:/wamp/bin/php/php7.0.7/ext/php_pdo_sqlite.dll -d extension=C:/wamp/bin/php/php7.0.7/ext/php_mbstring.dll "${dir}/composer.phar" $*

The parameters we passed to php are:

  • -d foo[=bar] => Define INI entry foo with value ‘bar’
  • -n => No configuration (ini) files will be used

I passed to the -d parameter the extension that I need php to load whenever it executes the composer command, feel free to change those to your needed extensions.

Using Xdebug on windows and PhpStorm

Check which php.ini you are using with  php --ini

Install Xdebug

  • Check which version of PHP are you using, thread safe or not? php -i|grep "Thread"
  • Download the right version for your php version from xdebug.org
  • Copy the downloaded dll into your php extensions, in my case C:/wamp/bin/php/php7.0.7/ext/
  • Edit the php.ini file by adding this :
    ; XDEBUG Extension
    zend_extension="C:/wamp/bin/php/php7.0.7/ext/php_xdebug-2.4.0-7.0-vc14-x86_64.dll"
    
    ; Xdebug features two ways of enabling the profiler, depending on the type of 
    ; application we want to profile. Setting the xdebug.profiler_enable directive 
    ; enables profiling for any application. For web applications, profiling can be 
    ; enabled on demand by specifying a special GET/POST variable or a cookie. 
    ; This can be done using the PhpStorm bookmarklets (or one of the Browser Debugging Extensions) 
    ; and setting the xdebug.profiler_enable_trigger directive to 1 in php.ini.
    ; More at https://confluence.jetbrains.com/display/PhpStorm/Profiling+PHP+applications+with+PhpStorm+and+Xdebug
    
    [Xdebug]
    xdebug.remote_enable=1
    xdebug.remote_port=9000
    
    ; If it is set to 1, then you can enable the profiler by using a GET/POST or 
    ; COOKIE variable of the name XDEBUG_PROFILE. xdebug.profiler_enable needs 
    ; to be set to 0
    xdebug.profiler_enable_trigger=1
    
    xdebug.profiler_enable=0
    xdebug.profiler_output_dir="C:/wamp/logs/profiling"
    xdebug.idekey=PHPSTORM

Using Grunt with requirejs

When using the grunt-contrib-requirejs module, it takes a lot of time to compile the final file. I needed a solution a quicker solution which uses grunt copy to copy files from bower modules folder to my target assets directory.

I can do this manually but I have to update 2 files each time I add a new requirejs dependency; I have to include it in main.js and also in the Gruntfile.js.. That’s a lot of work 🙁

So I finally came up with this approach that does those process above: A nice snippet that allows faster requirejs development.

In the main.js I have a sctructure like this:

require.config({
    paths: {
        /* jQuery */
        'jquery': '../../.build/bower_components/jquery/dist/jquery.min',

I want this to become:

require.config({
    paths: {
        /* jQuery */
        'jquery': 'vendor/jquery.min',

Also, I need the files to be copied to the vendor folder. So In the Gruntfile.js I have:

module.exports = function (grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        www: "../theme/",
        bower: "bower_components/",
        assets: "../.assets/",
        jsFromBower: "",
...
...
// Update paths in main.js
replace: {
    mainjs: {
        options: {
            patterns: [
                {
                    match: /..\/..\/.build\/bower_components\/.*\//mg,
                    replacement: 'vendor/'
                }
            ]
        },
        files: [
            {
                expand: true,
                flatten: true,
                src: ['<%= assets %>scripts/main.js'],
                dest: '<%= www %>assets/scripts/'
            }
        ]
    }
},
...

...
/**
* The dev task.
*
* It checks the main.js file to get bower_components and replaces the entries
* inside of the main.js file. This allows quicker theme developpement.
*/
grunt.registerTask('dev', 'description', function () {
var path = require('path'),
files = 0,
www = grunt.config.get('www'),
assets = grunt.config.get('assets'),
mainjs = grunt.file.read(assets + 'scripts/main.js'),
regexPattern = /(bower_components\/.*)'/gm,
matchArray;

/**
* Loop the main.js file: Since our regular expression uses the "g" flag,
* we can use the exec() method multiple times to find successive matches
* in the same string
*/
while ((matchArray = regexPattern.exec(mainjs)) !== null) {
if (matchArray.index === regexPattern.lastIndex) {
regexPattern.lastIndex++;
}

var filePath = matchArray[1] + '.js',
file = path.basename(filePath);
grunt.file.copy(filePath, www + 'assets/scripts/vendor/' + file);

files++;
}
//grunt.config.set('jsFromBower', files.join())

grunt.log.ok('Coppied ' + files + ' js scripts from "bower_components" to "theme/assets/scripts/vendor/"');

grunt.task.run(['imagemin', 'copy', 'replace', 'compass:dev', 'cssmin', 'watch']);
});

Perfect!

Quick Tip: Search shell for command-line history

While using Babun (a windows shell) sometimes I need to retype a previous command, so i just use the up/down arrow keys to navigate the commands. This is a nice feature, but what about retyping a command that you have used long time ago? no need to keep pushing the arrow keys to get it, just use this quick keyboard shortcut to search through previous commands:

  1. Open your shell and press ctrl + r
  2. Start typing a command
  3. You will have some suggestions
  4. Type again ctrl + r to narrow the suggestions

This is called reverse-i-search and it is available in terminal and bach etc.

From Stackoverflow.com:

Press Ctrl+R and type ssh. Ctrl+R will start search from most recent command to old one (reverse-search). If you have more than one command which starts with ssh, Press Ctrl+Ragain and again until you find the match.

Once you’ve found the match you can press Enter to execute the command or left / right cursor to just select the text of the command.

There is no default reverse option for Ctrl+R to invert the direction of the search but here you will find some suggestions about it.