簽出與使用 PayPal 付款

作者 :Erik Reitan

下載 Wingtip Toys 範例項目 (C#) 下載電子書 (PDF)

本教學課程系列將教導您使用 ASP.NET 4.5 和 Microsoft Visual Studio Express 2013 for Web 建置 ASP.NET Web Forms 應用程式的基本概念。 隨附本教學課程系列提供具有 C# 原始程式碼的 Visual Studio 2013 專案。

本教學課程說明如何修改 Wingtip Toys 範例應用程式,以包含使用 PayPal 的使用者授權、註冊和付款。 只有登入的使用者才能獲得購買產品的授權。 ASP.NET 4.5 Web Form 項目範本的內建用戶註冊功能已包含您所需的大部分專案。 您將新增 PayPal Express Checkout 功能。 在本教學課程中,您會使用 PayPal 開發人員測試環境,因此不會轉移實際資金。 在教學課程結束時,您將選取要新增至購物車的產品、按兩下結帳按鈕,以及將數據傳送至 PayPal 測試網站,以測試應用程式。 在 PayPal 測試網站上,您將確認您的出貨和付款資訊,然後返回本機 Wingtip Toys 範例應用程式,以確認並完成購買。

有數個有經驗的第三方付款處理器專門處理可解決延展性和安全性的在線購物。 ASP.NET 開發人員應該先考慮使用第三方付款解決方案的優點,再實作購物和購買解決方案。

注意

Wingtip Toys 範例應用程式的設計目的是要顯示可供 ASP.NET Web 開發人員使用的特定 ASP.NET 概念和功能。 此範例應用程式並未針對延展性和安全性的所有可能情況進行優化。

您將學到什麼:

  • 如何限制存取資料夾中的特定頁面。
  • 如何從匿名購物車建立已知的購物車。
  • 如何為專案啟用 SSL。
  • 如何將 OAuth 提供者新增至專案。
  • 如何使用 PayPal 測試環境購買產品。
  • 如何在 DetailsView 控件中顯示 PayPal 的詳細數據。
  • 如何使用從 PayPal 取得的詳細數據來更新 Wingtip Toys 應用程式的資料庫。

新增訂單追蹤

在本教學課程中,您將建立兩個新的類別,以追蹤使用者建立的順序中的數據。 類別會追蹤有關出貨資訊、購買總計和付款確認的數據。

新增 Order 和 OrderDetail 模型類別

稍早在本教學課程系列中,您會在 Models 資料夾中建立CategoryProductCartItem 類別,以定義類別、產品和購物車專案的架構。 現在,您將新增兩個新的類別,以定義產品訂單的架構和訂單的詳細數據。

  1. Models 資料夾中,新增名為 Order.cs 的新類別。
    新的類別檔案會顯示在編輯器中。

  2. 使用下列項目取代預設程式碼:

    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; }
      }
    }
    
  3. OrderDetail.cs 類別新增至 Models 資料夾。

  4. 使用下列程式碼來取代預設程式碼:

    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; }
    
        }
    }
    

OrderOrderDetail 類別包含架構,用來定義用於購買和出貨的訂單資訊。

此外,您必須更新管理實體類別的資料庫內容類別,並提供資料庫的數據存取。 若要這樣做,您會將新建立的 Order 和 OrderDetail 模型類別新增至 ProductContext 類別。

  1. 方案總管 中,尋找並開啟 ProductContext.cs 檔案。

  2. 將醒目提示的程序代碼新增至 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 範例應用程式可讓匿名使用者檢閱產品,並將產品新增至購物車。 不過,當匿名使用者選擇購買他們新增至購物車的產品時,他們必須登入網站。 登入之後,他們就可以存取 Web 應用程式的受限制頁面,以處理結帳和購買程式。 這些受限制的頁面會包含在應用程式的 [簽出 ] 資料夾中。

新增簽出資料夾和頁面

您現在將會建立 [結帳 ] 資料夾,以及客戶在結帳程序期間會看到的頁面。 您稍後將會在本教學課程中更新這些頁面。

  1. 以滑鼠右鍵按兩下 方案總管 中 (Wingtip Toys) 的項目名稱,然後選取 [新增資料夾]。

    使用 PayPal 簽出和付款 - 新增資料夾

  2. 將新資料夾命名為 [簽出]。

  3. 以滑鼠右鍵按兩下 [簽出] 資料夾,然後選取 [新增>專案]。

    使用 PayPal 簽出和付款 - 新增專案

  4. [ 加入新項目 ] 對話方塊隨即出現。

  5. 選取左側的 [Visual C# ->Web 範本] 群組。 然後,從中間窗格選取 [主版頁面的 Web 窗體],並將它命名為 CheckoutStart.aspx

    使用 PayPal 簽出和付款 - 新增項目對話框

  6. 如前所述,選取 Site.Master 檔案作為主版頁面。

  7. 使用上述相同步驟,將下列其他頁面新增至 [簽出 ] 資料夾:

    • CheckoutReview.aspx
    • CheckoutComplete.aspx
    • CheckoutCancel.aspx
    • CheckoutError.aspx

新增 Web.config 檔案

將新的 Web.config 檔案新增至 [簽出 ] 資料夾,您就可以限制存取資料夾中包含的所有頁面。

  1. 以滑鼠右鍵按兩下 [簽出] 資料夾,然後選取 [新增 ->新增專案]。
    [ 加入新項目 ] 對話方塊隨即出現。

  2. 選取左側的 [Visual C# ->Web 範本] 群組。 然後,從中間窗格選取 [ Web 組態檔]、接受 預設名稱Web.config,然後選取 [ 新增]。

  3. 使用下列內容來取代 Web.config 檔案中的現有 XML 內容:

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <authorization>
          <deny users="?"/>
        </authorization>
      </system.web>
    </configuration>
    
  4. 儲存 Web.config 檔案。

Web.config 檔案指定 Web 應用程式的所有未知使用者都必須拒絕存取 [簽出] 資料夾中包含的頁面。 不過,如果使用者已註冊帳戶並登入,他們將會是已知的使用者,而且可以存取 [簽出 ] 資料夾中的頁面。

請務必注意,ASP.NET 組態遵循階層,其中每個 Web.config 檔案都會將組態設定套用至其所在的資料夾,以及其下的所有子目錄。

對專案啟用 SSL

安全通訊端層 (SSL) 是一種定義的通訊協定,允許 Web 伺服器和 Web 用戶端透過加密,以更安全的方式進行通訊。 未使用 SSL 時,在用戶端和伺服器之間傳送的資料會開放給任何可實體存取網路的人員進行封包探查。 此外,數種常見驗證結構描述在一般的 HTTP 上並不是很安全。 尤其是,基本驗證和表單驗證會傳送未加密的認證。 為了安全的理由,這些驗證結構描述必須使用 SSL。

  1. 方案總管 中,按下 WingtipToys 專案,然後按 F4 以顯示 [屬性] 視窗。
  2. 將 [SSL 已啟用] 變更為 true
  3. 複製 SSL URL ,以便稍後使用。
    除非您先前已 (建立 SSL 網站,否則 SSL URL 將會 https://localhost:44300/ 是 ,如下所示) 。
    專案屬性
  4. 方案總管 中,以滑鼠右鍵按兩下 WingtipToys 項目,然後按兩下 [屬性]。
  5. 在左側索引標籤中按一下 [Web]
  6. 變更 [專案 URL ] 以使用您稍早儲存的 SSL URL
    Project Web 屬性
  7. CTRL+S儲存頁面。
  8. CTRL+F5 執行應用程式。 Visual Studio 將會顯示可避開 SSL 警告的選項。
  9. 按一下 [ ] 以信任 IIS Express SSL 憑證並繼續。
    IIS Express SSL 憑證詳細數據
    隨即顯示一則安全性警告。
  10. 按一下 [ ] 將憑證安裝到您的 localhost。
    [安全性警告] 對話框
    瀏覽器視窗隨即出現。

您現在可以輕鬆地使用 SSL 在本機測試 Web 應用程式。

新增 OAuth 2.0 提供者

ASP.NET Web Forms 提供成員資格和驗證的增強功能選項。 這些增強功能包括 OAuth。 OAuth 是一種開放式通訊協定,可讓 Web、行動和桌面應用程式以簡單、標準的方法執行安全授權。 ASP.NET Web Forms 範本會使用 OAuth 將 Facebook、Twitter、Google 和 Microsoft 公開為驗證提供者。 雖然本教學課程僅使用 Google 作為驗證提供者,但您可以輕易修改程式碼來使用任何提供者。 實作其他提供者的步驟,與您將在本教學課程中看到的步驟極為類似。

除了驗證,本教學課程也會使用角色來實作授權。 只有您新增至 canEdit 角色的使用者才能建立、編輯或刪除連絡人。

注意

Windows Live 應用程式只接受工作網站的即時 URL,因此您無法使用本機網站 URL 來測試登入。

下列步驟可新增 Google 驗證提供者。

  1. 開啟 App_Start\Startup.Auth.cs 檔案。

  2. 移除 app.UseGoogleAuthentication() 方法中的註解字元,然後此方法會顯示如下:

    app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    {
        ClientId = "",
        ClientSecret = ""
    });
    
  3. 瀏覽至 Google Developers Console。 您還需要使用您的 Google 開發人員電子郵件帳戶 (gmail.com) 登入。 如果您沒有 Google 帳戶,請選取 [ 建立帳戶 ] 連結。
    接下來,您會看到 [ Google 開發人員主控台]。
    Google 開發人員控制台

  4. 按兩下 [ 建立專案 ] 按鈕,然後輸入專案名稱和識別碼, (您可以使用預設值) 。 然後,按兩下 合約複選框 和 [ 建立] 按鈕。

    Google - 新增專案

    幾秒鐘內即可建立新的專案,您的瀏覽器便會顯示新的專案頁面。

  5. 在左索引標籤中,按兩下 [API & 驗證],然後按兩下 [ 認證]。

  6. 按兩下 [OAuth] 底下的 [建立新的用戶端標識符]。
    [ 建立用戶端識別碼 ] 對話方塊隨即出現。
    Google - 建立用戶端識別碼

  7. 在 [ 建立用戶端識別符] 對話框中,保留應用程式類型的預設 Web應用程式

  8. 除非您已建立其他 SSL 專案) ,否則請將本教學課程稍早使用的 授權 JavaScript 來源 設定為 SSL URL (https://localhost:44300/
    此 URL 會是應用程式的原始來源。 在此範例中,您將僅輸入 localhost 測試 URL。 不過,您可以輸入多個 URL 來考慮 localhost 和 production。

  9. 將 [Authorized Redirect URI] 設定如下:

    https://localhost:44300/signin-google
    

    此值是 ASP.NET OAuth 使用者與 Google OAuth 伺服器進行通訊的 URI。 請記住您在上面使用的 SSL URL ( https://localhost:44300/ ,除非您已) 建立其他 SSL 專案。

  10. 按兩下 [ 建立用戶端識別碼] 按鈕。

  11. 在 Google 開發人員控制台的左側功能表上,單擊 [ 同意] 畫面 功能表項,然後設定您的電子郵件地址和產品名稱。 當您完成表單時,請按兩下 [ 儲存]。

  12. 單擊 [API] 功能表項,向下捲動,然後按下Google+ API 旁的 [關閉] 按鈕。
    接受此選項將會啟用 Google+ API。

  13. 您也必須將 Microsoft.Owin NuGet 套件更新為 3.0.0 版。
    從 [ 工具 ] 功能表中,選取 [NuGet 套件管理員 ],然後選取 [ 管理方案的 NuGet 套件]。
    從 [ 管理 NuGet 套件 ] 視窗中,尋找 Microsoft.Owin 套件並將其更新為 3.0.0 版。

  14. 在 Visual Studio 中,藉由將 [用戶端識別符] 和 [用戶端密碼] 複製並貼到 方法,以更新 UseGoogleAuthenticationStartup.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"
                });
            }
        }
    }
    
  15. CTRL+F5 以建置並執行應用程式。 按一下 [登入] 連結。

  16. [使用其他服務登入] 下,按兩下 [Google]。
    登錄

  17. 如果您需要輸入認證,您會被重新導向至 Google 網站,您可以在此輸入認證。
    Google - 登入

  18. 輸入認證之後,系統會提示您將許可權授與您剛才建立的 Web 應用程式。
    專案預設服務帳戶

  19. 按一下 [接受]。 您現在會重新導向回 WingtipToys 應用程式的 [註冊] 頁面,您可以在其中註冊 Google 帳戶。
    向Google帳戶註冊

  20. 您可以選擇變更用於 Gmail 帳戶的本機電子郵件註冊名稱,但您通常會想保留預設電子郵件別名 (也就是,您用來驗證的名稱)。 按兩下 [ 登入 ],如上所示。

修改登入功能

如本教學課程系列先前所述,大部分的用戶註冊功能預設已包含在 ASP.NET Web Forms 範本中。 現在,您將修改預設 Login.aspxRegister.aspx 頁面來呼叫 MigrateCart 方法。 方法 MigrateCart 會將新登入的使用者與匿名購物車產生關聯。 藉由建立使用者和購物車的關聯,Wingtip Toys 範例應用程式將能夠維護造訪者之間使用者的購物車。

  1. 方案總管 中,尋找並開啟 [帳戶] 資料夾。

  2. 修改名為 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;
                    }
                }
            }
        }
    }
    
  3. 儲存 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 程式代碼後置檔案 ,以在使用者建立新的帳戶並登入時移轉購物車。

  1. 在 [ 帳戶] 資料夾中,開啟名為 Register.aspx.cs 的程式代碼後置檔案。

  2. 藉由將程式代碼納入黃色來修改程序代碼後置檔案,使其如下所示:

    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();
                }
            }
        }
    }
    
  3. 儲存 Register.aspx.cs 檔案。 同樣地,請忽略 方法的相關 MigrateCart 警告。

請注意,您在事件處理程式中使用的 CreateUser_Click 程式代碼非常類似您在 方法中使用的 LogIn 程式代碼。 當用戶註冊或登入網站時, MigrateCart 將會呼叫 方法。

移轉購物車

現在您已更新登入和註冊程式,您可以新增程序代碼,以使用 MigrateCart 方法來移轉購物車。

  1. 方案總管 中,尋找Logic資料夾並開啟 ShoppingCartActions.cs 類別檔案。

  2. 將黃色醒目提示的程序代碼新增至 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 範例應用程式遵循本教學課程,則必須重新建立預設成員資格資料庫。 藉由修改預設 連接字串,會在下次應用程式執行時建立成員資格資料庫。

  1. 在專案的根目錄開啟 Web.config 檔案。

  2. 更新預設 連接字串,使其如下所示:

    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=aspnet-WingtipToys;Integrated Security=True" providerName="System.Data.SqlClient" />
    

整合 PayPal

PayPal 是以 Web 為基礎的計費平臺,可接受在線商家的付款。 本教學課程接下來說明如何將 PayPal的快速結帳功能整合到您的應用程式中。 快速結帳可讓客戶使用 PayPal 支付他們新增至購物車的專案。

建立 PayPal 測試帳戶

若要使用 PayPal 測試環境,您必須建立並驗證開發人員測試帳戶。 您將使用開發人員測試帳戶來建立購買者測試帳戶和賣方測試帳戶。 開發人員測試帳戶認證也會允許 Wingtip Toys 範例應用程式存取 PayPal 測試環境。

  1. 在瀏覽器中,流覽至 PayPal 開發人員測試網站:
    https://developer.paypal.com

  2. 如果您沒有 PayPal 開發人員帳戶,請按兩下 [ 註冊] 並遵循註冊步驟來建立新的帳戶。 如果您有現有的 PayPal 開發人員帳戶,請按兩下 [ 登入] 來登入。 稍後在本教學課程中,您將需要您的 PayPal 開發人員帳戶來測試 Wingtip Toys 範例應用程式。

  3. 如果您剛註冊 PayPal 開發人員帳戶,您可能需要使用 PayPal 驗證您的 PayPal 開發人員帳戶。 您可以遵循 PayPal 傳送至電子郵件帳戶的步驟來確認您的帳戶。 驗證 PayPal 開發人員帳戶之後,請重新登入 PayPal 開發人員測試網站。

  4. 使用 PayPal 開發人員帳戶登入 PayPal 開發人員網站之後,如果您還沒有 PayPal 購買者測試帳戶,則需要建立 PayPal 購買者測試帳戶。 若要建立購買者測試帳戶,請在 PayPal 網站上按兩下 [ 應用程式 ] 索引卷標,然後按兩下 [ 沙箱帳戶]。
    [ 沙盒測試帳戶 ] 頁面隨即顯示。

    注意

    PayPal 開發人員網站已提供商家測試帳戶。

    顯示 [沙盒測試帳戶] 頁面的螢幕快照,其中已醒目提示 [應用程式] 索引卷標。

  5. 在 [沙盒測試帳戶] 頁面上,按兩下 [ 建立帳戶]。

  6. 在 [ 建立測試帳戶 ] 頁面上,選擇您選擇的購買者測試帳戶電子郵件和密碼。

    注意

    您將需要購買者電子郵件地址和密碼,才能在本教學課程結束時測試 Wingtip Toys 範例應用程式。

    [建立測試帳戶] 頁面的螢幕快照,其中顯示正在建立之帳戶的字段。

  7. 按兩下 [ 建立帳戶 ] 按鈕來建立購買者測試帳戶。
    [ 沙盒測試帳戶 ] 頁面隨即顯示。

    使用 PayPal 簽出和付款 - PayPal 帳戶

  8. [沙盒測試帳戶] 頁面上,按兩下 輔助功能 電子郵件帳戶。
    [配置檔 ] 和 [通知 ] 選項隨即出現。

  9. 選取 [配置檔] 選項,然後按兩下 [ API 認證 ] 以檢視商家測試帳戶的 API 認證。

  10. 將 TEST API 認證複製到記事本。

您需要顯示的傳統 TEST API 認證 (Username、Password 和 Signature) ,才能從 Wingtip Toys 範例應用程式對 PayPal 測試環境進行 API 呼叫。 您將在下一個步驟中新增認證。

新增 PayPal 類別和 API 認證

您將大部分的 PayPal 程式代碼放入單一類別。 這個類別包含用來與 PayPal 通訊的方法。 此外,您會將 PayPal 認證新增至此類別。

  1. 在 Visual Studio 內的 Wingtip Toys 範例應用程式中,以滑鼠右鍵按兩下 [邏輯] 資料夾,然後選取 [新增 ->新增專案]。
    [ 加入新項目 ] 對話方塊隨即出現。

  2. 從左側 [已安裝] 窗格的 [Visual C#] 底下,選取 [程序代碼]。

  3. 從中間窗格中,選取 [ 類別]。 將此新類別命名為 PayPalFunctions.cs

  4. 按一下 [新增]。
    新的類別檔案會顯示在編輯器中。

  5. 使用下列程式碼來取代預設程式碼:

    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;
      }
    }
    
  6. 新增您稍早在本教學課程中顯示 (用戶名稱、密碼和簽章) 的商家 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 可讓您根據 PayPal 的 API 規格包含選擇性購買詳細數據。 藉由擴充 Wingtip Toys 範例應用程式中的程式碼,您可以包含當地語系化詳細數據、產品描述、稅務、客戶服務號碼,以及其他許多選擇性字段。

請注意, ShortcutExpressCheckout 方法中指定的傳回和取消 URL 會使用埠號碼。

string returnURL = "https://localhost:44300/Checkout/CheckoutReview.aspx";
       string cancelURL = "https://localhost:44300/Checkout/CheckoutCancel.aspx";

當 Visual Web Developer 使用 SSL 執行 Web 專案時,通常會使用埠 44300 作為網頁伺服器。 如上所示,埠號碼為44300。 當您執行應用程式時,可能會看到不同的埠號碼。 您必須在程式代碼中正確設定您的埠號碼,才能在本教學課程結束時成功執行 Wingtip Toys 範例應用程式。 本教學課程的下一節說明如何擷取本機主機埠號碼並更新 PayPal 類別。

更新 PayPal 類別中的LocalHost埠號碼

Wingtip Toys 範例應用程式會流覽至 PayPal 測試網站,並返回 Wingtip Toys 範例應用程式的本機實例來購買產品。 若要讓 PayPal 傳回正確的 URL,您必須在上述 PayPal 程式代碼中指定本機執行範例應用程式的埠號碼。

  1. 以滑鼠右鍵按兩下 方案總管中的項目名稱 (WingtipToys) ,然後選取 [屬性]。

  2. 在左欄中,選取 [Web] 索引標籤。

  3. [專案 URL ] 方塊中擷取埠號碼。

  4. 如有需要,請更新 returnURL PayPal 類別中的 和 cancelURL (NVPAPICaller) PayPalFunctions.cs 檔案中的 ,以使用 Web 應用程式的埠號碼:

    string returnURL = "https://localhost:<Your Port Number>/Checkout/CheckoutReview.aspx";
    string cancelURL = "https://localhost:<Your Port Number>/Checkout/CheckoutCancel.aspx";
    

現在,您新增的程式代碼將會符合本機 Web 應用程式的預期埠。 PayPal 將能夠返回本機計算機上的正確 URL。

新增 PayPal 結帳按鈕

現在,主要 PayPal 函式已新增至範例應用程式,您可以開始新增呼叫這些函式所需的標記和程序代碼。 首先,您必須新增使用者會在購物車頁面上看到的結帳按鈕。

  1. 開啟 ShoppingCart.aspx 檔案。

  2. 捲動至檔案底部並尋找 <!--Checkout Placeholder --> 批注。

  3. 將批註取代為 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" />
    
  4. ShoppingCart.aspx.cs 檔案中,在接近檔案結尾的事件處理程序之後 UpdateBtn_Click ,新增 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");
    }
    
  5. 此外,在 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;
            }
        }
    }
    
  6. 將變更儲存至 ShoppingCart.aspx 檔案和 ShoppingCart.aspx.cs 檔案。

  7. 從功能表中,選取 [ 錯->建置 WingtipToys]。
    系統會使用新增的 ImageButton 控制項重建專案。

將購買詳細數據傳送至 PayPal

當使用者按兩下購物車頁面上的 [結帳 ] 按鈕時, (ShoppingCart.aspx) ,他們就會開始購買程式。 下列程式代碼會呼叫購買產品所需的第一個 PayPal 函式。

  1. [簽出 ] 資料夾中,開啟名為 CheckoutStart.aspx.cs 的程式代碼後置檔案。
    請務必開啟程序代碼後置檔案。

  2. 將現有的程式碼取代為下列程式碼:

    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 頁面。 當 CheckoutStart.aspx 頁面載入時,ShortcutExpressCheckout會呼叫 方法。 此時,用戶會轉移至 PayPal 測試網站。 在 PayPal 網站上,使用者輸入其 PayPal 認證、檢閱購買詳細數據、接受 PayPal 合約,並返回方法完成的 ShortcutExpressCheckout Wingtip Toys 範例應用程式。 ShortcutExpressCheckout方法完成時,會將使用者重新導向至 方法中指定的 ShortcutExpressCheckoutCheckoutReview.aspx 頁面。 這可讓使用者檢閱 Wingtip Toys 範例應用程式中的訂單詳細數據。

檢閱訂單詳細數據

從 PayPal 返回之後,Wingtip Toys 範例應用程式的 CheckoutReview.aspx 頁面會顯示訂單詳細數據。 此頁面可讓用戶在購買產品之前檢閱訂單詳細數據。 必須建立 CheckoutReview.aspx 頁面,如下所示:

  1. [簽出 ] 資料夾中,開啟名為 CheckoutReview.aspx 的頁面

  2. 以下列專案取代現有的標記:

    <%@ 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>
    
  3. 開啟名為 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 工作

使用 PayPal 簽出和付款 - 編輯欄位

選取 [ 編輯欄位],[ 字段 ] 對話框隨即出現。 在此對話框中,您可以輕鬆地控制DetailsView控件的視覺屬性,例如ItemStyle

使用 PayPal 簽出和付款 - 字段對話框

完成購買

CheckoutComplete.aspx 頁面會從 PayPal 購買。 如上所述,用戶必須先按兩下 [ 完成訂單] 按鈕,應用程式才能流覽至 CheckoutComplete.aspx 頁面。

  1. [簽出 ] 資料夾中,開啟名為 CheckoutComplete.aspx 的頁面

  2. 以下列專案取代現有的標記:

    <%@ 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>
    
  3. 開啟名為 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");
        }
      }
    }
    

載入 CheckoutComplete.aspx 頁面時,會 DoCheckoutPayment 呼叫 方法。 如先前所述, DoCheckoutPayment 此方法會完成從 PayPal 測試環境購買。 當 PayPal 完成訂單購買之後, CheckoutComplete.aspx 頁面就會向購買者顯示付款交易 ID

處理取消購買

如果用戶決定取消購買,系統會將他們導向 至 CheckoutCancel.aspx 頁面,他們會看到訂單已取消。

  1. [簽出] 資料夾中開啟名為 CheckoutCancel.aspx 的頁面

  2. 以下列專案取代現有的標記:

    <%@ 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 頁面。

  1. [簽出] 資料夾中開啟名為 CheckoutError.aspx 的頁面

  2. 以下列專案取代現有的標記:

    <%@ 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 測試環境中執行。 不會交換實際金錢。

  1. 請確定您的所有檔案都儲存在 Visual Studio 中。

  2. 開啟網頁瀏覽器並瀏覽至 https://developer.paypal.com

  3. 使用您稍早在本教學課程中建立的 PayPal 開發人員帳戶登入。
    針對 PayPal 的開發人員沙箱,您必須登入 https://developer.paypal.com 以測試快速結帳。 這隻適用於 PayPal 的沙箱測試,不適用於 PayPal 的實時環境。

  4. 在 Visual Studio 中,按 F5 以執行 Wingtip Toys 範例應用程式。
    資料庫重建之後,瀏覽器會開啟並顯示 Default.aspx 頁面。

  5. 選取產品類別,例如 「Cars」,然後按兩下每個產品旁邊的 [ 新增至購物車 ],將三個不同的產品新增至購物車。
    購物車會顯示您已選取的產品。

  6. 按兩下 [PayPal] 按鈕以簽出。

    使用 PayPal 簽出和付款 - 購物車

    取出需要您有 Wingtip Toys 範例應用程式的用戶帳戶。

  7. 按兩下頁面右側的 Google 連結,以使用現有的 gmail.com 電子郵件帳戶登入。
    如果您沒有 gmail.com 帳戶,您可以在 www.gmail.com 建立一個用於測試用途的帳戶。 您也可以按下 [註冊] 來使用標準本機帳戶。

    使用 PayPal 簽出和付款 - 登入

  8. 使用您的 gmail 帳戶和密碼登入。

    使用 PayPal 簽出和付款 - Gmail 登入

  9. 按兩下 [ 登入] 按鈕,以您的 Wingtip Toys 範例應用程式使用者名稱註冊您的 gmail 帳戶。

    使用 PayPal 簽出和付款 - 註冊帳戶

  10. 在 PayPal 測試網站上,新增您稍早在本教學課程中建立的 購買者 電子郵件地址和密碼,然後按兩下 [ 登入 ] 按鈕。

    使用 PayPal 簽出和付款 - PayPal 登入

  11. 同意 PayPal 原則,然後按兩下 [ 同意後繼續] 按鈕。
    請注意,此頁面只會在您第一次使用此 PayPal 帳戶時顯示。 同樣地,請注意,這是測試帳戶,不會交換任何實際金錢。

    使用 PayPal 簽出和付款 - PayPal 原則

  12. 檢閱 PayPal 測試環境檢閱頁面上的訂單資訊,然後按兩下 [ 繼續]。

    使用 PayPal 簽出和付款 - 檢閱資訊

  13. [CheckoutReview.aspx ] 頁面上,確認訂單金額並檢視產生的出貨位址。 然後按兩下 [ 完成訂單] 按鈕。

    使用 PayPal 簽出和付款 - 訂單檢閱

  14. CheckoutComplete.aspx 頁面會顯示付款交易標識符。

    使用 PayPal 簽出和付款 - 簽出完成

檢閱資料庫

藉由在執行應用程式之後檢閱 Wingtip Toys 範例應用程式資料庫中的更新數據,您可以看到應用程式已成功記錄產品的購買。

您可以使用 Visual Studio 中的 [資料庫總管] 視窗 (Visual Studio 中的 [伺服器總管] 視窗,來檢查 Wingtiptoys.mdf 資料庫檔案中包含的數據) ,如同您稍早在本教學課程系列中所做的一樣。

  1. 如果瀏覽器視窗仍在開啟,請關閉它。

  2. 在 Visual Studio 中,選取 方案總管 頂端的 [顯示所有檔案] 圖示,讓您展開App_Data資料夾。

  3. 展開 App_Data 資料夾。
    您可能需要選取資料夾的 [顯示所有檔案 ] 圖示。

  4. 以滑鼠右鍵按兩下 Wingtiptoys.mdf 資料庫檔案,然後選取 [ 開啟]。
    伺服器總 管隨即顯示。

  5. 展開 [資料表] 資料夾。

  6. 以滑鼠右鍵按兩下 [訂單]數據表,然後選取 [ 顯示數據表數據]。
    [ 訂單] 資料表隨即顯示。

  7. 檢閱 PaymentTransactionID 數據行以確認交易成功。

    使用 PayPal 簽出和付款 - 檢閱資料庫

  8. 關閉 [Orders 資料表] 視窗。

  9. 在 [伺服器總管] 中,以滑鼠右鍵按兩下 OrderDetails 數據表,然後選取 [ 顯示資料表數據]。

  10. OrderId檢閱 OrderDetails 數據表中的 和 Username 值。 請注意,這些值符合 OrderIdOrders 資料表中包含的 和 Username 值。

  11. 關閉 OrderDetails 資料表視窗。

  12. 以滑鼠右鍵按兩下 Wingtip Toys 資料庫檔案, (Wingtiptoys.mdf) ,然後選取 [ 關閉連線]。

  13. 如果您沒有看到 [方案總管] 視窗,請按兩下 [伺服器總管] 視窗底部的 [方案總管],再次顯示 方案總管

摘要

在本教學課程中,您已新增訂單和訂單詳細數據架構,以追蹤產品的購買。 您也已將 PayPal 功能整合到 Wingtip Toys 範例應用程式中。

其他資源

ASP.NET 組態概觀
使用成員資格、OAuth 和 SQL Database 部署安全 ASP.NET Web Forms 應用程式以 Azure App 服務
Microsoft Azure - 免費試用

免責聲明

本教學課程包含範例程序代碼。 這類範例程序代碼是以「原樣」提供,不含任何種類的保固。 因此,Microsoft 不保證範例程式代碼的正確性、完整性或品質。 您同意自行承擔使用範例程式代碼的風險。 在任何情況下,Microsoft 都會以任何範例程式代碼、內容,包括但不限於任何範例程式代碼、內容或任何因使用任何範例程序代碼而造成的任何遺失或損害,不限於任何錯誤或省略的任何錯誤或遺漏。 因此,您會收到通知並同意保證、儲存及保存 Microsoft 不受任何和所有損失、損失宣告、損害或損害任何種類的損害,包括您張貼、傳輸、使用或依賴之數據所發生的情況,包括但不限於該檢視所表示的檢視。