PayPal 登入

跳到主要內容

已不再支援此瀏覽器。

請升級至 Microsoft Edge,以利用最新功能、安全性更新和技術支援。

簽出與使用 PayPal 付款

  • 發行項
  • 09/23/2022

本文內容

作者 :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專案可供本教學課程系列使用。

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

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

注意

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

您將學到什麼:

  • 如何限制存取資料夾中的特定頁面。
  • 如何從匿名購物車建立已知的購物車。
  • 如何啟用專案的 SSL。
  • 如何將 OAuth 提供者新增至專案。
  • 如何使用PayPal使用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/ (,如下所示) 。
    PayPal 登入
  4. 方案總管中,以滑鼠右鍵按一下WingtipToys專案,然後按一下 [屬性]。
  5. 在左側索引標籤中按一下 [Web]
  6. [專案 URL ] 變更為使用您稍早儲存的 SSL URL
    PayPal 登入
  7. CTRL+S儲存頁面。
  8. CTRL+F5 執行應用程式。 Visual Studio 將會顯示可避開 SSL 警告的選項。
  9. 按一下 [ ] 以信任 IIS Express SSL 憑證並繼續。
    PayPal 登入

    隨即顯示一則安全性警告。
  10. 按一下 [ ] 將憑證安裝到您的 localhost。
    PayPal 登入

    瀏覽器視窗隨即出現。

您現在可以輕鬆地使用 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 開發人員主控台]。

    PayPal 登入

  4. 按一下 [ 建立專案 ] 按鈕,然後輸入專案名稱和識別碼, (您可以使用預設值) 。 然後,按一下 [合約] 核取方塊 和 [ 建立 ] 按鈕。

    PayPal 登入

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

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

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

    PayPal 登入

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

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

  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]。

    PayPal 登入

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

    PayPal 登入

  18. 輸入認證之後,系統會提示您提供您剛才建立之 Web 應用程式的許可權。

    PayPal 登入

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

    PayPal 登入

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

修改登入功能

如本教學課程系列先前所述,大部分的使用者註冊功能預設包含在 ASP.NET Web Forms範本中。 現在,您將修改預設 的 Login.aspx 和 Register.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 方法。 藉由檢查 [登入.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開發人員網站已供應商家測試帳戶。

    PayPal 登入

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

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

    注意

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

    PayPal 登入

  7. 按一下 [ 建立帳戶 ] 按鈕,以建立購買者測試帳戶。
    [ 沙箱測試帳戶 ] 頁面隨即顯示。

    PayPal 登入

  8. [沙箱測試帳戶] 頁面上,按一下 協助工具 電子郵件帳戶。
    [設定檔 ] 和 [通知 ] 選項隨即出現。

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

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

您需要顯示的傳統 TEST API 認證 (使用者名稱、密碼和簽章) ,才能從 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. 如有需要,請更新 returnURLpayPalFunctions中 PayPal 類別中的 和 cancelURL (NVPAPICaller) .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 頁面會顯示訂單詳細資料。 此頁面可讓使用者在購買產品之前檢閱訂單詳細資料。 簽出檢視.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">
        <h2>Order Review</h2>
        <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 物件。 當使用者按一下 [ 完成訂單] 按鈕時,系統會將其重新導向至 [結帳][完成][完成.aspx ] 頁面。

注意

提示

在 CheckoutReview.aspx 頁面的標記中,請注意卷 <ItemStyle> 標是用來變更 在頁面底部附近 DetailsView 控制項內專案的樣式。 藉由在 Visual Studio) 左下角選取 [ 設計 ] (檢視頁面,然後選取 [DetailsView ] 控制項,然後選取 [ 智慧標籤 ] (控制項右上方的箭號圖示) ,您將能夠看到 DetailsView 工作

PayPal 登入

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

PayPal 登入

完成購買

CheckoutComplete.aspx 頁面會從PayPal購買。 如上所述,使用者必須先按一下 [ 完成訂單 ] 按鈕,應用程式才能流覽至 [結帳][完成][完成][完成.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">
        <h2>Checkout Complete</h2>
        <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完成訂單購買之後, [結帳][完成.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">
        <h2>Checkout Cancelled</h2>
        <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">
        <h2>Checkout Error</h2>
        <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 範例應用程式。
    資料庫重建之後,瀏覽器會開啟並顯示 [預設.aspx ] 頁面。

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

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

    PayPal 登入

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

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

    PayPal 登入

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

    PayPal 登入

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

    PayPal 登入

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

    PayPal 登入

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

    PayPal 登入

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

    PayPal 登入

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

    PayPal 登入

  14. CheckoutComplete.aspx頁面會顯示付款交易識別碼。

    PayPal 登入

檢閱資料庫

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

您可以使用 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 不受任何和所有損失、損失宣告、損害或任何種類損害的損害,包括您張貼、傳輸、使用或依賴之資料所發生的情況,包括但不限於該處所表達的檢視。