Глава 8. Оформление заказа
Наступает момент, когда покупатель готов оформить свою покупку и разместить заказ. На этой стадии необходимо позаботиться о том, чтобы процесс проходил как можно более гладко и беспрепятственно, а также проследить за правильным сохранением всех данных.

Последовательность оформления заказа
В процессе оформления заказа участвует несколько страниц. Пользователь видит только три из них, однако за кулисами выполняется довольно большая работа. Основные стадии оформления заказа перечислены в табл. 8.1.
Таблица 8.1. Процесс оформления заказа


Стадия

Описание

Стоимость доставки

Прежде всего покупатель вводит информацию, необходимую для доставки заказа. На основании этих данных вычисляется стоимость доставки

Проверка реквизитов доставки

Данные, отправленные покупателем, необходимо немедленно проверить. Мы должны убедиться в наличии всей необходимой информации

Выписка счета

На этой странице покупатель вводит данные для выписки счета, а также данные кредитной карты. Кроме того, здесь выводятся суммы налога и стоимости доставки

Проверка данных для выписки счета

Данные выписки счета, как и данные доставки, необходимо проверить

Подтверждение

После успешного оформления заказа выводится подтверждающее сообщение и номер оформленного заказа

Процесс оформления заказа имеет весьма прямолинейную структуру, однако в нем используется вся основная бизнес-логика, на которой строится Web-сайт.
Оформление заказа начинается с корзины. Как видно из рисунка, на следующем этапе выполняется ввод данных доставки и их последующая проверка. Если в данных допущена ошибка, покупатель возвращается на предыдущую страницу с сообщением об ошибке.
Когда пользователь переходит к процессу выписки счета, мы вычисляем налог и стоимость доставки. В нашем примере для этого на Visual Basic будет построен объект СОМ, вызываемый из страниц ASP.
ПРИМЕЧАНИЕ
В нашем примере смоделирован относительно стандартный процесс. Впрочем, на эту тему существует много разных вариаций. Например, мы поддерживаем лишь пару дополнительных параметров для международной доставки, но во многих магазинах предусмотрены разные процессы оформления заказа в зависимости от того, где находится покупатель - в этой же стране или за рубежом.

Другой возможный подход заключается в объединении страниц доставки и выписки счета. В этом случае покупателю необходимо предоставить возможность указать, что для выписки счета и доставки должны использоваться одинаковые реквизиты, чтобы данные не приходилось вводить заново. Но и тогда на некоторой стадии оформления необходимо вывести вычисленные величины налога и стоимости доставки.
В некоторых магазинах (например, amazon.com) используются сложные варианты оформления заказов с сохранением различных адресов и платежных реквизитов. Это сделано для того, чтобы по возможности упростить процесс оплаты покупок. Кроме того, в магазинах типа amazon.com предусматриваются и такие возможности, как оформление заказа одним щелчком мыши.
После этого стадия выписки счета следует тем же принципам, что и ввод данных доставки. Пользователь вводит данные для выписки счета, и введенная информация проверяется на странице. Если проверка окажется успешной, покупателю отправляется сообщение по электронной почте, после чего выводится страница с подтверждением.

Определение профиля покупателя
Другой ключевой составляющей процесса оформления заказа является сохранение информации о покупателе в профиле. Профиль избавляет покупателя от необходимости заново вводить данные в процессе оформления заказа, а также упрощает получение информации о состоянии заказа после его размещения. Кроме того, если покупатель пожелает создать на своем компьютере cookie для автоматического чтения профиля, то в начале сеанса будет загружена его предыдущая корзина (если заказ не был оформлен).
Вероятно, вы обратили внимание на ссылку Profile, расположенную на панели ссылок в левой части страницы. При помощи этой ссылки покупатель редактирует существующий профиль. Профили, создаются в момент размещения заказа пользователем. При этом пользователю предоставляется возможность сохранения профиля. Основные функции профилей перечислены в табл. 8.2.
Таблица 8.2. Основные функции профилей


Функция

Описание

Редактирование профиля

Пользователь может отредактировать созданный профиль, щелкнув на ссылке, расположенной на панели ссылок. Редактирование профиля также осуществляется при каждом размещении заказа

Отправка пароля по электронной почте

Чтобы прочитать информацию профиля (или историю заказов), пользователь должен ввести имя и пароль. Если пользователь забудет пароль, он может запросить его по электронной почте

Создание профиля

Профиль создается при размещении пользователем заказа. На странице выписки счета пользователю предоставляется возможность выбора между созданием нового и обновлением существующего профиля

Автоматическая загрузка профиля

При установке cookie профиль пользователя будет автоматически загружен при возвращении в магазин. Кроме того, для загрузки профиля пользователь может ввести свое имя и пароль

Как видно из рисунка, данные профиля используются на нескольких стадиях процесса покупки. У нас имеется специальная страница для непосредственного редактирования профиля. На странице корзины профиль используется для загрузки предыдущей корзины, если она имеется у данного покупателя. Страницы оформления заказа обеспечивают средства для чтения и обновления данных профиля. Наконец, при помощи профиля покупатель может получить информацию о состоянии всех своих заказов.
Как будет видно из программного кода, обращение к данным профиля происходит примерно в тех же местах, что и на диаграмме. Страница Header.asp (см. главу 6) ищет на компьютере покупателя cookie для получения информации о нем. В табл. 8.3 описаны основные сценарии загрузки данных профиля.
Рис. 8.2. Использование профиля пользователя в процессе покупки
Таблица 8.3. Сценарии загрузки профиля


Сценарий

Описание

Cookie отсутствует

Если cookie отсутствует на компьютере, пользователь завершает процесс оформления заказа и создает новый профиль (если он не желает загрузить старый профиль посредством ввода имени и пароля)

Cookie присутствует

Страница Header.asp читает из cookie идентификатор покупателя. Затем она проверяет, существует ли у данного покупателя необработанная корзина

Cookie отсутствует - пользователь вводит имя и пароль

Если на компьютере покупателя нет cookie, но пользователь загружает профиль по имени и паролю, читается только информация о покупателе. Мы не должны стирать существующую корзину, загружая предыдущую, необработанную корзину

Предыдущий профиль не существует

Если профиль не существует, он создается при оформлении заказа независимо от того, вводится ли при этом пароль или нет. Впрочем, последующая загрузка профиля возможна лишь при наличии пароля

Применение профилей в нашем примере дает лишь начальное представление о том, как это делается на практике. Отслеживание истории заказов открывает множество возможностей для предоставления покупателю дополнительной информации и исчерпывающего анализа его предпочтений в выборе товаров.
СОВЕТ
Если вы захотите проверить различные сценарии чтения данных профиля покупателя, найдите cookie магазина Wild Willie в соответствующей локальной папке. Удаление cookie позволяет отменить автоматическую загрузку профиля. Способ хранения cookie зависит от типа броузера и его версии - за конкретной информацией обращайтесь к документации или справочным файлам, прилагаемым к броузеру.

Загрузка данных для вычисления налога и стоимости доставки
В процессе оформления заказа мы должны загрузить исходные данные, используемые при вычислении налога и стоимости доставки. Вспомните: мы создали в базе данных две таблицы для хранения этой информации.
Тарифы доставки хранятся в таблице Shipping. Стоимость доставки зависит от того, к какому интервалу принадлежит количество заказанных единиц товара. Интервал определяется верхней и нижней границей. Каждому интервалу соответствует определенная цена. Команды для занесения данных в таблицу приведены в листинге 8.1.
В нашем примере используется четыре уровня стоимости доставки. Последний интервал фактически задает фиксированную цену для всех заказов от 31 единицы и выше.
Листинг 8.1. Заполнение таблицы данными о стоимости доставки
insert into shipping(intLowQuantity, intHighQuantity, intFee)
values(1, 10, 200)
insert into shipping(intLowQuantity, intHighQuantity, intFee)
values(11, 20, 400)
insert into shipping(intLowQuantity, intHighQuantity, intFee)
values(21, 30, 600)
insert into shipping(intLowQuantity, intHighQuantity, intFee)
values(31, 999999, 800)
Данные о величине налога в зависимости от штата хранятся в таблице Tax. В нашем примере создаются записи для двух штатов, Виржинии (VA) и Техаса (ТХ), со специальной налоговой ставкой.
ПРИМЕЧАНИЕ
Для всех остальных штатов следует установить налоговую ставку, равную 0. Это значение будет использоваться в утилитах управления магазином.

Листинг 8.2. Заполнение таблицы данными налоговой ставки
insert into tax(chrState, fltTaxRate) values('VA', .045)
insert into tax(chrState, fltTaxRate) values('TX', .08)
Как упоминалось в главе 3, существует множество способов вычисления налога и расходов на доставку. Например, если корзина содержит более 31 позиций, стоимость доставки может вычисляться как процент от общей стоимости заказа; возможны и другие варианты.
В этой главе мы построим объект СОМ на Visual Basic 6. В этом объекте СОМ будут инкапсулированы правила вычисления налога и стоимости доставки. Дело в том, что в процессе работы могут измениться правила вычисления этих величин. В этом случае мы просто изменим структуру базы данных и перепишем код Visual Basic, не беспокоясь об изменении интерфейсной части магазина.
Итак, вы получили некоторое представление обо всех гранях процесса оформления заказа, составлении профиля покупателя и правилах вычисления налога и стоимости доставки. Перейдем к рассмотрению программного кода, реализующего все эти возможности.
Страница Shipping.asp
Процесс оформления заказа начинается с ввода данных, используемых при доставке заказа. Работа страницы Shipping.asp (см. листинг 8.3) начинается с проверки нескольких условий.
Листинг 8.3. Shipping.asp
<%@ Language=VBScript %>
<%
' ****************************************************
' Shipping.asp - ввод данных, используемых при доставке заказа
' Страница проверяет, можно ли загрузить эти данные из профиля
' ****************************************************
Сначала необходимо убедиться в том, что для текущего сеанса была создана корзина. Если корзины нет, то дальнейшее оформление заказа невозможно, и пользователь направляется на страницу Basket.asр. При попадании на эту страницу он получает сообщение о том, что корзина пуста. После проверки первого условия начинается стандартное построение страницы.
Листинг 8.4. Shipping.asp (продолжение)
' Проверить, была ли создана корзина.
if session("idBasket") = "" then
' Направить на страницу Basket
Response.Redirect "basket.asp"

end if
%>
<HTML>
<!-- #include file="include/header.asp" -->
<BR>
<center>
<font size="5"><b>Shipping Information</b></font>
</center>
Затем мы узнаем, загружался ли ранее профиль покупателя. Для этого проверяется сеансовая переменная ProfileRetrieve. Обратите внимание: профиль покупателя загружается всего один раз за сеанс (если только покупатель не загрузит его самостоятельно при помощи ссылки Profile). Дело в том, что пользователь может обновить на странице своп реквизиты доставки и выписки счета; мы не хотим, чтобы при возвращении пользователя на страницу Shipping.asp (например, вследствие допущенной ошибки) внесенные изменения пропали.
Если профиль не загружался, мы создаем ссылку на страницу Profile.asp, где пользователь может загрузить свой прежний профиль. Обычно эта ссылка выводится лишь в том случае, если на компьютере пользователя не существует cookie для загрузки профиля или прежнего профиля вообще не существует.
Листинг 8.5. Shipping.asp (продолжение)
<%
' Проверить, загружался ли ранее профиль покупателя.
if session("ProfileRetrieve") = "" then
' Если профиль не загружался, мы создаем ссылку для загрузки
' существующего профиля.
%>
<BR>
<B><i>Click <a href="profile.asp">here</a>
to retrieve an existing profile.</i></b>
<BR>
<%
else
Далее мы убеждаемся в том, что сеансовая переменная ProfileRetrieve не равна 1. Значение 0 присваивается переменной в том случае, если в Header.asp корзина не была загружена на основании cookie. После проверки адресные данные покупателя загружаются из профиля.
Если переменная не равна 1, мы создаем подключение к базе данных. Хранимая процедура sp_RetrieveProfilеВyld читает профиль по идентификатору покупателя, хранящемуся в сеансовой переменной.
После загрузки профиля мы загружаем в сеансовые переменные основные параметры, используемые при доставке. Затем сеансовой переменной ProfileRetrieve присваивается 1 - признак того, что адресная информация была загружена и ее не следует загружать снова.
Листинг 8.6. Shipping.asp (продолжение)
' Убедиться в том, что переменная ProfileRetrieve не равна 1.
' Только в этом случае следует попытаться загрузить профиль.
' Обратите внимание: при создании нового идентификатора покупателя
' в Header.asp переменной ProfileRetrieve присваивается 0.
if session("ProfileRetrieve") <> "1" then
' Создать объект подключения к базе данных
set dbProfile = server.createobject("adodb.connection")
' Создать набор записей
set rsProfile = server.CreateObject("adodb.recordset")
' Открыть подключение, используя файловый DSN ODBC
dbProfile.open("filedsn=WildWillieCDs")
' Построить команду вызова хранимой процедуры SQL
' для загрузки профиля по идентификатору покупателя
' (прочитанному из cookie).
sql = "execute sp_RetrieveProfileByID " & session("idShopper")
' Выполнить команду
set rsProfile = dbProfile.Execute(sql)
' Присвоить значения переменным
session("chrShipFirstName") = rsProfile("chrFirstName")
session("chrShipLastName") = rsProfile("chrLastName")
session("chrShipAddress") = rsProfile("chrAddress")
session("chrShipCity") = rsProfile("chrCity")
session("chrShipState") = rsProfile("chrState")
session("chrShipProvince") = rsProfile("chrProvince")
session("chrShipCountry") = rsProfile("chrCountry")
session("chrShipZipCode") = rsProfile("chrZipCode")
session("chrShipPhone") = rsProfile("chrPhone")
session("chrShipEmail") = rsProfile("chrEmail")
session("chrPassword") = rsProfile("chrPassword")
session("intCookie") = rsProfile("intCookie")
end if
' Присвоить сеансовой переменной значение, отменяющее повторную
' загрузку профиля.
session("ProfileRetrieve") = "1"
end if
Затем мы проверяем глобальную сеансовую переменную Error. Значение этой переменной присваивается на странице ValidateShipping.asp при обнаружении ошибок во введенных реквизитах доставки. Если переменная не равна пустой строке, значит в ней хранится строка с описанием ошибки. Эта строка выводится на странице, чтобы пользователь узнал о причинах возникшей ошибки. После вывода сообщения значение сеансовой переменной стирается.
Листинг 8.7. Shipping.asp (продолжение)
' Проверить наличие ошибок при отправке данных формы
if session("Error") <> "" then
' Вывести информацию об ошибке.
%>
<BR>
<b>You have an error in your shipping form, please correct the data:</b><BR><BR>
<!-- Построить таблицу для вывода сообщения. -->
<table>
<tr>
<td width="70">&nbsp;</td>
<td><i><%=Response.Write(session("Error"))%></i></td>
</tr>
</table>
<BR><BR>
<%
' Стереть значение переменной.
session("Error") = ""
else
%>
Наконец, можно перейти к построению формы для ввода реквизитов доставки. В каждой строке таблицы создается текстовое поле, в котором пользователь вводит соответствующие данные.
Обратите внимание - в каждое текстовое поле заносится значение соответствующей сеансовой переменной. Таким образом, поля заново заполняются информацией, введенной ранее на этой форме. Если ранее произошла загрузка профиля, сеансовым переменным будут присвоены соответствующие значения. Это упрощает процесс оформления заказа, если покупатель пожелает доставить товары по тому же адресу, что и в предыдущем заказе.
Листинг 8.8. Shipping.asp (продолжение)
<BR>
<b>Enter your shipping address:</b>
<BR><BR>
<%
end if
%>
<!-- Следующая форма отправляет данные странице ValidateShipping.asp,
проверяющей правильность введенной информации. -->
<center>
<!-- Форма для ввода реквизитов доставки. -->
<form method="post" action="ValidateShipping.asp">
<!-- Таблица для отображения текстовых полей. По умолчанию
поля инициализируются значениями сеансовых переменных. -->
<table>
<!-- Shipping First Name -->
<tr>
<td align="right">First Name:</td>
<td><input type="text" value="<%=session("chrShipFirstName")%>"
name="chrShipFirstName" size="30"></td>
</tr>
<!-- Shipping Last Name -->
<tr>
<td align="right">Last Name:</td>
<td><input type="text" value="<%=session("chrShipLastName")%>"
name="chrShipLastName" size="30"></td>
</tr>
<!-- Shipping Address -->
<tr>
<td align="right">Address:</td>
<td><input type="text" value="<%=session("chrShipAddress")%>"
name="chrShipAddress" size="30"></td>
</tr>
<!-- Shipping City -->
<tr>
<td align="right">City:</td>
<td><input type="text" value="<%=session("chrShipCity")%>"
name="chrShipCity" size="30"></td>
</tr>
Процесс заполнения списка несколько отличается от заполнения стандартных текстовых полей. По умолчанию в списке следует выбрать ранее выбранную или загруженную из профиля строку. Для этого в тег <OPTION> соответствующей строки включается ключевое слово HTML SELECTED.
Задача решается серией команд if. Команды проверяют, какой штат был выбран из списка, и присваивают соответствующей переменной строку SELECTED. Затем при построении списка все переменные включаются в соответствующие теги <OPTION>.
Листинг 8.9. Shipping.asp (продолжение)
<!-- Штат -->
<tr>
<td align="right">State:</td>
<td>

<%
' Проверить, какой штат был выбран ранее (если при вводе
' данных была допущена ошибка). Соответствующей переменной,
' вставляемой в тег HTML, присваивается строка 'SELECTED'.
if session("chrShipState") = "AL" then
SelAL = "Selected"
end if
if session("chrShipState") = "AK" then
SelAK = "Selected"
end if
if session("chrShipState") = "AZ" then
SelAZ = "Selected"
end if
if session("chrShipState") = "AR" then
SelAR = "Selected"
end if
if session("chrShipState") = "CA" then
SelCA = "Selected"
end if
if session("chrShipState") = "CT" then
SelCT = "Selected"
end if
if session("chrShipState") = "CO" then
SelCO = "Selected"
end if
if session("chrShipState") = "DC" then
SelDC = "Selected"
end if
if session("chrShipState") = "DE" then
SelDE = "Selected"
end if
if session("chrShipState") = "FL" then
SelFL = "Selected"
end if
if session("chrShipState") = "GA" then
SelGA = "Selected"
end if
if session("chrShipState") = "HI" then
SelHI = "Selected"
end if
if session("chrShipState") = "ID" then
SelID = "Selected"
end if
if session("chrShipState") = "IL" then
SelIL = "Selected"
end if
if session("chrShipState") = "IN" then
SelIN = "Selected"
end if
if session("chrShipState") = "IA" then
SelIA = "Selected"
end if
if session("chrShipState") = "KS" then
SelKS = "Selected"
end if
if session("chrShipState") = "KY" then
SelKY = "Selected"
end if
if session("chrShipState") = "LA" then
SelLA = "Selected"
end if
if session("chrShipState") = "ME" then
SelME = "Selected"
end if
if session("chrShipState") = "MA" then
SelMA = "Selected"
end if
if session("chrShipState") = "MD" then
SelMD = "Selected"
end if
if session("chrShipState") = "MI" then
SelMI = "Selected"
end if
if session("chrShipState") = "MN" then
SelMN = "Selected"
end if
if session("chrShipState") = "MS" then
SelMS = "Selected"
end if
if session("chrShipState") = "MO" then
SelMO = "Selected"
end if
if session("chrShipState") = "MT" then
SelMT = "Selected"
end if
if session("chrShipState") = "NE" then
SelNE = "Selected"
end if
if session("chrShipState") = "NV" then
SelNV = "Selected"
end if
if session("chrShipState") = "NH" then
SelNH = "Selected"
end if
if session("chrShipState") = "NJ" then
SelNJ = "Selected"
end if
if session("chrShipState") = "NM" then
SelNM = "Selected"
end if
if session("chrShipState") = "NY" then
SelNY = "Selected"
end if
if session("chrShipState") = "NC" then
SelNC = "Selected"
end if
if session("chrShipState") = "ND" then
SelND = "Selected"
end if
if session("chrShipState") = "OH" then
SelOH = "Selected"
end if
if session("chrShipState") = "OK" then
SelOK = "Selected"
end if
if session("chrShipState") = "OR" then
SelOR = "Selected"
end if
if session("chrShipState") = "PA" then
SelPA = "Selected"
end if
if session("chrShipState") = "RI" then
SelRI = "Selected"
end if
if session("chrShipState") = "SC" then
SelSC = "Selected"
end if
if session("chrShipState") = "SD" then
SelSD = "Selected"
end if
if session("chrShipState") = "TN" then
SelTN = "Selected"
end if
if session("chrShipState") = "TX" then
SelTX = "Selected"
end if
if session("chrShipState") = "UT" then
SelUT = "Selected"
end if
if session("chrShipState") = "VT" then
SelVT = "Selected"
end if
if session("chrShipState") = "VA" then
SelVA = "Selected"
end if
if session("chrShipState") = "WY" then
SelWY = "Selected"
end if
if session("chrShipState") = "WI" then
SelWI = "Selected"
end if
if session("chrShipState") = "WV" then
SelWV = "Selected"
end if
if session("chrShipState") = "WA" then
SelWA = "Selected"
end if
if session("chrShipState") = "FSO" then
SelFSO = "Selected"
end if

%>
После того как одной из переменных будет присвоено нужное значение, мы переходим к построению списка штатов. В каждую строку списка включается название штата и соответствующая переменная VBScript. Переменная, которой была присвоена строка SELECTED, вставит ключевое слово в тег. Таким образом, эта строка окажется выбранной в списке по умолчанию.

Листинг 8.10. Shipping.asp (продолжение)
<!-- Список для выбора штата. По умолчанию в списке
выбирается предыдущий выбранный штат -->
<select name="chrShipState">
<option value="">Select a State
<option value="AL" <%=SelAL%>>Alabama
<option value="AK" <%=SelAK%>>Alaska
<option value="AZ" <%=SelAZ%>>Arizona
<option value="AR" <%=SelAR%>>Arkansas
<option value="CA" <%=SelCA%>>California
<option value="CT" <%=SelCT%>>Connecticut
<option value="CO" <%=SelCO%>>Colorado
<option value="DC" <%=SelDC%>>D.C.
<option value="DE" <%=SelDE%>>Delaware
<option value="FL" <%=SelFL%>>Florida
<option value="GA" <%=SelGA%>>eorgia
<option value="HI" <%=SelHI%>>Hawaii
<option value="ID" <%=SelID%>>Idaho
<option value="IL" <%=SelIL%>>Illinois
<option value="IN" <%=SelIN%>>Indiana
<option value="IA" <%=SelIA%>>Iowa
<option value="KS" <%=SelKS%>>Kansas
<option value="KY" <%=SelKY%>>Kentucky
<option value="LA" <%=SelLA%>>Louisiana
<option value="ME" <%=SelME%>>Maine
<option value="MA" <%=SelMA%>>Massachusetts
<option value="MD" <%=SelMD%>>Maryland
<option value="MI" <%=SelMI%>>Michigan
<option value="MN" <%=SelMN%>>Minnesota
<option value="MS" <%=SelMS%>>Mississippi
<option value="MO" <%=SelMO%>>Missouri
<option value="MT" <%=SelMT%>>Montana
<option value="NE" <%=SelNE%>>Nebraska
<option value="NV" <%=SelNV%>>Nevada
<option value="NH" <%=SelNH%>>New Hampshire
<option value="NJ" <%=SelNJ%>>New Jersey
<option value="NM" <%=SelNM%>>New Mexico
<option value="NY" <%=SelNY%>>New York
<option value="NC" <%=SelNC%>>North Carolina
<option value="ND" <%=SelND%>>North Dakota
<option value="OH" <%=SelOH%>>Ohio
<option value="OK" <%=SelOK%>>Oklahoma
<option value="OR" <%=SelOR%>>Oregon
<option value="PA" <%=SelPA%>>Pennsylvania
<option value="RI" <%=SelRI%>>Rhode Island
<option value="SC" <%=SelSC%>>South Carolina
<option value="SD" <%=SelSD%>>South Dakota
<option value="TN" <%=SelTN%>>Tennessee
<option value="TX" <%=SelTX%>>Texas
<option value="UT" <%=SelUT%>>Utah
<option value="VT" <%=SelVT%>>Vermont
<option value="VA" <%=SelVA%>>Virginia
<option value="WA" <%=SelWA%>>Washington
<option value="WY" <%=SelWY%>>Wyoming
<option value="WI" <%=SelWI%>>Wisconsin
<option value="WV" <%=SelWV%>>West Virginia
<OPTION value="FSO" <%=SelFSO%>>Military Stuff
</select>
Поскольку мы хотим обеспечить международную доставку заказов, для тех стран, у которых нет штатов, также необходимо предусмотреть поле для ввода области. Покупатель вводит либо штат, либо область - это обстоятельство будет проверено на странице ValidateShipping.asp.
Далее пользователь выбирает страну, в которую доставляется заказ. В нашем примере поддерживается доставка только в Канаду и Мексику. По аналогии со списком штатов, в списке стран необходимо восстановить ранее выбранную строку. Как и прежде, переменной VBScript для соответствующей строки списка присваивается значение SELECTED. Если страна была выбрана ранее или загружена из профиля, она автоматически выбирается в списке.
Листинг 8.11. Shipping.asp (продолжение)
<!-- Список областей -->
or Province:<input type="text"
value="<%=trim(session("chrShipProvince"))%>"
name="chrShipProvince" size="15">

</td>
</tr>
<!-- Shipping Country -->
<tr>
<td align="right">Country:</td>
<td>

<%
' Проверить, какая страна была выбрана ранее (если при вводе
' данных была допущена ошибка). Соответствующей переменной,
' вставляемой в тег HTML, присваивается строка 'SELECTED'.
if session("chrShipCountry") = "US" then
SelUS = "Selected"
end if
if session("chrShipCountry") = "CA" then
SelCA = "Selected"
end if

if session("chrShipCountry") = "MX" then
SelMX = "Selected"
end if

%>

<!-- Список для выбора страны. По умолчанию в списке
выбирается предыдущая выбранная страна -->
<select name="chrShipCountry">
<option value="">Select a Country
<option value="US" <%=SelUS%>>United States
<option value="CA" <%=SelCA%>>Canada
<option value="MX" <%=SelMX%>>Mexico
</select>
</td>
</tr>
Оставшаяся часть адресной информации вводится в текстовых полях. В каждом текстовом поле по умолчанию восстанавливается его предыдущее содержимое. Таблица завершается кнопкой отправки данных формы, закрывающим тегом формы и закрывающим тегом Web-страницы.
Листинг 8.12. Shipping.asp (продолжение)
<!-- Почтовый индекс -->
<tr>
<td align="right">Zip/Postal Code:</td>
<td><input type="text" value="<%=session("chrShipZipCode")%>"
name="chrShipZipCode" size="30"></td>
</tr>
<!-- Телефон -->
<tr>
<td align="right">Phone:</td>
<td><input type="text" value="<%=session("chrShipPhone")%>"
name="chrShipPhone" size="30"></td>
</tr>
<!-- Электронная почта -->
<tr>
<td align="right">Email:</td>
<td><input type="text" value="<%=session("chrShipEmail")%>"
name="chrShipEmail" size="30"></td>
</tr>
<!-- Кнопка отправки данных -->
<tr>
<td colspan="2" align="center">
<input type="Submit" value="Submit" name="Submit">
</td>
</tr>
</table>
</form>
</center>
<!-- #include file="include/footer.asp" -->
</BODY>
</HTML>
Загрузка данных из ранее созданного профиля осуществляется при помощи хранимой процедуры sp_RetrieveProfileByID. Идентификатор покупателя передается процедуре в качестве параметра, а запрос выбирает записи из таблицы Shopper.
Листинг 8.13. Хранимая процедура sp_RetrieveProfileByID
/* Загрузка профиля покупателя */
CREATE PROCEDURE sp_RetrieveProfileByID
/* При вызове процедуре передается идентификатор покупателя */
@idShopper int
AS
/* Выборка информации о покупателе по идентификатору */
select * from shopper
where idShopper = @idShopper
Чтобы проверить, как работает паша страница, мы сначала рассмотрим ситуацию, при которой существующий профиль не загружается. Не забудьте удалить все cookie профилей, созданные при работе с примерами.
Включите несколько позиций в корзину и перейдите на страницу доставки. Пустая страница. Обратите внимание на ссылку, предлагающую загрузить предыдущий профиль.
Попробуем заполнить страницу данными. Заполните все поля, но не выбирайте штат и не заполняйте текстовое поле Province.
После щелчка на кнопке Submit управление передается странице ValidateShipping.asp. Вы получите сообщение об ошибке, поскольку на форме не был выбран штат. Теперь выберите штат и продолжите оформление заказа.

Проверка реквизитов доставки
Вы только что увидели страницу проверки в действии. Давайте посмотрим, как она устроена. Страница ValidateShipping.asp предназначена для проверки данных, введенных на странице Shipping.asp, и возможности их использования при оформлении заказа.
Работа страницы начинается с получения данных, введенных на форме. Затем выполняется проверка каждого поля. В большинстве случаев мы просто убеждаемся в том, что поле не было оставлено пустым. Такие сведения, как имена, фамилии и т. д., трудно проверить более подробно.
Рис. 8.5. Страница доставки с сообщением об ошибке
Листинг 8.14. ValidateShipping.asp
<%@ Language=VBScript %>
<%
' Прочитать полученные данные.
chrShipFirstName = request("chrShipFirstName")
chrShipLastName = request("chrShipLastName")
chrShipAddress = request("chrShipAddress")
chrShipCity = request("chrShipCity")
chrShipState = request("chrShipState")
chrShipProvince = request("chrShipProvince")
chrShipCountry = request("chrShipCountry")
chrShipZipCode = request("chrShipZipCode")
chrShipPhone = request("chrShipPhone")
chrShipEmail = request("chrShipEmail")
' Check to ensure a first name was entered.
if chrShipFirstName = "" then
strError = strError & "Invalid first name<BR>"
end if
' Check to ensure a last name was entered.
if chrShipLastName = "" then
strError = strError & "Invalid last name<BR>"
end if
' Check to ensure an address was entered.
if chrShipAddress = "" then
strError = strError & "Invalid address<BR>"
end if
' Check to ensure a city was entered.
if chrShipCity = "" then
strError = strError & "Invalid city<BR>"
end if
Поля штата, области и страны требуют дополнительной проверки. Мы не хотим, чтобы наш покупатель выбрал один из штатов США, а затем запросил доставку в Канаду. Аналогично, пользователь не должен ввести название области и затем выбрать США в качестве страны доставки.
Наша программа проверяет, какая страна была выбрана в списке. Затем в зависимости от результата проверяется правильность выбора страны или штата.
Листинг 8.15. ValidateShipping.asp (продолжение)
' Check to see if a US country was entered.
if chrShipCountry = "US" then
' If the state field is empty then build the
' appropriate error message.
if chrShipState = "" then
strError = strError & "Invalid state<BR>"
end if
else
' If it is an international country is entered
' then a province must be entered.
if chrShipProvince = "" then

strError = strError & "Invalid province<BR>"

end if
end if
' Check to see if a country was entered.
if chrShipCountry = "" then
strError = strError & "Invalid country<BR>"

end if
' Check to ensure a zip code was entered.
if chrShipZipCode = "" then
strError = strError & "Invalid zip code<BR>"

end if
' Check to ensure a phone number was entered.
if chrShipPhone = "" then
strError = strError & "Invalid phone number<BR>"

end if
Адрес электронной почты также можно проверить дополнительно. Как известно, любой адрес электронной почты должен содержать символ @ и точку. Наличие этих символов в адресе проверяется функцией VBScript InStr.
Листинг 8.16. ValidateShipping.asp (продолжение)
' Проверить адрес электронной почты. Адрес обязательно
' должен включать символ @ и точку.
if (instr(1, chrShipEmail, "@") = 0) or _
(instr(1, chrShipEmail, ".") = 0) then
strError = strError & "Invalid email address<BR>"
end if
Еще одна дополнительная проверка полей штата и области заключается в том, что эти поля не должны быть заполнены одновременно. Выше мы убедились в том, что в зависимости от выбранной страны была введена правильная информация (штат или область), однако покупатель мог ввести данные в двух полях сразу.
Листинг 8.17. ValidateShipping.asp (продолжение)
' Убедиться в том, что поля штата и области не были заполнены одновременно.
if (chrShipState <> "") and (chrShipProvince <> "") then
strError = strError & "You can not fill in both the " & _
"state and province fields<BR>"
end if
После всех проверок можно сохранить значения в сеансовых переменных, соответствующих разным полям. Тем самым мы обеспечим восстановление данных, введенных пользователем, на странице выписки счета или на странице доставки (если пользователь допустил ошибку).
Листинг 8.18. ValidateShipping.asp (продолжение)
' Скопировать реквизиты доставки в сеансовые переменные
session("chrShipFirstName") = request("chrShipFirstName")
session("chrShipLastName") = request("chrShipLastName")
session("chrShipAddress") = request("chrShipAddress")
session("chrShipCity") = request("chrShipCity")
session("chrShipState") = request("chrShipState")
session("chrShipProvince") = request("chrShipProvince")
session("chrShipCountry") = request("chrShipCountry")
session("chrShipZipCode") = request("chrShipZipCode")
session("chrShipPhone") = request("chrShipPhone")
session("chrShipEmail") = request("chrShipEmail")
Чтобы узнать о наличии ошибок в данных, мы проверяем переменную str-Еггог. При наличии ошибки мы сохраняем ее текст в сеансовой переменной и возвращаем покупателя на страницу Shipping.asp для повторного заполнения формы.
Листинг 8.19. ValidateShipping.asp (продолжение)
' Проверить, была ли обнаружена ошибка.
if strError <> "" then
' Сохранить ошибку в сеансовой переменной и вернуть
' покупателя на страницу shipping.asp.
session("Error") = strError
Response.Redirect "shipping.asp"
else
Если ошибок не было, значит, проверка прошла успешно. Поскольку для выписки счета и доставки обычно используются одни и те же адресные данные, мы присваиваем сеансовым переменным, определяющим реквизиты для выписки счета, значения соответствующих переменных для доставки. Затем пользователь направляется на страницу Payment.asp, на которой продолжается оформление заказа.
Листинг 8.20. ValidateShipping.asp (продолжение)
' Все данные введены правильно. Скопировать реквизиты доставки
' в сеансовые переменные реквизитов выписки счета, чтобы текстовые
' поля на форме заполнялись по умолчанию. При этом пользователю
' не придется вводить заново совпадающие реквизиты.
session("chrBillFirstName") = request("chrShipFirstName")
session("chrBillLastName") = request("chrShipLastName")
session("chrBillAddress") = request("chrShipAddress")
session("chrBillCity") = request("chrShipCity")
session("chrBillState") = request("chrShipState")
session("chrBillProvince") = request("chrShipProvince")
session("chrBillCountry") = request("chrShipCountry")
session("chrBillZipCode") = request("chrShipZipCode")
session("chrBillPhone") = request("chrShipPhone")
session("chrBillEmail") = request("chrShipEmail")
' Направить пользователя на страницу payment.asp.
Response.Redirect "Payment.asp"
end if
%>
ПРИМЕЧАНИЕ
В рассмотренном примере мы просто проверяем тот факт, что область или штат выбирается вместе с соответствующей страной. Кроме того, проверяется наличие информации в поле почтового индекса. Тем не менее, при больших объемах международных заказов потенциальные затраты, связанные с получением неверных данных, оказываются довольно высокими. Возможны и другие варианты. Например, возможна проверка почтовых индексов штата или заполнение списка областей в зависимости от выбранной страны. При необходимости вы можете самостоятельно организовать подобную проверку.

Вычисление стоимости доставки и налога
После проверки введенных реквизитов доставки оформление заказа переходит на следующую стадию. Но прежде чем рассматривать страницу Payment.asp, мы займемся программированием на Visual Basic 6. Как упоминалось выше, основные правила бизнес-логики (такие, как вычисление налога и стоимости доставки) будут инкапсулированы в объекте СОМ, доступном для страниц ASP. Основные этапы построения объекта СОМ перечислены ниже.

  1. Создание нового проекта Visual Basic 6.
  2. Создание нового класса, содержащего две функции для вычисления налога и стоимости доставки.
  3. Программирование обращений к базе данных и вычисления соответствующих величин.
  4. Построение библиотеки DLL, содержащей объект СОМ.
  5. Использование объекта СОМ в страницах ASP.

Прежде всего запустите Visual Basic и создайте новый проект.
Вам предлагается выбрать тип создаваемого проекта. В нашем примере следует выбрать значок ActiveX DLL. Созданная библиотека DLL устанавливается на сервере, на котором будет работать Web-сайт. Обращения к DLL из программного кода ASP происходят через интерфейс СОМ.
Создайте новый проект и сохраните его с именем ECStoreBizLogic. В некоторые свойства проекта необходимо внести дополнительные изменения. Выберите из меню Project команду <Project>Properties, где <Project>соответствует первоначальному имени проекта.
На вкладке General введите в поле Project Name строку ECStoreBizLogic. Щелкните на вкладке Make и повторите строку ECStoreBizLogic в поле Application Title. Тем самым мы обеспечиваем правильный вызов объекта СОМ из страниц ASP. Нажмите кнопку ОК - все готово к продолжению работы над проектом.
Поскольку подключение к базе данных будет осуществляться средствами ADO, ссылку на библиотеку ADO необходимо включить в проект Visual Basic. Для этого снова откройте меню Project и выберите команду References. На экране появляется диалоговое окно.
Установите флажок напротив строки Microsoft ActiveX Data Object 2 Library и нажмите кнопку ОК. Ссылка на объекты ADO включается в проект.
На следующем шаге мы переименуем класс, автоматически созданный в проекте. Имя класса Class1 заменяется именем ТaxShip. В объекте COM ECStoreBizLogic мы будем обращаться к объекту TaxShip для вызова функций вычисления налога и стоимости доставки.
Переходим к работе с программным кодом класса. Дважды щелкните на классе TaxShiр, чтобы открыть его для редактирования. Мы создадим в классе две функции, Tax и Shipping. Объявления этих функций выглядят следующим образом:
Public Function Tax (strState As String,
intOrderTotal As Long) As Long
Public Function Shipping (intQuantity As Long) As Long
Функции Tax передается строка с названием штата и общая стоимость заказа для вычисления налога. Значение, возвращаемое при вызове функции, представляет собой сумму налога.
Функции Shipping передается количество единиц товара в заказе. Как говорилось выше, стоимость доставки вычисляется на основании именно этой величины. Функция возвращает стоимость доставки для заданного заказа.
Код класса начинается с команды Option Explicit (см. листинг 8.21), означающей, что все переменные должны быть явно объявлены в программе. В такой мощной среде разработки, как Visual Basic, явное объявление переменных уменьшает количество ошибок.
Первая функция, Tax, начинается с приведенного выше объявления. Затем мы открываем подключение к базе данных средствами ADO. В сущности, этот фрагмент ничем не отличается от аналогичного фрагмента кода ASP.
Хранимая процедура sp_GetTaxRate вычисляет налоговую ставку для штата, переданного при вызове функции. Мы проверяем, вернула ли эта функция какое-либо значение. Если значение отсутствует, налоговая ставка равна 0. В противном случае общая стоимость заказа, переданная функции, умножается на налоговую ставку. Полученная величина возвращается приложению, из которого была вызвана функция Tax.
Листинг 8.21. TaxShip.cls
Option Explicit
' Функция возвращает величину налога, вычисленную на основании общей
' стоимости заказа и штата, в который осуществляется доставка.
Public Function Tax(strState As String, intOrderTotal As Long) As Long
' Declare our variables
Dim dbTax As New ADODB.Connection
Dim rsTax As New ADODB.Recordset
Dim strSQL As String
' Open the connection
dbTax.Open "filedsn=WildWillieCDs"
' Creat the SQL statement and pass in the
' state to get the appropriate rate
strSQL = "execute sp_GetTaxRate '" & strState & "'"
' Get the record set.
Set rsTax = dbTax.Execute(strSQL)
' See if a rate was returned.
If rsTax.EOF = False Then
' Set the amount of tax.
Tax = (rsTax("fltTaxRate") * intOrderTotal)
Else
' Return no tax.
Tax = 0
End If
End Function
Следующая функция класса предназначена для вычисления стоимости доставки. На этот раз мы открываем подключение данных ADO для загрузки информации о стоимости доставки из базы данных.
Загрузка интервалов осуществляется хранимой процедурой sp_GetShippingRate. Мы перебираем интервалы и сравниваем с их границами величину, переданную функции. Если количество находится между нижней и верхней границей интервала, функция возвращает стоимость доставки для данного интервала.

Листинг 8.22. TaxShip.cls (продолжение)
' Функция вычисляет стоимость доставки на основании
' количества единиц товара в заказе.
Public Function Shipping(intQuantity As Long) As Long
' Declare our variables.
Dim dbShipping As New ADODB.Connection
Dim rsShipping As New ADODB.Recordset
Dim strSQL As String
' Open the connection
dbShipping.Open "filedsn=WildWillieCDs"
' Declare the SQL statement
strSQL = "execute sp_GetShippingRate"
' Retrieve the record set
Set rsShipping = dbShipping.Execute(strSQL)
' Start out with a shipping rate of $0
Shipping = 0
' Loop through the quantity settings
Do Until rsShipping.EOF
' Check to see if the quantity is between the
' current low and high values
If intQuantity >= rsShipping("intLowQuantity") And _
intQuantity <= rsShipping("intHighQuantity") Then

' If so set the shipping fee.
Shipping = rsShipping("intFee")

End If
' Move to the next row
rsShipping.MoveNext
Loop
End Function
Хранимая процедура sp_GetShippingRate загружает все записи интервалов стоимости доставки из таблицы Shipping.
Листинг 8.23. Хранимая процедура sp_GetShippingRate
/* Загрузка интервальных расценок доставки, используемых магазином */
CREATE PROCEDURE sp_GetShippingRate
AS
/* Вернуть все записи из таблицы Shipping */
select * from shipping
Хранимой процедуре sp_GetTaxRate передается сокращенное название штата. Процедура возвращает налоговую ставку для заданного штата, если она присутствует в базе данных.
Листинг 8.24. Хранимая процедура sp_GetTaxRate
/* Получение налоговой ставки для заданного штата. */
CREATE PROCEDURE sp_GetTaxRate
/* При вызове процедуре передается сокращенное название штата */
@chrState varchar(2)
AS
/* Получить налоговую ставку для заданного штата */
select fltTaxRate from tax where chrState = @chrState
Вот и весь код класса. Как видите, программный код для вычисления налога и стоимости доставки устроен достаточно просто. Пора переходить к построению объекта.
СОВЕТ
Чтобы протестировать код объекта перед его использованием на странице ASP, создайте второй проект Visual Basic со стандартным интерфейсом форм (Standart EXE). Ссылка на DLL (ECStoreBizLogic) встречается в диалоговом окне References. Протестируйте функции Tax и Shipping в Visual Basic и убедитесь в правильности их работы.

Чтобы использовать объект СОМ, необходимо откомпилировать приложение в библиотеку DLL. Если DLL была откомпилирована на том сервере, где она будет использоваться, остается лишь включить в программу соответствующее обращение к объекту СОМ. Если DLL включается в пакет, предназначенный для дальнейшего распространения, воспользуйтесь мастером Package and Deployment Wizard из поставки Visual Basic 6 - он поддерживает несколько вариантов распространения компонентов. За подробным описанием обращайтесь к документации Visual Basic 6.
После установки библиотеки DLL на сервере ее можно использовать в страницах ASP. Как и при создании объектов подключения ADO, мы воспользуемся синтаксической конструкцией server. Createobject для реализации объекта СОМ ECStoreBizLogic. Пример использования объекта СОМ приведен в следующем фрагменте.
set EC = _
server.CreateObject("ECStoreBizLogic .TaxShip")
Tax = EC.Tax('VA', 10000)
Shipping = EC.Shipping(10)
В первой строке этого фрагмента мы создаем экземпляр объекта TaxShiр. Для ссылок на объект будет использоваться имя Е С. В следующей строке мы вызываем функцию Tax, передаем ей сокращенное название штата Вирджиния и общую стоимость заказа в $100.00. В следующей строке мы вызываем функцию Shipping для 10 единиц товара.
От реализации бизнес-логики вычисления налога и стоимости доставки мы переходим к странице Payment, на которой расположена последняя форма для ввода данных покупателем.
Страница Payment
После успешной проверки реквизитов доставки пользователь попадает на страницу Payment.asp (см. листинг 8.25). На этой странице он вводит данные кредитной карты и реквизиты для выписки счета.
Страница Payment.asp является последним важным шагом в процессе оформления заказа. Прежде всего мы проверяем, перешел ли пользователь на эту страницу по предусмотренному пути. Для этой цели используется переменная HTTP_RE -FERER коллекции ServerVariables объекта Request. Переменная HTTP_REFERER указывает, с какой страницы покупатель перешел на текущую страницу. Если переход не был осуществлен со страниц Shipping.asp, ValidatePayment.asp или Payment.asp (вследствие обновления страницы), пользователь направляется на страницу корзины Basket.asp.
Листинг 8.25. Payment.asp
<%@ Language=VBScript %>
<%
' ****************************************************
' Payment.asp - ввод покупателем информации для выписки
' счета и данных кредитной карты.
' ****************************************************
' Убедиться в том. что пользователь попал на эту страницу со страницы
' shipping, страницы payment (кнопка back или ошибка) или
' вследствие обновления страницы. Для чтения заголовка HTTP
' используется коллекция ServerVariables.
if instr(lcase(Request.ServerVariables("HTTP_REFERER")), _
"shipping.asp") = 0 and _
instr(lcase(Request.ServerVariables("HTTP_REFERER")), _
"validatepayment.asp") = 0 and _
instr(lcase(Request.ServerVariables("HTTP_REFERER")), _
"payment.asp") = 0 then

' Вернуть пользователя на страницу корзины
Response.Redirect "basket.asp"

end if
Переходим к выводу, общей стоимости заказа. Страница открывает подключение к базе данных и вызывает хранимую процедуру sp_BasketSubtotal для загрузки общей стоимости текущей корзины. Затем хранимая процедура sp_BasketQuantity загружает текущее количество единиц товара в корзине. Обе величины сохраняются в сеансовых переменных для последующих ссылок на них при сохранении заказа в базе данных.
Листинг 8.26. Payment.asp (продолжение)
' Создать объект подключения к базе данных
set dbBasket = server.createobject("adodb.connection")
' Создать набор записей
set rsBasket = server.CreateObject("adodb.recordset")
' Открыть подключение, используя файловый ODBC DSN
dbBasket.open("filedsn=WildWillieCDs")
' Построить команду вызова хранимой процедуры SQL для
' загрузки общей стоимости корзины
sql = "execute sp_BasketSubtotal " & session("idBasket")
' Выполнить команду
set rsBasket = dbBasket.Execute(sql)
' Загрузить общую стоимость и сохранить ее в сеансовой переменной.
SubTotal = rsBasket("subtotal")
session("Subtotal") = Subtotal
' Построить запрос для загрузки количества единиц товара в корзине
sql = "execute sp_BasketQuantity " & session("idBasket")
' Выполнить команду
set rsBasket = dbBasket.Execute(sql)
Все готово к использованию объекта СОМ для вычисления налога и стоимости доставки корзины. Мы создаем экземпляр объекта TaxShip при помощи синтаксической конструкции server.CreateObject.
Мы проверяем, что возвращенное количество единиц не равно 0. В противном случае покупатель направляется на страницу корзины. Если все нормально, мы выполняем метод Shipping, который возвращает стоимость доставки корзины. Полученное значение сохраняется в сеансовой переменной.
Листинг 8.27. Payment.asp (продолжение)
' Создать экземпляр компонента ECStoreBizLogic для вычисления
' налога и стоимости доставки.
set BizLogic = server.CreateObject("ECStoreBizLogic.TaxShip")
' Проверить количество единиц, загруженное из базы данных
if rsBasket("quantity") > 0 then
' Вызвать функцию Shipping нашего компонента. Передать ей
' количество единиц товара в формате Long
' Функция возвращает стоимость доставки.
Shipping = BizLogic.Shipping(clng(rsBasket("quantity")))

else

' Направить покупателя на страницу корзины, поскольку
' количество единиц равно 0
Response.Redirect("Basket.asp")

end if
' Сохранить стоимость доставки в сеансовой переменной
session("Shipping") = Shipping
' Сохранить количество единиц в сеансовой переменной
session("Quantity") = rsBasket("quantity")
Затем мы выполняем метод Tax объекта СОМ и передаем ему общую стоимость корзины. Сумма налога на заказ также сохраняется в сеансовой переменной. Затем мы вычисляем итоговую стоимость заказа, суммируя общую стоимость, стоимость доставки и налог.
Листинг 8.28. Payment.asp (продолжение)
' Вызвать функцию Tax нашего компонента для вычисления налога.
' Функции передается сокращенное название штата и общая стоимость
' заказа. Полученное значение сохраняется в сеансовой переменной.
Tax = BizLogic.tax(session("chrShipState"), clng(subtotal))
session("Tax") = Tax
' Вычислить итоговую стоимость заказа и сохранить ее в сеансовой переменной.
Total = SubTotal + Shipping + Tax
session("Total") = Total
%>
После завершения всех проверок и вычислений можно перейти к построению страницы. Как обычно, страница начинается с включения заголовка. Затем мы выводим сводку данных о заказе на основании только что выполненных вычислений.
Для этого создается простая таблица, в каждой строке которой выводится соответствующая величина. Сначала выводится общая стоимость заказа, за ней - стоимость доставки, затем налог, и, наконец, итоговая стоимость заказа. Таким образом покупатель узнает итоговую сумму, которая будет оплачена при помощи его кредитной карты.
Листинг 8.29. Payment.asp (продолжение)
<HTML>
<!-- #include file="include/header.asp" -->
<BR>
<center>
<font size="5"><b>Billing Information</b></font>
</center>
<BR>
<b>Order Recap:</b>
<BR><BR>
<!-- Построить таблицу для отображения итоговой сводки -->
<table>
<!-- Вывести общую стоимость заказа -->
<tr>
<td align="right">Subtotal:</td>
<td><%=formatcurrency(Subtotal/100, 2)%></td>
</tr>
<!-- Вывести стоимость доставки -->
<tr>
<td align="right">Shipping:</td>
<td><%=formatcurrency(Shipping/100, 2)%></td>
</tr>
<!-- Вывести величину налога -->
<tr>
<td align="right">Tax:</td>
<td><%=formatcurrency(Tax/100, 2)%></td>
</tr>
<!-- Вывести итоговую стоимость заказа -->
<tr>
<td align="right"><B>Total:</b></td>
<td><b><%=formatcurrency(Total/100, 2)%></b></td>
</tr>
</table>
В следующей части страницы создаются поля для ввода данных кредитной карты покупателя. Введенные данные отправляются на страницу ValidatePayment.asp.
В поле chrCCName вводится имя владельца карты, в поле chrCCNumber - номер кредитной карты, а в поле chrCCType - тип кредитной карты. В поле chrCCExpMonth выбирается месяц, а в поле chrCCExpYear - год окончания действия карты.
СОВЕТ
При первом посещении страницы Payment.asp в поле имени владельца кредитной карты можно занести имя, взятое из профиля. Единственная опасность заключается в том, что в профиле указывается имя вида John Doe, а кредитная карта может быть оформлена на имя John E Doe. Если покупатель забудет указать средний инициал, при проверке кредитной карты возникнут проблемы.

Листинг 8.30. Payment.asp (продолжение)
<!-- Ввод данных кредитной карты -->
<BR>
<b>Enter your Credit Card information:</b>
<BR><BR>
<!-- Форма передает введенные данные на страницу ValidatePayment -->
<form method="post" action="ValidatePayment.asp" id=form1 name=form1>
<!-- Таблица для ввода данных кредитной карты -->
<table>
<!-- Имя владельца кредитной карты -->
<tr>
<td align="right">Name on Card:</td>
<td><input type="text" value="<%=session("chrCCName")%>"
name="chrCCName" size="55"></td>
</tr>
<!-- Номер кредитной карты -->
<tr>
<td align="right">Card Number:</td>
<td><input type="text" value="<%=session("chrCCNumber")%>"
name="chrCCNumber" size="55"></td>
</tr>
В части данных кредитной карты находятся три списка. Всегда существует вероятность того, что покупатель допустит ошибку при вводе данных на этой странице и вернется на нее. В этом случае мы должны восстановить в каждом списке ранее выбранное значение.
Как было показано при описании страницы Shipping.asp, для восстановления ранее выбранной строки используется серия проверок. Если текущее значение было выбрано ранее, соответствующей переменной присваивается текст SELECTED. При построении тегов списка на странице в соответствующий тег будет включен текст SELECTED. Первый список предназначен для выбора типа карты.
Листинг 8.31. Payment.asp (продолжение)
<!-- Тип кредитной карты -->
<tr>
<td align="right">Card Type:</td>
<td>
<!-- Определить, какой тип карты был выбран ранее в случае ошибки -->
<%
' Check to see which card was selected previously
' if there was an error.
if session("chrCCType") = "Visa" then
SelVisa = "Selected"
end if
if session("chrCCType") = "MasterCard" then
SelMasterCard = "Selected"
end if
if session("chrCCType") = "Amex" then
SelAmex = "Selected"
end if
%>
<!-- Создать список, в котором по умолчанию выбирается ранее выбранный тип карты -->
<select name="chrCCType">
<option value="Visa" <%=SelVisa%>>Visa
<option value="MasterCard" <%=SelMasterCard%>>Master Card
<option value="Amex" <%=SelAmex%>>American Express
</select>
В следующем списке выбирается месяц окончания срока действия кредитной карты. Обратите внимание: в базе данных месяц хранится в виде числа, но в списке выводится полное название месяца.
Листинг 8.32. Payment.asp (продолжение)
<%
' Определить, какой месяц был выбран ранее.
if session("chrCCExpMonth") = "1" then
SelJan = "Selected"
end if
if session("chrCCExpMonth") = "2" then
SelFeb = "Selected"
end if
if session("chrCCExpMonth") = "3" then
SelMar = "Selected"
end if
if session("chrCCExpMonth") = "4" then
SelApr = "Selected"
end if
if session("chrCCExpMonth") = "5" then
SelMay = "Selected"
end if
if session("chrCCExpMonth") = "6" then
SelJun = "Selected"
end if
if session("chrCCExpMonth") = "7" then
SelJul = "Selected"
end if
if session("chrCCExpMonth") = "8" then
SelAug = "Selected"
end if
if session("chrCCExpMonth") = "9" then
SelSep = "Selected"
end if
if session("chrCCExpMonth") = "10" then
SelOct = "Selected"
end if
if session("chrCCExpMonth") = "11" then
SelNov = "Selected"
end if
if session("chrCCExpMonth") = "12" then
SelDec = "Selected"
end if
%>

<!-- Select option box to allow the user to
select a card expiration month -->
Month:
<select name="chrCCExpMonth">
<option value="1" <%=SelJan%>>January
<option value="2" <%=SelFeb%>>February
<option value="3" <%=SelMar%>>March
<option value="4" <%=SelApr%>>April
<option value="5" <%=SelMay%>>May
<option value="6" <%=SelJun%>>June
<option value="7" <%=SelJul%>>July
<option value="8" <%=SelAug%>>August
<option value="9" <%=SelSep%>>September
<option value="10" <%=SelOct%>>October
<option value="11" <%=SelNov%>>November
<option value="12" <%=SelDec%>>December
</select>
В последнем списке выбирается год окончания строка действия кредитной карты. Значение по умолчанию выбирается по тому же принципу.
ПРИМЕЧАНИЕ
Обратите внимание: в приведенном примере год жестко кодируется в программе. Это означает, что после истечения 1999 года нам придется удалить его и, вероятно, добавить новую строку в конец списка. Можно написать код VBScript для автоматического построения списка X годов, начиная с текущего, однако при этом логика восстановления предыдущего выбора несколько усложняется.

Листинг 8.33. Payment.asp (продолжение)
<%
' Определить, какой год был выбран ранее в случае ошибки.
if session("chrCCExpYear") = "1999" then
Sel1999 = "Selected"
end if
if session("chrCCExpYear") = "2000" then
Sel2000 = "Selected"
end if

if session("chrCCExpYear") = "2001" then
Sel2001 = "Selected"
end if
if session("chrCCExpYear") = "2002" then
Sel2002 = "Selected"
end if
%>

<!-- Option box to select the card expiration year -->
Year:
<select name="chrCCExpYear">
<option value="1999" <%=Sel1999%>>1999
<option value="2000" <%=Sel2000%>>2000
<option value="2001" <%=Sel2001%>>2001
<option value="2002" <%=Sel2002%>>2002
</select>

</td>
</tr>
</table>
Далее выводятся все сообщения об ошибках, обнаруженных в процессе проверки. По аналогии со страницей Shipping.asp описания ошибок присваиваются сеансовой переменной Error. Мы просто проверяем, задано ли значение этой переменной, и если оно есть - выводим его на странице. После вывода сообщений об ошибках значение сеансовой переменной Error сбрасывается.
Листинг 8.34. Payment.asp (продолжение)
<%
' Проверить наличие ошибок.
if session("Error") <> "" then
%>
<!-- Diplay the error message -->
<BR>
<b>You have an error in your billing form,
please correct the data:</b><BR><BR>
<!-- Write out the error. -->
<table>
<tr>
<td width="70">&nbsp;</td>
<td><i><%=Response.Write(session("Error"))%></i></td>
</tr>
</table>
<BR><BR>
<%
' Show the error.
session("Error") = ""
else
%>
Далее на странице начинаются поля, в которых вводится адрес для выписки счета. Эти поля заполняются по умолчанию либо данными, введенными на странице Shipping.asp, либо данными, введенными ранее на текущей странице.
СОВЕТ
В зависимости от типа покупателей на странице доставки покупателю можно предоставить возможность выбрать, должны ли введенные данные автоматически копироваться на следующую страницу. У крупных покупателей-фирм адреса доставки и выписки счета чаще различаются, нежели совпадают. В этом случае покупателю приходится вручную заменять данные по умолчанию - весьма утомительное занятие. В этом случае данные профиля используются по умолчанию только на этой странице (по желанию покупателя).

Листинг 8.35. Payment.asp (продолжение)
<BR>
<b>Enter your billing address:</b>
<BR><BR>
<%
end if
%>
<center>
В каждой строке таблицы, которую мы создаем, находится одно из адресных полей. Как и в случае формы для ввода реквизитов доставки, большинство данных вводится в текстовых полях. По умолчанию текстовые поля инициализируются значениями сеансовых переменных, значения которых были присвоены на странице ValidateShipping.asp.
Листинг 8.36. Payment.asp (продолжение)
<!-- Таблица для ввода реквизитов выписки счета -->
<table>
<!-- Имя -->
<tr>
<td align="right">First Name:</td>
<td><input type="text" value="<%=session("chrBillFirstName")%>"
name="chrBillFirstName" size="30"></td>
</tr>
<!-- Фамилия -->
<tr>
<td align="right">Last Name:</td>
<td><input type="text" value="<%=session("chrBillLastName")%>"
name="chrBillLastName" size="30"></td>
</tr>
<!-- Адрес -->
<tr>
<td align="right">Address:</td>
<td><input type="text" value="<%=session("chrBillAddress")%>"
name="chrBillAddress" size="30"></td>
</tr>
<!-- Город -->
<tr>
<td align="right">City:</td>
<td><input type="text" value="<%=session("chrBillCity")%>"
name="chrBillCity" size="30"></td>
</tr>
Список штатов обрабатывается так же, как и в форме для ввода адреса доставки. Здесь восстановление прежнего состояния оказывается еще более важным, поскольку в подавляющем большинстве случаев при выписке счета указывается тот же штат, что и при доставке. При этом используется уже знакомый метод с присваиванием одной из переменных, включаемых в теги, строки SELECTED.
Листинг 8.37. Payment.asp (продолжение)
<!-- Штат -->
<tr>
<td align="right">State:</td>
<td>

<%
' Check to see which state was selected previously
' if there was an error.
if session("chrBillState") = "AL" then
SelAL = "Selected"
end if
if session("chrBillState") = "AK" then
SelAK = "Selected"
end if
if session("chrBillState") = "AZ" then
SelAZ = "Selected"
end if
if session("chrBillState") = "AR" then
SelAR = "Selected"
end if
if session("chrBillState") = "CA" then
SelCA = "Selected"
end if
if session("chrBillState") = "CT" then
SelCT = "Selected"
end if
if session("chrBillState") = "CO" then
SelCO = "Selected"
end if
if session("chrBillState") = "DC" then
SelDC = "Selected"
end if
if session("chrBillState") = "DE" then
SelDE = "Selected"
end if
if session("chrBillState") = "FL" then
SelFL = "Selected"
end if
if session("chrBillState") = "GA" then
SelGA = "Selected"
end if
if session("chrBillState") = "HI" then
SelHI = "Selected"
end if
if session("chrBillState") = "ID" then
SelID = "Selected"
end if
if session("chrBillState") = "IL" then
SelIL = "Selected"
end if
if session("chrBillState") = "IN" then
SelIN = "Selected"
end if
if session("chrBillState") = "IA" then
SelIA = "Selected"
end if
if session("chrBillState") = "KS" then
SelKS = "Selected"
end if
if session("chrBillState") = "KY" then
SelKY = "Selected"
end if
if session("chrBillState") = "LA" then
SelLA = "Selected"
end if
if session("chrBillState") = "ME" then
SelME = "Selected"
end if
if session("chrBillState") = "MA" then
SelMA = "Selected"
end if
if session("chrBillState") = "MD" then
SelMD = "Selected"
end if
if session("chrBillState") = "MI" then
SelMI = "Selected"
end if
if session("chrBillState") = "MN" then
SelMN = "Selected"
end if
if session("chrBillState") = "MS" then
SelMS = "Selected"
end if
if session("chrBillState") = "MO" then
SelMO = "Selected"
end if
if session("chrBillState") = "MT" then
SelMT = "Selected"
end if
if session("chrBillState") = "NE" then
SelNE = "Selected"
end if
if session("chrBillState") = "NV" then
SelNV = "Selected"
end if
if session("chrBillState") = "NH" then
SelNH = "Selected"
end if
if session("chrBillState") = "NJ" then
SelNJ = "Selected"
end if
if session("chrBillState") = "NM" then
SelNM = "Selected"
end if
if session("chrBillState") = "NY" then
SelNY = "Selected"
end if
if session("chrBillState") = "NC" then
SelNC = "Selected"
end if
if session("chrBillState") = "ND" then
SelND = "Selected"
end if
if session("chrBillState") = "OH" then
SelOH = "Selected"
end if
if session("chrBillState") = "OK" then
SelOK = "Selected"
end if
if session("chrBillState") = "OR" then
SelOR = "Selected"
end if
if session("chrBillState") = "PA" then
SelPA = "Selected"
end if
if session("chrBillState") = "RI" then
SelRI = "Selected"
end if
if session("chrBillState") = "SC" then
SelSC = "Selected"
end if
if session("chrBillState") = "SD" then
SelSD = "Selected"
end if
if session("chrBillState") = "TN" then
SelTN = "Selected"
end if
if session("chrBillState") = "TX" then
SelTX = "Selected"
end if
if session("chrBillState") = "UT" then
SelUT = "Selected"
end if
if session("chrBillState") = "VT" then
SelVT = "Selected"
end if
if session("chrBillState") = "VA" then
SelVA = "Selected"
end if
if session("chrBillState") = "WY" then
SelWY = "Selected"
end if
if session("chrBillState") = "WI" then
SelWI = "Selected"
end if
if session("chrBillState") = "WV" then
SelWV = "Selected"
end if
if session("chrBillState") = "WA" then
SelWA = "Selected"
end if
%>
После того как переменной штата будет присвоено соответствующее значение, можно приступить к построению списка. Еще раз посмотрите, как выводятся значения переменных. Лишь одна переменная, значение которой было присвоено выше, будет содержать текст SELECTED.
Листинг 8.38. Payment.asp (продолжение)
<!-- Option box to select the billing state -->
<select name="chrBillState">
<option value="">Select a State
<option value="AL" <%=SelAL%>>Alabama
<option value="AK" <%=SelAK%>>Alaska
<option value="AZ" <%=SelAZ%>>Arizona
<option value="AR" <%=SelAR%>>Arkansas
<option value="CA" <%=SelCA%>>California
<option value="CT" <%=SelCT%>>Connecticut
<option value="CO" <%=SelCO%>>Colorado
<option value="DC" <%=SelDC%>>D.C.
<option value="DE" <%=SelDE%>>Delaware
<option value="FL" <%=SelFL%>>Florida
<option value="GA" <%=SelGA%>>eorgia
<option value="HI" <%=SelHI%>>Hawaii
<option value="ID" <%=SelID%>>Idaho
<option value="IL" <%=SelIL%>>Illinois
<option value="IN" <%=SelIN%>>Indiana
<option value="IA" <%=SelIA%>>Iowa
<option value="KS" <%=SelKS%>>Kansas
<option value="KY" <%=SelKY%>>Kentucky
<option value="LA" <%=SelLA%>>Louisiana
<option value="ME" <%=SelME%>>Maine
<option value="MA" <%=SelMA%>>Massachusetts
<option value="MD" <%=SelMD%>>Maryland
<option value="MI" <%=SelMI%>>Michigan
<option value="MN" <%=SelMN%>>Minnesota
<option value="MS" <%=SelMS%>>Mississippi
<option value="MO" <%=SelMO%>>Missouri
<option value="MT" <%=SelMT%>>Montana
<option value="NE" <%=SelNE%>>Nebraska
<option value="NV" <%=SelNV%>>Nevada
<option value="NH" <%=SelNH%>>New Hampshire
<option value="NJ" <%=SelNJ%>>New Jersey
<option value="NM" <%=SelNM%>>New Mexico
<option value="NY" <%=SelNY%>>New York
<option value="NC" <%=SelNC%>>North Carolina
<option value="ND" <%=SelND%>>North Dakota
<option value="OH" <%=SelOH%>>Ohio
<option value="OK" <%=SelOK%>>Oklahoma
<option value="OR" <%=SelOR%>>Oregon
<option value="PA" <%=SelPA%>>Pennsylvania
<option value="RI" <%=SelRI%>>Rhode Island
<option value="SC" <%=SelSC%>>South Carolina
<option value="SD" <%=SelSD%>>South Dakota
<option value="TN" <%=SelTN%>>Tennessee
<option value="TX" <%=SelTX%>>Texas
<option value="UT" <%=SelUT%>>Utah
<option value="VT" <%=SelVT%>>Vermont
<option value="VA" <%=SelVA%>>Virginia
<option value="WA" <%=SelWA%>>Washington
<option value="WY" <%=SelWY%>>Wyoming
<option value="WI" <%=SelWI%>>Wisconsin
<option value="WV" <%=SelWV%>>West Virginia
</select>
Также в форме присутствует поле для ввода области, предназначенное для покупателей за пределами США. Обратите внимание: адрес для выписки счета вполне может находиться за границей, а адрес доставки - в США, и наоборот.
После области находится список для выбора страны. Восстановление в нем предыдущего значения организовано уже знакомым способом.
Листинг 8.39. Payment.asp (продолжение)
<!-- Или разрешить пользователю ввести название области -->
or Province:<input type="text" value="<%=session("chrBillProvince")%>"
name="chrBillProvince" size="15">

</td>
</tr>
<!-- Country selection -->
<tr>
<td align="right">Country:</td>
<td>

<%
' Check to see which country was selected previously
' if there was an error.
if session("chrBillCountry") = "US" then
SelUS = "Selected"
end if
if session("chrBillCountry") = "CA" then
SelCA = "Selected"
end if

if session("chrBillCountry") = "MX" then
SelMX = "Selected"
end if

%>

<!-- Option box for the billing country -->
<select name="chrBillCountry">
<option value="">Select a Country
<option value="US" <%=SelUS%>>United States
<option value="CA" <%=SelCA%>>Canada
<option value="MX" <%=SelMX%>>Mexico
</select>
</td>
</tr>
Оставшаяся часть адресной информации вводится в текстовых полях.
Листинг 8.40. Payment.asp (продолжение)
<!-- Почтовый индекс -->
<tr>
<td align="right">Zip/Postal Code:</td>
<td><input type="text" value="<%=session("chrBillZipCode")%>"
name="chrBillZipCode" size="30"></td>
</tr>
<!-- Billing phone number -->
<tr>
<td align="right">Phone:</td>
<td><input type="text" value="<%=session("chrBillPhone")%>"
name="chrBillPhone" size="30"></td>
</tr>
<!-- Billing email address -->
<tr>
<td align="right">Email:</td>
<td><input type="text" value="<%=session("chrBillEmail")%>"
name="chrBillEmail" size="30"></td>
</tr>
Далее следуют операции с профилем. Мы хотим предоставить покупателю две возможности. Во-первых, он может сохранить профиль в cookie на своем компьютере. В этом случае профиль будет автоматически загружен при следующем посещении магазина. Во-вторых, пользователь может сохранить в профиле пароль, чтобы получить информацию о состоянии заказа или загрузить профиль с другого компьютера, на котором нет cookie.
ПРИМЕЧАНИЕ
При создании cookie пароль покупателя будет загружен при следующем посещении сайта. Но если пароль не задан, покупатель не сможет обратиться к информации о состоянии заказа. Даже при наличии cookie для получения этой информации необходимо ввести адрес электронной почты и пароль.

Предыдущее состояние кнопок-переключателей, управляющих созданием cookie, восстанавливается так же, как и в случае списков. Если при предыдущем посещении сайта или возврате на страницу вследствие ошибки покупатель установил один из этих переключателей, его выбор необходимо восстановить. На этот раз в тег элемента включается ключевое слово CHECKE-D. Мы используем тот же принцип, как и при работе со списками, но вместо SELECTED включается текст CHECKED.
Листинг 8.41. Payment.asp (продолжение)
<!-- Переключатели, управляющие сохранением профиля в cookie -->
<tr>
<td align="right">Save Profile Cookie?</td>
<td>
<%
' Check to see if a previous setting was selected.
if session("intCookie") = 1 then

YesChecked = "CHECKED"

else

NoChecked = "CHECKED"

end if
%>

<!-- Radio boxes to select the cookie setting -->
<input type="radio" value="1" name="intCookie"
<%=YesChecked%>> Yes
<input type="radio" value="0" name="intCookie"
<%=NoChecked%>> No

</td>
</tr>
Для ввода пароля используется стандартный элемент HTML типа PASSWORD, чтобы вводимый пароль не отображался на экране. По умолчанию в поле заносится исходный пароль, введенный покупателем. Обратите внимание - у покупателя появляется возможность сменить пароль, для этого он просто изменяет содержимое поля.
Листинг 8.42. Payment.asp (продолжение)
<!-- Поле для ввода пароля -->
<tr>
<td align="right">Password for Shopper Profile:</td>
<td><input type="password" value="<%=session("chrPassword")%>"
name="chrPassword" size="10"></td>
</tr>
Страница завершается кнопкой для отправки данных и закрывающим тегом формы. Далее следует включение стандартного файла Footer.asp и закрывающие теги страницы.
Листинг 8.43. Payment.asp (продолжение)
<!-- Кнопка отправки данных -->
<tr>
<td colspan="2" align="center">
<input type="Submit" value="Submit" name="Submit">
</td>
</tr>
</table>
</form>
</center>
<!-- #include file="include/footer.asp" -->
</BODY>
</HTML>
Хранимая процедура sp_BasketQuantity предназначена для получения общего количества единиц товара в заказе покупателя. Идентификатор корзины передается хранимой процедуре в качестве параметра. Функция SQL Sum используется для суммирования столбца intQuantity в записях таблицы BasketItem.

Листинг 8.44. Хранимая процедура sp_BasketQuantity
/* Вычисление общего количества единиц товара в корзине */
CREATE PROCEDURE sp_BasketQuantity
/* Команда суммирует количество единиц товара в каждой позиции корзины */
@idBasket int
AS
/* Select statement to sum up the quantity of items
in the basket */
select quantity=sum(intQuantity)
from basketitem
where idBasket = @idBasket
Хранимая процедура sp_BasketSubTotal работает практически так же. Однако на этот раз суммируется количество единиц, умноженное на цену каждого товара в таблице Basketitem для заданной корзины.
Листинг 8.45. Хранимая процедура sp_BasketSubTotal
/* Вычисление общей стоимости всех товаров в корзине. */
CREATE PROCEDURE sp_BasketSubTotal
/* При вызове процедуре передается идентификатор корзины */
@idBasket int
AS
/* Прочитать цену и количество единиц для каждой позиции в корзине. Вычислить общую сумму. */
select subtotal=sum(intQuantity * intPrice)
from basketitem where idBasket = @idBasket
Обратите внимание - данные кредитной карты не указаны, поскольку это первое посещение данной страницы. В данных для выписки счета по умолчанию выбирается штат и страна.
В верхней части страницы отображается информация о дополнительных сборах. В нашем случае имеется налог, поскольку адрес доставки находится в Вирджинии, а в этом штате налоговая ставка равна 4,5%. Стоимость доставки равна $2.00, поскольку корзина содержит от 1 до 10 единиц товара.
Попробуем ввести данные кредитной карты. Чтобы проверить механизм проверки платежных реквизитов, рассмотренный ниже, введите срок действия кредитной карты, предшествующий текущей дате. Заполните данными остальные поля.
При попытке отправить данные для проверки снова появляется страница Payment.asp с сообщением о неверно заданном сроке действия кредитной карты.
Итак, мы посмотрели, как покупатель вводит данные и как происходит проверка этих данных. Давайте разберемся, как же реализована проверка платежных реквизитов в процессе оформления заказа.
Страница ValidatePayment
Оформление заказа завершается на странице ValidatePayment.asp. Разумеется, мы должны проверить данные и убедиться в наличии всей необходимой информации, но на этой странице также обрабатывается размещение заказа.
Проверка начинается с загрузки данных, введенных пользователем. Значения переменных задаются вызовом функции Request для соответствующих полей. Программный код страницы ValidatePayment.asp приведен в листинге 8.46.
Листинг 8.46. ValidatePayment.asp
<%@ Language=VBScript %>
<%
' Прочитать данные кредитной карты
chrCCName = request("chrCCName")
chrCCNumber = request("chrCCNumber")
chrCCType = request("chrCCType")
chrCCExpMonth = request("chrCCExpMonth")
chrCCExpYear = request("chrCCExpYear")
' Прочитать данные для выписки счета
chrBillFirstName = request("chrBillFirstName")
chrBillLastName = request("chrBillLastName")
chrBillAddress = request("chrBillAddress")
chrBillCity = request("chrBillCity")
chrBillState = request("chrBillState")
chrBillProvince = request("chrBillProvince")
chrBillCountry = request("chrBillCountry")
chrBillZipCode = request("chrBillZipCode")
chrBillPhone = request("chrBillPhone")
chrBillEmail = request("chrBillEmail")
chrPassword = request("chrPassword")
intCookie = request("intCookie")
Затем начинается собственно проверка ошибок. Сначала проверяются данные кредитной карты. Мы убеждаемся в том, что поле имени владельца не было оставлено пустым.
Листинг 8.47. ValidatePayment.asp (продолжение)
' Убедиться в том, что на форме введено имя владельца карты
if chrCCName = "" then
' Построить сообщение об ошибке.
strError = strError & "Invalid name on credit card<BR>"
end if
На следующем шаге проверяется номер кредитной карты. Как правило, покупатель вводит номер карты либо в виде одного числа, либо разделяя его части дефисами или пробелами. В любом случае необходимо проверить* что введенные данные являются числовыми.
Сначала из строки удаляются все дефисы и пробелы, в результате чего она преобразуется в одно длинное число. Затем мы проверяем, что полученная строка содержит символы и соответствует числовому формату.
Листинг 8.48. ValidatePayment.asp (продолжение)
' Удалить все дефисы и пробелы, чтобы осталось только число
chrCCNumber = replace(chrCCNumber, "-", "")
chrCCNumber = replace(chrCCNumber, " ", "")
' Убедиться в том, что номер кредитной карты содержит
' символы и является числом.
if (chrCCNumber = "") or (isNumeric(chrCCNumber) = False) then
strError = strError & "Invalid credit card number<BR>"
end if
Далее проверяется срок действия кредитной карты. Введенная дата не должна предшествовать текущей дате. Сначала мы проверяем, совпадает ли введенный год с текущим. В случае совпадения необходимо дополнительно убедиться в том, что введенный месяц не предшествует текущему месяцу. Затем мы проверяем, что введенный год не предшествует текущему году.
Листинг 8.49. ValidatePayment.asp (продолжение)
' Проверить, совпадает ли введенный год с текущим
if cint(chrCCExpYear) = year(date) then
' Для текущего года необходимо дополнительно убедиться в том,
' что месяц не предшествует текущему месяцу.
if cint(chrCCExpMonth) < month(date) then
strError = strError & "Invalid expiration month<BR>"
end if
end if
' Check to ensure the year is not less than the current year
if chrCCExpYear < year(date) then
strError = strError & "Invalid expiration date<BR>"
end if
ПРИМЕЧАНИЕ
Проверка правильности данных кредитной карты играет чрезвычайно важную роль для нормальной оплаты заказа. Существует ряд дополнительных действий, которые можно предпринять для проверки введенных данных. Прежде всего, вычислением контрольной суммы следует убедиться в том, что номер кредитной карты соответствует правилам соответствующей компании.

Также при помощи таких средств, как Cybercash, можно произвести немедленную проверку данных в момент ввода. На рынке даже существуют программы, ведущие списки поддельных карт - по этим спискам можно проверять карты клиентов, чтобы гарантировать получение оплаты.
Переходим к базовой проверке адреса для выписки счета. Проверка стандартных полей гарантирует, что эти поля не остались пустыми.
Листинг 8.50. ValidatePayment.asp (продолжение)
' Убедиться в том, что на форме было введено имя
if chrBillFirstName = "" then
strError = strError & "Invalid first name<BR>"
end if
' Убедиться в том, что на форме была введена фамилия
if chrBillLastName = "" then
strError = strError & "Invalid last name<BR>"
end if
' Убедиться в том, что на форме был введен адрес
if chrBillAddress = "" then
strError = strError & "Invalid address<BR>"
end if
' Убедиться в том, что на форме был введен город.
if chrBillCity = "" then
strError = strError & "Invalid city<BR>"
end if
Как и на странице ValidateShipping.asp, мы проверяем страну и штат, на которые выписывается счет. Как и прежде, при выборе Соединенных Штатов необходимо убедиться в том, что в списке был выбран штат. Для других стран вводится область и название страны.
Листинг 8.51. ValidatePayment.asp (продолжение)
' Определить, какая страна была выбрана -- США
' или другая страна.
if chrBillCountry = "US" then
' Ensure a bill to state was entered
if chrBillState = "" then
strError = strError & "Invalid state<BR>"
end if
else
' Если выбрана другая страна, убедиться в том,
' что на форме была введена область
if chrBillProvince = "" then

strError = strError & "Invalid province<BR>"

end if
end if
' Проверить, была ли введена страна
if chrBillCountry = "" then
strError = strError & "Invalid country<BR>"

end if
Остальные поля проверяются стандартным способом. Для адреса электронной почты проверяется наличие символа @ и точки.
Листинг 8.52. ValidatePayment.asp (продолжение)
' Убедиться в том, что на форме был введен почтовый индекс
if chrBillZipCode = "" then
strError = strError & "Invalid zip code<BR>"

end if
' Убедиться в том, что на форме был введен телефон
if chrBillPhone = "" then
strError = strError & "Invalid phone number<BR>"

end if
' Проверить адрес электронной почты. Адрес обязательно
' должен включать символ @ и точку
if (instr(1, chrBillEmail, "@") = 0) or _
(instr(1, chrBillEmail, ".") = 0) then
strError = strError & "Invalid email address<BR>"

end if
Как и прежде, мы убеждаемся в том, что на форме была введена либо область, либо штат (но не область со штатом одновременно!).
Листинг 8.53. ValidatePayment.asp (продолжение)
' Убедиться в том, что пользователь не ввел штат и область одновременно.
if (chrBillState <> "") and (chrBillProvince <> "") then
strError = strError & "You can not fill in both " & _
"the state and province fields<BR>"
end if
Наконец, остается проверить, был ли установлен признак ошибки. В этом случae данные, введенные покупателем, копируются в сеансовые переменные. Значения этих переменных будут прочитаны на странице Payment.asp. B процессе восстановления данных, введенных покупателем.
Затем мы присваиваем значение сеансовой переменной Error и возвращаем Покупателя на страницу Payment.asp для обновления введенных данных.
Листинг 8.54. ValidatePayment.asp (продолжение)
' Проверить, были ли обнаружены ошибки.
if strError <> "" then
' Retrieve all of the billing data and store it in session
' variables
session("chrCCName") = request("chrCCName")
session("chrCCNumber") = request("chrCCNumber")
session("chrCCType") = request("chrCCType")
session("chrCCExpMonth") = request("chrCCExpMonth")
session("chrCCExpYear") = request("chrCCExpYear")
session("chrBillFirstName") = request("chrBillFirstName")
session("chrBillLastName") = request("chrBillLastName")
session("chrBillAddress") = request("chrBillAddress")
session("chrBillCity") = request("chrBillCity")
session("chrBillState") = request("chrBillState")
session("chrBillProvince") = request("chrBillProvince")
session("chrBillCountry") = request("chrBillCountry")
session("chrBillZipCode") = request("chrBillZipCode")
session("chrBillPhone") = request("chrBillPhone")
session("chrBillEmail") = request("chrBillEmail")
session("chrPassword") = request("chrPassword")
' Store the error in a session variable
session("Error") = strError

' Send the user back to the payment page
Response.Redirect "payment.asp"
else
Если данные были введены правильно, мы переходим к оформлению заказа. Процесс оформления состоит из нескольких этапов, перечисленных в табл. 8.4.
Таблица 8.4. Этапы оформления заказа


Этап

Описание

Подготовка данных

Необходимо убедиться в том, что реквизиты доставки готовы к сохранению в базе данных SQL

Загрузка данных

Реквизиты выписки счета, введенные на форме, читаются и сохраняются в сеансовых переменных

Сохранение данных заказа

Информация о заказе сохраняется в базе данных

Сохранение платежных реквизитов

Платежные реквизиты сохраняются в базе данных

Инициализация состояния заказа

Происходит инициализация данных, используемых для дальнейшего отслеживания заказа

Обновление корзины

Обновляются данные корзины

Обновление профиля

Обновляется профиль покупателя

Отправка отчета по электронной почте

Покупателю по электронной почте отправляется отчет, подтверждающий оформление заказа

Запись cookie

На компьютере покупателя создается файл cookie с идентификатором профиля

Перейдем к программной реализации каждого из этих этапов. Оформление заказа начинается с подготовки данных. Мы должны проследить за тем, чтобы все апострофы в данных доставки (например, в фамилии O'Mailey) были продублированы перед записью в базу.
Листинг 8.55. ValidatePayment.asp (продолжение)
' Переходим к сохранению и оформлению заказа
' Этот процесс состоит из нескольких этапов.
'***********************************************
'**** 1. Подготовка данных.
'***********************************************

' Все апострофы в строковых данных необходимо удвоить
session("chrShipFirstName") = replace(session("chrShipFirstName"), "'", "''")
session("chrShipLastName") = session("chrShipLastName")
session("chrShipAddress") = session("chrShipAddress")
session("chrShipCity") = session("chrShipCity")
session("chrShipProvince") = session("chrShipProvince")
Затем мы загружаем данные для выписки счета и готовим их к записи в базу. В строковых полях перед сохранением необходимо заменить одиночные апострофы двойными.
Листинг 8.56. ValidatePayment.asp (продолжение)
'***********************************************
'**** 2. Загрузка данных
'***********************************************
' Retrieve all of the payemnt data and ensure that it is
' ready for a SQL insert.
session("chrCCName") = replace(request("chrCCName"), "'", "''")
session("chrCCNumber") = request("chrCCNumber")
session("chrCCType") = request("chrCCType")
session("chrCCExpMonth") = request("chrCCExpMonth")
session("chrCCExpYear") = request("chrCCExpYear")
session("chrBillFirstName") = replace(request("chrBillFirstName"), "'", "''")
session("chrBillLastName") = replace(request("chrBillLastName"), "'", "''")
session("chrBillAddress") = replace(request("chrBillAddress"), "'", "''")
session("chrBillCity") = replace(request("chrBillCity"), "'", "''")
session("chrBillState") = request("chrBillState")
session("chrBillProvince") = replace(request("chrBillProvince"), "'", "''")
session("chrBillCountry") = request("chrBillCountry")
session("chrBillZipCode") = request("chrBillZipCode")
session("chrBillPhone") = request("chrBillPhone")
session("chrBillEmail") = request("chrBillEmail")
session("chrPassword") = request("chrPassword")
session("intCookie") = request("intCookie")
После завершения подготовки данных можно переходить к сохранению их в базе. Обмен данными с базой осуществляется через открытое подключение ADO.
Затем мы начинаем строить длинную команду SQL INSERT, используя при этом хранимую процедуру sp_InsertOrderData. В базе сохраняются все данные доставки и выписки счета. Хранимая процедура возвращает идентификатор заказа, который будет сообщен покупателю. Идентификатор сохраняется в сеансовой переменной и в дальнейшем используется на странице Confirmed.asp.
Листинг 8.57. ValidatePayment.asp (продолжение)
'***********************************************
'**** 3. Insert the order information into
'**** the database
'***********************************************
' Create an ADO database connection
set dbOrderData = server.createobject("adodb.connection")
set rsOrderData = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbOrderData.open("filedsn=WildWillieCDs")
' SQL update statement to insert the the order
' data into the OrderData table.
sql = "execute sp_InsertOrderData " & _
session("idShopper") & ", '" & _
session("chrShipFirstName") & "', '" & _
session("chrShipLastName") & "', '" & _
session("chrShipAddress") & "', '" & _
session("chrShipCity") & "', '" & _
session("chrShipState") & "', '" & _
session("chrShipProvince") & "', '" & _
session("chrShipCountry") & "', '" & _
session("chrShipZipCode") & "', '" & _
session("chrShipPhone") & "', '" & _
session("chrShipEmail") & "', '" & _
session("chrBillFirstName") & "', '" & _
session("chrBillLastName") & "', '" & _
session("chrBillAddress") & "', '" & _
session("chrBillCity") & "', '" & _
session("chrBillState") & "', '" & _
session("chrBillProvince") & "', '" & _
session("chrBillCountry") & "', '" & _
session("chrBillZipCode") & "', '" & _
session("chrBillPhone") & "', '" & _
session("chrBillEmail") & "', " & _
session("idBasket")
' Execute the SQL statement
set rsOrderData = dbOrderData.execute(sql)
session("idOrder") = rsOrderData("idOrder")
Как говорилось выше, платежные реквизиты хранятся отдельно от данных заказа. Это упрощает операции с данными кредитной карты, которые, вероятно, будут храниться в течение непродолжительного времени. Мы вызываем хранимую процедуру sp_InsertPaymentData, которая сохраняет в базе данные кредитной карты с идентификатором заказа.
Листинг 8.58. ValidatePayment.asp (продолжение)
'***********************************************
'**** 4. Сохранить в базе данных платежные реквизиты
'***********************************************

' Построить команду SQL для сохранения платежных реквизитов в таблице
sql = "execute sp_InsertPaymentData " & _
session("idOrder") & ", '" & _
session("chrCCType") & "', '" & _
session("chrCCNumber") & "', '" & _
session("chrCCExpMonth") & "/" & session("chrCCExpYear") & "', '" & _
session("chrCCName") & "'"
' Выполнить команду SQL
set rsOrderData = dbOrderData.execute(sql)
На следующем этапе инициализируются данные состояния текущего заказа - в базе данных сохраняется информация о получении заказа. Задача решается при помощи хранимой процедуры sp_InitializeOrderStatus. Обратите внимание: применение хранимой процедуры инкапсулирует бизнес-логику, на которой построена система отслеживания заказов. Если код первоначального состояния заказа изменится, нам не придется вносить изменения в страницу.
Листинг 8.59. ValidatePayment.asp (продолжение)
'***********************************************
'**** 5. Инициализировать состояние заказа и
'**** сохранить информацию о его получении
'***********************************************

' Построить команду SQL для сохранения платежных
' реквизитов в таблице
sql = "execute sp_InitializeOrderStatus " & _
session("idOrder")
' Выполнить команду SQL
set rsOrderData = dbOrderData.execute(sql)
На следующем этапе мы убеждаемся в том, что корзина содержит актуальные данные. Вспомните - в корзине были сохранены общее количество единиц, общая стоимость заказа, стоимость доставки, налог и итоговая стоимость заказа. Однако следует учесть, что правила вычисления налога и стоимости доставки
могут изменяться со временем. Мы хотим, чтобы в корзине хранились величины, зафиксированные на момент оформления заказа.
Листинг 8.60. ValidatePayment.asp (продолжение)
'***********************************************
'**** 6. Обновление корзины окончательными данными заказа.
'***********************************************

' Finally we need to update the basket with the final
' amounts for quantity, subtotal, shipping, tax and
' total
sql = "execute sp_UpdateBasket " & _
session("idBasket") & ", " & _
session("Quantity") & ", " & _
session("Subtotal") & ", " & _
session("Shipping") & ", " & _
session("Tax") & "," & _
session("Total") & ", 1"
' Execute the SQL statement
set rsOrderData = dbOrderData.execute(sql)
Затем мы обновляем профиль покупателя. Как упоминалось выше, если профиль не был загружен ранее, новый профиль создается в начале работы покупателя в электронном магазине.
Обновление данных выполняется хранимой процедурой spUpdateShopper. В нашем примере обновляются реквизиты выписки счета и основные данные профиля. В принципе также можно было бы обновить реквизиты доставки и загрузить их позднее из базы данных.
Обратите внимание: признак сохранения cookie и пароль также сохраняются в профиле покупателя. Если покупатель не ввел пароль, позднее он не сможет загрузить профиль для получения информации о состоянии заказа. Если он выбрал запись cookie, профиль будет автоматически загружен при следующем посещении магазина.
Листинг 8.61. ValidatePayment.asp (продолжение)
'***********************************************
'**** 7. Обновить профиль на основании новых
'**** реквизитов выписки счета.
'***********************************************

' Create an ADO database connection
set dbProfile = server.createobject("adodb.connection")

' Open the connection using our ODBC file DSN
dbProfile.open("filedsn=WildWillieCDs")
' SQL insert statement to update the profile in the
' database
sql = "execute sp_UpdateShopper '" & _
session("chrBillFirstName") & "', '" & _
session("chrBillLastName") & "', '" & _
session("chrBillAddress") & "', '" & _
session("chrBillCity") & "', '" & _
session("chrBillState") & "', '" & _
session("chrBillProvince") & "', '" & _
session("chrBillCountry") & "', '" & _
session("chrBillZipCode") & "', '" & _
session("chrBillPhone") & "', '" & _
session("chrBillFax") & "', '" & _
session("chrBillEmail") & "', '" & _
session("chrPassword") & "', " & _
session("intCookie") & ", " & _
session("idShopper")
' Execute the SQL statement
dbProfile.execute(sql)
В наше время подтверждение заказа но электронной почте считается практически обязательным требованием. Задача легко решается при помощи Collaboration Data Objects for Windows NT (CDONTS). Объект NewMail позволяет легко отправить почту с сервера NT с использованием сервера SMTP. В табл. 8.5 перечислены ключевые свойства и методы объекта NewMail.
Таблица 8.5. Ключевые свойства и методы объекта NewMail


Свойство/метод

Описание

Bcc

Адреса для отправки копий (невидимые для других получателей)

Body

Основной текст сообщения электронной почты

BodyFormat

Формат основного текста сообщения. Допустимые значения - HTML и простой текст

Cc

Адреса для отправки копий

ContentBase

База для всех URL, относящихся к основному тексту сообщения

ContentLocation

Абсолютный или относительный путь для всех URL, относящихся к основному тексту сообщения

From

Адрес отправителя сообщения

Importance

Важность сообщения

MailFormat

Кодировка объекта NewMail. Допустимые значения - MIME и простой текст

Subject

Тема сообщения

To

Адрес, на который отправляется сообщение

Value

Имя и содержимое дополнительного атрибута объекта NewMail

Version

Версия CDONTS

AttachFile

Создает вложение на основе файла

AttachUrl

Создает вложение и связывает с ним URL (Universal Resource Locator)

Send

Отправляет объект NewMail заданным получателям

SetLocaleIDs

Устанавливает идентификаторы локального контекста

СОВЕТ
Описание работы с сервером SMTP из поставки Windows NT выходит за рамки этой книги. Полезную информацию по этой теме можно найти в Microsoft Developer Network (MSDN). Ваши возможности не ограничиваются использованием сервера SMTP от Microsoft - описанное решение адаптируется для других SMTP-совместимых почтовых серверов и почтовых объектов, помимо CDONTS.

Сначала мы создаем экземпляр объекта NewMail методом Server.CreateObject, а затем задаем значения его свойств. В свойство То заносится адрес электронной почты, указанный в реквизитах выписки счета. Текст свойства Subject указывает, что данное сообщение подтверждает оформление заказа.
Затем мы переходим к построению текста сообщения. В нашем примере в сообщение включается идентификатор заказа и сводная информация по корзине. При желании можно было бы перебрать все позиции корзины и вывести полную информацию о заказе. Впрочем, покупатель всегда может получить эти сведения на сайте.
Переменная strBody назначается свойству Body, после чего сообщение отправляется методом Send объекта NewMail.
Листинг 8.62. ValidatePayment.asp (продолжение)
'***********************************************
'**** 8. Отправка подтверждения покупателю
'***********************************************

set Mailer = Server.CreateObject("CDONTS.NewMail")
Mailer.From = "support@wildwillieinc.com"
Mailer.To = session("chrBillEmail")
Mailer.Subject = "Wild Willie CD Receipt"

strBody = "Thank You for your Order!" & vbCrLf & vbCrLf
strBody = strBody & "Order Id = " & session("idOrder") & vbCrLf
strBody = strBody & "Subtotal = " & formatcurrency(session("Subtotal")/100, 2) & vbCrLf
strBody = strBody & "Subtotal = " & formatcurrency(session("Shipping")/100, 2) & vbCrLf
strBody = strBody & "Subtotal = " & formatcurrency(session("Tax")/100, 1) & vbCrLf
strBody = strBody & "Subtotal = " & formatcurrency(session("Total")/100, 2) & vbCrLf & vbCrLf
strBody = strBody & "Please call 1-800-555-Wild with any questions. "
strBody = strBody & "Be sure and check back to retrieve your order status."

Mailer.Body = strBody

Mailer.Send
На последнем этапе оформления заказа мы проверяем, хочет ли пользователь создать cookie, чтобы упростить загрузку своего профиля. Для этой цели используется коллекция Cookies объекта Request. Cookie создается с именем WWCD (сокращение от Wild Willie's CDs). Все, что мы хотим сохранить в cookie, - это идентификатор пользователя.
При создании cookie также задается срок действия, в течение которого файл cookie будет существовать на компьютере пользователя. Задача решается при помощи свойства Expires коллекции Cookies. После задания срока действия покупатель направляется на страницу подтверждения.
Листинг 8.63. ValidatePayment.asp (продолжение)
'***********************************************
'**** 9. Записать cookie, если этого хочет
'**** пользователь
'***********************************************
' Write out the cookie if they selected the 'Yes'
' radio button.
if request("intCookie") = 1 then

Response.Cookies("WWCD") = session("idShopper")
Response.Cookies("WWCD").Expires = "December 31, 2001"

end if
' Send the user to the confirmation page
Response.Redirect "Confirmed.asp"
end if
%>
Страница использует несколько хранимых процедур, связанных с процессом оформления заказа. Первая хранимая процедура, sp_InitializeOrderStatus, получает идентификатор заказа и вставляет в таблицу OrderStatus новую запись. При вставке записи поле idStage по умолчанию инициализируется нулевым значением.
Листинг 8.64. Хранимая процедура sp_InitializeOrderStatus
/* Создание в таблице OrderStatus записи для нового заказа */
CREATE PROCEDURE sp_InitializeOrderStatus
/* При вызове процедуре передается идентификатор заказа */
@idOrder int
AS
/* Создать новую запись и присвоить значение идентификатору заказа */
insert into OrderStatus(idOrder) values(@idOrder)
Следующая хранимая процедура, sp_InsertOrderData, собирает все важнейшие реквизиты доставки и выписки счета и создает запись нового заказа командой INSERT. Обратите внимание - эти данные сохраняются независимо от профиля покупателя.
Листинг 8.65. Хранимая процедура sp_InsertOrderData
/* Сохранение информации о заказе в базе данных OrderData */
CREATE PROCEDURE sp_InsertOrderData
/* All key values are inserted into the
database.
*/
@idShopper int,
@chrShipFirstName varchar(150),
@chrShipLastName varchar(150),
@chrShipAddress varchar(150),
@chrShipCity varchar(150),
@chrShipState varchar(25),
@chrShipProvince varchar(150),
@chrShipCountry varchar(150),
@chrShipZipCode varchar(150),
@chrShipPhone varchar(150),
@chrShipEmail varchar(150),
@chrBillFirstName varchar(150),
@chrBillLastName varchar(150),
@chrBillAddress varchar(150),
@chrBillCity varchar(150),
@chrBillState varchar(25),
@chrBillProvince varchar(150),
@chrBillCountry varchar(150),
@chrBillZipCode varchar(150),
@chrBillPhone varchar(150),
@chrBillEmail varchar(150),
@idBasket int
AS
/* Insert the data */
insert into orderdata(idShopper, chrShipFirstName,
chrShipLastName, chrShipAddress,
chrShipCity, chrShipState,
chrShipProvince, chrShipCountry,
chrShipZipCode, chrShipPhone,
chrShipEmail, chrBillFirstName,
chrBillLastName, chrBillAddress,
chrBillCity, chrBillState,
chrBillProvince, chrBillCountry,
chrBillZipCode, chrBillPhone,
chrBillEmail, idBasket)

values(@idShopper, @chrShipFirstName,
@chrShipLastName, @chrShipAddress,
@chrShipCity, @chrShipState,
@chrShipProvince, @chrShipCountry,
@chrShipZipCode, @chrShipPhone,
@chrShipEmail, @chrBillFirstName,
@chrBillLastName, @chrBillAddress,
@chrBillCity, @chrBillState,
@chrBillProvince, @chrBillCountry,
@chrBillZipCode, @chrBillPhone,
@chrBillEmail, @idBasket)
select idOrder = @@identity
Последняя команда хранимой процедуры SQL выбирает значение переменной @@identity в качестве возвращаемого значения процедуры. Переменной @@identity присваивается последнее значение столбца-счетчика, полученное при включении записи в таблицу. В нашем примере возвращается значение столбца idOrder таблицы OrderData.
Следующая хранимая процедура сохраняет в базе данных платежные реквизиты. В качестве параметров ей передается идентификатор заказа, тип карты, номер карты, срок действия и имя владельца. Обратите внимание на то, что срок действия представляет собой комбинацию месяца и года, введенных в форме. Затем хранимая процедура записывает данные в таблицу PaymentData.
Листинг 8.66. Хранимая процедура sp_InsertPaymentData
/* Сохранение платежных реквизитов заказа */
CREATE PROCEDURE sp_InsertPaymentData
/* При вызове процедуре передается идентификатор заказа, тип кредитной карты, номер кредитной карты, срок действия и имя владельца */
@idOrder int,
@chrCardType varchar(100),
@chrCardNumber varchar(50),
@chrExpDate varchar(25),
@chrCardName varchar(150)
AS
/* Создание записи в таблице PaymentData */
insert into paymentdata(idOrder, chrCardType,
chrCardNumber, chrExpDate,
chrCardName)
values(@idOrder, @chrCardType,
@chrCardNumber, @chrExpDate,
@chrCardName)
На этом оформление заказа подходит к концу. К настоящему моменту покупатель сохранил свой заказ и обновил профиль. Теперь он может получить номер заказа и продолжить работу.
Страница Confirmed
Страница подтверждения устроена несложно. Ее главной задачей является вывод информации об успешном оформлении заказа. Покупатель получает благодарственное сообщение и номер заказа для последующего отслеживания.
Работа страницы начинается с проверки идентификатора заказа. Если значение идентификатора не задано, покупатель не получит доступ к странице. Вместо этого он возвращается на страницу Basket.asp для продолжения покупок.
Листинг 8.67. Confirmed.asp
<%@ Language=VBScript %>
<!-- вывод номера заказа и благодарственного сообщения -->
<%
' Убедиться в том, что идентификатору заказа было присвоено значение
if session("idOrder") = "" then Response.Redirect "basket.asp"
%>
Далее следует стандартный заголовок и текст благодарственного сообщения. Чтобы вывести номер заказа, мы обращаемся к соответствующей сеансовой переменной.
Остается лишь стереть все сеансовые данные на тот случай, если покупатель захочет продолжить покупки. Задача решается методом Abandon объекта Session.
Листинг 8.68. Confirmed.asp
<HTML>
<!-- #include file="include/header.asp" -->
Thank you for your order! Have fun grooving to the cool music
you have ordered! <BR><BR>
<!-- Вывести идентификатор заказа, полученный при записи в базу данных. -->
Your order number is <B><%=session("idOrder")%></b> for your reference.<BR>
<%
' Clear the session so all data is lost. This way
' we ensure that the old basket is not retrieved, etc.
session.Abandon
%>
<!-- #include file="include/footer.asp" -->
</BODY>
</HTML>
Сбросить состояние сеанса, чтобы стереть все данные. Тем самым мы гарантируем, что покупатель не сможет добавить новые товары в старую корзину и т. д.
Страница подтверждения после оформления заказа. Если щелкнуть на ссылке Basket, содержимое корзины будет стерто, и начнется новый сеанс покупок.

Итоги
На этой стадии покупатель успешно завершил формирование своего заказа. В нашем магазине реализованы все основные составляющие этого процесса: просмотр товаров, управление корзиной и оформление заказа. Как упоминалось в этой главе, существуют разные варианты управления процессом оформления заказа, но конечная цель заключается в том, чтобы оформление происходило как можно быстрее и при этом были собраны все необходимые данные.
Следующая глава будет посвящена взаимодействию покупателя с сайтом при следующем посещении. В частности, мы рассмотрим операции с профилем и загрузку данных об истории заказов.

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика