Payment Processing
Now that Orange Whip Studios' storefront and shopping-cart mechanisms are in place, it's time to tackle the checkout process. While by no means difficult, this part generally takes the most time to get into place because you must make some decisions about how to accept and process the actual payments from your users.Depending on the nature of your application, you might not need real-time payment processing. For instance, if your company bills its customers at the end of each month, you probably just need to perform some type of query to determine the status of the user's account, rather than worrying about collecting a credit-card number and charging the card when the user checks out.However, most online shopping applications call for getting a credit-card number from a user at checkout time and charging the user's credit-card account in real time. That is the focus of this section.
Payment-Processing Solutions
Assuming you'll be collecting credit-card information from your users, you must first decide how you will process the credit-card charges that come through your application. ColdFusion doesn't ship with any specific functionality for processing credit-card transactions. However, a number of third-party packages enable you to accept payments via credit cards and checks.
NOTE
Because it is quite popular, the examples in this chapter use the Payflow Pro payment-processing service from VeriSign ( www.verisign.com ). But VeriSign's service is just one of several solutions available. You are encouraged to investigate other payment-processing software to find the service or package that makes the most sense for your project.Processing a Payment
The exact ColdFusion code you use to process payments will vary according to the payment-processing package you decide to use. This section uses VeriSign's Payflow Pro as an example. Please understand that other options are available and that Payflow Pro shouldn't necessarily be considered as superior or better suited than other solutions just because I discuss it here.
Getting Started with Payflow Pro
If you want to try the payment-processing code examples that follow in this section, you must download and install the Java version of the Payflow Pro software.
NOTE
At the time of this writing, the items discussed here were available for free download from VeriSign's Web site. It was necessary to sign up for a free test vendor account first to download and use the software.To get started, do the following:1. | Go to VeriSign's Web site and register for a free test vendor account. |
2. | Download and install the Pure Java version of the Payflow Pro software. At the time of this writing, that meant unzipping the downloaded software to an appropriate location on your computer's hard drive. The guts of the software is the Java archive file named Verisign.jar. For purposes of this discussion, we will assume that you are using Windows and have unzipped the downloaded files so that the Verisign.jar file is at this location: c:\versign\payflowpro\java |
3. | On the Java and JVM page of the ColdFusion Administrator, add the full path of the Verisign.jar file to the Class Path field (for instance, c:\versign\payflowpro\java\Verisign.jar). If the Class Path field already contains a value, add the path at the end, separated with a comma. Make sure to restart the ColdFusion service to make the changes take effect. |
NOTE
The Payflow Pro software on your server needs to communicate with VeriSign's network over the Internet, so your ColdFusion server must be capable of accessing the Internet before you can start testing. Depending on your situation, you might need to configure Payflow Pro so it can get past your firewall or proxy software, or take other special steps. See the Payflow Pro documentation for details. Please understand that successful installation of VeriSign's software is not the primary focus of this chapter.The <cf_VerisignPayflowPro> Custom Tag
VeriSign provides a Java CFX tag called <CFX_PAYFLOWPRO> for ColdFusion developers. Unfortunately, at the time of this writing, the <CFX_PAYFLOWPRO> tag did not work correctly with ColdFusion. This situation may be remedied by the time you read this book, in which case you can just use the official <CFX_PAYFLOWPRO> tag provided by VeriSign. See VeriSign's site for details.To take the place of VeriSign's own <CFX_PAYFLOWPRO>, I have provided a CFML Custom Tag called <cf_VerisignPayflowPro>. This Custom Tag is a wrapper around the Pure Java API that VeriSign provides. While this API was designed with Java coders in mind, you can use it quite easily with CFML. Table 28.3 shows the attributes supported by the Custom Tag. Even if you don't end up needing this custom tag, it makes for an interesting example of what you can do with ColdFusion and APIs designed for Java.Table 28.3 shows the attributes supported by <cf_VerisignPayflowPro>.
NOTE
I don't have the space here to discuss the code used to build the <cf_VerisignPayflowPro> tag in detail. In general, connecting to native Java objects is conceptually beyond the scope of this book. That is why I've provided this Custom Tag, so that you can have a complete working example without needing to understand how the Java API is used (aren't custom tags great?). That said, I invite you to examine the code for <cf_VerisignPayflowPro>, which is included on the CD-ROM. You may find it a useful guide for creating your own CFML wrappers around other Java APIs. For more information about using native Java objects in ColdFusion templates, see the ColdFusion documentation or this book's companion volume, Advanced Macromedia ColdFusion MX 7 Application Development.
Writing a Custom Tag Wrapper to Accept Payments
To make the <cf_VerisignPayflowPro> tag easier to use in your own ColdFusion templates, and to make it easier to switch to some other payment-processing solution in the future, you might consider hiding all payment-package-specific code within a more abstract, general-purpose Custom Tag that encapsulates the notion of processing a payment.Listing 28.10 creates a CFML Custom Tag called <cf_ProcessPayment>. It accepts a Processor attribute to indicate which payment-processing software should process the payment. As you will see, this example supports Processor="PayflowPro" and Processor="JustTesting". You would need to expand the tag by adding the package-specific code necessary for any additional software.
NOTE
Actually, the tag also includes code for Processor="CyberCash". CyberCash was a payment processing solution that was similar to Payflow Pro; it is no longer in operation. The last edition of this book used CyberCash as the main example. I am including the CyberCash-specific code in Listing 28.10 so that you can see how different payment processing solutions could be handled within a single custom tag. The CyberCash setting won't actually work, though, because its servers no longer respond to requests.The idea here is similar to the idea behind the <cf_ShoppingCart> tag created earlier in this chapter: Keep all the mechanics in the Custom Tag template, so each individual page can use simpler, more goal-oriented syntax. In addition to the Processor attribute, this sample version of the <cf_ProcessPayment> tag accepts the following attributes:- orderID.
This is passed along to the credit-card company as a reference number. - orderAmount, creditCard, creditExpM, creditExpY, and creditName0
These describe the actual payment to be processed. orderAmount is the total of the order. creditCard is the credit card number. creditExpM and creditExpY are the month and year when the credit card expires. creditName is the name on the credit card. - returnVariable.
This indicates a variable name the Custom Tag should use to report the status of the attempted payment transaction. The returned value is a ColdFusion structure that contains a number of status values.
Listing 28.10. ProcessPayment.cfmCreating the <cf_ProcessPayment> Custom Tag
Chapter 23).Thus, the generic <cf_ProcessPayment> tag is able to support Payflow Pro.
<!---
Filename: ProcessPayment.cfm
Created by: Nate Weiss (NMW)
Please Note Creates the <CF_ProcessPayment> Custom Tag
Purpose: Handles credit card and other transactions
--->
<!--- Tag Parameters --->
<cfparam name="ATTRIBUTES.processor" type="string">
<cfparam name="ATTRIBUTES.orderID" type="numeric">
<cfparam name="ATTRIBUTES.orderAmount" type="numeric">
<cfparam name="ATTRIBUTES.creditCard" type="string">
<cfparam name="ATTRIBUTES.creditExpM" type="string">
<cfparam name="ATTRIBUTES.creditExpY" type="string">
<cfparam name="ATTRIBUTES.returnVariable" type="variableName">
<cfparam name="ATTRIBUTES.creditName" type="string">
<!--- Depending on the PROCESSOR attribute --->
<cfswitch expression="#ATTRIBUTES.processor#">
<!--- If PROCESSOR="PayflowPro" --->
<cfcase value="PayflowPro">
<!--- Force expiration into MM and YY format --->
<cfset expM = numberFormat(ATTRIBUTES.creditExpM, "00")>
<cfset expY = numberFormat(right(ATTRIBUTES.creditExpY, 2), "00")>
<!--- Attempt transaction with Payflow Pro --->
<cf_VerisignPayflowPro
certPath="c:\verisign\payflowpro\java\certs"
serverName="test-payflow.verisign.com"
payflowPartner="VeriSign"
payflowVendor="YOUR_INFO_HERE"
payflowPassword="YOUR_INFO_HERE"
acct="#ATTRIBUTES.creditCard#"
expDate="#expM##expY#"
amt="#numberFormat(ATTRIBUTES.orderAmount, '9.00')#"
comment1="Orange Whip OrderID: #ATTRIBUTES.orderID#"
comment2="Customer Name on Card: #ATTRIBUTES.creditName#"
returnVariable="PayflowResult">
<!--- Values to return to calling template --->
<cfset s = structNew()>
<!--- Always return IsSuccessful (Boolean) --->
<cfset s.isSuccessful = PayflowResult.RESULT eq 0>
<!--- Always return status of transaction --->
<cfset s.status = PayflowResult.RESPMSG>
<!--- If Successful, return the Auth Code --->
<cfif s.isSuccessful>
<cfset s.authCode = PayflowResult.AUTHCODE>
<cfset s.orderID = ATTRIBUTES.orderID>
<cfset s.orderAmount = ATTRIBUTES.orderAmount>
<!--- If not successful, return the error --->
<cfelse>
<cfset s.errorCode = PayflowResult.RESULT>
<cfset s.errorMessage = PayflowResult.RESPMSG>
</cfif>
<!--- Return values to calling template --->
<cfset "Caller.#ATTRIBUTES.returnVariable#" = s>
</cfcase>
<!--- If PROCESSOR="JustTesting" --->
<!--- This puts the tag into a "testing" mode --->
<!--- Where any transaction will always succeed --->
<cfcase value="JustTesting">
<!--- Values to return to calling template --->
<cfset s = structNew()>
<!--- Always return IsSuccessful (Boolean) --->
<cfset s.isSuccessful = True>
<!--- Always return status of transaction --->
<cfset s.status = "success">
<!--- Return other data, as if transaction succeeded --->
<cfset s.authCode = "DummyAuthCode">
<cfset s.orderID = ATTRIBUTES.orderID>
<cfset s.orderAmount = ATTRIBUTES.orderAmount>
<!--- Return values to calling template --->
<cfset "Caller.#ATTRIBUTES.returnVariable#" = s>
</cfcase>
<!--- If PROCESSOR="CyberCash" --->
<!--- *** This is now defunct but remains as an example --->
<cfcase value="CyberCash">
<!--- Force expiration into MM and YY format --->
<cfset expM = numberFormat(ATTRIBUTES.creditExpM, "00")>
<cfset expY = numberFormat(right(ATTRIBUTES.creditExpY, 2), "00")>
<!--- Attempt to process the transaction --->
<CFX_CYBERCASH
VERSION="3.2"
CONFIGFILE="C:\mck-3.3.1-NT\test-mck\conf\merchant_conf"
MO_ORDER_ID="8767767#ATTRIBUTES.orderID#"
MO_VERSION="3.3.1"
MO_PRICE="USD #numberFormat(ATTRIBUTES.orderAmount, '9.00')#"
CPI_CARD_NUMBER="#ATTRIBUTES.creditCard#"
CPI_CARD_EXP="#expM#/#expY#"
CPI_CARD_NAME="#ATTRIBUTES.creditName#"
OUTPUTPOPQUERY="Charge">
<!--- Values to return to calling template --->
<cfset s = structNew()>
<!--- Always return IsSuccessful (Boolean) --->
<cfset s.isSuccessful = charge.STATUS eq "success">
<!--- Always return status of transaction --->
<cfset s.status = Charge.STATUS>
<!--- If Successful, return the Auth Code --->
<cfif s.isSuccessful>
<cfset s.authCode = Charge.AUTH_CODE>
<cfset s.orderID = ATTRIBUTES.orderID>
<cfset s.orderAmount = ATTRIBUTES.orderAmount>
<!--- If not successful, return the error --->
<cfelse>
<cfset s.errorCode = Charge.ERROR_CODE>
<cfset s.errorMessage = Charge.ERROR_MESSAGE>
</cfif>
<!--- Return values to calling template --->
<cfset "Caller.#ATTRIBUTES.returnVariable#" = s>
</cfcase>
<!--- If the PROCESSOR attribute is unknown --->
<cfdefaultcase>
<cfthrow message="Unknown PROCESSOR attribute.">
</cfdefaultcase>
</cfswitch>
NOTE
A similar <cfcase> block is also provided, which will execute if Processor="JustTesting" is passed to the tag. This provides an easy way to test the checkout functionality even if you don't want to bother registering for Payflow Pro. Just change the Processor attribute to JustTesting in Listing 28.11.
NOTE
If you adapt this Custom Tag to handle other payment processors, try to return the same value names (IsSuccessful, AuthCode, and so on) to a structure. In this way, you will build a common API to deal with payment processing in an application-agnostic way.Processing a Complete Order
In addition to explaining how to build commerce applications, this chapter emphasizes the benefits of hiding the mechanics of complex operations within goal-oriented Custom Tag wrappers that can accomplish whole tasks on their own. Actual page templates that use these Custom Tags look very clean, since they deal only with the larger concepts at hand rather than including a lot of low-level code. That's the difference, for instance, between the two versions of the StoreCart.cfm template (Listings 28.4 and 28.6).In keeping with that notion, Chapter 27, "Interacting with Email"
Listing 28.11. PlaceOrder.cfmCreating the <cf_PlaceOrder> Custom Tag
At the top of the template is a rather large number of <cfparam> tags that define the various attributes for the <cf_PlaceOrder> Custom Tag. The Processor, ReturnVariable, and four Credit attributes are passed directly to <cf_ProcessPayment>. The MerchList and QuantList attributes specify which item is actually being ordered, in the same comma-separated format that the CLIENT variables use in Listing 28.4. The contactID and the six ship attributes are needed for the MerchandiseOrders table. The
<!---
Filename: PlaceOrder.cfm (creates <CF_PlaceOrder> Custom Tag)
Created by: Nate Weiss (NMW)
Please Note Depends on <CF_ProcessPayment> and
<CF_SendOrderConfirmation>
Purpose: Handles all operations related to
placing a customer's order
--->
<!--- Tag Parameters --->
<cfparam name="ATTRIBUTES.processor" type="string" default="PayflowPro">
<cfparam name="ATTRIBUTES.merchList" type="string">
<cfparam name="ATTRIBUTES.quantList" type="string">
<cfparam name="ATTRIBUTES.contactID" type="numeric">
<cfparam name="ATTRIBUTES.creditCard" type="string">
<cfparam name="ATTRIBUTES.creditExpM" type="string">
<cfparam name="ATTRIBUTES.creditExpY" type="string">
<cfparam name="ATTRIBUTES.creditName" type="string">
<cfparam name="ATTRIBUTES.shipAddress" type="string">
<cfparam name="ATTRIBUTES.shipCity" type="string">
<cfparam name="ATTRIBUTES.shipCity" type="string">
<cfparam name="ATTRIBUTES.shipState" type="string">
<cfparam name="ATTRIBUTES.shipZIP" type="string">
<cfparam name="ATTRIBUTES.shipCountry" type="string">
<cfparam name="ATTRIBUTE274Mail" type="boolean">
<cfparam name="ATTRIBUTES.returnVariable" type="variableName">
<!--- Begin "order" database transaction here --->
<!--- Can be rolled back or committed later --->
<cftransaction action="begin">
<!--- Insert new record into Orders table --->
<cfquery datasource="#APPLICATION.dataSource#">
INSERT INTO MerchandiseOrders (
ContactID,
OrderDate,
ShipAddress, ShipCity,
ShipState, ShipZip,
ShipCountry)
VALUES (
#ATTRIBUTES.contactID#,
<cfqueryparam cfsqltype="CF_SQL_TIMESTAMP"
VALUE="#dateFormat(now())# #timeFormat(now())#">,
'#ATTRIBUTES.shipAddress#', '#ATTRIBUTES.shipCity#',
'#ATTRIBUTES.shipState#', '#ATTRIBUTES.shipZip#',
'#ATTRIBUTES.shipCountry#'
)
</cfquery>
<!--- Get just-inserted OrderID from database --->
<cfquery datasource="#APPLICATION.dataSource#" name="getNew">
SELECT MAX(OrderID) AS NewID
FROM MerchandiseOrders
</cfquery>
<!--- For each item in user's shopping cart --->
<cfloop from="1" to="#listLen(ATTRIBUTES.merchList)#" index="i">
<cfset thisMerchID = listGetAt(ATTRIBUTES.merchList, i)>
<cfset thisQuant = listGetAt(ATTRIBUTES.quantList, i)>
<!--- Add the item to "OrdersItems" table --->
<cfquery datasource="#APPLICATION.dataSource#">
INSERT INTO MerchandiseOrdersItems
(OrderID, ItemID, OrderQty, ItemPrice)
SELECT
#getNew.NewID#, MerchID, #thisQuant#, MerchPrice
FROM Merchandise
WHERE MerchID = #thisMerchID#
</cfquery>
</cfloop>
<!--- Get the total of all items in user's cart --->
<cfquery datasource="#APPLICATION.dataSource#" name="getTotal">
SELECT SUM(ItemPrice * OrderQty) AS OrderTotal
FROM MerchandiseOrdersItems
WHERE OrderID = #getNew.NewID#
</cfquery>
<!--- Attempt to process the transaction --->
<cf_ProcessPayment
processor="#ATTRIBUTES.processor#"
orderID="#getNew.NewID#"
orderAmount="#getTotal.OrderTotal#"
creditCard="#ATTRIBUTES.creditCard#"
creditExpM="#ATTRIBUTES.creditExpM#"
creditExpY="#ATTRIBUTES.creditExpY#"
creditName="#ATTRIBUTES.creditName#"
returnVariable="chargeInfo">
<!--- If the order was processed successfully --->
<cfif chargeInfo.IsSuccessful>
<!--- Commit the transaction to database --->
<cftransaction action="Commit"/>
<cfelse>
<!--- Rollback the Order from the Database --->
<cftransaction action="RollBack"/>
</cfif>
</cftransaction>
<!--- If the order was processed successfully --->
<cfif ChargeInfo.isSuccessful>
<!--- Send Confirmation E-Mail, via Custom Tag --->
<cf_SendOrderConfirmation
orderID="#getNew.NewID#"
u274="#ATTRIBUTE274Mail#">
</cfif>
<!--- Return status values to callling template --->
<cfset "Caller.#ATTRIBUTES.returnVariable#" = chargeInfo>
1. | A new order record is inserted into the MerchandiseOrders table. |
2. | The getNew query obtains the OrderID number for the just-inserted order record. |
3. | A simple <cfloop> tag inserts one record into the MerchandiseOrdersItems table for each item supplied to the MerchList attribute. Each record includes the new OrderID, the appropriate quantity from the QuantList attribute, and the current price for the item (as listed in the Merchandise table). (The INSERT/SELECT syntax used here is explained in Chapter 30.) |
4. | The getTotal query obtains the total price of the items purchased by adding up the price of each item times its quantity. |
5. | The <cf_ProcessPayment> tag (refer to Chapter 27. Because this tag performs database interactions of its own, it should sit outside the <cftransaction> block. |
9. | Finally, the chargeInfo structure returned by <cf_PlaceOrder> is passed back to the calling template so it can understand whether the order was placed successfully. |
Creating the Checkout Page
Now that you've created the <cf_PlaceOrder> Custom Tag, actually creating the Checkout page for Orange Whip Studios' visitors is simple. Figure 28.2).
Listing 28.12. StoreCheckout.cfmAllowing the User to Complete the Online Transaction
First, the standard page header for the online store is displayed with the <cfinclude> tag at the top of Chapter 21.Of course, if you want to use the client variablebased <cf_ShoppingCart> Custom Tag instead of the session variablebased ShoppingCart CFC, you can easily do so by changing the first <cfset> line (near the top of Listing 28.12) to this:
<!---
Filename: StoreCheckout.cfm (save with Chapter 28's listings)
Created by: Nate Weiss (NMW)
Purpose: Provides final Checkout/Payment page
Please Note Depends on <CF_PlaceOrder> and StoreCheckoutForm.cfm
--->
<!--- Show header images, etc., for Online Store --->
<cfinclude template="StoreHeader.cfm">
<!--- Get current cart contents, as a query object --->
<cfset getCart = SESSION.myShoppingCart.List()>
<!--- Stop here if user's cart is empty --->
<cfif getCart.recordCount eq 0>
There is nothing in your cart.
<cfabort>
</cfif>
<!--- If user is not logged in, force them to now --->
<cfif not isDefined("SESSION.auth.isLoggedIn")>
<cfinclude template="LoginForm.cfm">
<cfabort>
</cfif>
<!--- If user is attempting to place order --->
<cfif isDefined("FORM.isPlacingOrder")>
<cftry>
<!--- Attempt to process the transaction --->
<!--- Change to PayflowPro to use VeriSign --->
<cf_PlaceOrder
Processor="JustTesting"
contactID="#SESSION.auth.contactID#"
merchList="#valueList(getCart.MerchID)#"
quantList="#valueList(getCart.Quantity)#"
creditCard="#FORM.creditCard#"
creditExpM="#FORM.creditExpM#"
creditExpY="#FORM.creditExpY#"
creditName="#FORM.creditName#"
shipAddress="#FORM.shipAddress#"
shipState="#FORM.shipState#"
shipCity="#FORM.shipCity#"
shipZIP="#FORM.shipZIP#"
shipCountry="#FORM.shipCountry#"
htmlMail="#FOR268Mail#"
returnVariable="orderInfo">
<!--- If any exceptions in the "
ows.MerchOrder" family are thrown... --->
<cfcatch type="ows.MerchOrder">
<p>Unfortunately, we are not able to
process your order at the moment.<br>
Please try again later. We apologize for the inconvenience.<br>
<cfabort>
</cfcatch>
</cftry>
<!--- If the order was processed successfully --->
<cfif orderInfo.isSuccessful>
<!--- Empty user's shopping cart --->
<cfset SESSION.myShoppingCart.Empty()>
<!--- Display Success Message --->
<cfoutput>
<h2>Thanks For Your Order</h2>
<p><b>Your Order Has Been Placed.</b><br>
Your order number is: #orderInfo.orderID#<br>
Your credit card has been charged:
#lsCurrencyFormat(orderInfo.OrderAmount)#<br>
<p>A confirmation is being Emailed to you.<br>
</cfoutput>
<!--- Stop here. --->
<cfabort>
<cfelse>
<!--- Display "Error" message --->
<font color="red">
<strong>Your credit card could not be processed.</strong><br>
Please verify the credit card number, expiration date, and
name on the card.<br>
</font>
<!--- Show debug info if viewing page on server --->
<cftrace inline="True" var="OrderInfo">
</cfif>
</cfif>
<!--- Show Checkout Form (Ship Address/Credit Card) --->
<cfinclude template="StoreCheckoutForm.cfm">
At the bottom of the template, a <cfinclude> tag includes the form shown in Figure 28.4, which asks the user for shipping and credit-card information. The form is self-submitting, so when the user clicks the Place Order Now button, the code in Listing 28.12 is executed again. This is when the large <cfif> block kicks in.
<!--- Get current cart contents, as a query object --->
<cf_ShoppingCart
action="List"
returnVariable="GetCart">
Figure 28.4. Users provide credit-card information on the Checkout page.
[View full size image]

Listing 28.13. StoreCheckoutForm.cfmCollecting Shipping and Card Information from the User
<!---
Filename: StoreCheckoutForm.cfm
Created by: Nate Weiss (NMW)
Please Note Included by StoreCheckout.cfm
Purpose: Displays a simple checkout form
--->
<!--- Get the user's contact info from database --->
<cfquery name="getContact" datasource="#APPLICATION.DataSource#">
SELECT
FirstName, LastName, Address,
City, State, Zip, Country, Email
FROM Contacts
WHERE ContactID = #SESSION.auth.contactID#
</cfquery>
<!--- Used to pre-fill user's choice o261 or Plain email --->
<cfparam name="FOR268Mail" type="string" default="Yes">
<cfoutput>
<cfform action="#CGI.script_name#" method="post" preservedata="Yes">
<cfinput type="hidden" name="isPlacingOrder" value="Yes">
<table border="0" cellspacing="4">
<tr>
<th colspan="2" bgcolor="silver">Shipping Information</th>
</tr>
<tr>
<th align="right">Ship To:</th>
<td>
#getContact.FirstName# #getContact.LastName#
</td>
</tr>
<tr>
<th align="right">Address:</th>
<td>
<cfinput name="shipAddress" size="30"
required="yes" value="#getContact.Address#"
message="Please don't leave the Address blank!">
</td>
</tr>
<tr>
<th align="right">City:</th>
<td>
<cfinput name="shipCity" size="30"
required="yes" value="#getContact.City#"
message="Please don't leave the City blank!">
</td>
</tr>
<tr>
<th align="right">State:</th>
<td>
<cfinput name="shipState" size="30"
required="yes" value="#getContact.State#"
message="Please don't leave the State blank!">
</td>
</tr>
<tr>
<th align="right">Postal Code:</th>
<td>
<cfinput name="shipZIP" size="10"
required="yes" value="#getContact.ZIP#"
message="Please don't leave the ZIP blank!">
</td>
</tr>
<tr>
<th align="right">Country:</th>
<td>
<cfinput name="shipCountry" size="10" required="Yes"
value="#getContact.Country#"
message="Please don't leave the Country blank!">
</td>
</tr>
<tr>
<th align="right">Credit Card Number:</th>
<td>
<cfinput name="creditCard" size="30"
required="yes" validate="creditcard"
message="You must provide a credit card number.">
</td>
</tr>
<tr>
<th align="right">Credit Card Expires:</th>
<td>
<cfselect name="creditExpM">
<cfloop from="1" to="12" index="i">
<option value="#i#">#numberFormat(i, "00")#
</cfloop>
</cfselect>
<cfselect name="creditExpY">
<cfloop from="#year(now())#"
to="#val(year(now())+10)#" index="i">
<option value="#i#">#i#
</cfloop>
</cfselect>
</td>
</tr>
<tr>
<th align="right">Name On Card:</th>
<td>
<cfinput name="creditName" size="30" required="Yes"
value="#getContact.FirstName# #getContact.LastName#"
message="You must provide the Name on the Credit Card.">
</td>
</tr>
<tr valign="baseline">
<th align="right">Confirmation:</th>
<td>
We will send a confirmation message to you at
#getContact.EMail#<br>
<cfinput type="radio" name="htmlMail" value="Yes"
checked="#for268Mail#">
HTML (I sometimes see pictures in Email messages)<br>
<cfinput type="radio" name="htmlMail" value="No"
checked="#not for268mail#">
No269 (I never see any pictures in my messages)<br>
</td>
</tr>
<tr>
<td> </td>
<td>
<cfinput type="submit" name="submit"
value="Place Order Now">
</td>
</tr>
</table>
</cfform>
</cfoutput>
