Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Скачайте пример проекта Wingtip Toys (C#) или скачайте электронную книгу (PDF)
В этой серии учебников вы узнаете, как создавать приложение веб-форм ASP.NET с помощью ASP.NET 4.5 и Microsoft Visual Studio Express 2013 для Интернета. Проект Visual Studio 2013 с исходным кодом C# доступен для сопровождения этой серии учебников.
В этом руководстве описывается изменение примера приложения Wingtip Toys для включения авторизации пользователя, регистрации и оплаты с помощью PayPal. Только пользователи, вошедшие в систему, будут иметь авторизацию на покупку продуктов. Встроенные функции регистрации пользователей в шаблоне проекта веб-форм ASP.NET 4.5 уже включают большую часть необходимых функций. Вы добавите функции PayPal Express Checkout. В этом руководстве вы будете использовать среду тестирования для разработчиков PayPal, поэтому фактические средства не будут переведены. В конце руководства вы протестируете приложение, выбрав товары для добавления в корзину покупок, нажав кнопку оформления заказа и передав данные на тестовый сайт PayPal. На тестовом сайте PayPal вы подтвердите информацию о доставке и оплате, а затем вы вернетесь в приложение-образец Wingtip Toys, чтобы подтвердить и завершить покупку.
Существует несколько опытных внешних обработчиков платежей, которые специализируются на онлайн-торговле и обеспечивают масштабируемость и безопасность. ASP.NET разработчики должны рассмотреть преимущества использования стороннего решения оплаты перед реализацией решения для покупок и приобретения.
Замечание
Пример приложения Wingtip Toys был разработан для отображения конкретных ASP.NET концепций и функций, доступных для ASP.NET веб-разработчиков. Этот пример приложения не оптимизирован для всех возможных обстоятельств в отношении масштабируемости и безопасности.
Что вы узнаете:
- Как ограничить доступ к определенным страницам в папке.
- Как создать известную корзину для покупок из анонимной корзины покупок.
- Включение SSL для проекта.
- Добавление поставщика OAuth в проект.
- Как использовать PayPal для приобретения продуктов с помощью среды тестирования PayPal.
- Как отобразить сведения из PayPal в элементе управления DetailsView.
- Как обновить базу данных приложения Wingtip Toys с подробными сведениями, полученными из PayPal.
Добавление отслеживания заказов
В этом руководстве вы создадите два новых класса для отслеживания данных из заказа, созданного пользователем. Классы отслеживают данные о доставке, сумме покупки и подтверждении оплаты.
Добавьте классы моделей Order и OrderDetail
Ранее в этом руководстве вы определили схему для категорий, продуктов и элементов корзины для покупок, создав классы Category, Product и CartItem в папке Models. Теперь вы добавите два новых класса, чтобы определить схему заказа на продукт и сведения о заказе.
В папке Models добавьте новый класс с именем Order.cs.
Новый файл класса отображается в редакторе.Замените код по умолчанию следующим образом:
using System; using System.ComponentModel.DataAnnotations; using System.Collections.Generic; using System.ComponentModel; namespace WingtipToys.Models { public class Order { public int OrderId { get; set; } public DateTime OrderDate { get; set; } public string Username { get; set; } [Required(ErrorMessage = "First Name is required")] [DisplayName("First Name")] [StringLength(160)] public string FirstName { get; set; } [Required(ErrorMessage = "Last Name is required")] [DisplayName("Last Name")] [StringLength(160)] public string LastName { get; set; } [Required(ErrorMessage = "Address is required")] [StringLength(70)] public string Address { get; set; } [Required(ErrorMessage = "City is required")] [StringLength(40)] public string City { get; set; } [Required(ErrorMessage = "State is required")] [StringLength(40)] public string State { get; set; } [Required(ErrorMessage = "Postal Code is required")] [DisplayName("Postal Code")] [StringLength(10)] public string PostalCode { get; set; } [Required(ErrorMessage = "Country is required")] [StringLength(40)] public string Country { get; set; } [StringLength(24)] public string Phone { get; set; } [Required(ErrorMessage = "Email Address is required")] [DisplayName("Email Address")] [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "Email is is not valid.")] [DataType(DataType.EmailAddress)] public string Email { get; set; } [ScaffoldColumn(false)] public decimal Total { get; set; } [ScaffoldColumn(false)] public string PaymentTransactionId { get; set; } [ScaffoldColumn(false)] public bool HasBeenShipped { get; set; } public List<OrderDetail> OrderDetails { get; set; } } }Добавьте класс OrderDetail.cs в папку Models .
Замените код по умолчанию следующим кодом:
using System.ComponentModel.DataAnnotations; namespace WingtipToys.Models { public class OrderDetail { public int OrderDetailId { get; set; } public int OrderId { get; set; } public string Username { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public double? UnitPrice { get; set; } } }
Классы Order и OrderDetail содержат схему для описания сведений о заказе, используемых для оформления покупки и доставки.
Кроме того, необходимо обновить класс контекста базы данных, который управляет классами сущностей и предоставляет доступ к базе данных. Для этого вы добавите в класс только что созданные классы OrderDetail Order и ProductContext model.
В обозревателе решений найдите и откройте файл ProductContext.cs .
Добавьте выделенный код в файл ProductContext.cs , как показано ниже:
using System.Data.Entity; namespace WingtipToys.Models { public class ProductContext : DbContext { public ProductContext() : base("WingtipToys") { } public DbSet<Category> Categories { get; set; } public DbSet<Product> Products { get; set; } public DbSet<CartItem> ShoppingCartItems { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } } }
Как упоминалось ранее в этом руководстве, код в файле ProductContext.cs добавляет System.Data.Entity пространство имен, чтобы иметь доступ ко всем основным функциям Entity Framework. Эта функция включает возможность запрашивать, вставлять, обновлять и удалять данные, работая с строго типизированными объектами. Приведенный выше код в классе ProductContext добавляет доступ Entity Framework к новым классам Order и OrderDetail.
Добавление доступа к оформлению заказа
Пример приложения Wingtip Toys позволяет анонимным пользователям просматривать и добавлять продукты в корзину покупок. Однако, когда анонимные пользователи выбирают покупку продуктов, которые они добавили в корзину покупок, они должны войти на сайт. После входа в систему они могут получить доступ к ограниченным страницам веб-приложения, обрабатывающим процесс регистрации и покупки. Эти ограниченные страницы содержатся в папке "Выход " приложения.
Добавьте папку и страницы оформления заказа
Теперь вы создадите папку checkout и страницы в ней, которые клиент увидит во время процесса выхода. Эти страницы будут обновлены далее в этом руководстве.
Щелкните правой кнопкой мыши имя проекта (Wingtip Toys) в обозревателе решений и выберите "Добавить новую папку".
Назовите новую папку Checkout.
Щелкните правой кнопкой мыши папку "Выход", а затем выберите "Добавить>новый элемент".
Откроется диалоговое окно Добавление нового элемента .
Выберите группу шаблонов Visual C# ->Web слева. Затем в средней области выберите веб-форму с главной страницейи назовите ее CheckoutStart.aspx.
Как и раньше, выберите файл Site.Master в качестве главной страницы.
Добавьте следующие дополнительные страницы в папку checkout , выполнив описанные выше действия.
- CheckoutReview.aspx
- CheckoutComplete.aspx
- CheckoutCancel.aspx
- CheckoutError.aspx
Добавление файла Web.config
Добавив новый файлWeb.config в папку Checkout , вы сможете ограничить доступ ко всем страницам, содержащимся в папке.
Щелкните правой кнопкой мыши папку "Оформить" и выберите "Добавить">"Новый элемент".
Откроется диалоговое окно Добавление нового элемента .Выберите группу шаблонов Visual C# ->Web слева. Затем в средней области выберите файл веб-конфигурации, примите имя Web.configпо умолчанию и нажмите кнопку "Добавить".
Замените существующее XML-содержимое в файлеWeb.config следующим образом:
<?xml version="1.0"?> <configuration> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </configuration>Сохраните файл Web.config .
Файл Web.config указывает, что всем неизвестным пользователям веб-приложения должен быть отказан доступ к страницам, содержащимся в папке Checkout. Однако если пользователь зарегистрировал учетную запись и вошел в систему, он будет известным пользователем и будет иметь доступ к страницам в папке "Выход ".
Важно отметить, что ASP.NET конфигурация следует иерархии, где каждый файл Web.config применяет параметры конфигурации к папке, в которой она находится и ко всем дочерним каталогам под ним.
Включение SSL для проекта
Secure Sockets Layer (SSL) — это протокол, который позволяет веб-серверам и веб-клиентам безопасно взаимодействовать с помощью шифрования. Если протокол SSL не используется, данные, отправленные между клиентом и сервером, уязвимы для перехвата любым, кто имеет физический доступ к сети. Кроме того, несколько распространенных схем проверки подлинности не защищены по протоколу HTTP. В частности, обычная проверка подлинности и проверка подлинности форм отправляют незашифрованные учетные данные. Чтобы обеспечить безопасность, эти схемы проверки подлинности должны использовать SSL.
- В обозревателе решений щелкните проект WingtipToys , а затем нажмите клавишу F4 , чтобы отобразить окно "Свойства ".
- Измените SSL включено на
true. - Скопируйте URL-адрес SSL , чтобы использовать его позже.
URL-адрес SSL будет отображатьсяhttps://localhost:44300/, если вы ранее не создали веб-сайты SSL (как показано ниже).
- В обозревателе решений щелкните правой кнопкой мыши проект WingtipToys и щелкните "Свойства".
- На левой вкладке щелкните "Интернет".
- Измените URL-адрес проекта , чтобы использовать сохраненный ранее URL-адрес SSL .
- Сохраните страницу, нажав клавиши CTRL+S.
- Нажмите клавиши CTRL+F5 , чтобы запустить приложение. Visual Studio отобразит параметр, позволяющий избежать предупреждений SSL.
- Нажмите кнопку "Да", чтобы доверять SSL-сертификату IIS Express и продолжить.
Отображается предупреждение системы безопасности. - Нажмите кнопку "Да" , чтобы установить сертификат в localhost.
Откроется окно браузера.
Теперь вы можете легко протестировать веб-приложение локально с помощью SSL.
Добавление поставщика OAuth 2.0
ASP.NET веб-формы предоставляют расширенные возможности для членства и проверки подлинности. К этим улучшениям относятся OAuth. OAuth — это открытый протокол, который разрешает безопасную авторизацию в простом и стандартном методе из веб-, мобильных и классических приложений. Шаблон веб-форм ASP.NET использует OAuth для предоставления доступа к Facebook, Twitter, Google и Майкрософт в качестве поставщиков проверки подлинности. Хотя в этом руководстве используется только Google в качестве поставщика проверки подлинности, вы можете легко изменить код, чтобы использовать любой из поставщиков. Шаги по внедрению других поставщиков похожи на те, которые вы увидите в этом руководстве.
Помимо проверки подлинности, руководство также будет использовать роли для реализации авторизации. Только те пользователи, которые вы добавляете canEdit в роль, смогут изменять данные (создавать, изменять или удалять контакты).
Замечание
Приложения Windows Live принимают только динамический URL-адрес для рабочего веб-сайта, поэтому вы не можете использовать URL-адрес локального веб-сайта для тестирования имен входа.
Ниже описано, как добавить поставщика проверки подлинности Google.
Откройте файл App_Start\Startup.Auth.cs .
Удалите символы комментариев из метода
app.UseGoogleAuthentication(), чтобы метод отображался следующим образом.app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() { ClientId = "", ClientSecret = "" });Перейдите в консоль разработчиков Google. Вам также потребуется войти с помощью учетной записи электронной почты разработчика Google (gmail.com). Если у вас нет учетной записи Google, выберите ссылку "Создать учетную запись ".
Затем вы увидите консоль разработчиков Google.
Нажмите кнопку "Создать проект" и введите имя проекта и идентификатор (можно использовать значения по умолчанию). Затем установите галочку в поле соглашения и нажмите кнопку Создать.
Через несколько секунд будет создан новый проект, и браузер отобразит новую страницу проектов.
На левой вкладке щелкните API и проверку подлинности, а затем щелкните "Учетные данные".
Щелкните "Создать идентификатор клиента " в разделе OAuth.
Откроется диалоговое окно создания идентификатора клиента .
В диалоговом окне "Создание идентификатора клиента" сохраните веб-приложение по умолчанию для типа приложения.
Задайте Авторизованные источники JavaScript на URL-адрес SSL, который вы использовали ранее в этом руководстве (
https://localhost:44300/если вы не создали другие проекты SSL).
Этот URL-адрес является источником приложения. В этом примере вы введете только URL-адрес теста localhost. Однако можно ввести несколько URL-адресов для учетной записи localhost и рабочей среды.Задайте URI авторизованного перенаправления следующим образом:
https://localhost:44300/signin-googleЭто значение является унифицированным идентификатором ресурса (URI), который используется пользователями ASP.NET OAuth для взаимодействия с сервером Google OAuth. Помните URL-адрес SSL, который вы использовали выше (
https://localhost:44300/, если не были созданы другие SSL-проекты).Нажмите кнопку "Создать идентификатор клиента ".
В меню слева консоли разработчиков Google щелкните пункт меню "Согласие ", а затем задайте адрес электронной почты и имя продукта. После завершения формы нажмите кнопку "Сохранить".
Щелкните пункт меню API , прокрутите вниз и нажмите кнопку выключения рядом с API Google+.
При принятии этого параметра будет включен API Google+.Кроме того, необходимо обновить пакет NuGet Microsoft.Owin до версии 3.0.0.
В меню "Сервис" выберите диспетчер пакетов NuGet и выберите "Управление пакетами NuGet для решения".
В окне "Управление пакетами NuGet" найдите и обновите пакет Microsoft.Owin до версии 3.0.0.В Visual Studio обновите
UseGoogleAuthenticationметод страницы Startup.Auth.cs путем копирования и вставки идентификатора клиента и секрета клиента в метод. Приведенные ниже значения идентификатора клиента и секрета клиента являются примерами и не будут работать.using System; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.DataProtection; using Microsoft.Owin.Security.Google; using Owin; using WingtipToys.Models; namespace WingtipToys { public partial class Startup { // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301883 public void ConfigureAuth(IAppBuilder app) { // Configure the db context, user manager and signin manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); // Enable the application to use a cookie to store information for the signed in user // and to use a cookie to temporarily store information about a user logging in with a third party login provider // Configure the sign in cookie app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } }); // Use a cookie to temporarily store information about a user logging in with a third party login provider app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process. app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5)); // Enables the application to remember the second login verification factor such as phone or email. // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from. // This is similar to the RememberMe option when you log in. app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); // Uncomment the following lines to enable logging in with third party login providers //app.UseMicrosoftAccountAuthentication( // clientId: "", // clientSecret: ""); //app.UseTwitterAuthentication( // consumerKey: "", // consumerSecret: ""); //app.UseFacebookAuthentication( // appId: "", // appSecret: ""); app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() { ClientId = "000000000000.apps.googleusercontent.com", ClientSecret = "00000000000" }); } } }Нажмите клавиши CTRL+F5 , чтобы создать и запустить приложение. Щелкните ссылку "Войти".
В разделе "Использование другой службы для входа" щелкните Google.
Если вам нужно ввести свои учетные данные, вы будете перенаправлены на сайт Google, где вы введете свои учетные данные.
После ввода учетных данных вам будет предложено предоставить разрешения только что созданному веб-приложению.
Щелкните Принять. Теперь вы будете перенаправлены обратно на страницу регистрации приложения WingtipToys , где можно зарегистрировать учетную запись Google.
Вы можете изменить имя локальной регистрации электронной почты, используемое для учетной записи Gmail, но обычно вы хотите сохранить псевдоним электронной почты по умолчанию (то есть используемый для проверки подлинности). Нажмите кнопку "Войти" , как показано выше.
Изменение функциональных возможностей входа
Как упоминалось ранее в этом руководстве, большая часть функций регистрации пользователей была включена в шаблон веб-форм ASP.NET по умолчанию. Теперь вы измените страницы по умолчанию Login.aspx и Register.aspx, чтобы вызывать метод MigrateCart. Метод MigrateCart связывает только что вошедшего в систему пользователя с анонимной корзиной покупок. Связав пользователя и корзину покупок, пример приложения Wingtip Toys сможет поддерживать корзину покупок пользователя между посещениями.
В обозревателе решений найдите и откройте папку "Учетная запись ".
Измените страницу кода с именем Login.aspx.cs , чтобы включить выделенный желтым цветом код, чтобы он отображалась следующим образом:
using System; using System.Web; using System.Web.UI; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Owin; using WingtipToys.Models; namespace WingtipToys.Account { public partial class Login : Page { protected void Page_Load(object sender, EventArgs e) { RegisterHyperLink.NavigateUrl = "Register"; // Enable this once you have account confirmation enabled for password reset functionality //ForgotPasswordHyperLink.NavigateUrl = "Forgot"; OpenAuthLogin.ReturnUrl = Request.QueryString["ReturnUrl"]; var returnUrl = HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"]); if (!String.IsNullOrEmpty(returnUrl)) { RegisterHyperLink.NavigateUrl += "?ReturnUrl=" + returnUrl; } } protected void LogIn(object sender, EventArgs e) { if (IsValid) { // Validate the user password var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>(); var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>(); // This doen't count login failures towards account lockout // To enable password failures to trigger lockout, change to shouldLockout: true var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false); switch (result) { case SignInStatus.Success: WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions(); String cartId = usersShoppingCart.GetCartId(); usersShoppingCart.MigrateCart(cartId, Email.Text); IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response); break; case SignInStatus.LockedOut: Response.Redirect("/Account/Lockout"); break; case SignInStatus.RequiresVerification: Response.Redirect(String.Format("/Account/TwoFactorAuthenticationSignIn?ReturnUrl={0}&RememberMe={1}", Request.QueryString["ReturnUrl"], RememberMe.Checked), true); break; case SignInStatus.Failure: default: FailureText.Text = "Invalid login attempt"; ErrorMessage.Visible = true; break; } } } } }Сохраните файл Login.aspx.cs .
Теперь можно игнорировать предупреждение о том, что для MigrateCart метода не существует определения. Вы добавите это чуть позже в этом руководстве.
Файл кода Login.aspx.cs поддерживает метод LogIn. Проверив страницу Login.aspx, вы увидите, что эта страница содержит кнопку "Войти в систему", при нажатии на которую срабатывает обработчик LogIn в коде страницы.
Login При вызове метода в Login.aspx.cs создается новый экземпляр корзины usersShoppingCart покупок. Идентификатор корзины покупок (GUID) извлекается и устанавливается в cartId переменную. Затем вызывается метод MigrateCart, куда передается cartId и имя пользователя, вошедшего в систему. При переносе корзины для покупок идентификатор GUID, используемый для идентификации анонимной корзины покупок, заменяется именем пользователя.
Помимо изменения файла Login.aspx.cs с кодом, чтобы перенести корзину покупок при входе пользователя, необходимо также изменить файл Register.aspx.cs с кодом, чтобы перенести корзину покупок, когда пользователь создает новую учетную запись, и входит в систему.
В папке "Учетная запись" откройте файл кода с именем Register.aspx.cs.
Измените файл программной части, включив код желтым цветом, чтобы он отображалась следующим образом:
using System; using System.Linq; using System.Web; using System.Web.UI; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Owin; using WingtipToys.Models; namespace WingtipToys.Account { public partial class Register : Page { protected void CreateUser_Click(object sender, EventArgs e) { var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>(); var user = new ApplicationUser() { UserName = Email.Text, Email = Email.Text }; IdentityResult result = manager.Create(user, Password.Text); if (result.Succeeded) { // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771 //string code = manager.GenerateEmailConfirmationToken(user.Id); //string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request); //manager.SendEmail(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>."); IdentityHelper.SignIn(manager, user, isPersistent: false); using (WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions()) { String cartId = usersShoppingCart.GetCartId(); usersShoppingCart.MigrateCart(cartId, user.Id); } IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response); } else { ErrorMessage.Text = result.Errors.FirstOrDefault(); } } } }Сохраните файл Register.aspx.cs . Еще раз игнорировать предупреждение о методе
MigrateCart.
Обратите внимание, что код, используемый в обработчике CreateUser_Click событий, очень похож на код, используемый в методе LogIn . Когда пользователь регистрирует или входит на сайт, будет выполнен вызов MigrateCart метода.
Перенос корзины покупок
После обновления процесса входа и регистрации можно добавить код для переноса корзины покупок с помощью MigrateCart метода.
В обозревателе решений найдите папку логики и откройте файл класса ShoppingCartActions.cs .
Добавьте выделенный желтым цветом код в существующий код в файле ShoppingCartActions.cs , чтобы код в файле ShoppingCartActions.cs отображалось следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; namespace WingtipToys.Logic { public class ShoppingCartActions : IDisposable { public string ShoppingCartId { get; set; } private ProductContext _db = new ProductContext(); public const string CartSessionKey = "CartId"; public void AddToCart(int id) { // Retrieve the product from the database. ShoppingCartId = GetCartId(); var cartItem = _db.ShoppingCartItems.SingleOrDefault( c => c.CartId == ShoppingCartId && c.ProductId == id); if (cartItem == null) { // Create a new cart item if no cart item exists. cartItem = new CartItem { ItemId = Guid.NewGuid().ToString(), ProductId = id, CartId = ShoppingCartId, Product = _db.Products.SingleOrDefault( p => p.ProductID == id), Quantity = 1, DateCreated = DateTime.Now }; _db.ShoppingCartItems.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity. cartItem.Quantity++; } _db.SaveChanges(); } public void Dispose() { if (_db != null) { _db.Dispose(); _db = null; } } public string GetCartId() { if (HttpContext.Current.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name)) { HttpContext.Current.Session[CartSessionKey] = HttpContext.Current.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class. Guid tempCartId = Guid.NewGuid(); HttpContext.Current.Session[CartSessionKey] = tempCartId.ToString(); } } return HttpContext.Current.Session[CartSessionKey].ToString(); } public List<CartItem> GetCartItems() { ShoppingCartId = GetCartId(); return _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId).ToList(); } public decimal GetTotal() { ShoppingCartId = GetCartId(); // Multiply product price by quantity of that product to get // the current price for each of those products in the cart. // Sum all product price totals to get the cart total. decimal? total = decimal.Zero; total = (decimal?)(from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity * cartItems.Product.UnitPrice).Sum(); return total ?? decimal.Zero; } public ShoppingCartActions GetCart(HttpContext context) { using (var cart = new ShoppingCartActions()) { cart.ShoppingCartId = cart.GetCartId(); return cart; } } public void UpdateShoppingCartDatabase(String cartId, ShoppingCartUpdates[] CartItemUpdates) { using (var db = new WingtipToys.Models.ProductContext()) { try { int CartItemCount = CartItemUpdates.Count(); List<CartItem> myCart = GetCartItems(); foreach (var cartItem in myCart) { // Iterate through all rows within shopping cart list for (int i = 0; i < CartItemCount; i++) { if (cartItem.Product.ProductID == CartItemUpdates[i].ProductId) { if (CartItemUpdates[i].PurchaseQuantity < 1 || CartItemUpdates[i].RemoveItem == true) { RemoveItem(cartId, cartItem.ProductId); } else { UpdateItem(cartId, cartItem.ProductId, CartItemUpdates[i].PurchaseQuantity); } } } } } catch (Exception exp) { throw new Exception("ERROR: Unable to Update Cart Database - " + exp.Message.ToString(), exp); } } } public void RemoveItem(string removeCartID, int removeProductID) { using (var _db = new WingtipToys.Models.ProductContext()) { try { var myItem = (from c in _db.ShoppingCartItems where c.CartId == removeCartID && c.Product.ProductID == removeProductID select c).FirstOrDefault(); if (myItem != null) { // Remove Item. _db.ShoppingCartItems.Remove(myItem); _db.SaveChanges(); } } catch (Exception exp) { throw new Exception("ERROR: Unable to Remove Cart Item - " + exp.Message.ToString(), exp); } } } public void UpdateItem(string updateCartID, int updateProductID, int quantity) { using (var _db = new WingtipToys.Models.ProductContext()) { try { var myItem = (from c in _db.ShoppingCartItems where c.CartId == updateCartID && c.Product.ProductID == updateProductID select c).FirstOrDefault(); if (myItem != null) { myItem.Quantity = quantity; _db.SaveChanges(); } } catch (Exception exp) { throw new Exception("ERROR: Unable to Update Cart Item - " + exp.Message.ToString(), exp); } } } public void EmptyCart() { ShoppingCartId = GetCartId(); var cartItems = _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId); foreach (var cartItem in cartItems) { _db.ShoppingCartItems.Remove(cartItem); } // Save changes. _db.SaveChanges(); } public int GetCount() { ShoppingCartId = GetCartId(); // Get the count of each item in the cart and sum them up int? count = (from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity).Sum(); // Return 0 if all entries are null return count ?? 0; } public struct ShoppingCartUpdates { public int ProductId; public int PurchaseQuantity; public bool RemoveItem; } public void MigrateCart(string cartId, string userName) { var shoppingCart = _db.ShoppingCartItems.Where(c => c.CartId == cartId); foreach (CartItem item in shoppingCart) { item.CartId = userName; } HttpContext.Current.Session[CartSessionKey] = userName; _db.SaveChanges(); } } }
Метод MigrateCart использует существующий cartId для поиска корзины покупок пользователя. Затем код перебирает все элементы корзины покупок и заменяет свойство CartId (согласно схеме CartItem) именем авторизованного пользователя.
Обновление подключения к базе данных
Если вы используете это руководство с помощью предварительно созданного примера приложения Wingtip Toys, необходимо повторно создать базу данных членства по умолчанию. Изменив строку подключения по умолчанию, база данных членства будет создана при следующем запуске приложения.
Откройте файлWeb.config в корне проекта.
Обновите строку подключения по умолчанию, чтобы она отображалась следующим образом:
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=aspnet-WingtipToys;Integrated Security=True" providerName="System.Data.SqlClient" />
Интеграция PayPal
PayPal — это веб-платформа выставления счетов, которая принимает платежи от онлайн-продавцов. В этом руководстве далее объясняется, как интегрировать функциональность Express Checkout от PayPal в ваше приложение. Экспресс-касса позволяет клиентам использовать PayPal для оплаты товаров, добавленных в корзину покупок.
Создание тестовых учетных записей PayPal
Чтобы использовать среду тестирования PayPal, необходимо создать и проверить тестовую учетную запись разработчика. Вы будете использовать тестовую учетную запись разработчика для создания тестовой учетной записи покупателя и тестовой учетной записи продавца. Учетные данные тестовой учетной записи разработчика также позволят примеру приложения Wingtip Toys получить доступ к среде тестирования PayPal.
В браузере перейдите на сайт тестирования разработчика PayPal:
https://developer.paypal.comЕсли у вас нет учетной записи разработчика PayPal, создайте новую учетную запись, нажав кнопку "Регистрация" и выполнив действия по регистрации. Если у вас есть учетная запись разработчика PayPal, войдите, нажав кнопку "Войти". Вам потребуется учетная запись разработчика PayPal для тестирования примера приложения Wingtip Toys далее в этом руководстве.
Если вы только что зарегистрировались для учетной записи разработчика PayPal, вам может потребоваться проверить учетную запись разработчика PayPal с помощью PayPal. Чтобы подтвердить свою учетную запись, выполните действия, которые PayPal отправил на вашу электронную почту. После проверки учетной записи разработчика PayPal войдите на веб-сайт для тестирования разработчиков PayPal.
После входа на сайт разработчика PayPal с помощью учетной записи разработчика PayPal необходимо создать тестовую учетную запись PayPal покупателя, если у вас еще нет учетной записи разработчика. Чтобы создать тестовую учетную запись покупателя, на сайте PayPal щелкните вкладку "Приложения " и выберите учетные записи песочницы.
Отображается страница «Тестовые учетные записи песочницы».Замечание
Сайт разработчика PayPal уже предоставляет тестовую учетную запись продавца.
На странице "Тестовые учетные записи песочницы" нажмите кнопку "Создать учетную запись".
На странице "Создание тестовой учетной записи " выберите электронную почту и пароль тестовой учетной записи покупателя.
Замечание
Вам потребуется адрес электронной почты покупателя и пароль для тестирования примера приложения Wingtip Toys в конце этого руководства.
Создайте тестовую учетную запись покупателя, нажав кнопку "Создать учетную запись ".
Отображается страница "Тестовые учетные записи песочницы".
На странице "Тестовые учетные записи Sandbox" щелкните по учетной записи электронной почты facilitator.
Отображаются параметры профиля и уведомления.Выберите параметр "Профиль" , а затем щелкните учетные данные API, чтобы просмотреть учетные данные API для тестовой учетной записи продавца.
Скопируйте учетные данные API TEST в блокнот.
Вам потребуются отображаемые классические учетные данные API TEST (имя пользователя, пароль и подпись) для вызова API из примера приложения Wingtip Toys в среду тестирования PayPal. Вы добавите учетные данные на следующем шаге.
Добавление класса PayPal и учетных данных API
Большинство кода PayPal помещатся в один класс. Этот класс содержит методы, используемые для взаимодействия с PayPal. Кроме того, вы добавите учетные данные PayPal в этот класс.
В примере приложения Wingtip Toys в Visual Studio щелкните правой кнопкой мыши папку логики и выберите "Добавить ->Новый элемент".
Откроется диалоговое окно Добавление нового элемента .В разделе Visual C# в области "Установленные " слева выберите "Код".
В средней области выберите класс. Назовите этот новый класс PayPalFunctions.cs.
Нажмите кнопку Добавить.
Новый файл класса отображается в редакторе.Замените код по умолчанию следующим кодом:
using System; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Net; using System.Text; using System.Data; using System.Configuration; using System.Web; using WingtipToys; using WingtipToys.Models; using System.Collections.Generic; using System.Linq; public class NVPAPICaller { //Flag that determines the PayPal environment (live or sandbox) private const bool bSandbox = true; private const string CVV2 = "CVV2"; // Live strings. private string pEndPointURL = "https://api-3t.paypal.com/nvp"; private string host = "www.paypal.com"; // Sandbox strings. private string pEndPointURL_SB = "https://api-3t.sandbox.paypal.com/nvp"; private string host_SB = "www.sandbox.paypal.com"; private const string SIGNATURE = "SIGNATURE"; private const string PWD = "PWD"; private const string ACCT = "ACCT"; //Replace <Your API Username> with your API Username //Replace <Your API Password> with your API Password //Replace <Your Signature> with your Signature public string APIUsername = "<Your API Username>"; private string APIPassword = "<Your API Password>"; private string APISignature = "<Your Signature>"; private string Subject = ""; private string BNCode = "PP-ECWizard"; //HttpWebRequest Timeout specified in milliseconds private const int Timeout = 15000; private static readonly string[] SECURED_NVPS = new string[] { ACCT, CVV2, SIGNATURE, PWD }; public void SetCredentials(string Userid, string Pwd, string Signature) { APIUsername = Userid; APIPassword = Pwd; APISignature = Signature; } public bool ShortcutExpressCheckout(string amt, ref string token, ref string retMsg) { if (bSandbox) { pEndPointURL = pEndPointURL_SB; host = host_SB; } string returnURL = "https://localhost:44300/Checkout/CheckoutReview.aspx"; string cancelURL = "https://localhost:44300/Checkout/CheckoutCancel.aspx"; NVPCodec encoder = new NVPCodec(); encoder["METHOD"] = "SetExpressCheckout"; encoder["RETURNURL"] = returnURL; encoder["CANCELURL"] = cancelURL; encoder["BRANDNAME"] = "Wingtip Toys Sample Application"; encoder["PAYMENTREQUEST_0_AMT"] = amt; encoder["PAYMENTREQUEST_0_ITEMAMT"] = amt; encoder["PAYMENTREQUEST_0_PAYMENTACTION"] = "Sale"; encoder["PAYMENTREQUEST_0_CURRENCYCODE"] = "USD"; // Get the Shopping Cart Products using (WingtipToys.Logic.ShoppingCartActions myCartOrders = new WingtipToys.Logic.ShoppingCartActions()) { List<CartItem> myOrderList = myCartOrders.GetCartItems(); for (int i = 0; i < myOrderList.Count; i++) { encoder["L_PAYMENTREQUEST_0_NAME" + i] = myOrderList[i].Product.ProductName.ToString(); encoder["L_PAYMENTREQUEST_0_AMT" + i] = myOrderList[i].Product.UnitPrice.ToString(); encoder["L_PAYMENTREQUEST_0_QTY" + i] = myOrderList[i].Quantity.ToString(); } } string pStrrequestforNvp = encoder.Encode(); string pStresponsenvp = HttpCall(pStrrequestforNvp); NVPCodec decoder = new NVPCodec(); decoder.Decode(pStresponsenvp); string strAck = decoder["ACK"].ToLower(); if (strAck != null && (strAck == "success" || strAck == "successwithwarning")) { token = decoder["TOKEN"]; string ECURL = "https://" + host + "/cgi-bin/webscr?cmd=_express-checkout" + "&token=" + token; retMsg = ECURL; return true; } else { retMsg = "ErrorCode=" + decoder["L_ERRORCODE0"] + "&" + "Desc=" + decoder["L_SHORTMESSAGE0"] + "&" + "Desc2=" + decoder["L_LONGMESSAGE0"]; return false; } } public bool GetCheckoutDetails(string token, ref string PayerID, ref NVPCodec decoder, ref string retMsg) { if (bSandbox) { pEndPointURL = pEndPointURL_SB; } NVPCodec encoder = new NVPCodec(); encoder["METHOD"] = "GetExpressCheckoutDetails"; encoder["TOKEN"] = token; string pStrrequestforNvp = encoder.Encode(); string pStresponsenvp = HttpCall(pStrrequestforNvp); decoder = new NVPCodec(); decoder.Decode(pStresponsenvp); string strAck = decoder["ACK"].ToLower(); if (strAck != null && (strAck == "success" || strAck == "successwithwarning")) { PayerID = decoder["PAYERID"]; return true; } else { retMsg = "ErrorCode=" + decoder["L_ERRORCODE0"] + "&" + "Desc=" + decoder["L_SHORTMESSAGE0"] + "&" + "Desc2=" + decoder["L_LONGMESSAGE0"]; return false; } } public bool DoCheckoutPayment(string finalPaymentAmount, string token, string PayerID, ref NVPCodec decoder, ref string retMsg) { if (bSandbox) { pEndPointURL = pEndPointURL_SB; } NVPCodec encoder = new NVPCodec(); encoder["METHOD"] = "DoExpressCheckoutPayment"; encoder["TOKEN"] = token; encoder["PAYERID"] = PayerID; encoder["PAYMENTREQUEST_0_AMT"] = finalPaymentAmount; encoder["PAYMENTREQUEST_0_CURRENCYCODE"] = "USD"; encoder["PAYMENTREQUEST_0_PAYMENTACTION"] = "Sale"; string pStrrequestforNvp = encoder.Encode(); string pStresponsenvp = HttpCall(pStrrequestforNvp); decoder = new NVPCodec(); decoder.Decode(pStresponsenvp); string strAck = decoder["ACK"].ToLower(); if (strAck != null && (strAck == "success" || strAck == "successwithwarning")) { return true; } else { retMsg = "ErrorCode=" + decoder["L_ERRORCODE0"] + "&" + "Desc=" + decoder["L_SHORTMESSAGE0"] + "&" + "Desc2=" + decoder["L_LONGMESSAGE0"]; return false; } } public string HttpCall(string NvpRequest) { string url = pEndPointURL; string strPost = NvpRequest + "&" + buildCredentialsNVPString(); strPost = strPost + "&BUTTONSOURCE=" + HttpUtility.UrlEncode(BNCode); HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url); objRequest.Timeout = Timeout; objRequest.Method = "POST"; objRequest.ContentLength = strPost.Length; try { using (StreamWriter myWriter = new StreamWriter(objRequest.GetRequestStream())) { myWriter.Write(strPost); } } catch (Exception) { // No logging for this tutorial. } //Retrieve the Response returned from the NVP API call to PayPal. HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse(); string result; using (StreamReader sr = new StreamReader(objResponse.GetResponseStream())) { result = sr.ReadToEnd(); } return result; } private string buildCredentialsNVPString() { NVPCodec codec = new NVPCodec(); if (!IsEmpty(APIUsername)) codec["USER"] = APIUsername; if (!IsEmpty(APIPassword)) codec[PWD] = APIPassword; if (!IsEmpty(APISignature)) codec[SIGNATURE] = APISignature; if (!IsEmpty(Subject)) codec["SUBJECT"] = Subject; codec["VERSION"] = "88.0"; return codec.Encode(); } public static bool IsEmpty(string s) { return s == null || s.Trim() == string.Empty; } } public sealed class NVPCodec : NameValueCollection { private const string AMPERSAND = "&"; private const string EQUALS = "="; private static readonly char[] AMPERSAND_CHAR_ARRAY = AMPERSAND.ToCharArray(); private static readonly char[] EQUALS_CHAR_ARRAY = EQUALS.ToCharArray(); public string Encode() { StringBuilder sb = new StringBuilder(); bool firstPair = true; foreach (string kv in AllKeys) { string name = HttpUtility.UrlEncode(kv); string value = HttpUtility.UrlEncode(this[kv]); if (!firstPair) { sb.Append(AMPERSAND); } sb.Append(name).Append(EQUALS).Append(value); firstPair = false; } return sb.ToString(); } public void Decode(string nvpstring) { Clear(); foreach (string nvp in nvpstring.Split(AMPERSAND_CHAR_ARRAY)) { string[] tokens = nvp.Split(EQUALS_CHAR_ARRAY); if (tokens.Length >= 2) { string name = HttpUtility.UrlDecode(tokens[0]); string value = HttpUtility.UrlDecode(tokens[1]); Add(name, value); } } } public void Add(string name, string value, int index) { this.Add(GetArrayName(index, name), value); } public void Remove(string arrayName, int index) { this.Remove(GetArrayName(index, arrayName)); } public string this[string name, int index] { get { return this[GetArrayName(index, name)]; } set { this[GetArrayName(index, name)] = value; } } private static string GetArrayName(int index, string name) { if (index < 0) { throw new ArgumentOutOfRangeException("index", "index cannot be negative : " + index); } return name + index; } }Добавьте учетные данные API продавца (имя пользователя, пароль и подпись), отображаемые ранее в этом руководстве, чтобы вы могли вызывать функции в среде тестирования PayPal.
public string APIUsername = "<Your API Username>"; private string APIPassword = "<Your API Password>"; private string APISignature = "<Your Signature>";
Замечание
В этом примере приложения вы просто добавляете учетные данные в файл C# (.cs). Однако в реализованном решении следует рассмотреть возможность шифрования учетных данных в файле конфигурации.
Класс NVPAPICaller содержит большинство функций PayPal. Код в классе предоставляет методы, необходимые для выполнения тестовой покупки из среды тестирования PayPal. Для покупки используются следующие три функции PayPal:
-
SetExpressCheckoutфункция -
GetExpressCheckoutDetailsфункция -
DoExpressCheckoutPaymentфункция
Метод ShortcutExpressCheckout собирает сведения о тестовых покупках и сведения о продукте из корзины покупок и вызывает SetExpressCheckout функцию PayPal. Метод GetCheckoutDetails подтверждает сведения о покупке и вызывает GetExpressCheckoutDetails функцию PayPal перед выполнением тестовой покупки. Метод DoCheckoutPayment завершает тестовую покупку из среды тестирования путем вызова DoExpressCheckoutPayment функции PayPal. Оставшийся код поддерживает методы и процессы PayPal, такие как строки кодирования, декодирование строк, обработка массивов и определение учетных данных.
Замечание
PayPal позволяет включать необязательные сведения о покупке на основе спецификации API PayPal. Расширив код в примере приложения Wingtip Toys, вы можете включить сведения о локализации, описания продуктов, налог, номер обслуживания клиентов, а также многие другие необязательные поля.
Обратите внимание, что URL-адреса возврата и отмены, указанные в методе ShortcutExpressCheckout , используют номер порта.
string returnURL = "https://localhost:44300/Checkout/CheckoutReview.aspx";
string cancelURL = "https://localhost:44300/Checkout/CheckoutCancel.aspx";
При запуске веб-проекта Visual Web Developer с помощью SSL обычно используется порт 44300 для веб-сервера. Как показано выше, номер порта равен 44300. При запуске приложения можно увидеть другой номер порта. Номер порта должен быть правильно задан в коде, чтобы вы могли успешно запустить пример приложения Wingtip Toys в конце этого руководства. В следующем разделе этого руководства объясняется, как получить номер порта локального узла и обновить класс PayPal.
Обновление номера порта LocalHost в классе PayPal
Пример приложения Wingtip Toys приобретает продукты, перейдя на сайт тестирования PayPal и вернувшись к локальному экземпляру примера приложения Wingtip Toys. Чтобы PayPal вернуться к правильному URL-адресу, необходимо указать номер порта локально работающего примера приложения в приведенном выше коде PayPal.
Щелкните правой кнопкой мыши имя проекта (WingtipToys) в обозревателе решений и выберите "Свойства".
В левом столбце выберите веб-вкладку .
Получите номер порта из поля URL-адреса проекта .
При необходимости обновите
returnURLиcancelURLв классе PayPal (NVPAPICaller) в файле PayPalFunctions.cs, чтобы использовать номер порта вашего веб-приложения.string returnURL = "https://localhost:<Your Port Number>/Checkout/CheckoutReview.aspx"; string cancelURL = "https://localhost:<Your Port Number>/Checkout/CheckoutCancel.aspx";
Теперь добавленный код будет соответствовать ожидаемому порту для локального веб-приложения. PayPal сможет вернуться к правильному URL-адресу на локальном компьютере.
Добавление кнопки PayPal "Возврат"
Теперь, когда основные PayPal функции были добавлены в пример приложения, можно приступить к добавлению разметки и кода, необходимого для вызова этих функций. Сначала необходимо добавить кнопку выхода, которую пользователь увидит на странице корзины покупок.
Откройте файл ShoppingCart.aspx .
Прокрутите внизу файла и найдите
<!--Checkout Placeholder -->комментарий.Замените комментарий элементом
ImageButtonуправления, чтобы разметка была заменена следующим образом:<asp:ImageButton ID="CheckoutImageBtn" runat="server" ImageUrl="https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif" Width="145" AlternateText="Check out with PayPal" OnClick="CheckoutBtn_Click" BackColor="Transparent" BorderWidth="0" />В файле ShoppingCart.aspx.cs, после обработчика событий, расположенного близко к концу файла, добавьте
CheckOutBtn_Clickновый обработчик событий.protected void CheckoutBtn_Click(object sender, ImageClickEventArgs e) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { Session["payment_amt"] = usersShoppingCart.GetTotal(); } Response.Redirect("Checkout/CheckoutStart.aspx"); }Кроме того, в файле ShoppingCart.aspx.cs добавьте ссылку на
CheckoutBtn, чтобы новая кнопка изображения была обозначена следующим образом:protected void Page_Load(object sender, EventArgs e) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { decimal cartTotal = 0; cartTotal = usersShoppingCart.GetTotal(); if (cartTotal > 0) { // Display Total. lblTotal.Text = String.Format("{0:c}", cartTotal); } else { LabelTotalText.Text = ""; lblTotal.Text = ""; ShoppingCartTitle.InnerText = "Shopping Cart is Empty"; UpdateBtn.Visible = false; CheckoutImageBtn.Visible = false; } } }Сохраните изменения как в файле ShoppingCart.aspx , так и в файле ShoppingCart.aspx.cs .
В меню выберите Отладка->Сборка WingtipToys.
Проект будет перестроен с вновь добавленным элементом управления ImageButton.
Отправка сведений о покупке в PayPal
Когда пользователь нажимает кнопку "Возврат " на странице корзины покупок (ShoppingCart.aspx), он начнет процесс покупки. Следующий код вызывает первую функцию PayPal, необходимую для приобретения продуктов.
В папке checkout откройте файл кода с именем CheckoutStart.aspx.cs.
Не забудьте открыть файл кода.Замените существующий код следующим кодом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WingtipToys.Checkout { public partial class CheckoutStart : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { NVPAPICaller payPalCaller = new NVPAPICaller(); string retMsg = ""; string token = ""; if (Session["payment_amt"] != null) { string amt = Session["payment_amt"].ToString(); bool ret = payPalCaller.ShortcutExpressCheckout(amt, ref token, ref retMsg); if (ret) { Session["token"] = token; Response.Redirect(retMsg); } else { Response.Redirect("CheckoutError.aspx?" + retMsg); } } else { Response.Redirect("CheckoutError.aspx?ErrorCode=AmtMissing"); } } } }
Когда пользователь приложения нажимает кнопку "Проверить " на странице корзины покупок, браузер перейдет на страницу CheckoutStart.aspx . При загрузке страницы ShortcutExpressCheckout вызывается метод. На этом этапе пользователь передается на веб-сайт тестирования PayPal. На сайте PayPal пользователь вводит свои учетные данные PayPal, проверяет сведения о покупке, принимает соглашение PayPal и возвращается в пример приложения Wingtip Toys, где ShortcutExpressCheckout метод завершает работу.
ShortcutExpressCheckout После завершения метода пользователь перенаправит пользователя на страницу CheckoutReview.aspx, указанную в методеShortcutExpressCheckout. Это позволяет пользователю просматривать сведения о заказе из примера приложения Wingtip Toys.
Просмотр сведений о заказе
После возвращения из PayPal страница CheckoutReview.aspx примера приложения Wingtip Toys отображает сведения о заказе. Эта страница позволяет пользователю просматривать сведения о заказе перед покупкой продуктов. Страница CheckoutReview.aspx должна быть создана следующим образом:
В папке "Выход " откройте страницу с именем CheckoutReview.aspx.
Замените существующую разметку следующим образом:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutReview.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutReview" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <h1>Order Review</h1> <p></p> <h3 style="padding-left: 33px">Products:</h3> <asp:GridView ID="OrderItemList" runat="server" AutoGenerateColumns="False" GridLines="Both" CellPadding="10" Width="500" BorderColor="#efeeef" BorderWidth="33"> <Columns> <asp:BoundField DataField="ProductId" HeaderText=" Product ID" /> <asp:BoundField DataField="Product.ProductName" HeaderText=" Product Name" /> <asp:BoundField DataField="Product.UnitPrice" HeaderText="Price (each)" DataFormatString="{0:c}"/> <asp:BoundField DataField="Quantity" HeaderText="Quantity" /> </Columns> </asp:GridView> <asp:DetailsView ID="ShipInfo" runat="server" AutoGenerateRows="false" GridLines="None" CellPadding="10" BorderStyle="None" CommandRowStyle-BorderStyle="None"> <Fields> <asp:TemplateField> <ItemTemplate> <h3>Shipping Address:</h3> <br /> <asp:Label ID="FirstName" runat="server" Text='<%#: Eval("FirstName") %>'></asp:Label> <asp:Label ID="LastName" runat="server" Text='<%#: Eval("LastName") %>'></asp:Label> <br /> <asp:Label ID="Address" runat="server" Text='<%#: Eval("Address") %>'></asp:Label> <br /> <asp:Label ID="City" runat="server" Text='<%#: Eval("City") %>'></asp:Label> <asp:Label ID="State" runat="server" Text='<%#: Eval("State") %>'></asp:Label> <asp:Label ID="PostalCode" runat="server" Text='<%#: Eval("PostalCode") %>'></asp:Label> <p></p> <h3>Order Total:</h3> <br /> <asp:Label ID="Total" runat="server" Text='<%#: Eval("Total", "{0:C}") %>'></asp:Label> </ItemTemplate> <ItemStyle HorizontalAlign="Left" /> </asp:TemplateField> </Fields> </asp:DetailsView> <p></p> <hr /> <asp:Button ID="CheckoutConfirm" runat="server" Text="Complete Order" OnClick="CheckoutConfirm_Click" /> </asp:Content>Откройте страницу кода с именем CheckoutReview.aspx.cs и замените существующий код следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; namespace WingtipToys.Checkout { public partial class CheckoutReview : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { NVPAPICaller payPalCaller = new NVPAPICaller(); string retMsg = ""; string token = ""; string PayerID = ""; NVPCodec decoder = new NVPCodec(); token = Session["token"].ToString(); bool ret = payPalCaller.GetCheckoutDetails(token, ref PayerID, ref decoder, ref retMsg); if (ret) { Session["payerId"] = PayerID; var myOrder = new Order(); myOrder.OrderDate = Convert.ToDateTime(decoder["TIMESTAMP"].ToString()); myOrder.Username = User.Identity.Name; myOrder.FirstName = decoder["FIRSTNAME"].ToString(); myOrder.LastName = decoder["LASTNAME"].ToString(); myOrder.Address = decoder["SHIPTOSTREET"].ToString(); myOrder.City = decoder["SHIPTOCITY"].ToString(); myOrder.State = decoder["SHIPTOSTATE"].ToString(); myOrder.PostalCode = decoder["SHIPTOZIP"].ToString(); myOrder.Country = decoder["SHIPTOCOUNTRYCODE"].ToString(); myOrder.Email = decoder["EMAIL"].ToString(); myOrder.Total = Convert.ToDecimal(decoder["AMT"].ToString()); // Verify total payment amount as set on CheckoutStart.aspx. try { decimal paymentAmountOnCheckout = Convert.ToDecimal(Session["payment_amt"].ToString()); decimal paymentAmoutFromPayPal = Convert.ToDecimal(decoder["AMT"].ToString()); if (paymentAmountOnCheckout != paymentAmoutFromPayPal) { Response.Redirect("CheckoutError.aspx?" + "Desc=Amount%20total%20mismatch."); } } catch (Exception) { Response.Redirect("CheckoutError.aspx?" + "Desc=Amount%20total%20mismatch."); } // Get DB context. ProductContext _db = new ProductContext(); // Add order to DB. _db.Orders.Add(myOrder); _db.SaveChanges(); // Get the shopping cart items and process them. using (WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions()) { List<CartItem> myOrderList = usersShoppingCart.GetCartItems(); // Add OrderDetail information to the DB for each product purchased. for (int i = 0; i < myOrderList.Count; i++) { // Create a new OrderDetail object. var myOrderDetail = new OrderDetail(); myOrderDetail.OrderId = myOrder.OrderId; myOrderDetail.Username = User.Identity.Name; myOrderDetail.ProductId = myOrderList[i].ProductId; myOrderDetail.Quantity = myOrderList[i].Quantity; myOrderDetail.UnitPrice = myOrderList[i].Product.UnitPrice; // Add OrderDetail to DB. _db.OrderDetails.Add(myOrderDetail); _db.SaveChanges(); } // Set OrderId. Session["currentOrderId"] = myOrder.OrderId; // Display Order information. List<Order> orderList = new List<Order>(); orderList.Add(myOrder); ShipInfo.DataSource = orderList; ShipInfo.DataBind(); // Display OrderDetails. OrderItemList.DataSource = myOrderList; OrderItemList.DataBind(); } } else { Response.Redirect("CheckoutError.aspx?" + retMsg); } } } protected void CheckoutConfirm_Click(object sender, EventArgs e) { Session["userCheckoutCompleted"] = "true"; Response.Redirect("~/Checkout/CheckoutComplete.aspx"); } } }
Элемент управления DetailsView используется для отображения сведений о заказе, возвращенных из PayPal. Кроме того, приведенный выше код сохраняет сведения о заказе в базу данных Wingtip Toys в качестве OrderDetail объекта. Когда пользователь нажимает кнопку "Полный заказ ", он перенаправляется на страницу CheckoutComplete.aspx .
Замечание
Совет
В разметке страницы CheckoutReview.aspx обратите внимание, что <ItemStyle> тег используется для изменения стиля элементов в элементе управления DetailsView в нижней части страницы. Просмотр страницы в режиме конструктора (выбрав конструктор в левом нижнем углу Visual Studio), а затем выбрав элемент управления DetailsView и выбрав смарт-тег (значок со стрелкой в правом верхнем углу элемента управления), вы сможете просмотреть задачи DetailsView.
Выбрав "Изменить поля", появится диалоговое окно "Поля ". В этом диалоговом окне можно легко управлять визуальными свойствами, такими как ItemStyle, элемента управления DetailsView .
Завершение покупки
CheckoutComplete.aspx страница делает покупку с PayPal. Как упоминалось выше, пользователь должен нажать кнопку "Полный заказ ", прежде чем приложение перейдет на страницу CheckoutComplete.aspx .
В папке "Выход " откройте страницу с именем CheckoutComplete.aspx.
Замените существующую разметку следующим образом:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutComplete.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutComplete" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <h1>Checkout Complete</h1> <p></p> <h3>Payment Transaction ID:</h3> <asp:Label ID="TransactionId" runat="server"></asp:Label> <p></p> <h3>Thank You!</h3> <p></p> <hr /> <asp:Button ID="Continue" runat="server" Text="Continue Shopping" OnClick="Continue_Click" /> </asp:Content>Откройте страницу кода с именем CheckoutComplete.aspx.cs и замените существующий код следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; namespace WingtipToys.Checkout { public partial class CheckoutComplete : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // Verify user has completed the checkout process. if ((string)Session["userCheckoutCompleted"] != "true") { Session["userCheckoutCompleted"] = string.Empty; Response.Redirect("CheckoutError.aspx?" + "Desc=Unvalidated%20Checkout."); } NVPAPICaller payPalCaller = new NVPAPICaller(); string retMsg = ""; string token = ""; string finalPaymentAmount = ""; string PayerID = ""; NVPCodec decoder = new NVPCodec(); token = Session["token"].ToString(); PayerID = Session["payerId"].ToString(); finalPaymentAmount = Session["payment_amt"].ToString(); bool ret = payPalCaller.DoCheckoutPayment(finalPaymentAmount, token, PayerID, ref decoder, ref retMsg); if (ret) { // Retrieve PayPal confirmation value. string PaymentConfirmation = decoder["PAYMENTINFO_0_TRANSACTIONID"].ToString(); TransactionId.Text = PaymentConfirmation; ProductContext _db = new ProductContext(); // Get the current order id. int currentOrderId = -1; if (Session["currentOrderId"] != string.Empty) { currentOrderId = Convert.ToInt32(Session["currentOrderID"]); } Order myCurrentOrder; if (currentOrderId >= 0) { // Get the order based on order id. myCurrentOrder = _db.Orders.Single(o => o.OrderId == currentOrderId); // Update the order to reflect payment has been completed. myCurrentOrder.PaymentTransactionId = PaymentConfirmation; // Save to DB. _db.SaveChanges(); } // Clear shopping cart. using (WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions()) { usersShoppingCart.EmptyCart(); } // Clear order id. Session["currentOrderId"] = string.Empty; } else { Response.Redirect("CheckoutError.aspx?" + retMsg); } } } protected void Continue_Click(object sender, EventArgs e) { Response.Redirect("~/Default.aspx"); } } }
При загрузке страницы DoCheckoutPayment вызывается метод. Как упоминалось ранее, DoCheckoutPayment метод завершает покупку из среды тестирования PayPal. После того как PayPal завершит покупку заказа, на странице CheckoutComplete.aspx отображается платежная транзакция ID покупателю.
Обработка отмены покупки
Если пользователь решит отменить покупку, он будет направлен на страницу CheckoutCancel.aspx , где он увидит, что заказ был отменен.
Откройте страницу с именем CheckoutCancel.aspx в папке "Выход ".
Замените существующую разметку следующим образом:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutCancel.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutCancel" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <h1>Checkout Cancelled</h1> <p></p> <h3>Your purchase has been cancelled.</h3> </asp:Content>
Обработка ошибок покупки
Ошибки во время процесса покупки будут обрабатываться страницей CheckoutError.aspx . Код страницы CheckoutStart.aspx, страницы CheckoutReview.aspxи страницыCheckoutComplete.aspx будет перенаправляться на страницу CheckoutError.aspx при возникновении ошибки.
Откройте страницу с именем CheckoutError.aspx в папке "Выход ".
Замените существующую разметку следующим образом:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutError.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutError" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <h1>Checkout Error</h1> <p></p> <table id="ErrorTable"> <tr> <td class="field"></td> <td><%=Request.QueryString.Get("ErrorCode")%></td> </tr> <tr> <td class="field"></td> <td><%=Request.QueryString.Get("Desc")%></td> </tr> <tr> <td class="field"></td> <td><%=Request.QueryString.Get("Desc2")%></td> </tr> </table> <p></p> </asp:Content>
Страница CheckoutError.aspx отображается с сведениями об ошибке в случае возникновения ошибки во время процесса оформления заказа.
Запуск приложения
Запустите приложение, чтобы узнать, как приобрести продукты. Обратите внимание, что вы будете работать в среде тестирования PayPal. Фактические деньги не обмениваются.
Убедитесь, что все файлы сохранены в Visual Studio.
Откройте веб-браузер и перейдите на https://developer.paypal.com.
Войдите с помощью учетной записи разработчика PayPal, созданной ранее в этом руководстве.
Для песочницы разработчика PayPal необходимо войти в https://developer.paypal.com, чтобы протестировать экспресс-оформление заказа. Это относится только к тестированию песочницы PayPal, а не к динамической среде PayPal.В Visual Studio нажмите клавишу F5 , чтобы запустить пример приложения Wingtip Toys.
После перестроения базы данных браузер откроет и отобразит страницу Default.aspx .Добавьте три разных продукта в корзину покупок, выбрав категорию продукта, например "Автомобили", а затем нажмите кнопку "Добавить в корзину " рядом с каждым продуктом.
Корзина покупок отобразит выбранный продукт.Нажмите кнопку PayPal, чтобы оформить покупку.
Для проверки потребуется, чтобы у вас была учетная запись пользователя для примера приложения Wingtip Toys.
Щелкните ссылку Google справа от страницы, чтобы войти с помощью существующей учетной записи gmail.com электронной почты.
Если у вас нет учетной записи gmail.com, ее можно создать для тестирования в www.gmail.com. Вы также можете использовать стандартную локальную учетную запись, нажав кнопку "Зарегистрировать".
Войдите с помощью учетной записи Gmail и пароля.
Нажмите кнопку "Войти" , чтобы зарегистрировать учетную запись Gmail с помощью примера имени пользователя приложения Wingtip Toys.
На тестовом сайте PayPal добавьте адрес электронной почты покупателя и пароль, созданный ранее в этом руководстве, а затем нажмите кнопку "Войти в систему ".
Примите политику PayPal и нажмите кнопку "Согласиться и продолжить ".
Обратите внимание, что эта страница отображается только при первом использовании этой учетной записи PayPal. Опять же обратите внимание, что это тестовый счет, реальные деньги не обмениваются.
Просмотрите сведения о заказе на странице проверки среды тестирования PayPal и нажмите кнопку "Продолжить".
На странице CheckoutReview.aspx проверьте сумму заказа и просмотрите созданный адрес доставки. Затем нажмите кнопку "Завершить заказ ".
Страница CheckoutComplete.aspx отображается с идентификатором транзакции оплаты.
Проверка базы данных
Просматривая обновленные данные в базе данных образцов приложения Wingtip Toys после его запуска, можно увидеть, что приложение успешно записало покупку товаров.
Данные, содержащиеся в файле базы данных Wingtiptoys.mdf , можно проверить с помощью окна обозревателя баз данных (окно обозревателя серверов в Visual Studio), как было показано ранее в этом руководстве.
Закройте окно браузера, если оно по-прежнему открыто.
В Visual Studio щелкните значок "Показать все файлы" в верхней части обозревателя решений , чтобы разрешить развернуть папку App_Data .
Разверните папку App_Data.
Может потребоваться выбрать значок "Показать все файлы " для папки.Щелкните правой кнопкой мыши файл базы данных Wingtiptoys.mdf и выберите "Открыть".
Отображается обозреватель серверов.Разверните папку Таблицы.
Щелкните правой кнопкой мыши таблицу "Заказы"и выберите "Показать данные таблицы".
Отображается таблица "Заказы ".Проверьте столбец PaymentTransactionID , чтобы подтвердить успешные транзакции.
Закройте окно таблицы "Заказы ".
В обозревателе серверов щелкните правой кнопкой мыши таблицу OrderDetails и выберите "Показать данные таблицы".
Просмотрите
OrderIdиUsernameзначения в таблице OrderDetails . Обратите внимание, что эти значения соответствуют значениямOrderIdиUsername, включенным в таблицу Orders.Закройте окно таблицы OrderDetails .
Щелкните правой кнопкой мыши файл базы данных Wingtip Toys (Wingtiptoys.mdf) и выберите "Закрыть подключение".
Если окно обозревателя решений не отображается, щелкните обозреватель решений в нижней части окна обозревателя серверов , чтобы снова отобразить обозреватель решений .
Сводка
В этом руководстве вы добавили схемы заказов и сведений о заказах для отслеживания покупки товаров. Вы также интегрировали функциональность PayPal в пример приложения Wingtip Toys.
Дополнительные ресурсы
Обзор конфигурации ASP.NET
Развертывание безопасного приложения ASP.NET веб-форм с членством, OAuth и базой данных SQL в службе приложений Azure
Microsoft Azure — бесплатная пробная версия
Отказ от ответственности
В этом руководстве содержится пример кода. Такой пример кода предоставляется "как есть" без каких-либо гарантий. Соответственно, корпорация Майкрософт не гарантирует точность, целостность или качество примера кода. Вы соглашаетесь использовать пример кода по вашему собственному риску. При каких-либо обстоятельствах корпорация Майкрософт не несет ответственности за любой пример кода, содержимого, включая, но не ограничен, любые ошибки или упущения в любом примере кода, содержимого или любые потери или ущерб любого рода, возникшие в результате использования любого примера кода. Настоящим вы уведомлены и соглашаетесь освободить компанию Microsoft от ответственности за любые убытки, претензии, потери, травмы или повреждения любого рода, включая, но не ограничиваясь теми, что вызваны или связаны с материалами, которые вы размещаете, передаете, используете или на которые полагаетесь, включая выраженные в них мнения.