Private site / shop contribution for osCommerce (2.3.1)

Featured

Although there are many online shopping cart system available and many of them are open source, but you can not find any system that can support a private site. Why do we need private site? Well for wholesale and distribution companies, franchise businesses, many of them do not want the site to be accessible by the public, the shopping cart system only open to their registered customers or member businesses. Here I will show you how to turn osCommerce (V2.3.1) shopping cart system into a private one.

Step 1: Database change

Options add to osCommerce configuration table to provide the switch to turn on/off the private site options. You will need to log in to your site’s phpAdmin page to add there two lines into database

INSERT INTO `configuration`(`configuration_id`,`configuration_title`,`configuration_key`,`configuration_value`,
`configuration_description`,`configuration_group_id`,`sort_order`,`last_modified`,`date_added`,
`use_function`,`set_function`)
VALUES (272,’Must login to access the store’,’CUSTOMER_MUST_LOGIN’,’false’,
‘Whether or not customer must login in order to access the webstore’,1,26,NULL,
‘0000-00-00 00:00:00′,NULL,’tep_cfg_select_option(array(\’true\’, \’false\’), ‘);

INSERT INTO `configuration`(`configuration_id`,`configuration_title`,`configuration_key`,`configuration_value`,
`configuration_description`,`configuration_group_id`,`sort_order`,`last_modified`,`date_added`,
`use_function`,`set_function`)
VALUES (273,’Do not allow customer self-registration’,’NO_CUSTOMER_SELF_CREATION’,’false’,
‘Whether or not allow customers to register in the website by themselves’,1,27,NULL,
‘0000-00-00 00:00:00′,NULL,’tep_cfg_select_option(array(\’true\’, \’false\’),’);

After the modification, in osCommerce administration panel (configuration->My Store), you will be able to see following lines and you can them turn on/off for your needs:

Step 2: Modify file includes/application_top.php

Add following lines after line 300, this will force the customer to login first in order to use the site, if the “Must login to access the store” switch is turned on:
if (defined('CUSTOMER_MUST_LOGIN') &&
CUSTOMER_MUST_LOGIN=='true' &&
!tep_session_is_registered('customer_id') &&
(basename($PHP_SELF)!=FILENAME_LOGIN) &&
(basename($PHP_SELF)!=FILENAME_ABOUT_US) &&
(basename($PHP_SELF)!=FILENAME_CONTACT_US) &&
(basename($PHP_SELF)!=FILENAME_CONDITIONS) &&
(basename($PHP_SELF)!=FILENAME_PRIVACY) &&
(basename($PHP_SELF)!=FILENAME_CREATE_ACCOUNT) &&
(basename($PHP_SELF)!=FILENAME_CREATE_ACCOUNT_SUCCESS) &&
(basename($PHP_SELF)!=FILENAME_PASSWORD_FORGOTTEN) &&
(basename($PHP_SELF)!=FILENAME_LOGOFF) &&
(basename($PHP_SELF)!=FILENAME_PASSWORD_FORGOTTEN) &&
(basename($PHP_SELF)!=FILENAME_SHIPPING))
{
tep_redirect(tep_href_link(FILENAME_LOGIN));
}

Step 3: Remove the customer self-registration to completely make the site private

modify file login.php, add following lines after line 105:

 <?php
   }
 ?>

modify file login.php, add following lines after line 93:
if(!defined('NO_CUSTOMER_SELF_CREATION') || NO_CUSTOMER_SELF_CREATION !='true')
{

Step 4: After removing the customer self-registration, in the administration panel we need function to add new customer.

modify file admin/customers.php, add following lines after line 789:
if(defined('NO_CUSTOMER_SELF_CREATION') && NO_CUSTOMER_SELF_CREATION =='true')
{
echo tep_draw_button(TEXT_INFO_NEW_CUSTOMER, 'plusthick', tep_href_link(FILENAME_CUSTOMERS, 'cID=-1&action=edit'));
}

Step 5: Modify file admin/customers.php to allow adding new customer

Find line 224 to line 228, and change to following:

default:
if (((int)$HTTP_GET_VARS['cID'])'-1',
'customers_gender'=>'',
'customers_firstname'=>'',
'customers_lastname'=>'',
'customers_dob'=>'',
'customers_email_address'=>'',
'customers_pricelist_id'=>'',
'entry_company'=>'',
'entry_street_address'=>'',
'entry_suburb'=>'',
'entry_postcode'=>'',
'entry_city'=>'',
'entry_state'=>'',
'entry_zone_id'=>'',
'entry_country_id'=>'',
'customers_telephone'=>'',
'customers_fax'=>'',
'customers_newsletter'=>'',
'customers_password'=>'',
'customers_default_address_id'=>'');
} else {
$customers_query = tep_db_query("select c.customers_id, c.customers_gender, c.customers_firstname, c.customers_lastname, c.customers_dob, c.customers_email_address, c.customers_pricelist_id, a.entry_company, a.entry_street_address, a.entry_suburb, a.entry_postcode, a.entry_city, a.entry_state, a.entry_zone_id, a.entry_country_id, c.customers_telephone, c.customers_fax, c.customers_newsletter, c.customers_default_address_id from " . TABLE_CUSTOMERS . " c left join " . TABLE_ADDRESS_BOOK . " a on c.customers_default_address_id = a.address_book_id where a.customers_id = c.customers_id and c.customers_id = '" . (int)$HTTP_GET_VARS['cID'] . "'");
$customers = tep_db_fetch_array($customers_query);
}
$cInfo = new objectInfo($customers);

Now we have finished everything, the site can be switched into a private site. If you turned the switches on, the customer will have to login first in order to access the site.

If anyone need the source code, please contact me and I can send the modifications.

Problem with NuGet to install Entity Framework with Visual Studio 2015 Community

Recently in the testing project when installing Entity Framework with following command with NuGet management console:

Install-Package EntityFramework -projectname SportsStore.Domain

The installation failed with following message:

Attempting to gather dependencies information for package ‘EntityFramework.6.1.3’ with respect to project ‘SportsStore.Domain’, targeting ‘.NETFramework,Version=v4.5.1’
Attempting to resolve dependencies for package ‘EntityFramework.6.1.3’ with DependencyBehavior ‘Lowest’
Resolving actions to install package ‘EntityFramework.6.1.3’
Resolved actions to install package ‘EntityFramework.6.1.3’
Adding package ‘EntityFramework.6.1.3’ to folder ‘C:\git\SportsStore\packages’
Added package ‘EntityFramework.6.1.3’ to folder ‘C:\git\SportsStore\packages’
Added package ‘EntityFramework.6.1.3’ to ‘packages.config’
Executing script file ‘C:\git\SportsStore\packages\EntityFramework.6.1.3\tools\init.ps1’
Executing script file ‘C:\git\SportsStore\packages\EntityFramework.6.1.3\tools\install.ps1’Type ‘get-help EntityFramework’ to see all available Entity Framework commands.
Successfully installed ‘EntityFramework 6.1.3’ to SportsStore.Domain
Install failed. Rolling back…
Removed package ‘EntityFramework 6.1.3’ from ‘packages.config’
Removing package ‘EntityFramework 6.1.3’ from folder ‘C:\git\SportsStore\packages’
Access to the path ‘C:\git\SportsStore\packages\EntityFramework.6.1.3\tools\EntityFramework.PowerShell.Utility.dll’ is denied.
Access to the path ‘EntityFramework.PowerShell.Utility.dll’ is denied.
Removed package ‘EntityFramework 6.1.3’ from folder ‘C:\git\SportsStore\packages’
Install-Package : An item with the same key has already been added.
At line:1 char:1
+ Install-Package EntityFramework -projectname SportsStore.Domain
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Install-Package], Exception
+ FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand

One or more packages could not be completely uninstalled: ‘C:\git\SportsStore\packages\EntityFramework.6.1.3’. Restart Visual Studio to finish uninstall.

After google around for a while it turned out that it is the NuGet itself is causing the problem, although the error message seems otherwise. After download and install NuGet Package Manager for Visual Studio 2015, the problem got resolved.

 

 

Stream was not writable error when accessing response stream

I have following code (C#) for reading contents from a given url:


private string GetData(string url)
{
//Initialization
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//This time, our method is GET.
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
using (StreamWriter stOut = new StreamWriter(response.GetResponseStream(), System.Text.Encoding.ASCII))
{
string postData = null;
stOut.Write(postData);
return postData;
}
}
else
{
throw new Exception(string.Format("Error, statuscode: {0}\r\n{1}", response.StatusCode.ToString(), response.StatusDescription));
}
}

But sometime the function will throw error “Stream was not writable” when accessing the response stream, the status code returned is HttpStatusCode.OK, but just cannot access the response stream. On some URLs, the code works fine.

After struggling for a while, I finally found a solution to the error, it turns out that I have to set the Credentials for the request, and with following modification the “Stream was not writable” error was gone and the function works as expected.


request.ContentType = "application/x-www-form-urlencoded";
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

osCommerce V2.3 Canada Post Shipping Service (REST) Module

Canada Post changed its previous Sell Online services with the new REST or SOAP services. Searching the web or in the community contributions, cannot find anything based on this new services. So I decided to write my own module.

REST or SOAP?

Canada Post support two services: REST and SOAP, so which one is the right one? The demo website (it is a demonstration site for a template based on osCommerce V2.3, do not place any order and pay with your credit card!) I am testing on is hosted on IPOWER, I was having problem with the SOAP service, since by default, the SOAP module is not enabled! The REST service is based on XML and is available almost on any hosting services, so I turned to the REST service for Canada Post.

What do you need?

In order to use the Canada Post service, you will need to have a customer number and API Keys from them. Please refer to here for more details.

Dimension support

The shipping rate for Canada Post service is based on parcel weight, it also support the dimensions (length/height/width) to get more accurate rate. So both are considered in my implementation. This module also supports packing, for example, pack several small items into one box for shipping. The packing support is based on one of the community contribution for upsxml, so the credits goes to the original contributor, although I have modified it to work better with osCommerce V2.3.

The setup screen for Canada Post shipping module

setup screen shot Canada Post shipping module for osCommerce

Now here is the shopping cart check out screen using Canada Post Service

This Canada Post shipping module supports shipping to Canada, US and other countries (internationally). There is one thing I do not like about this Canada Post service is, if I have more than one parcel to send, I have to call Canada Post service twice to get the rates separately. Unlike UPS, you could define all the items in one request. But anyway, it works this way and I am happy it is been done.

UPS XML Shipping Module for osCommerce V2.3.1

Recently trying to add UPS shipping support for my new modified osCommerce (v2.3.1) site, but the contribution found on osCommerce website or recommended by UPS is not all ready for V2.3.1 . Along the way during the installation it did give me some troubles, some of them related to my developing environment (windows+xampp) and others need to be improved, but pretty much most of the things worked just fine.

Some special notes here for the modifications:

  • curl support has to be enabled:
    From followings files from your xampp folder:
    php5.ini
    php\php.ini
    php\php4\php.ini
    apache\bin\php.ini
    find line ;extension=php_curl.dll and remove the ‘;’ (semi-colon) before this line in each file and save, remember to start your apache server
  • Mercury email server need to enabled, otherwise turn off the “Email UPS errors”.
  • uncomment following lines from includes/modules/shipping/upsxml.php
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    otherwise it gave me an error in my testing environment:
    Error from cURL: Error [60]: SSL certificate problem, verify that the CA cert is OK. Details:
    error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed experienced by customer with id 1 on 2012-10-01 10:17:50

The file, “admin/packaging.php”, has been modified to work with osCommerce V2.3.1; the file from original contribution is for osCommerce V2.2. You can download the modification from www.4zmagic.com. Please note that in this modification, product package split and boxes used options from the original contribution are not included.

Obviously, in order to test the UPS shipping module, you will need a testing account from UPS.

GlobalPayment Credit Card Processing Issue with Vx810

One of the credit card processing modules I have done for ACE Retail POS 3000 is to integrate with Globalpayment, using Verifone PINPAD Vx510 or Vx810. The problem I had for one customer TOSS & SERVE was that sometimes the PINPAD would hold the POS for update to minutes and building the frustration for customers and cashiers.

Further analyzing from the processing logs, I found that some transactions (several out of hundreds) would hold POS for exactly 60 seconds and it happens after the POS got the response from the pinapd. I went over from the code, checking the documents and protocols (ECR Interface for Softpay V2 and V3), discussed the matter with support from Global, nothing seems wrong. But from this precise 60 seconds delay, it hardly could be a software mistake in my code. Unfortunately I have no access to get the inside information from Vx810.

After getting the response from the pinpad, there are only two related things happens regarding the pinpad, sening an acknowledgement signal, ACK, to the pinpad and close the serial port. it is so simple that no one could make mistake here. While all the efforts seem getting nowhere, one line of my code, just for precaution, actually solved the problem: I added a 100 milliseconds delay before sending ACK signal.

I guess that, somehow and sometimes, the Vx810 pinpad may not expect the ACK right away. Since I have no access to the internal process to the Vx810, why it would hold for exactly 60 seconds (several out of hundreds transactions) remains a mystery. But I am glad finally solved the issue: after adding the 100 milliseconds delay, there never been any report of such problem.