使用 C# 和 ASP.NET Core 实现 PayPal 智能按钮的客户端/服务器端






4.45/5 (5投票s)
按照 PayPal 推荐的方式将 PayPal 智能按钮集成到您的 ASP.NET Core Web 应用程序中
学习成果
- 如何设置 PayPal 商家账户
- 如何获取沙盒 API 凭证 – 即客户端 ID 和密钥
- 如何获取上线 API 凭证 – 即客户端 ID 和密钥
- 要在数据存储中包含哪些字段
- 服务器端代码
必备组件
- 您已为您的电子商务网站注册了一个 URL,例如 www.delaneys.space。
- 该网站已使用 SSL 证书进行保护。
- 您的客户可以使用 OAuth 或其他安全方法以安全的方式注册和登录您的网站。
- 您的网站有一个后端数据存储,并且您知道如何维护它。
- 您熟悉 Visual Studio。
创建一个包含以下行标题的电子表格
1 | 仪表板 URL | https://www.paypal.com/mep/dashboard |
2 | 开发者仪表板 URL | https://developer.paypal.com/developer/applications |
3 | 沙盒详情 | |
4 | URL | www.sandbox.paypal.com/ |
5 | 应用名称 | |
6 | 商用版 | |
7 | 用户名 | …@business.example.com |
8 | 密码 | |
9 | 客户端 ID | |
10 | 密钥 | |
11 | 个人(客户) | |
12 | 用户名 | ...@personal.example.com |
13 | 密码 | |
14 | 上线详情 | |
15 | 用户名 | info@... |
16 | 密码 | |
17 | 应用名称 | |
18 | 客户端 ID | |
19 | 密钥 |
创建 PayPal 商家账户
要创建 PayPal 商家账户,您无需拥有企业或拥有企业银行账户。话虽如此,您应该采取措施将您的个人金融交易与您的电子商务网站的交易分开。因此,我建议使用一个单独的个人银行账户来链接您的 PayPal“商家”账户。
- 导航至 www.PayPal.com。
-
点击“注册”,即使您已经拥有个人 PayPal 账户。请记住,切勿将业务与娱乐混淆。
- 选择“商家账户”并点击“下一步”。
-
完成简短问卷。
- 对于问题“我主要想接受付款方式:”,选择“在我的网站上”。
-
选择问题“年交易量是:”的答案。
-
提供电子邮件地址并点击“继续”。
这可以是任何有效的电子邮件地址,但您可能希望使用“info@您的电子商务.com”。
-
提供密码并点击“继续”。
确认邮件将发送到提供的地址。
更新您的电子表格。
15 用户名 info@... 16 密码 -
填写商家联系方式页面,然后点击“同意并创建账户”。
他们将要求您提供联系人姓名、公司名称、电话号码和公司地址。
-
选择企业类型。
从个人、独资企业、合伙企业、私人公司、上市公司、非营利组织、政府实体、信托 – 投资和家庭列表中选择。
-
添加个人详细信息。
这包括您的姓名、出生日期和地址。
-
点击“提交”。
您现在已经设置了您的 PayPal 商家账户。您应该会看到仪表板显示。
获取 API 凭证 – 即客户端 ID 和密钥
-
转到开发者仪表板
2 开发者仪表板 URL https://developer.paypal.com/developer/applications “沙盒”选项将预先选择。因此,我们先进行设置。
- 点击“创建应用”。
-
创建沙盒应用名称并存储在您的电子表格中。
17 应用名称 -
再次点击“创建应用”。
在您的电子表格中更新以下内容
6 商用版 7 用户名 …@business.example.com 9 客户端 ID 10 密钥 现在我们获取密码。
-
点击“沙盒 | 账户”按钮或 (developer.paypal.com/developer/accounts/)。
您应该看到两个电子邮件地址,分别代表一个商家账户和一个个人账户。
-
点击商家账户的“...”按钮并选择“查看/编辑账户”。
-
在您的电子表格中记录系统生成的密码。
8 密码 - 点击“关闭”。
-
点击个人账户的“...”按钮并选择“查看/编辑账户”。
在您的电子表格中记录用户名和密码。
11 个人(客户) 12 用户名 ...@personal.example.com 13 密码
获取上线 API 凭证
- 点击“我的应用与凭证”(developer.paypal.com/developer/applications/)。
-
点击“上线”按钮。
-
点击“创建应用”。
在您的电子表格中记录应用名称。
17 应用名称 -
点击“创建应用”(再次)。
在您的电子表格中记录客户端 ID 和密钥。
18 客户端 ID 19 密钥
要添加到数据存储的字段
- 选择一个数据存储,例如 SQL Servers。
-
您将需要一个
Basket
、Item
和Invoice
模型/核心类。将以下蓝色突出显示的字段添加到您的发票表中。这些字段将由 PayPal 使用。请注意,
FirstName
、LastName
和Email
存储在User
表中,但也在Invoice
表中重复。这是因为客户 PayPal 账户中的数据可能与User
表中的数据不同。如何将蓝色字段包含在您的数据存储中由您决定。
服务器端代码
- 使用 Visual Studio 创建一个 ASP.NET Core 3.x MVC 应用程序。
- 转到 NuGet 包管理器并添加以下包
-
PayPalCheckoutSdk
,包版本 1.0.3。我使用了撰写时的最新版本。PayPalCheckoutSdk
只是一个类库。库中不包含任何逻辑。这些类使用属性修饰,以帮助将其序列化为 JSON。 -
PayPalHttp
v1.0.0。 -
Microsoft.AspNetCore.Mvc.NewtonsoftJson
。随着 ASP.NET Core 3.0 的发布,Microsoft 破坏了他们的 JSON 序列化实现。搜索“ASP.NET Core: 升级到 3.0 后返回空白 Json {}”以找出要在Startup.ConfigureServices
中添加的内容或从下一步中选择一个选项。
-
-
更新
Startup.ConfigureServices
以调用AddNewtonsoftJson
。services.AddMvc() .AddNewtonsoftJson();
或者
services.AddMvc() .AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
- 在您的 ASP.NET Core 项目中创建一个名为 PayPal 的文件夹。
- 在该文件夹中创建一个名为
PayPalClient
的类,代码如下。
请记住使用沙盒和上线客户端 ID 和密钥来填充突出显示的字符串内容。using System; using PayPalCheckoutSdk.Core; using System.IO; using System.Text; using System.Runtime.Serialization.Json; namespace PayPal { public class PayPalClient { // Place these static properties into a settings area. public static string SandboxClientId { get; set; } = "<alert>{PayPal SANDBOX Client Id}</alert>"; public static string SandboxClientSecret { get; set; } = "<alert>{PayPal SANDBOX Client Secret}</alert>"; public static string LiveClientId { get; set; } = "<alert>{PayPal LIVE Client Id}</alert>"; public static string LiveClientSecret { get; set; } = "<alert>{PayPal LIVE Client Secret}</alert>"; ///<summary> /// Set up PayPal environment with sandbox credentials. /// In production, use LiveEnvironment. ///</summary> public static PayPalEnvironment Environment() { #if DEBUG // You may want to create a UAT (user exceptance tester) // role and check for this: // "if(_unitOfWork.IsUATTester(GetUserId())" instead of fcomiler directives. return new SandboxEnvironment(<alert>SandboxClientId</alert>, <alert>SandboxClientSecret</alert>); #else return new LiveEnvironment(<alert>LiveClientId</alert>, <alert>LiveClientSecret</alert>); #endif } ///<summary> /// Returns PayPalHttpClient instance to invoke PayPal APIs. ///</summary> public static PayPalCheckoutSdk.Core.PayPalHttpClient Client() { return new PayPalHttpClient(Environment()); } public static PayPalCheckoutSdk.Core.PayPalHttpClient Client(string refreshToken) { return new PayPalHttpClient(Environment(), refreshToken); } ///<summary> /// Use this method to serialize Object to a JSON string. ///</summary> public static String ObjectToJSONString(Object serializableObject) { MemoryStream memoryStream = new MemoryStream(); var writer = JsonReaderWriterFactory.CreateJsonWriter(memoryStream, Encoding.UTF8, true, true, " "); var ser = new DataContractJsonSerializer(serializableObject.GetType(), new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true }); ser.WriteObject(writer, serializableObject); memoryStream.Position = 0; StreamReader sr = new StreamReader(memoryStream); return sr.ReadToEnd(); } } }
- 在该文件夹中创建一个名为
SmartButtonHttpResponse
的类,包含代码。using System.Net; using System.Net.Http.Headers; namespace PayPal { public class SmartButtonHttpResponse { readonly PayPalCheckoutSdk.Orders.Order _result; public SmartButtonHttpResponse(PayPalHttp.HttpResponse httpResponse) { Headers = httpResponse.Headers; StatusCode = httpResponse.StatusCode; _result = httpResponse.Result<PayPalCheckoutSdk.Orders.Order>(); } public HttpHeaders Headers { get; } public HttpStatusCode StatusCode { get; } public PayPalCheckoutSdk.Orders.Order Result() { return _result; } public string orderID { get; set; } } }
-
在该文件夹中创建一个名为
OrderBuilder
的类,包含代码。using PayPalCheckoutSdk.Orders; using System.Collections.Generic; namespace PayPal { public static class OrderBuilder { /// <summary> /// Use classes from the PayPalCheckoutSdk to build an OrderRequest /// </summary> /// <returns></returns> public static OrderRequest Build() { OrderRequest orderRequest = new OrderRequest(); // Add code to fill out the order request properties <alert>// See the attached source code for a more detailed example.</alert> return orderRequest; } } }
-
在 Controllers 文件夹中创建一个控制器类,名为
CheckoutController
。添加以下代码using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using PayPalCheckoutSdk.Orders; namespace Test.Controllers { public class CheckoutController : Controller { /// <summary> /// Action to display the cart form for the SERVER side integration /// </summary> /// <returns></returns> public IActionResult Index() { #if DEBUG // You may want to create a UAT (user exceptance tester) role // and check for this: // "if(_unitOfWork.IsUATTester(GetUserId())" // Company SANDBOX Client Id. To go live replace this with the live ID. ViewBag.ClientId = <alert>PayPal.PayPalClient.SandboxClientId</alert>; // Get from a // data store or stettings #else // Company LIVE Client Id. To go live replace this with the live ID. ViewBag.ClientId = <alert>PayPal.PayPalClient.LiveClientId</alert>; // Get from a // data store or stettings #endif ViewBag.CurrencyCode = "GBP"; // Get from a data store ViewBag.CurrencySign = "£"; // Get from a data store return View(); } /// <summary> /// This action is called when the user clicks on the PayPal button. /// </summary> /// <returns></returns> [Route("api/paypal/checkout/order/create")] public async Task<PayPal.SmartButtonHttpResponse> Create() { var request = new PayPalCheckoutSdk.Orders.OrdersCreateRequest(); request.Prefer("return=representation"); request.RequestBody(PayPal.OrderBuilder.Build()); // Call PayPal to set up a transaction var response = await PayPal.PayPalClient.Client().Execute(request); // Create a response, with an order id. var result = response.Result<PayPalCheckoutSdk.Orders.Order>(); var payPalHttpResponse = new PayPal.SmartButtonHttpResponse(response) { orderID = result.Id }; return payPalHttpResponse; } /// <summary> /// This action is called once the PayPal transaction is approved /// </summary> /// <param name="orderId"></param> /// <returns></returns> [Route("api/paypal/checkout/order/approved/{orderId}")] public IActionResult Approved(string orderId) { return Ok(); } /// <summary> /// This action is called once the PayPal transaction is complete /// </summary> /// <param name="orderId"></param> /// <returns></returns> [Route("api/paypal/checkout/order/complete/{orderId}")] public IActionResult Complete(string orderId) { // 1. Update the database. // 2. Complete the order process. Create and send invoices etc. // 3. Complete the shipping process. return Ok(); } /// <summary> /// This action is called once the PayPal transaction is complete /// </summary> /// <param name="orderId"></param> /// <returns></returns> [Route("api/paypal/checkout/order/cancel/{orderId}")] public IActionResult Cancel(string orderId) { // 1. Remove the orderId from the database. return Ok(); } /// <summary> /// This action is called once the PayPal transaction is complete /// </summary> /// <param name="orderId"></param> /// <returns></returns> [Route("api/paypal/checkout/order/error/{orderId}/{error}")] public IActionResult Error(string orderId, string error) { // Log the error. // Notify the user. return NoContent(); } } }
-
在 Views 文件夹中创建一个
Checkout
文件夹,并添加一个名为 index.cshtml 的视图。将以下代码添加到视图以创建 PayPal 智能按钮。
<!-- Set up a container element for the PayPal smart button --> <div id="paypal-button-container"></div> <!-- Include the PayPal JavaScript SDK --> <script src="https://www.paypal.com/sdk/js?client-id=@ViewBag.ClientId& currency=@ViewBag.CurrencyCode"></script> <script> // This is stored just in case the user cancels the other // or there is an error in the other process. var orderId; // Render the PayPal smart button into #paypal-button-container paypal.Buttons({ // Set up the transaction createOrder: function (data, actions) { orderId = data.orderID; return fetch('/api/paypal/checkout/order/create/', { method: 'post' }).then(function (res) { return res.json(); }).then(function (data) { return data.orderID; }); }, // Finalise the transaction onApprove: function (data, actions) { return fetch('/api/paypal/checkout/order/approved/' + data.orderID, { method: 'post' }).then(function (res) { return actions.order.capture(); }).then(function (details) { // (Preferred) Notify the server that the transaction id complete // and have an option to display an order completed screen. window.location.replace('/api/paypal/checkout/order/complete/' + data.orderID + '/@ViewBag.CurrencyCode'); // OR // Notify the server that the transaction id complete //httpGet('/api/paypal/checkout/order/complete/' + data.orderID); // Show a success message to the buyer alert('Transaction completed by ' + details.payer.name.given_name + '!'); }); }, // Buyer cancelled the payment onCancel: function (data, actions) { httpGet('/api/paypal/checkout/order/cancel/' + data.orderID); }, // An error occurred during the transaction onError: function (err) { httpGet('/api/paypal/checkout/order/error/' + orderId + '/' + encodeURIComponent(err)); } }).render('#paypal-button-container'); </script>
除了 URL,此代码对于所有解决方案都是相同的。
-
添加以下 JavaScript 函数
function httpGet(url) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open("GET", url, false); xmlHttp.send(null); return xmlHttp.responseText; }
-
在 PayPal 文件夹中创建一个名为 Values 的文件夹。
-
在 Values 文件夹中添加一个名为 CheckoutPaymentIntent.cs 的类。
添加以下代码using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PayPal.Values { /// <summary> /// The intent to either capture payment immediately or /// authorize a payment for an order after order creation. /// </summary> public static class CheckoutPaymentIntent { /// <summary> /// The merchant intends to capture payment immediately after /// the customer makes a payment. /// </summary> public static string CAPTURE { get; private set; } = "CAPTURE"; /// <summary> /// The merchant intends to authorize a payment and /// place funds on hold after the customer makes a payment. /// Authorized payments are guaranteed for up to three days but /// are available to capture for up to 29 days. /// After the three-day honor period, the original authorized payment expires /// and you must re-authorize the payment. /// You must make a separate request to capture payments on demand. /// This intent is not supported when you have more than one `purchase_unit` /// within your order. /// </summary> public static string AUTHORIZE { get; private set; } = "AUTHORIZE"; } }
-
在 Values 文件夹中添加一个名为 CurrencyCode.cs 的类。
添加以下代码using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PayPal.Values { public static class CurrencyCode { /// <summary> /// Great British Pounds /// </summary> public static string GBP { get; private set; } = "GBP"; /// <summary> /// US Dolars /// </summary> public static string USD { get; private set; } = "USD"; /// <summary> /// Euros /// </summary> public static string EUR { get; private set; } = "EUR"; } }
根据需要添加其他货币。
-
在 Values 文件夹中添加一个名为 LandingPage.cs 的类。
添加以下代码using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PayPal.Values { /// <summary> /// The type of landing page to show on the PayPal site for customer checkout. /// Default: NO_PREFERENCE. /// Source: https://developer.paypal.com/docs/api/orders/v2/ /// </summary> public class LandingPage { /// <summary> /// When the customer clicks PayPal Checkout, /// the customer is redirected to a page to log in to PayPal /// and approve the payment. /// </summary> public static string LOGIN { get; private set; } = "LOGIN"; /// <summary> /// When the customer clicks PayPal Checkout, /// the customer is redirected to a page to enter credit or /// debit card and other relevant billing information required to /// complete the purchase. /// </summary> public static string BILLING { get; private set; } = "BILLING"; /// <summary> /// When the customer clicks PayPal Checkout, /// the customer is redirected to either a page to log in to PayPal and /// approve the payment or to a page to enter credit or /// debit card and other relevant billing information /// required to complete the purchase, depending on their /// previous interaction with PayPal. /// </summary> public static string NO_PREFERENCE { get; private set; } = "NO_PREFERENCE"; } }
-
在 Values 文件夹中添加一个名为 ShippingPreference.cs 的类。
添加以下代码namespace PayPal.Values { /// <summary> /// The shipping preference: /// /// * Displays the shipping address to the customer. /// * Enables the customer to choose an address on the PayPal site. /// * Restricts the customer from changing the address /// during the payment-approval process. /// /// Default: GET_FROM_FILE. /// Source: https://developer.paypal.com/docs/api/orders/v2/ /// </summary> public static class ShippingPreference { /// <summary> /// Use the customer-provided shipping address on the PayPal site. /// </summary> public static string GET_FROM_FILE { get; private set; } = "GET_FROM_FILE"; /// <summary> /// Redact the shipping address from the PayPal site. /// Recommended for digital goods. /// </summary> public static string NO_SHIPPING { get; private set; } = "NO_SHIPPING"; /// <summary> /// Use the merchant-provided address. /// The customer cannot change this address on the PayPal site. /// </summary> public static string SET_PROVIDED_ADDRESS { get; private set; } = "SET_PROVIDED_ADDRESS"; } }
-
在 Values 文件夹中添加一个名为 UserAction.cs 的类。
添加以下代码using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PayPal.Values { /// <summary> /// Configures a Continue or Pay Now checkout flow. /// Source: https://developer.paypal.com/docs/api/orders/v2/ /// </summary> public static class UserAction { /// <summary> /// After you redirect the customer to the PayPal payment page, /// a Continue button appears. Use this option when the final amount is not known /// when the checkout flow is initiated and you want to redirect /// the customer to the merchant page without processing the payment. /// </summary> public static string CONTINUE { get; private set; } = "CONTINUE"; /// <summary> /// After you redirect the customer to the PayPal payment page, /// a Pay Now button appears. /// Use this option when the final amount is known when the checkout is initiated /// and you want to process the payment immediately /// when the customer clicks Pay Now. /// </summary> public static string PAY_NOW { get; private set; } = "PAY_NOW"; } }
-
在 PayPal\Values 文件夹中创建一个名为 Item 的文件夹。
-
在 Values 文件夹中添加一个名为 Category.cs 的类。
添加以下代码using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PayPal.Values.Item { /// <summary> /// The item category type. /// </summary> public static class Category { /// <summary> /// Goods that are stored, delivered, and /// used in their electronic format. /// This value is not currently supported for API callers that leverage the /// [PayPal for Commerce Platform] /// (https://www.paypal.com/us/webapps/mpp/commerce-platform) product. /// </summary> public static string DIGITAL_GOODS { get; private set; } = "DIGITAL_GOODS"; /// <summary> /// A tangible item that can be shipped with proof of delivery. /// </summary> public static string PHYSICAL_GOODS { get; private set; } = "PHYSICAL_GOODS"; } }
- 最后一步是编写代码来处理调用以下代码时发生的情况
- api/paypal/checkout/order/create
- api/paypal/checkout/order/approved/{orderId}
- api/paypal/checkout/order/complete/{orderId}
- api/paypal/checkout/order/cancel/{orderId}
- api/paypal/checkout/order/error/{orderId}/{error}
- 祝您好运!
历史
- 2020年8月3日:初始版本