Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
por Erik Reitan
Baixar o Projeto de Exemplo da Wingtip Toys (C#) ou Baixar o E-book (PDF)
Esta série de tutoriais ensinará as noções básicas da criação de um aplicativo ASP.NET Web Forms usando o ASP.NET 4.5 e o Microsoft Visual Studio Express 2013 para Web. Um projeto do Visual Studio 2013 com código-fonte C# está disponível para acompanhar esta série de tutoriais.
Este tutorial descreve como modificar o aplicativo de exemplo Wingtip Toys para incluir autorização, registro e pagamento do usuário usando PayPal. Somente os usuários conectados terão autorização para comprar produtos. A funcionalidade de registro de usuário interna do modelo de projeto do ASP.NET 4.5 Web Forms já inclui muito do que você precisa. Você adicionará a funcionalidade do PayPal Express Checkout. Neste tutorial, você usará o ambiente de teste de PayPal desenvolvedor, portanto, nenhum fundo real será transferido. No final do tutorial, você testará o aplicativo selecionando produtos para adicionar ao carrinho de compras, clicando no botão de check-out e transferindo dados para o site de teste do PayPal. No site de teste do PayPal, você confirmará suas informações de envio e pagamento e, em seguida, retornará ao aplicativo de exemplo wingtip toys local para confirmar e concluir a compra.
Há vários processadores de pagamento de terceiros experientes especializados em compras online que abordam a escalabilidade e a segurança. ASP.NET desenvolvedores devem considerar as vantagens de utilizar uma solução de pagamento de terceiros antes de implementar uma solução de compra e compra.
Observação
O aplicativo de exemplo Wingtip Toys foi projetado para mostrar conceitos de ASP.NET específicos e recursos disponíveis para ASP.NET desenvolvedores Web. Este aplicativo de exemplo não foi otimizado para todas as circunstâncias possíveis em relação à escalabilidade e à segurança.
O que você aprenderá:
- Como restringir o acesso a páginas específicas em uma pasta.
- Como criar um carrinho de compras conhecido a partir de um carrinho de compras anônimo.
- Como habilitar o SSL para o projeto.
- Como adicionar um provedor OAuth ao projeto.
- Como usar PayPal para comprar produtos usando o ambiente de teste PayPal.
- Como exibir detalhes de PayPal em um controle DetailsView .
- Como atualizar o banco de dados do aplicativo Wingtip Toys com detalhes obtidos do PayPal.
Adicionando o controle de pedidos
Neste tutorial, você criará duas novas classes para acompanhar os dados da ordem que um usuário criou. As classes acompanharão os dados referentes às informações de envio, ao total da compra e à confirmação de pagamento.
Adicionar as classes de modelo Order e OrderDetail
Anteriormente nesta série de tutoriais, você definiu o esquema para categorias, produtos e itens de carrinho de compras criando o Category, Producte CartItem classes na pasta Modelos . Agora você adicionará duas novas classes para definir o esquema para a ordem do produto e os detalhes do pedido.
Na pasta Modelos , adicione uma nova classe chamada Order.cs.
O novo arquivo de classe é exibido no editor.Substitua o código padrão pelo seguinte:
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; } } }Adicione uma classe OrderDetail.cs à pasta Modelos .
Substitua o código padrão pelo seguinte código:
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; } } }
As classes Order e OrderDetail contêm a estrutura para definir as informações do pedido usadas para compra e envio.
Além disso, você precisará atualizar a classe de contexto do banco de dados que gerencia as classes de entidade e que fornece acesso a dados ao banco de dados. Para fazer isso, você adicionará as classes Order e OrderDetail model recém-criadas à ProductContext classe.
No Gerenciador de Soluções, localize e abra o arquivo ProductContext.cs .
Adicione o código realçado ao arquivo ProductContext.cs , conforme mostrado abaixo:
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; } } }
Conforme mencionado anteriormente nesta série de tutoriais, o código no arquivo ProductContext.cs adiciona o System.Data.Entity namespace para que você tenha acesso a todas as funcionalidades principais do Entity Framework. Trabalhando com objetos fortemente tipados, essa funcionalidade inclui a capacidade de consultar, inserir, atualizar e excluir dados. O código acima na classe ProductContext adiciona acesso ao Entity Framework às classes recém-adicionadas Order e OrderDetail.
Adicionando acesso à finalização de compra
O aplicativo de exemplo Wingtip Toys permite que usuários anônimos examinem e adicionem produtos a um carrinho de compras. No entanto, quando os usuários anônimos optarem por comprar os produtos adicionados ao carrinho de compras, eles deverão fazer logon no site. Depois de fazer logon, eles podem acessar as páginas restritas do aplicativo Web que lidam com o processo de compra e check-out. Essas páginas restritas estão contidas na pasta Checkout do aplicativo.
Adicionar uma pasta e páginas de checkout
Agora você criará a pasta Checkout e as páginas nela que o cliente verá durante o processo de check-out. Você atualizará essas páginas mais adiante neste tutorial.
Clique com o botão direito do mouse no nome do projeto (Wingtip Toys) no Gerenciador de Soluções e selecione Adicionar uma Nova Pasta.
Nomeie a nova pasta Checkout.
Clique com o botão direito do mouse na pasta Checkout e selecione Adicionar>Novo Item.
É apresentada a caixa de diálogo Adicionar Novo Item .
Selecione o grupo de modelos do Visual C# ->Web à esquerda. Em seguida, no painel central, selecione Web Form com Página Mestrae nomeie-o CheckoutStart.aspx.
Como antes, selecione o arquivo Site.Master como a página mestra.
Adicione as seguintes páginas adicionais à pasta Checkout usando as mesmas etapas acima:
- CheckoutReview.aspx
- CheckoutComplete.aspx
- CheckoutCancel.aspx
- CheckoutError.aspx
Adicionar um arquivo de Web.config
Ao adicionar um novo arquivo Web.config à pasta Checkout , você poderá restringir o acesso a todas as páginas contidas na pasta.
Clique com o botão direito do mouse na pasta Checkout e selecione Adicionar ->Novo Item.
É apresentada a caixa de diálogo Adicionar Novo Item .Selecione o grupo de modelos do Visual C# ->Web à esquerda. Em seguida, no painel central, selecione Arquivo de Configuração da Web, aceite o nome padrão de Web.confige, em seguida, selecione Adicionar.
Substitua o conteúdo XML existente no arquivo Web.config pelo seguinte:
<?xml version="1.0"?> <configuration> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </configuration>Salve o arquivo Web.config .
O arquivo Web.config especifica que todos os usuários desconhecidos do aplicativo Web devem ter acesso negado às páginas contidas na pasta Checkout . No entanto, se o usuário tiver registrado uma conta e estiver conectado, ele será um usuário conhecido e terá acesso às páginas na pasta Checkout .
É importante observar que ASP.NET configuração segue uma hierarquia, em que cada arquivo Web.config aplica configurações à pasta em que está e a todos os diretórios filho abaixo dela.
Habilitar o SSL para o Projeto
A SSL (Secure Sockets Layer) é um protocolo definido para permitir que servidores Web e clientes Web se comuniquem com mais segurança por meio do uso da criptografia. Quando o SSL não é usado, os dados enviados entre o cliente e o servidor são abertos para detecção de pacotes por qualquer pessoa com acesso físico à rede. Além disso, vários esquemas de autenticação comuns não são seguros sobre HTTP simples. Em particular, a autenticação básica e a autenticação de formulários enviam credenciais não criptografadas. Para serem seguros, esses esquemas de autenticação devem usar o SSL.
- No Gerenciador de Soluções, clique no projeto WingtipToys e pressione F4 para exibir a janela Propriedades .
- Alterar o SSL habilitado para
true. - Copie a URL SSL para que você possa usá-la mais tarde.
A URL SSL seráhttps://localhost:44300/, a menos que você tenha criado sites SSL anteriormente (conforme mostrado abaixo).
- No Gerenciador de Soluções, clique com o botão direito do mouse no projeto WingtipToys e clique em Propriedades.
- Na guia esquerda, clique na Web.
- Altere a URL do Projeto para usar a URL SSL que você salvou anteriormente.
- Salve a página pressionando CTRL+S.
- Pressione Ctrl+F5 para executar o aplicativo. O Visual Studio exibirá uma opção para permitir que você evite avisos SSL.
- Clique em Sim para confiar no certificado SSL do IIS Express e continuar.
Um Aviso de Segurança é exibido. - Clique em Sim para instalar o certificado no localhost.
A janela do navegador será exibida.
Agora você pode testar facilmente seu aplicativo Web localmente usando SSL.
Adicionar um provedor OAuth 2.0
ASP.NET Web Forms fornece opções aprimoradas para associação e autenticação. Esses aprimoramentos incluem OAuth. O OAuth é um protocolo aberto que permite a autorização segura em um método simples e padrão de aplicativos web, móveis e desktop. O modelo ASP.NET Web Forms usa o OAuth para expor o Facebook, o Twitter, o Google e a Microsoft como provedores de autenticação. Embora este tutorial use apenas o Google como provedor de autenticação, você pode modificar facilmente o código para usar qualquer um dos provedores. As etapas para implementar outros provedores são muito semelhantes às etapas que você verá neste tutorial.
Além da autenticação, o tutorial também usará funções para implementar a autorização. Somente os usuários que você adicionar à canEdit função poderão alterar dados (criar, editar ou excluir contatos).
Observação
Os aplicativos do Windows Live aceitam apenas uma URL dinâmica para um site funcional, portanto, você não pode usar uma URL do site local para testar logons.
As etapas a seguir permitirão que você adicione um provedor de autenticação do Google.
Abra o arquivo App_Start\Startup.Auth.cs .
Remova os caracteres de comentário do
app.UseGoogleAuthentication()método para que o método apareça da seguinte maneira:app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() { ClientId = "", ClientSecret = "" });Navegue até o Console de Desenvolvedores do Google. Você também precisará entrar com sua conta de email do desenvolvedor do Google (gmail.com). Se você não tiver uma conta do Google, selecione o link Criar uma conta .
Em seguida, você verá o Console de Desenvolvedores do Google.
Clique no botão Criar Projeto e insira um nome de projeto e uma ID (você pode usar os valores padrão). Em seguida, clique na caixa de seleção do contrato e no botão Criar .
Em alguns segundos, o novo projeto será criado e seu navegador exibirá a nova página de projetos.
Na guia esquerda, clique em APIs &auth e em Credenciais.
Clique em Criar nova ID de cliente em OAuth.
A caixa de diálogo Criar ID do Cliente será exibida.
Na caixa de diálogo Criar ID do Cliente , mantenha o aplicativo Web padrão para o tipo de aplicativo.
Defina as Origens Do JavaScript Autorizadas para a URL SSL que você usou anteriormente neste tutorial (
https://localhost:44300/a menos que você tenha criado outros projetos SSL).
Essa URL é a origem do aplicativo. Para este exemplo, você só inserirá a URL de teste localhost. No entanto, você pode inserir várias URLs para abranger o localhost e o ambiente de produção.Defina o URI de Redirecionamento Autorizado com o seguinte:
https://localhost:44300/signin-googleEsse valor é o URI que o ASP.NET OAuth usa para se comunicar com o servidor OAuth do Google. Lembre-se da URL SSL usada acima (
https://localhost:44300/a menos que você tenha criado outros projetos SSL).Clique no botão Criar ID do Cliente .
No menu à esquerda do Console de Desenvolvedores do Google, clique no item de menu de tela consentimento e defina o endereço de email e o nome do produto. Quando concluir o formulário, clique em Salvar.
Clique no item de menu APIs , role para baixo e clique no botão Desativar ao lado da API do Google+.
Aceitar essa opção habilitará a API do Google+.Você também deve atualizar o pacote NuGet Microsoft.Owin para a versão 3.0.0.
No menu Ferramentas , selecione Gerenciador de Pacotes NuGet e, em seguida, selecione Gerenciar Pacotes NuGet para solução.
Na janela Gerenciar Pacotes NuGet , localize e atualize o pacote Microsoft.Owin para a versão 3.0.0.No Visual Studio, atualize o
UseGoogleAuthenticationmétodo da página Startup.Auth.cs copiando e colando a ID do cliente e o Segredo do Cliente no método. Os valores da ID do Cliente e do Segredo do Cliente mostrados abaixo são exemplos e não funcionarão.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" }); } } }Pressione CTRL+F5 para compilar e executar o aplicativo. Clique no link Login.
Em Usar outro serviço para fazer logon, clique no Google.
Se você precisar inserir suas credenciais, será redirecionado para o site do Google, no qual inserirá suas credenciais.
Depois de inserir suas credenciais, você será solicitado a conceder permissões ao aplicativo Web que acabou de criar.
Clique em Aceitar. Agora você será redirecionado de volta para a página Registrar do aplicativo WingtipToys , onde poderá registrar sua conta do Google.
Você tem a opção de alterar o nome de registro de email local usado para sua conta do Gmail, mas geralmente deseja manter o alias de email padrão (ou seja, aquele usado para autenticação). Clique em Fazer logon , conforme mostrado acima.
Modificando a funcionalidade de logon
Como mencionado anteriormente nesta série de tutoriais, grande parte da funcionalidade de registro do usuário foi incluída no modelo ASP.NET Web Forms por padrão. Agora você modificará as páginas padrão Login.aspx e Register.aspx para chamar o método MigrateCart. O MigrateCart método associa um usuário conectado recentemente a um carrinho de compras anônimo. Ao associar o usuário e o carrinho de compras, o aplicativo de exemplo Wingtip Toys poderá manter o carrinho de compras do usuário entre as visitas.
No Gerenciador de Soluções, localize e abra a pasta Conta .
Modifique a página code-behind chamada Login.aspx.cs para incluir o código realçado em amarelo, para que ele apareça da seguinte maneira:
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; } } } } }Salve o arquivo Login.aspx.cs .
Por enquanto, você pode ignorar o aviso de que não há definição para o MigrateCart método. Você o adicionará um pouco mais tarde neste tutorial.
O arquivo Login.aspx.cs code-behind dá suporte a um método LogIn. Inspecionando a página Login.aspx, você verá que essa página inclui um botão "Fazer logon" que, ao clicar, dispara o LogIn manipulador no code-behind.
Quando o Login método no Login.aspx.cs é chamado, uma nova instância do carrinho de compras chamada usersShoppingCart é criada. A ID do carrinho de compras (um GUID) é recuperada e definida como a cartId variável. Em seguida, o método MigrateCart é chamado, passando tanto o cartId quanto o nome do usuário logado para este método. Quando o carrinho de compras é migrado, o GUID usado para identificar o carrinho de compras anônimo é substituído pelo nome de usuário.
Além de modificar o Login.aspx.cs arquivo code-behind para migrar o carrinho de compras quando o usuário fizer logon, você também deve modificar o arquivo Register.aspx.cs code-behind para migrar o carrinho de compras quando o usuário criar uma nova conta e fizer logon.
Na pasta Conta , abra o arquivo code-behind chamado Register.aspx.cs.
Modifique o arquivo code-behind incluindo o código em amarelo, para que ele apareça da seguinte maneira:
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(); } } } }Salve o arquivo Register.aspx.cs . Mais uma vez, ignore o aviso sobre o
MigrateCartmétodo.
Observe que o código usado no CreateUser_Click manipulador de eventos é muito semelhante ao código usado no LogIn método. Quando o usuário registra ou faz logon no site, uma chamada para o MigrateCart método será feita.
Migrando o carrinho de compras
Agora que você tem o processo de logon e registro atualizado, você pode adicionar o código para migrar o carrinho de compras usando o MigrateCart método.
No Gerenciador de Soluções, localize a pasta Lógica e abra o arquivo de classe ShoppingCartActions.cs .
Adicione o código realçado em amarelo ao código existente no arquivo ShoppingCartActions.cs , para que o código no arquivo ShoppingCartActions.cs apareça da seguinte maneira:
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(); } } }
O MigrateCart método usa a cartId existente para localizar o carrinho de compras do usuário. Em seguida, o código percorre todos os itens do carrinho de compras e substitui a CartId propriedade (conforme especificado pelo esquema) pelo CartItem nome de usuário conectado.
Atualizando a conexão de banco de dados
Se você estiver seguindo este tutorial usando o aplicativo de exemplo Wingtip Toys pré-construído, você deve recriar o banco de dados padrão de associação. Modificando a cadeia de conexão padrão, o banco de dados de associação será criado na próxima vez que o aplicativo for executado.
Abra o arquivo Web.config na raiz do projeto.
Atualize a cadeia de conexão padrão para que ela apareça da seguinte maneira:
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=aspnet-WingtipToys;Integrated Security=True" providerName="System.Data.SqlClient" />
Integrando PayPal
PayPal é uma plataforma de cobrança baseada na Web que aceita pagamentos por comerciantes online. Este tutorial explica em seguida como integrar a funcionalidade do Express Checkout da PayPal ao seu aplicativo. O Express Checkout permite que seus clientes usem PayPal pagar pelos itens que eles adicionaram ao carrinho de compras.
Criar contas de teste de PayPal
Para usar o ambiente de teste PayPal, você deve criar e verificar uma conta de teste do desenvolvedor. Você usará a conta de teste do desenvolvedor para criar uma conta de teste do comprador e uma conta de teste do vendedor. As credenciais da conta de teste do desenvolvedor também permitirão que o aplicativo de exemplo Wingtip Toys acesse o ambiente de teste de PayPal.
Em um navegador, navegue até o site de teste do desenvolvedor PayPal:
https://developer.paypal.comSe você não tiver uma conta de desenvolvedor PayPal, crie uma nova conta clicando em Inscrever-see seguindo as etapas de inscrição. Se você tiver uma conta de desenvolvedor PayPal existente, entre clicando em Fazer Logon. Você precisará de sua conta de desenvolvedor PayPal para testar o aplicativo de exemplo Wingtip Toys mais adiante neste tutorial.
Se você acabou de se inscrever para sua conta de desenvolvedor PayPal, talvez seja necessário verificar sua conta de desenvolvedor PayPal com PayPal. Você pode verificar sua conta seguindo as instruções que o PayPal enviou para sua conta de email. Depois de verificar sua conta de desenvolvedor PayPal, faça logon novamente no site de teste do desenvolvedor PayPal.
Depois de fazer logon no site do desenvolvedor do PayPal com sua conta de desenvolvedor PayPal, você precisará criar uma conta de teste PayPal comprador se ainda não tiver uma. Para criar uma conta de teste do comprador, no site PayPal clique na guia Aplicativos e, em seguida, clique em contas de área restrita.
A página de contas de teste Sandbox é mostrada.Observação
O site do desenvolvedor PayPal já fornece uma conta de teste do comerciante.
Na página de contas de teste da Área Restrita, clique em Criar Conta.
Na página Criar conta de teste , escolha um email de conta de teste do comprador e a senha de sua escolha.
Observação
Você precisará dos endereços de email e senha do comprador para testar o aplicativo de exemplo Wingtip Toys no final deste tutorial.
Crie a conta de teste do comprador clicando no botão Criar Conta .
A página Sandbox Test accounts é exibida.
Na página Sandbox de contas de teste, clique na conta de email do facilitador.
Opções de Perfil e Notificação aparecem.Selecione a opção Perfil e clique em credenciais de API para exibir suas credenciais de API para a conta de teste do comerciante.
Copie as credenciais da API TEST para o bloco de notas.
Você precisará de suas credenciais de API de TESTE Clássica exibidas (Nome de usuário, senha e assinatura) para fazer chamadas à API do aplicativo de exemplo Wingtip Toys para o ambiente de teste PayPal. Você adicionará as credenciais na próxima etapa.
Adicionar Classe PayPal e as credenciais de API
Você colocará a maior parte do código PayPal em uma única classe. Essa classe contém os métodos usados para se comunicar com PayPal. Além disso, você adicionará suas credenciais de PayPal a essa classe.
No aplicativo de exemplo Wingtip Toys no Visual Studio, clique com o botão direito do mouse na pasta Lógica e selecione Adicionar ->Novo Item.
É apresentada a caixa de diálogo Adicionar Novo Item .No painel Instalado à esquerda do Visual C#, selecione Código.
No painel do meio, selecione Classe. Nomeie essa nova classe PayPalFunctions.cs.
Clique em Adicionar.
O novo arquivo de classe é exibido no editor.Substitua o código padrão pelo seguinte código:
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; } }Adicione as credenciais da API do Comerciante (Nome de usuário, senha e assinatura) exibidas anteriormente neste tutorial para que você possa fazer chamadas de função para o ambiente de teste PayPal.
public string APIUsername = "<Your API Username>"; private string APIPassword = "<Your API Password>"; private string APISignature = "<Your Signature>";
Observação
Neste aplicativo de exemplo, você está simplesmente adicionando credenciais a um arquivo C# (.cs). No entanto, em uma solução implementada, você deve considerar criptografar suas credenciais em um arquivo de configuração.
A classe NVPAPICaller contém a maioria da funcionalidade PayPal. O código na classe fornece os métodos necessários para fazer uma compra de teste do ambiente de teste PayPal. As três funções de PayPal a seguir são usadas para fazer compras:
-
SetExpressCheckoutFunção -
GetExpressCheckoutDetailsFunção -
DoExpressCheckoutPaymentFunção
O ShortcutExpressCheckout método coleta as informações de compra de teste e os detalhes do produto do carrinho de compras e chama a SetExpressCheckout função PayPal. O GetCheckoutDetails método confirma os detalhes da compra e chama a função do GetExpressCheckoutDetails PayPal antes de fazer a compra de teste. O método DoCheckoutPayment conclui a compra de teste no ambiente de testes ao chamar a função DoExpressCheckoutPayment PayPal. O código restante dá suporte aos métodos e ao processo PayPal, como codificação de cadeias de caracteres, decodificação de cadeias de caracteres, processamento de matrizes e determinação de credenciais.
Observação
PayPal permite incluir detalhes opcionais de compra com base na especificação da API do PayPal. Ao estender o código no aplicativo de exemplo Wingtip Toys, você pode incluir detalhes de localização, descrições de produto, impostos, um número de atendimento ao cliente, bem como muitos outros campos opcionais.
Observe que as URLs de retorno e cancelamento especificadas no método ShortcutExpressCheckout usam um número de porta.
string returnURL = "https://localhost:44300/Checkout/CheckoutReview.aspx";
string cancelURL = "https://localhost:44300/Checkout/CheckoutCancel.aspx";
Quando o Visual Web Developer executa um projeto Web usando SSL, geralmente a porta 44300 é usada para o servidor Web. Conforme mostrado acima, o número da porta é 44300. Ao executar o aplicativo, você pode ver um número de porta diferente. O número da porta precisa ser definido corretamente no código para que você possa executar com êxito o aplicativo de exemplo Wingtip Toys no final deste tutorial. A próxima seção deste tutorial explica como recuperar o número da porta do host local e atualizar a classe PayPal.
Atualizar o número da porta LocalHost na classe PayPal
O aplicativo de exemplo Wingtip Toys compra produtos navegando pelo site de testes do PayPal e retornando à sua instância local do aplicativo de exemplo Wingtip Toys. Para que PayPal retorne à URL correta, você precisa especificar o número da porta do aplicativo de exemplo em execução local no código PayPal mencionado acima.
Clique com o botão direito do mouse no nome do projeto (WingtipToys) no Gerenciador de Soluções e selecione Propriedades.
Na coluna à esquerda, selecione a guia Web .
Recupere o número da porta no campo URL do Projeto.
Se necessário, atualize as marcas
returnURLecancelURLna classe PayPal (NVPAPICaller) no arquivo PayPalFunctions.cs para usar o número da porta do seu aplicativo web.string returnURL = "https://localhost:<Your Port Number>/Checkout/CheckoutReview.aspx"; string cancelURL = "https://localhost:<Your Port Number>/Checkout/CheckoutCancel.aspx";
Agora, o código que você adicionou corresponderá à porta esperada para seu aplicativo Web local. PayPal poderá retornar à URL correta em seu computador local.
Adicionar o botão PayPal Checkout
Agora que as funções de PayPal primárias foram adicionadas ao aplicativo de exemplo, você pode começar a adicionar a marcação e o código necessários para chamar essas funções. Primeiro, você deve adicionar o botão de check-out que o usuário verá na página do carrinho de compras.
Abra o arquivo ShoppingCart.aspx .
Role até a parte inferior do arquivo e localize o
<!--Checkout Placeholder -->comentário.Substitua o comentário por um
ImageButtoncontrole para que a marcação seja substituída da seguinte maneira:<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" />No arquivo ShoppingCart.aspx.cs , após o
UpdateBtn_Clickmanipulador de eventos perto do final do arquivo, adicione oCheckOutBtn_Clickmanipulador de eventos:protected void CheckoutBtn_Click(object sender, ImageClickEventArgs e) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { Session["payment_amt"] = usersShoppingCart.GetTotal(); } Response.Redirect("Checkout/CheckoutStart.aspx"); }Também no arquivo ShoppingCart.aspx.cs, adicione uma referência ao
CheckoutBtn, para que o novo botão de imagem seja referenciado da seguinte maneira: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; } } }Salve suas alterações no arquivo ShoppingCart.aspx e no arquivo ShoppingCart.aspx.cs .
No menu, selecione Debug->Build WingtipToys.
O projeto será recriado com o controle ImageButton recém-adicionado.
Enviar detalhes de compra para PayPal
Quando o usuário clicar no botão Checkout na página do carrinho de compras (ShoppingCart.aspx), ele iniciará o processo de compra. O código a seguir chama a primeira função de PayPal necessária para comprar produtos.
Na pasta Checkout , abra o arquivo code-behind chamado CheckoutStart.aspx.cs.
Certifique-se de abrir o arquivo code-behind.Substitua o código existente pelo seguinte:
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"); } } } }
Quando o usuário do aplicativo clicar no botão Checkout na página do carrinho de compras, o navegador navegará até a página CheckoutStart.aspx . Quando a página CheckoutStart.aspx é carregada, o ShortcutExpressCheckout método é chamado. Neste ponto, o usuário é transferido para o site de teste de PayPal. No site PayPal, o usuário insere suas credenciais de PayPal, analisa os detalhes da compra, aceita o contrato de PayPal e retorna ao aplicativo de exemplo Wingtip Toys em que o ShortcutExpressCheckout método é concluído. Quando o ShortcutExpressCheckout método for concluído, ele redirecionará o usuário para a página CheckoutReview.aspx especificada no ShortcutExpressCheckout método. Isso permite que o usuário examine os detalhes do pedido de dentro do aplicativo de exemplo Wingtip Toys.
Examinar detalhes do pedido
Depois de retornar do PayPal, a página CheckoutReview.aspx do aplicativo de exemplo Wingtip Toys exibe os detalhes do pedido. Esta página permite que o usuário examine os detalhes do pedido antes de comprar os produtos. A página CheckoutReview.aspx deve ser criada da seguinte maneira:
Na pasta Checkout , abra a página chamada CheckoutReview.aspx.
Substitua a marcação existente pelo seguinte:
<%@ 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>Abra a página code-behind chamada CheckoutReview.aspx.cs e substitua o código existente pelo seguinte:
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"); } } }
O controle DetailsView é usado para exibir os detalhes da ordem que foram retornados de PayPal. Além disso, o código acima salva os detalhes do pedido no banco de dados Wingtip Toys como um OrderDetail objeto. Quando o usuário clica no botão Concluir Pedido , ele é redirecionado para a página CheckoutComplete.aspx .
Observação
Dica
Na marcação da página CheckoutReview.aspx, observe que a tag <ItemStyle> é usada para alterar o estilo dos itens dentro do controle DetailsView perto do final da página. Ao exibir a página no Modo design (selecionando Design no canto inferior esquerdo do Visual Studio), selecionando o controle DetailsView e selecionando a Marca Inteligente (o ícone de seta no canto superior direito do controle), você poderá ver as Tarefas do DetailsView.
Ao selecionar Editar Campos, a caixa de diálogo Campos será exibida. Nesta caixa de diálogo, você pode controlar facilmente as propriedades visuais, como ItemStyle, do controle DetailsView .
Concluir a compra
CheckoutComplete.aspx faz a compra através do PayPal. Conforme mencionado acima, o usuário deve clicar no botão Concluir Pedido antes que o aplicativo navegue até a página CheckoutComplete.aspx .
Na pasta Checkout , abra a página chamada CheckoutComplete.aspx.
Substitua a marcação existente pelo seguinte:
<%@ 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>Abra a página code-behind chamada CheckoutComplete.aspx.cs e substitua o código existente pelo seguinte:
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"); } } }
Quando a página CheckoutComplete.aspx é carregada, o DoCheckoutPayment método é chamado. Conforme mencionado anteriormente, o DoCheckoutPayment método conclui a compra do ambiente de teste PayPal. Depois que PayPal concluir a compra do pedido, a página CheckoutComplete.aspx exibirá uma transação ID de pagamento ao comprador.
Gerenciar o cancelamento de compra
Se o usuário decidir cancelar a compra, ele será direcionado para a página CheckoutCancel.aspx em que verá que o pedido foi cancelado.
Abra a página chamada CheckoutCancel.aspx na pasta Checkout .
Substitua a marcação existente pelo seguinte:
<%@ 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>
Gerenciar erros de compra
Os erros durante o processo de compra serão tratados pela página CheckoutError.aspx . Se ocorrer um erro, o code-behind das páginas CheckoutStart.aspx, CheckoutReview.aspx, e CheckoutComplete.aspx redirecionará para a página CheckoutError.aspx.
Abra a página chamada CheckoutError.aspx na pasta Checkout .
Substitua a marcação existente pelo seguinte:
<%@ 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>
A página CheckoutError.aspx é exibida com os detalhes do erro quando ocorre um erro durante o processo de check-out.
Executando o aplicativo
Execute o aplicativo para ver como comprar produtos. Observe que você estará operando no ambiente de teste do PayPal. Nenhum dinheiro real está sendo trocado.
Verifique se todos os arquivos foram salvos no Visual Studio.
Abra um navegador da Web e navegue até https://developer.paypal.com.
Faça logon com sua conta de desenvolvedor PayPal que você criou anteriormente neste tutorial.
Para a sandbox do desenvolvedor do PayPal, você precisa estar logado em https://developer.paypal.com para testar o checkout expresso. Isso só se aplica ao teste de área restrita do PayPal, não ao ambiente ao vivo do PayPal.No Visual Studio, pressione F5 para executar o aplicativo de exemplo Wingtip Toys.
Depois que o banco de dados for recompilado, o navegador será aberto e mostrará a página Default.aspx .Adicione três produtos diferentes ao carrinho de compras selecionando a categoria de produto, como "Carros" e, em seguida, clicando em Adicionar ao Carrinho ao lado de cada produto.
O carrinho de compras exibirá o produto selecionado.Clique no botão PayPal para fazer check-out.
O checkout requer que você tenha uma conta de usuário no aplicativo de exemplo Wingtip Toys.
Clique no link do Google à direita da página para fazer logon com uma conta de email gmail.com existente.
Se você não tiver uma conta gmail.com, poderá criar uma para fins de teste em www.gmail.com. Você também pode usar uma conta local padrão clicando em "Registrar".
Entre com sua conta e senha do Gmail.
Clique no botão Fazer logon para registrar sua conta do Gmail com o nome de usuário do aplicativo de exemplo Wingtip Toys.
No site de teste PayPal, adicione o endereço de email e a senha do comprador que você criou anteriormente neste tutorial e clique no botão Fazer Logon .
Concorde com a política de PayPal e clique no botão Concordar e Continuar .
Observe que esta página só é exibida na primeira vez que você usa essa conta PayPal. Observe novamente que esta é uma conta de teste, nenhum dinheiro real é trocado.
Examine as informações do pedido na página de revisão do ambiente de teste do PayPal e clique em Continuar.
Na página CheckoutReview.aspx , verifique o valor do pedido e exiba o endereço de envio gerado. Em seguida, clique no botão Concluir Pedido .
A página CheckoutComplete.aspx é exibida com uma ID de transação de pagamento.
Revisando o banco de dados
Ao examinar os dados atualizados no banco de dados do aplicativo de exemplo Wingtip Toys, após executar o aplicativo, você pode ver que este registrou com êxito a compra dos produtos.
Você pode inspecionar os dados contidos no arquivo de banco de dados Wingtiptoys.mdf usando a janela do Gerenciador de Banco de Dados (janela do Gerenciador de Servidores no Visual Studio), como fez anteriormente nesta série de tutoriais.
Feche a janela do navegador se ela ainda estiver aberta.
No Visual Studio, selecione o ícone Mostrar Todos os Arquivos na parte superior do Gerenciador de Soluções para permitir que você expanda a pasta App_Data .
Expanda a pasta App_Data .
Talvez seja necessário selecionar o ícone Mostrar Todos os Arquivos para a pasta.Clique com o botão direito do mouse no arquivo de banco de dados Wingtiptoys.mdf e selecione Abrir.
O Gerenciador de Servidores é exibido.Expanda a pasta Tabelas .
Clique com o botão direito do mouse na tabela Pedidose selecione Mostrar Dados da Tabela.
A tabela Pedidos é exibida.Examine a coluna PaymentTransactionID para confirmar transações bem-sucedidas.
Feche a janela tabela Pedidos .
No Gerenciador de Servidores, clique com o botão direito do mouse na tabela OrderDetails e selecione Mostrar Dados da Tabela.
Revise os valores
OrderIdeUsernamena tabela OrderDetails. Observe que esses valores correspondem aos valoresOrderIdeUsernameincluídos na tabela Pedidos.Feche a janela da tabela OrderDetails .
Clique com o botão direito do mouse no arquivo de banco de dados Wingtip Toys (Wingtiptoys.mdf) e selecione Fechar Conexão.
Se você não vir a janela do Gerenciador de Soluções , clique no Gerenciador de Soluções na parte inferior da janela do Gerenciador de Servidores para mostrar o Gerenciador de Soluções novamente.
Resumo
Neste tutorial, você adicionou esquemas de pedidos e detalhes de pedidos para monitorar a compra de produtos. Você também integrou PayPal funcionalidade ao aplicativo de exemplo Wingtip Toys.
Recursos adicionais
Visão geral da configuração do ASP.NET
Implantar um aplicativo Web Forms do ASP.NET seguro com Gerenciamento de Contas, OAuth e Banco de Dados SQL no Serviço de Aplicativo do Azure
Microsoft Azure – Avaliação gratuita
Disclaimer
Este tutorial contém o código de exemplo. Esse código de exemplo é fornecido "como está" sem garantia de qualquer tipo. Assim, a Microsoft não garante a precisão, a integridade ou a qualidade do código de exemplo. Você concorda em usar o código de exemplo por sua conta e risco. Em nenhuma circunstância, a Microsoft será responsável a você de qualquer forma por qualquer código de exemplo, conteúdo, incluindo, mas não limitado a, quaisquer erros ou omissões em qualquer código de exemplo, conteúdo ou qualquer perda ou dano de qualquer tipo incorrido como resultado do uso de qualquer código de exemplo. Você é notificado e, portanto, concorda em indenizar e manter a Microsoft isenta de responsabilidade contra toda e qualquer perda, reclamações de perda, lesão ou dano de qualquer tipo, incluindo, sem limitação, aqueles ocasionados por ou decorrentes de material que você posta, transmite, usa ou dos quais você depende, incluindo, mas não se limitando a, os pontos de vista expressos no referido material.