Microsoft Commerce Server 2000 Pocket Consultant

Microsoft Commerce Server 2000 Pocket Consultant

by Bradley M Wist, Brad Wist
     
 

This is the ideal concise, on-the-go reference that administrators and developers will want to carry with them as they support and manage this server product. Designed as a quick reference for both developers and IT Professionals, this pocket handbook is a concise guide to Commerce Server's management services and tools, site architecture and basic components, user

Overview

This is the ideal concise, on-the-go reference that administrators and developers will want to carry with them as they support and manage this server product. Designed as a quick reference for both developers and IT Professionals, this pocket handbook is a concise guide to Commerce Server's management services and tools, site architecture and basic components, user authentication, catalogue system, site transactions, business processing pipelines, and more.

Product Details

ISBN-13:
9780735614161
Publisher:
Microsoft Press
Publication date:
12/01/1901
Edition description:
REV
Pages:
479
Product dimensions:
5.49(w) x 7.98(h) x 1.33(d)

Read an Excerpt

Chapter 15.
Order Processing


  • Creating the OrderGroup Object
  • Working with the Basket
    • Creating a New Basket
    • Retrieving a Basket
    • Retrieving the OrderForm from the OrderGroup
    • Displaying the Basket
    • Running a Pipeline
    • Adding an Item to the Basket
    • Removing an Item from the Basket
    • Removing All Items from the Basket
    • Aggregating Items in the Basket
    • Saving the Basket
  • Processing the Order
    • Adding Addresses
    • Adding Shipping Types and Charges
    • Adding Handling Charges
    • Adding Taxes
    • Calculating the Total Cost of the Order
    • Getting Payment Information
    • Getting Order Confirmation
    • Verifying the Order Amounts
    • Saving Data from the OrderForms
    • Saving an Order
  • Postorder Processing
    • Sending E-Mail for an Order
    • Submitting an Order to BizTalk
    • Displaying a Receipt
  • Order Templates
    • Saving an Order Template
    • Loading an Order Template
    • Adding Items from a Template
  • Order Processing Components Reference
    • DictionaryXMLTransforms
    • ShippingMethodManager


Chapter 15   Order Processing

As your shopper moves through the purchase process, there are a number of common tasks that you'll need to perform. You'll use the OrderGroup, OrderForm, Dictionary, SimpleList, and other objects to aid in processing the order. In addition, you'll probably have to write some SQL stored procedures and perform queries using ActiveX Data Object (ADO). You'll certainly make use of pipelines, which are covered in the next few chapters, to assist in processing the order through the system.

Creating the OrderGroup Object

As Chapter 14, "Order Basics," pointed out, the OrderGroup object is the central element to working with orders in your system. The first step in working with the object is to create and initialize it. You'll create the object using the PROGID Commerce.OrderGroup and initialize it by providing the database connection string to the database that holds the order information (see the following code sample). You also provide a user ID that you are using to open the order. Typically, this is the user ID of the currently logged in user. However, if this user is an administrator, he or she can open another user's order and must specify that user's ID.

Set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize _
   mscsOptionsDictionary.s_TransactionConfigConnectionString, _
   mscsUserProfile("generalinfo.user_id").value

Working with the Basket

There are a number of tasks that you'll need to complete when working with the user's basket or shopping cart, which are detailed in the following sections.

Creating a New Basket

You can create a new basket by loading and initializing an OrderGroup. If there are no OrderForms in the OrderGroup, you can create a default OrderForm. To do so, you'll create an OrderForm object and then add it to the OrderGroup using the AddOrderForm method, shown in the following code sample:

if mscsOrderGroup.Value.orderforms.count = 0 then

   set mscsOrderForm = server.CreateObject("Commerce.OrderForm")

   call mscsOrderGroup.AddOrderform(mscsOrderForm,"default")

   mscsOrderGroup.SaveAsBasket

   mscsOrderGroup.LoadBasket

end if

Retrieving a Basket

You can retrieve the shopper's current basket by using the OrderGroup's LoadBasket method, shown here:

Call mscsOrderGroup.LoadBasket()

The basket is identified by the user's ID in the database, and that ID is used
to retrieve the basket. That ID was specified when the OrderGroup object was initialized.

Retrieving the OrderForm from the OrderGroup

At times, you might need to retrieve an individual OrderForm from the OrderGroup. The OrderForms within the OrderGroup are identified by name of the trading partner with which they're associated. If the OrderForm is not associated with a trading partner, the name will be default, as shown here:

Set mscsOrderForm = mscsOrderGroup("OrderForms").Value("default")

Another syntax that performs the same function is the following:

Set mscsOrderForm = mscsOrderGroup.Value.OrderForms("default")

This is because the OrderGroup is another special implementation of the
Dictionary object, and values are retrieved in the same manner as all of the rest of the keys in a Dictionary.

You can also identify the number of OrderForms that are in the OrderGroup using this code:

lCount = mscsOrderGroup.Value.OrderForms.Count

Displaying the Basket

Building a basket page entails retrieving the data from each OrderForm and listing them on the page. Of course, you must recognize that many of the values you might want to display are added to the OrderForm through various pipeline components. In particular, item data is completed by being retrieved from the database or being calculated. For instance, the line item subtotal (_cy_oadjust_adjustedprice) is calculated based on the quantity of items (quantity) being ordered and the current price (_cy_iadjust_currentprice) of the item.


NOTE:
Any key in the order that begins with the underscore character ( _ ) needs to be created after the OrderGroup is loaded, because these values are not persisted in the database. Typically, these values are completed through the pipelines.

Displaying a Single OrderForm Basket

To start, you can directly build a page that displays the order entry by retrieving the desired values and retrieving each of the items from the order. You'll start by retrieving the Basket from the OrderGroup object, as shown in the following code sample:

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
m_UserGUID

mscsOrderGroup.LoadBasket

You'll then run the basket pipeline, which is covered in more detail in the following chapters. For now, you'll have to accept this at face value. The pipeline helps populate some of the calculated keys in the OrderForm, using the code shown here:

Call RunPipe("basket", mscsOrderGroup)

Then, you'll retrieve the default OrderForm and parse through it to get the item values that you want to display, as shown in in the following code sample:

Set mscsOrderForm = mscsOrderGroup.Value.OrderForms("default")

htmItems = "<table><tr>" _
   & "<th>Product ID</th>" _
   & "<th>Description</th>" _
   & "<th>Qty</th>" _
   & "<th>Price</th>" _
   & "<th>Total</th>" _
   & "<th>Remove</th>" _
   & "</tr>"

For i = 1 to mscsOrderForm.Items.Count

   Set item = mscsOrderGroup.GetItemInfo(i-1, "default")

   htmItems = htmItems & "<tr>" _
      & "<td>" & item("_product_name") & "</td>" _
      & "<td>" & item("_product_description") & "</td>" _
      & "<td>" & item("quantity") & "</td>" _
      & "<td>" & _
         mscsDataFunctions.LocalizeCurrency( _
         item("_cy_iadjust_currentprice"),"1033","$") _
         & "</td>" _
      & "<td>" & _
         mscsDataFunctions.LocalizeCurrency( _
         item("_cy_oadjust_adjustedprice"),"1033","$") _
         & "</td>" _
      & "<td><a href=""_removeitem.asp?i=" & i-1 _
         & """>Remove</a></td>" _
      & "</tr>"

next


NOTE:
In the previous code, as well as other code in this chapter, the LocalizeCurrency method is used to display the local currency. In this code, the locale is hard-coded as 1033. In your code, you should provide a locale for the individual user. See Chapter 8, "Common Components," for more info on the DataFunctions' LocalizeCurrency method.

Finally, you can retrieve the order-level data, which in the case shown in the following code, is the subtotal for the order. You can then display the entire basket contents.

htmItems = htmItems & "<tr>" _
   & "<td colspan=5 align=right><hr width=100></td>" _
   & "</tr>"

htmItems = htmItems & "<tr>" _
   & "<td colspan=4 align=right>SubTotal</td>" _
   & "<td>" & _
         mscsDataFunctions.LocalizeCurrency( _
         mscsOrderForm("_cy_oadjust_subtotal"),"1033","$") _
         & "</td>" _
   & "</tr>"

htmItems = htmItems & "</table>"

htmPage = "<HTML><HEAD></HEAD><BODY>" _
   & htmItems _
   & "</BODY></HTML>"

Response.Write htmPage

Displaying a Multiple OrderForm Basket

Of course, if you're attempting to display items that are listed in separate OrderForms within the OrderGroup, you'll have to parse through each OrderForm to retrieve the items from each. You'll also need to retrieve the OrderForm values, such as the subtotal and other values, for each OrderForm and aggregate those as well. To accomplish this, you can modify the previous code to loop through each OrderForm in the OrderGroup and gather the selected data, as shown in the following code:

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
m_UserGUID

mscsOrderGroup.LoadBasket

Call RunPipe("basket", mscsOrderGroup)

htmItems = "<table><tr>" _
   & "<th>Product ID</th>" _
   & "<th>Description</th>" _
   & "<th>Qty</th>" _
   & "<th>Price</th>" _
   & "<th>Total</th>" _
   & "<th>Remove</th>" _
   & "</tr>"

cySubTotal = 0.0

For each OrderFormName in mscsOrderGroup.Value.OrderForms

   Set mscsOrderForm = _
    mscsOrderGroup.Value.OrderForms("OrderFormName")

For i = 1 to mscsOrderForm.Items.Count

   Set item = mscsOrderGroup.GetItemInfo(i-1, "default")

   htmItems = htmItems & "<tr>" _
            & "<td>" & item("_product_name") & "</td>" _
            & "<td>" & item("_product_description") & "</td>" _
            & "<td>" & item("quantity") & "</td>" _
            & "<td>" & _
   mscsDataFunctions.LocalizeCurrency( _
   item("_cy_iadjust_currentprice"),"1033","$") _
   & "</td>" _
               & "<td>" & _
   mscsDataFunctions.LocalizeCurrency( _
   item("_cy_oadjust_adjustedprice"),"1033","$") _
   & "</td>" _
   & "<td><a href=""_removeitem.asp?o=" & OrderFormName _
   & "i=" & i-1 & """>Remove</a></td>" _
               & "</tr>"

next

cySubTotal = cySubTotal + mscsOrderForm("_cy_oadjust_subtotal")

Next

htmItems = htmItems & "<tr>" _
      & "<td colspan=5 align=right><hr width=100></td>" _
      & "</tr>"

htmItems = htmItems & "<tr>" _
      & "<td colspan=4 align=right>SubTotal</td>" _
      & "<td>" & _
      mscsDataFunctions.LocalizeCurrency( _
      cySubTotal,"1033","$") _
      & "</td>" _
      & "</tr>"

htmItems = htmItems & "</table>"

htmPage = "<HTML><HEAD></HEAD><BODY>" _
      & htmItems _
      & "</BODY></HTML>"

Response.Write htmPage

Running a Pipeline

To populate the nonpersistent and calculated values in the order, you'll run an appropriate Order Processing Pipeline by calling the RunPipe method of the OrderGroup object, as shown here:

Call mscsOrderGroup.RunPipe(sPath & sPipeFile, _
"Commerce.MtsPipeline", dContext)


NOTE:
Building and running pipelines is covered in more detail in Chapter 16, "Pipelines," and Chapter 17, "Building Custom Pipeline Components."

Adding an Item to the Basket

To add an item to the user's basket, you'll first build a Dictionary object that holds all of the desired item data. At a minimum, you must provide the following fields:

  • product_catalog
  • product_id
  • product_variant_id (if this product is a variant)
  • quantity

You'll then call the OrderGroup's AddItem method, providing the Item Dictionary and the name of the OrderForm to which the item should be added, as seen here:

set dItem = Server.CreateObject("Commerce.Dictionary")

dItem.product_id = _
"Microsoft Age of Empires II: The Age of Kings: Inside Moves"

dItem.quantity = iQty

dItem.product_catalog = sCatalogName

dItem.product_catalog_base = sCatalogName

mscsOrderGroup.AddItem dItem, "default"

mscsOrderGroup.SaveAsBasket

Removing an Item from the Basket

If a user wants to remove an item from his or her basket, you'll need to provide a means to do so. To do this, you specify the index value for the line item in the RemoveItem method of the OrderGroup, as shown in the following code:

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _    m_UserGUID

mscsOrderGroup.LoadBasket

iLineItem = Request.QueryString("i")

Call mscsOrderGroup.RemoveItem(i, "default")

mscsOrderGroup.SaveAsBasket

Removing All Items from the Basket

A user might want to empty his or her basket completely, removing all of the items. This can be done by calling the RemoveItem method without specifying a line item index, as shown in in the following code:

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _    m_UserGUID

mscsOrderGroup.LoadBasket

Call mscsOrderGroup.RemoveItem(,"default")

mscsOrderGroup.SaveAsBasket

Aggregating Items in the Basket

A shopper might add an item to the basket more than once. You can leave the item in the basket multiple times, but you might want to combine those line items so that the item only appears once in the basket. Of course, you'll need to modify the quantity for the item to reflect each of the instances of the item in the OrderForm.

The first thing you'll do is retrieve the Basket and OrderForm for which you'll be aggregating values. You'll also need a Dictionary object that will hold each of the items being processed, as shown in in the following code. Each item will be held in the Dictionary with a unique key, based on the product_id.

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
m_UserGUID

mscsOrderGroup.LoadBasket

Set mscsOrderForm = mscsOrderGroup.Value.OrderForms("default")

set dItem = createobject("Commerce.Dictionary")

set slItems = createobject("Commerce.SimpleList")

You'll then loop through each item in the OrderForm. If the item has not yet been added to the temporary Item Dictionary, you'll add it, as shown here:

i = 0

For Each Item In mscsOrderForm.items

   If IsNull(dItem(Item.product_id)) Then

   ‘this is a new product, just add the dictionary to it

      i = i + 1

      Item.lineitem_id = i

      Set dItem(Item.product_id) = Item

If the item has already been added, you'll increment the quantity in the item in the temporary Item Dictionary by the amount in the current instance of the product, as shown in the following code:

   Else

      ‘this product exists, add the quantities

      Set d = dItem(Item.product_id)

      d.quantity = cint(d.quantity) + cint(Item.quantity)

      Set dItem(Item.product_id) = d

   End If

Next

After you've parsed through each of the items in the Basket, you'll build a SimpleList of each of the aggregated items, as shown in in the following code. This list is then saved back into the OrderForm, replacing the existing Items SimpleList.

For Each vItem In dItem

   slItems.Add dItem(vItem)

Next

Set mscsOrderForm.items = slItems

mscsOrderGroup.SaveAsBasket


TIP:
You can perform the code necessary to aggregate the products in the basket in Activer Server Page (ASP). However, this is a very good candidate to be moved into a Pipeline component, which is discussed in Chapter 17, "Building Custom Pipeline Components."

Saving the Basket

When you make a change to the basket, you must remember to call the SaveAsBasket method of the OrderGroup, shown here, to save those changes to the database:

mscsOrderGroup.SaveAsBasket

Otherwise, when you leave the current page and the OrderGroup object goes out of scope, the changes are lost.

Processing the Order

Once shoppers have finished adding items to their basket, it is time to process and accept the order. Before the order can be completed, a number of tasks must be accomplished to gather the needed information, including the following:

  • Get shipping and billing address information
  • Select shipping method
  • Calculate shipping, handling, and tax charges
  • Get payment information
  • Confirm the order

Adding Addresses

Chapter 10, "Profiles," discussed the use of an address book, which allows users to select an address they have already created and use it for shipping or billing. In gathering the shipping and billing addresses for users, you should allow them to select from their address book, but you should also provide a means for them to enter a new address. In either case, the process of adding the address to the order is the same, with a small difference.

The first step is to gather the address and place it in a Dictionary object with the desired fields, as shown in the following sample. If the address is an existing one, you can retrieve it from the Address profile object.

const BOTH_ADDRESS = 0

const SHIPPING_ADDRESS = 1

const BILLING_ADDRESS = 2

Set dAddress = server.CreateObject("Commerce.Dictionary")

‘Identifies the Address ID, if existing Address

guidAddress = Request.QueryString("AddressID")

‘Identifies the Type of Address

iAddressType = Request.QueryString("AddressType")

if len(guidAddress)>0 then

   ‘got an Address ID, so we can retrieve the Address from the        ‘Profile

   bNewAddress=False

   set mscsAddressProfile = _
    mscsProfileServer.GetProfile(guidAddress, "Address")

   dAddress.Address_Line1 = _
    mscsAddressProfile("GeneralInfo.Address_Line1").value

   dAddress.Address_Line2 = _
    mscsAddressProfile("GeneralInfo.Address_Line2").value   

   dAddress.City = mscsAddressProfile("GeneralInfo.City").value

   dAddress.Region_Code = _
    mscsAddressProfile("GeneralInfo.Region_Code").value

   dAddress.State_Name = _
    mscsAddressProfile("GeneralInfo.State_Name").value

   dAddress.Postal_Code = _
    mscsAddressProfile("GeneralInfo.Postal_Code").value

   dAddress.Country_Code = _
    mscsAddressProfile("GeneralInfo.Country_Code").value

   dAddress.Country_Name = _
    mscsAddressProfile("GeneralInfo.Country_Name").value

   dAddress.Last_Name = _
    mscsAddressProfile("GeneralInfo.Last_Name").value

   dAddress.First_Name = _
    mscsAddressProfile("GeneralInfo.First_Name").value

If the address is a newly entered one, you can pass the address values into the processing form through the QueryString or a Hypertext Markup Language (HTML) post and retrieve them from the Request object. The one difference is that you must generate an Address ID for the new address so that it can be properly referenced in the order. You can do this using the Commerce Server GenID object's GenGUIDString method, shown here:

else

   ‘This is a newly entered address

   ‘it's being passed in through the QueryString from another
    ‘page

   bNewAddress=true

   set mscsGenID = Application("MSCSGenID")

   guidAddress = mscsGenID.GenGUIDString

   dAddress.Address_Line1 = Request.QueryString("Address_Line1")

   dAddress.Address_Line2 = Request.QueryString("Address_Line2")

   dAddress.City = Request.QueryString("City")

   dAddress.Region_Code = Request.QueryString("Region_Code")

   dAddress.State_Name = Request.QueryString("State_Name")

   dAddress.Postal_Code = Request.QueryString("Postal_Code")

   dAddress.Country_Code = Request.QueryString("Country_Code")

   dAddress.Country_Name = Request.QueryString("Country_Name")

   dAddress.Last_Name = Request.QueryString("Last_Name")

   dAddress.First_Name = Request.QueryString("First_Name")

end if

dAddress.Address_Type = iAddressType

You'll then have to retrieve the user's basket, as shown in here, which you've done on other pages.

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
   mscsUserProfile("GeneralInfo.user_id").value

mscsOrderGroup.LoadBasket

If this address can be used as a shipping address, you'll save it to the order using the OrderGroup's SetAddress method. You'll then identify it as a shipping address using the SetShippingAddress method (see the following code). This method assigns the shipping address to the specified items in the selected OrderForms. If you don't specify the items or the OrderForm, it is assigned to all items in all OrderForms in the order.

if iAddressType = BOTH_ADDRESS _
   or iAddressType = SHIPPING_ADDRESS Then

   mscsOrderGroup.SetAddress(guidAddress, dAddress)

   mscsOrderGroup.SetShippingAddress(guidAddress,True)

end if

If this address is to be used as the billing address, you can save it to the order using the OrderGroup's SetAddress method. You can also identify it on each OrderForm as the billing address by saving it to a key, in this case billing_address_id, on each OrderForm using the OrderGroup's PutOrderFormValue method, as shown here:

if iAddressType = BOTH_ADDRESS _
   or iAddressType = BILLING_ADDRESS Then

   mscsOrderGroup.SetAddress(guidAddress, dAddress)

   mscsOrderGroup.PutOrderFormValue("billing_address_id", _
    guidAddress)

end if

The Solution Sites that Microsoft developed identify addresses as being one of three potential types, as follows:

  • Neutral: 0
  • Shipping: 1
  • Billing: 2

Adding Shipping Types and Charges

You'll have to provide a way for your users to select the shipping method they want to use, if more than one method is available. Typically, sites provide a standard shipping option and an express shipping option. Of course, the rates for each would depend on the shipping service being used.

The first step in providing shipping support is to build a page that displays the shipping methods supported by your site. You can retrieve this list using the ShippingMethodManager object's GetInstalledMethodList method, shown in the following code. This returns a recordset that lists the shipping methods that have been installed for this site. Once you have that recordset, you can display it so that shoppers can select the method they wish to use.

Set mscsShipMgr = _
Server.CreateObject("Commerce.ShippingMethodManager")

Call mscsShipMgr.Initialize( _
mscsOptionsDictionary.s_TransactionConfigConnectionString)

aShipCols = Array("shipping_method_id", _
"shipping_method_name", "description")

Set rsShippingMethods = _
mscsShipMgr.GetInstalledMethodList("enabled=1", _
"shipping_method_name", aShipCols)

htmShipMethods = "<FORM ACTION=""_setship.asp"" METHOD=POST>" _
& "<table>"

do while not rsShippingMethods.Eof   
htmShipMethods = htmShipMethods & "<TR>" _
   & "<TD><INPUT TYPE=radio NAME=""shipping_method_id""" _
      & "" VALUE=""" _
      & rsShippingMethods("shipping_method_id") & """></TD>" _
   & "<TD>" & rsShippingMethods("shipping_method_name") _
   & "</TD>" _
   & "<TD>" & rsShippingMethods("description") & "</TD>" _
   & "</TR>"

rsShippingMethods.MoveNext

loop

htmShipMethods = htmShipMethods _
   & "</TABLE><INPUT TYPE=SUBMIT></FORM>"

htmPage = "<HTML><HEAD></HEAD><BODY>" _
   & htmShipMethods _
   & "</BODY></HTML>"

Response.Write htmPage

Once the user has selected the shipping method, you can pass it into a processing page that saves that method to each item in the order (see the following code). The shipping method should be noted for each item in the order. This is used by the shipping components in the pipeline to define the separate shipments and the shipping costs.

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
mscsUserProfile("GeneralInfo.user_id").value

mscsOrderGroup.LoadBasket

sSelectedMethodID = Request.Form("shipping_method_id")

Set mscsShipMgr = _
Server.CreateObject("Commerce.ShippingMethodManager")

Call mscsShipMgr.Initialize( _
mscsOptionsDictionary.s_TransactionConfigConnectionString)

aShipCols = Array("shipping_method_id", _
"shipping_method_name", "description")

Set rsShippingMethods = _
mscsShipMgr.GetInstalledMethodList("shipping_method_id='" _
& sSelectedMethodID & "‘", _
"shipping_method_name", aShipCols)

sSelectedMethodName = rsShippingMethods("shipping_method_name")

Call mscsOrderGrp.PutItemValue("shipping_method_id", _
sSelectedMethodID, True)

Call mscsOrderGrp.PutItemValue("shipping_method_name", _
sSelectedMethodName, True)


NOTE:
The method shown of retrieving the shipping method name can probably be improved, potentially through the use of a stored procedure that retrieves the data directly. This is left for you to implement if you wish.

Adding Handling Charges

You can set up your site to apply handling charges to an order if you wish. Most commonly, handling charges are applied by a pipeline component. If you want to apply handling charges in your code, you'll save the handling charges for the order in the _cy_handling_total key in the order, as shown here:

mscsOrderForm("_cy_handling_total") = "10.00"

Adding Taxes

Taxes are generally applied by using a pipeline component that calculates taxes for the order. There are a number of third-party tax calculation components that are available for use on your site. These components are better suited to tracking and applying the variety of tax laws throughout the world than anything you're likely to develop for your own site.


NOTE:
Microsoft Commerce Server 2000 provides a sample tax calculation pipeline component that can be used for development purposes. However, this component should be replaced in a production environment with the third-party component of your choice.

The calculated tax for an order is saved in the _cy_tax_total key of the OrderForm.

Calculating the Total Cost of the Order

Of course, you'll also need to calculate the total cost of an order. This is another process that is appropriately handled by a pipeline component, in this case, DefaultTotalCy. This component sums the values from the following keys:

  • _cy_oadjust_subtotal
  • _cy_shipping_total
  • _cy_handling_total
  • _cy_tax_total

It then puts the calculated total into the _cy_total_total key. This is the final cost of the order and the amount charged to the user.

Getting Payment Information

If you're going to accept payment on your site by credit card, you'll need to provide a form to gather payment information. Table 15-1 lists the minimum fields you need to provide information for, as well as the fields in the OrderForm.

Table 15-1. Minimum Data Required to Accept and Validate Credit Card Information

Required InformationField in OrderForm
Type of credit card (VISA, MasterCard, American Express, and so on)cc_type
Credit card number_cc_number
Expiration month_cc_expmonth
Expiration year_cc_expyear
Name on credit cardcc_name

When a credit card is authorized for payment, you'll save the authorization code in the OrderForm's _payment_auth_code field.

You should note that the credit card number is, by default, not persisted in the database. However, you might notice on the Internet that some sites maintain the last four or five digits of the credit card number with the data for verification purposes. You can do this using code similar to the following:

mscsOrderForm("cc_savedigits") = _
right(mscsOrderForm("_cc_number"), 5)

Credit card authorization is generally performed using a pipeline component. There are a number of third-party components that perform credit card validation and authorization to process payments. When selecting the component that is right for you, check with your bank to see what systems they can work with.


NOTE:
Commerce Server 2000 provides a sample credit card validation pipeline component, but this component does not accept and process credit card payments. You should replace it with an appropriate third-party component that performs credit card acceptance, authorizations, and validation.

Getting Order Confirmation

After you have gathered all of the payment information and calculated all of the pricing for the order, you should display all of this information to the user one last time. You then ask the user to confirm the order. Until the user does so, the order should not be processed and payment should not be accepted and authorized.

The process of displaying this information is much like that shown previously for displaying the basket. However, this page also retrieves values for the shipping and billing addresses, as well as the calculated shipping, handling, tax, and total amounts.

Once the order is confirmed by the user, you typically run the order through the final pipeline process that includes the components that accept and authorize the payment. In the Solution Sites, this is done with the CHECKOUT.PCF pipeline.

Verifying the Order Amounts

When you display the order confirmation page, as previously described, you should also generate verification fields that can be submitted when users confirm their purchase. These fields should contain the values that have been shown to the user, such as the order total. These fields are submitted to the processing page and used to verify that the calculated amounts have not changed, for any reason, from what the user last saw and confirmed. If the values have changed, the order confirmation screen should be redisplayed. This ensures that the payment is authorized only for the amount that the user confirmed.

You can easily generate the Verification fields using the AppFramework component's VerifyWith method, as follows:

htmVerifyWith = mscsAppFrameWork.VerifyWith(mscsOrderForm, _
"_cy_total_total")

This results in hidden fields being built for the form based on the key that you've specified for the OrderForm, such as the following:

<INPUT TYPE=HIDDEN NAME="_VERIFY_WITH"        VALUE="_cy_total_total=123.45">

Saving Data from the OrderForms

When you have finished processing an order, you'll want to save some of the data so that it can be persisted in the database. This data is needed when you display an order receipt or report on previous orders. You don't want to regenerate these values, as some of the pricing might change and you want to see the calculated values as they were when the purchase was made. The CopyFields scriptor component in the TOTAL.PCF pipeline is used to copy the fields from the key names that are prefixed with the underscore character ( _ ). In addition, it copies values from the user and organization profiles to the order. If you want to save fields beyond those in the default scriptor, you can modify the script to copy additional fields. The original scriptor code is provided in the following code:

Function MSCSExecute(config, orderform, context, flags)

   Dim objProfileService, user_id, objUserProfile, org_id, _
    objOrgProfile

   MSCSExecute = 1

   ‘Copy some lineitem-level fields

   Dim item

   For Each item In orderform.value("items")

      item.value("cy_unit_price") = _
         item.value("_cy_iadjust_regularprice")

      item.value("cy_lineitem_total") = _
         item.value("_cy_oadjust_adjustedprice")

      item.value("description") = _
         Mid(item.value("_product_description"), 1, 127)

   item.value("saved_product_name") = _                  item.value("_product_name")

   Next

   ‘Copy some orderform-level fields

   orderform.value("saved_cy_oadjust_subtotal") = _
      orderform.value("_cy_oadjust_subtotal")

   orderform.value("saved_cy_total_total") = _
      orderform.value("_cy_total_total")

   orderform.value("saved_shipping_discount_description") = _
      orderform.value("_shipping_discount_description")

   ‘ **************************************************

   ‘ Copy some fields from the user profile

   ‘ **************************************************

   ‘ Get the ProfileService from the context

   If Not IsObject(context.ProfileService) Then Exit Function

   Set objProfileService = context.ProfileService

   If objProfileService Is Nothing Then Exit Function

   ‘ Get the user's profile

   user_id = orderform.user_id

   If IsNull(user_id) Then Exit Function

   Set objUserProfile = objProfileService.GetProfileByKey( _
    "user_id", user_id, "UserObject", False)

   If objUserProfile Is Nothing Then Exit Function

   ‘ Copy some user fields

   orderform.Value("user_first_name") = _
    objUserProfile.Fields.Item("GeneralInfo.first_name")

   orderform.Value("user_last_name") = _
    objUserProfile.Fields.Item("GeneralInfo.last_name")   

   orderform.Value("user_email_address") = _
    objUserProfile.Fields.Item("GeneralInfo.email_address")

   orderform.Value("user_tel_number") = _
    objUserProfile.Fields.Item("GeneralInfo.tel_number")

   orderform.Value("user_fax_number") = _
    objUserProfile.Fields.Item("GeneralInfo.fax_number")

   ‘ Get the user's org profile, if avail

   org_id = objUserProfile.Fields.Item("AccountInfo.org_id")

   If IsNull(org_id) Then Exit Function

   Set objOrgProfile = _
    objProfileService.GetProfileByKey("org_id", _
    org_id, "Organization", False)

   If objOrgProfile Is Nothing Then Exit Function

   ‘ Copy some org fields

   orderform.Value("user_org_id") = org_id

   orderform.Value("user_org_name") = _
    objOrgProfile.Fields.Item("GeneralInfo.name")

End Function

Saving an Order

Once the processing of an order is completed, it is ready to be saved as a new order with the OrderGroup's SaveAsOrder method. This method, shown here, saves the data in the OrderGroup into the various Order tables in the database:

call mscsOrderGroup.SaveAsOrder(lOrderNumber)

In addition, if you provide a variable as the parameter, it returns the value of the Order Tracking Number for the order.

If the OrderGroup already has an order_number key, a new tracking number is not generated. However, if the key does not exist, it is created. A number is retrieved from the Counters table in the database from an entry named OrderTracking.

If you want to use an Order Number from another system, perhaps an external Enterprise Resource Planning (ERP) system, you can retrieve the order number and write it into the OrderGroup's order_number key as follows:

oOrderGroup("order_number") = lOrderNumber

Postorder Processing

Once the order has been completed, there might be additional tasks that you want to complete to finish the order processing. Each of these items is optional, but each can be useful, depending on the system you have in place. For instance, you might want to send an e-mail to confirm the purchase to the user. You also might want to submit the order to BizTalk to be automatically transmitted to your suppliers. You can do this in two ways: by submitting the order directly to BizTalk or by placing the order in a queue and having BizTalk retrieve the order from the queue.

Sending E-Mail for an Order

If you want to send an order confirmation e-mail to your user, you can do so using Collaboration Data Objects (CDO) for Microsoft Windows 2000. This functionality allows you to send Simple Mail Transfer Protocol (SMTP) e-mail from your Web server, and can be configured to send it through any available SMTP server. In this case, you might create a SendMail function, as shown in the following code, that you can use in various places throughout your site.

function SendMail(sTo, sFrom, sSubject, sBody)

   Dim iMsg

   Set iMsg = CreateObject("CDO.Message")

   Dim iConf

   Set iConf = CreateObject("CDO.Configuration")

   Dim Flds

   Set Flds = iConf.Fields

   Flds.Item( _
    "http://schemas.microsoft.com/cdo/configuration/sendusing") _
      = 2 ‘ cdoSendUsingPort

   Flds.Update

   Set iMsg.Configuration = iConf

   iMsg.To         = sTo

   iMsg.From         = sFrom

   iMsg.Subject   = sSubject

   iMsg.TextBody   = sBody

   iMsg.Send

end function

With this function, you can send an e-mail to the current user by calling the function and passing the user's e-mail address, as well as the contents of the
e-mail, as shown here:

Call SendMail(mscsUserProfile( _
   "GeneralInfo.Email_address").value, _
   "customerservice@mysitename.com", _
sSubject, _
sBody)

Submitting an Order to BizTalk

When the order is completed, you may wish to automatically transmit the appropriate contents to your suppliers. This is an excellent use for BizTalk Server. You can submit the order as an Extensible Markup Language (XML) document from Commerce Server to BizTalk. The first step is converting the order from the internal Commerce Server 2000 representation to an XML document. You'll do this using the DictionaryXMLTransforms object. This process requires three steps:

  1. Create the DictionaryXMLTransforms Object, as shown here:
  2.       Set mscsXMLTransforms = _
    Server.CreateObject("Commerce.DictionaryXMLTransforms")

  3. Load the appropriate XML schema for the Commerce Server order using the GetXMLFromFile method. The POSCHEMA.XML file that accompanies the Solution Sites provides an excellent starting point for this document. You can modify it as needed to include additional fields:
  4.       sFilePath = Server.MapPath("\" & _
    mscsAppFrameWork.VirtualDirectory) & "\poschema.xml"

          Set mscsXMLSchema = _
    mscsXMLTransforms.GetXMLFromFile(sFilePath)

  5. Generate the XML document that contains the order using the GenerateXMLForDictionaryUsingSchema method, shown here:
  6.       Set mscsOrderformXML = _
    mscsXMLTransforms.GenerateXMLForDictionaryUsingSchema( _
    mscsOrderForm, mscsXMLSchema)

          sXML = mscsOrderformXML.xml

Submit Using BizTalk Interchange

Once you have converted the OrderForm into an XML document, you can submit it to BizTalk using the Interchange object. This will work if the BizTalk objects are loaded locally on the Web server. You can start by reading the BizTalk configuration settings from the App Default Configuration (see the following code). These must be set using Commerce Server Manager (see Figure 15-1).

‘––––––––––––––––––––

‘Set the BizTalk document routing information

‘––––––––––––––––––––

sDocName = mscsOptionsDictionary.s_BizTalkOrderDocType

sSourceQualifierID = _
   mscsOptionsDictionary.s_BizTalkSourceQualifierID

sSourceQualifierValue = _
   mscsOptionsDictionary.s_BizTalkSourceQualifierValue

Figure 15-1. You can establish the BizTalk integration using the BizTalk configuration entries in the App Default Config module in Commerce Server Manager. (Image unavailable)

You can then use the BizTalk Interchange object to submit the order directly to BizTalk, as shown here:

‘––––––––––––––––––––

‘Get the BizTalk object and submit the XML file

‘––––––––––––––––––––

Set btsInterchange = Server.CreateObject("BizTalk.Interchange")

oRes = btsInterchange.Submit(iBIZTALKOPENNESS, _
   sXML, _
   sDocName, _
   sSourceQualifierID, _
   sSourceQualifierValue, _
   sVendorQual, _
   sVendorQualValue)

Submitting an Order Using Microsoft Message Queue Service

If you don't have the BizTalk components on your Web server, or you have a Web farm established that is served by a single BizTalk setup, you will probably find it more advantageous to simply put the order, in its XML format, into a queue. BizTalk can set up receive functions to monitor the queues on each of the Web servers. As orders are placed in the queue, BizTalk can retrieve them and act on them.

To submit the order from the queue, you'll start by defining the Microsoft Message Queue Service (MSMQ) queue that you are going to use in the MSMQQueueInfo object, as shown here:

sQueueName = ".\Private$\mybiztalkQ"

set MSMQInfo = server.CreateObject("MSMQ.MSMQQueueInfo")

MSMQInfo.PathName = sQueueName

You'll then establish a connection to the queue using the MSMQQueueInfo object's Open method, shown here:

set MSMQ = MSMQInfo.Open(2,0)   ‘open as SEND ACCESS and DENY NONE

Next, you'll define the Message Queue message that you want to send and call its Send method, passing the Queue object, as shown here:

set MSMQMsg = server.CreateObject("MSMQ.MSMQMessage")

MSMQMsg.Body = sXML

call MSMQMsg.Send(MSMQ)

Displaying a Receipt

Once your users have completed their purchase, it's typical to provide them with a receipt or at least give them the ability to display one on their own, if they wish. Displaying an order receipt is very similar to displaying the basket. The difference is that you'll retrieve it using the LoadOrder method, shown in the following code, providing the OrderGroupID as a parameter to identify the order to load.

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
mscsUserProfile("GeneralInfo.user_id").value

mscsOrderGroup.LoadOrder( _
   "{F22178C0-08B6-4858-9BD0 D5D83E8ACB89}")

In addition, you won't run this OrderGroup through any pipelines. Instead, you'll used the values that have been saved to the database. For instance, Table 15-2 shows a list of some of the item and order values that have been renamed so that they're saved in the completed order. You'll use the new saved names to display the values on the receipt page.

Table 15-2. Temporary Keys in the Order Basket and Their Corresponding Keys in the Completed Order

Basket Key NameCompleted Order Key Name
Item._cy_iadjust_regularpriceItem.cy_unit_price
Item._cy_oadjust_adjustedpriceItem.cy_lineitem_total
Item._product_descriptionItem.description
Orderform._cy_oadjust_subtotalOrderform.saved_cy_oadjust_subtotal
Orderform._cy_total_totalOrderform.saved_total_total

In this case, you would build the receipt page similarly to the basket page, as the following code shows.

htmItems = "<table><tr>" _
   & "<th>Product ID</th>" _
   & "<th>Description</th>" _
   & "<th>Qty</th>" _
   & "<th>Price</th>" _
   & "<th>Total</th>" _
   & "</tr>"

cySubTotal = 0.0

For each OrderFormName in mscsOrderGroup.Value.OrderForms

   Set mscsOrderForm = _           mscsOrderGroup.Value.OrderForms("OrderFormName")

   For i = 1 to mscsOrderForm.Items.Count

      Set item = mscsOrderGroup.GetItemInfo(i-1, "default")

      htmItems = htmItems & "<tr>" _
       & "<td>" & item("product_id") & "</td>" _
       & "<td>" & item("description") & "</td>" _
       & "<td>" & item("quantity") & "</td>" _
       & "<td>" _
       & mscsDataFunctions.LocalizeCurrency( _
       item("cy_unit_price"),"1033","$") _
       & "</td>" _
       & "<td>" _
       & mscsDataFunctions.LocalizeCurrency( _
       item("cy_lineitem_total"),"1033","$") _
       & "</td>" _
       & "</tr>"

   next

   cySubTotal = cySubTotal + _                    mscsOrderForm("saved_cy_oadjust_subtotal")

Next

htmItems = htmItems & "<tr>" _
& "<td colspan=5 align=right><hr width=100></td>" _
& "</tr>"

htmItems = htmItems & "<tr>" _
& "<td colspan=4 align=right>SubTotal</td>" _
& "<td>" _
& mscsDataFunctions.LocalizeCurrency( _
   cySubTotal,"1033","$") _
& "</td>" _
& "</tr>"

htmItems = htmItems & "</table>"

htmPage = "<HTML><HEAD></HEAD><BODY>" _
& htmItems _
& "</BODY></HTML>"

Response.Write htmPage

Order Templates

Commerce Server 2000 provides the ability to save an existing basket so that it can be used as the basis for creating or populating baskets. These saved baskets are referred to as templates. There are several tasks you can accomplish with templates, including the following:

  • Saving a template
  • Loading a template
  • Loading items from a template into a basket

Saving an Order Template

To save a template, you'll use the OrderGroup's SaveAsTemplate method and provide a name for the template (see the following code). Your user can use this name to identify the template at a later point.

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
mscsUserProfile("GeneralInfo.user_id").value

mscsOrderGroup.LoadBasket

sTemplateName = Request.QueryString("SavedName")

mscsOrderGroup.SaveAsTemplate(sTemplateName)

Loading an Order Template

Your users can list the current templates in the database using a query similar to the following:

CREATE PROCEDURE sProc_GetTemplates

AS

SELECT

   saved_order_name, ordergroup_id

FROM

   BasketGroup

WHERE

   order_status_code = 2

GO

Of course, this query does not identify if the user is permitted to retrieve the template. You should modify the query to apply such a security measure.

Once you have the ordergroup_id of the desired template, you can load it as the current basket using the LoadTemplate method (see the following code). You'll have to provide the ID of the template as a parameter of the method.

sTemplateID = Request.QueryString("TemplateID")

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
   mscsUserProfile("GeneralInfo.user_id").value

mscsOrderGroup.LoadTemplate(sTemplateID)

Adding Items from a Template

Once you have the ordergroup_id of the desired template, you can load the
items of the template into the current basket using the OrderGroup's AddItemsFromTemplate method and providing the ID of the template as a
parameter, as shown here:

sTemplateID = Request.QueryString("TemplateID")

set mscsOrderGroup = Server.CreateObject("Commerce.OrderGroup")

mscsOrderGroup.Initialize Application("sTransactionCS"), _
mscsUserProfile("GeneralInfo.user_id").value

mscsOrderGroup.LoadBasket

mscsOrderGroup.AddItemsFromTemplate(sTemplateID)

Once this is done, your users will be able to continue shopping as normal and complete their purchases when they desire.

Order Processing Components Reference

The following sections detail some of the components used as a part of the order processing system in Commerce Server 2000 that are covered in this chapter.

DictionaryXMLTransforms

The DictionaryXMLTransforms object provides support in converting between Dictionary objects and XML documents. Its methods are listed in Table 15-3.

Table 15-3. DictionaryXMLTransforms Methods

Method NameDescription
GenerateSampleXMLInstanceFromDictionaryCreates an instance of an XML document based on the given Dictionary object
GenerateXMLForDictionaryUsingSchemaCreates an instance of an XML document based on the given Dictionary object transformed by the given XML schema
GetXMLFromFileCreates an XML document from the given file
ReconstructDictionaryFromXMLCreates a Dictionary object based on the provided XML document that complies with the provided XML schema

ShippingMethodManager

The ShippingMethodManager is used to manage the shipping methods that are available on your site. This object's methods are described in Table 15-4.

Table 15-4. ShippingMethodManager Methods

Method NameDescription
CreateMethodInstanceCreates an empty instance of a shipping method
DeleteMethodInstanceDeletes the current instance of a shipping method
GetComponentConfigRetrieves a Dictionary object that contains the configuration for the shipping component that implements the current shipping method
GetInstalledMethodListRetrieves a recordset that holds data about the available shipping methods
GetMethodInfoRetrieves a recordset that holds data about the current shipping method
InitializeInitializes the object and connects it to the database that holds the ShippingConfig table
LoadMethodInstanceLoads a shipping method
SaveMethodConfigSaves configuration information about the current shipping method
SetCachableComponentConfigSaves configuration and other information about the current component
SetMethodConfigSets configuration settings for the current shipping method

Meet the Author

Customer Reviews

Average Review:

Write a Review

and post it to your social network

     

Most Helpful Customer Reviews

See all customer reviews >