电商支付-Paypal普通支付对接基于Restful-api-v1.Net(二)

PayPal支付对接实战:基于Restful API v1的.Net实现
本文档介绍了如何使用.Net进行PayPal支付接口的对接,详细阐述了同步和异步回调流程,以及配置准备、请求授权、创建支付和执行扣款的步骤。通过示例代码,帮助跨境电商平台实现与PayPal的集成。

前言

跨境电商行业中,支付环节相信大家都不陌生,对接的国外支付平台可能很多,但是PayPal作为全球最重要的支付方式之一,支付转化率也有保证,一直深受各跨境电商平台青睐,本章将继续介绍PayPal对接示例实战,
本次讲解是针对PayPal官方文档Payments支付流程介绍,基于.Net代码示例
PayPal参考文档

名词解释

  1. 同步回调:及时处理支付状态的处理流程
  2. 异步回调:异步处理支付状态的处理流程(支付状态最终以异步回传为准)

配置准备工作

登录PayPal沙盒开发者中心
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

endpoint:paypal api地址
mode:沙盒/生产
connectionTimeout: 超时时间
requestRetries:重试次数
clientId:客户端ID
clientSecret:私钥(不要泄漏)
webhook.id:回调ID

配置文件配置如下

 <configSections>
    <section name="paypal" type="PayPal.Manager.SDKConfigHandler, PayPalCoreSDK" />
  </configSections>
  <paypal>
    <settings>
      <add name="endpoint" value="https://api.sandbox.paypal.com/" />
      <add name="oauth.EndPoint" value="https://api.sandbox.paypal.com/" />
      <add name="mode" value="sandbox" />
      <add name="connectionTimeout" value="360000" />
      <add name="requestRetries" value="1" />
      <add name="clientId" value="「clientId」" />
      <add name="clientSecret" value="「clientSecret」" />
      <add name="webhook.id" value="「webhook.id」" />
    </settings>
  </paypal>

请求授权获取Token

定义PaypalUtils工具类

        static PaypalUtils()
        {
            var config = GetConfig();
            ClientId = config["clientId"];
            ClientSecret = config["clientSecret"];
            Endpoint = config["endpoint"];
            WebhookId = config["webhook.id"];
        }
        // Create the configuration map that contains mode and other optional configuration details.
        public static Dictionary<string, string> GetConfig()
        {
            var result = ConfigManager.Instance.GetProperties();
            result["clientId"] = result["clientId"];
            result["clientSecret"] = result["clientSecret"];
            result["webhook.id"] = result["webhook.id"];
            return result;
        }
        // Create accessToken
        public static string GetAccessToken()
        {
            // ###AccessToken
            // Retrieve the access token from
            // OAuthTokenCredential by passing in
            // ClientID and ClientSecret
            // It is not mandatory to generate Access Token on a per call basis.
            // Typically the access token can be generated once and
            // reused within the expiry window 
            var accessToken = string.Empty;
            try
            {
                ServicePointManager.Expect100Continue = true;
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
                ServicePointManager.DefaultConnectionLimit = 9999;
                accessToken = new OAuthTokenCredential(ClientId, ClientSecret, GetConfig()).GetAccessToken();
            }
            catch (Exception ex)
            {
                PaymentHepler.WriteErrorLog(PaypalUtils.LogName, $"GetAccessToken error, {ex.ToString()}", ex);
            }

            return accessToken;
        }
        /// <summary>
        /// 获取授权头信息
        /// </summary>
        /// <returns></returns>
        public static Dictionary<string, string> GetHeaders(string token)
        {
            var headers = new Dictionary<string, string>()
            {
                { "authorization", $"{token}" },
            };
            return headers;
        }
        /// <summary>
        /// create payment
        /// https://developer.paypal.com/docs/api/payments/v1/#payment_create
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static v1.Payment CreatePayment(string token, Payment.Paypal.v1.Payment request)
        {
            var requestJson = Newtonsoft.Json.JsonConvert.SerializeObject(request);
            // $"create请求开始";
            var result = PaymentHepler.HttpPost($"{Endpoint}v1/payments/payment/", requestJson, GetHeaders(token));
            var response = GetPaypalResult(result);
            // $"create响应: {result}";
            return response;

        }
        /// <summary>
        /// execute payment
        /// https://developer.paypal.com/docs/api/payments/v1/#payment_execute
        /// </summary>
        /// <param name="payerId"></param>
        /// <param name="paymentId"></param>
        /// <param name="transactions"></param>
        /// <returns></returns>
        public static Payment.Paypal.v1.Payment ExecutePayment(string payerId, string paymentId, string token,  List<Payment.Paypal.v1.Transaction> transactions)
        {
            var request = new Dictionary<string, object>();
            request.Add("payer_id", payerId);
            request.Add("transactions", transactions);
            var requestJson = Newtonsoft.Json.JsonConvert.SerializeObject(request);
            // $"execute请求开始";
            var result = PaymentHepler.HttpPost($"{Endpoint}v1/payments/payment/{paymentId}/execute", requestJson, GetHeaders(token));
            // $"execute响应: {paymentId} {result}";
            var response = GetPaypalResult(result);
            return response;
        }

请求支付网关Create Payment

构建请求参数,请求支付网关

        /// <summary>
        /// 构造交易信息
        /// </summary>
        /// <returns></returns>
        public List<Transaction> BuildTransactions()
        {
            var transactions = new List<Transaction>();
            var transaction = new Transaction()
            {
                Custom = orderId.ToString(),
                InvoiceNumber = orderId.ToString(),
                Amount = new Amount()
                {
                    Currency = currencyCode,
                    Total = amount.ToString(),
                    Details = new AmountDetails()
                    {
                        Subtotal = subTotal.ToString(),
                        Insurance = insurancePrice.ToString(),
                        HandlingFee = localReturnService.ToString(),
                        Shipping = selectedFreightPrice.ToString(),
                        ShippingDiscount = shippingDiscount.ToString(),
                    }
                },
            };
            transaction.ItemList = new ItemList()
            {
                Items = items
            };
            
            transaction.ItemList.ShippingAddress = new v1.ShippingAddress()
             {
                 RecipientName = "FirstName" + " " + "LastName",
                 City = "City",
                 CountryCode = "CountryCode",
                 PostalCode = "ZipCode",
                 Phone = "PhoneNumber",
                 Line1 = "Addressline1",
                 Line2 = "Addressline2",
                 State = "Regin",
         	}
            transactions.Add(transaction);
            return transactions;
        }
        /// <summary>
        /// 创建支付请求
        /// </summary>
        /// <returns></returns>
        public static void CreatePayment()
        {
            var returnUrl = "https://xxx.com/xxxx/Return?id=唯一交易号";
              
            var cancelReturnUrl = "https://xxx.com/xxxx/Detail?id=唯一交易号";
            // 构造交易信息
            var transactions = this.BuildTransactions();
            // paypal请求参数
            var paypalRequest = new v1.Payment()
            {
                Intent = "sale", // 支付模式,及时扣款
                RedirectUrls = new RedirectUrls()
                {
                    ReturnUrl = returnUrl, // 同步回调地址
                    CancelUrl = cancelReturnUrl, // 取消跳转页面
                },
                Payer = new Payer()
                {
                    PaymentMethod = "paypal"
                },
                ApplicationContext = new ApplicationContext()
	            {
	                LandingPage = "login", // 着陆页面登录页面
	                ShippingPreference = "SET_PROVIDED_ADDRESS", // 使用传递过去的地址,不能更改
	                UserAction = "commit", // 提交
	            },
                Transactions = transactions,
            };
            var token = PaypalUtils.GetAccessToken();
            // 创建支付请求
            var result = PaypalUtils.CreatePayment(token, paypalRequest);
            if (result?.State?.ToLower() == "created")
            {
                // $"paypal create payment success.";
                return status;
            }
            else
            {
                // $"paypal create payment failed.";
                return status;
            }
        }
        

处理同步回传,执行扣款请求Excute Payment

PayPal页面选择卡支付完后,点击commit,跳转至returnUrl,商城接收到参数:

  1. token
  2. payerId
  3. paymentId
        #region paypal V1同步处理请求
        /// <summary>
        /// 同步执行付款操作
        /// </summary>
        /// <returns></returns>
        public ActionResult Return(string id, string token, string payerId, string paymentId)
        {
               // 验证同步请求
       		   var token = PaypalUtils.GetAccessToken();
       		   // 构造交易信息
               var transactions = this.BuildTransactions();
               // 执行付款操作
               var result = PaypalUtils.ExecutePayment(payerId,  paymentId, token, transactions);
               return Redirect("支付结果页面");
        }
        #endregion

定义异步webhook接口,处理异步逻辑

       #region V1异步处理请求
        /// <summary>
        /// webhook 异步通知处理
        /// https://developer.paypal.com/docs/integration/direct/webhooks/rest-webhooks/#verify-event-notifications
        /// https://developer.paypal.com/docs/integration/direct/customer-disputes/webhooks/?mark=disputed_transactions#webhook-details
        /// </summary>
        /// <returns></returns>
        public ActionResult Notify()
        {
            var orderId = 0;
            string ip = ""
            var requestBody = string.Empty;
            using (var reader = new StreamReader(Request.InputStream))
            {
                requestBody = reader.ReadToEnd();
            }
            try
            {
                var isValid = PaypalUtils.ValidateReceivedEvent(requestHeaders, requestBody);
                if (isValid)
                {
                    // $"paypal notify is start";
                    var request = Newtonsoft.Json.JsonConvert.DeserializeObject<Event<Object>>(requestBody);
                    var resourceType = request?.ResourceType.ToUpper();
                    var eventType = request?.EventType.ToUpper();
                    if (resourceType == "DISPUTE")   // 争议流程
                    {
                        #region 纠纷状态更新
                        var resource = Newtonsoft.Json.JsonConvert.DeserializeObject<Dispute>(request.Resource.ToString());
                        Int32.TryParse(resource?.DisputedTransactions.FirstOrDefault()?.InvoiceNumber, out orderId);
                        var disputeId = resource?.DisputeId; // 纠纷ID
                        var disputeDate = Convert.ToDateTime(resource?.CreateTime); // 纠纷日期
                        var reason = resource?.Reason; // 纠纷类型
                        var disputeCurrencyCode = resource?.DisputeAmount.CurrencyCode; // 纠纷金额币种
                        var disputeAmount = Convert.ToDecimal(resource?.DisputeAmount.Value);  // 纠纷金额
                        var status = resource?.Status?.ToUpper(); // 纠纷状态
                        var disputeOutcome = resource?.DisputeOutcome?.OutcomeCode; // 纠纷处理结果
                        var remark = resource?.Messages?.LastOrDefault()?.Content; // 获取最后一个备注内容
                        var disputeLifeCycleStage = resource?.DisputeLifeCycleStage; // 争议生命周期中的阶段。
                        var disputeChannel = resource?.DisputeChannel;  // 客户提出争议的渠道。
                        var caseState = default(CaseStateEnum);
                        var caseReason = (CaseReasonEnum)Enum.Parse(typeof(CaseReasonEnum), reason);
                        var caseType = this.GetPaypalCaseType(disputeLifeCycleStage, disputeChannel);
                        if (eventType == "CUSTOMER.DISPUTE.CREATED" || eventType == "RISK.DISPUTE.CREATED")
                        {
                            // 创建争议
                            // Todo
                        }
                        else if (eventType == "CUSTOMER.DISPUTE.UPDATED")
                        {
                            switch (status)
                            {
                                case "WAITING_FOR_SELLER_RESPONSE":
                                    // Todo
                                    break;
                                case "WAITING_FOR_BUYER_RESPONSE":
                                    // Todo
                                    break;
                                case "UNDER_REVIEW":
                                    // Todo
                                    break;
                                case "RESOLVED":
                                    // Todo
                                    break;
                                case "OPEN":
                                    // Todo
                                    break;
                                default:
                                    // Todo
                                    break;
                            }
                        }
                        else if (eventType == "CUSTOMER.DISPUTE.RESOLVED")
                        {
                            // 解决争议
                            // Todo
                        }
                        // Todo
                        #endregion
                    }
                    else if (resourceType == "REFUND") // 退款流程
                    {
                        #region 退款状态更新
                        var resource = Newtonsoft.Json.JsonConvert.DeserializeObject<DetailedRefund>(request.Resource.ToString());
                        Int32.TryParse(resource.InvoiceNumber, out orderId);
                        var state = resource?.State.ToUpper();
                        var currency = resource?.Amount.Currency;
                        var amount = Convert.ToDecimal(resource?.Amount?.Total); // 退款总金额 = 总净额 + Paypal费用
                        var receivedAmount = resource?.RefundFromReceivedAmount?.Value; // 总净额
                        var transactionFee = resource?.RefundFromTransactionFee?.Value; // PayPal费用
                        var transactionId = resource?.Id; // 退款交易ID
                        var remark = $"总净额: {receivedAmount}, PayPal费用: {transactionFee}";
                        if (eventType == "PAYMENT.SALE.REFUNDED")
                        {
                            // 退款完成
                            // Todo
                        }
                        #endregion
                    }
                    else if (resourceType == "SALE")  // 交易流程
                    {
                        #region 付款状态更新
                        var resource = Newtonsoft.Json.JsonConvert.DeserializeObject<Sale>(request.Resource.ToString());
                        Int32.TryParse(resource.InvoiceNumber, out orderId);

                        var state = resource?.State.ToUpper();
                        var currency = resource?.Amount.Currency;
                        var amount = Convert.ToDecimal(resource?.Amount?.Total);
                        var transactionId = resource?.Id;
                        var paymentMethod = PaymentMethod.Paypal;
                        var remark = $"{eventType}";
                        if (eventType == "PAYMENT.SALE.COMPLETED")
                        {
                            // 付款完成
                            // Todo
                        }
                        else if (eventType == "PAYMENT.SALE.PENDING")
                        {
                            // 付款pending, 电子支票
                            // Todo
                        }
                        else if (eventType == "PAYMENT.SALE.DENIED")
                        {
                            // 付款拒绝
                            // Todo
                        }
                        #endregion
                    }
                }
                else
                {
                    // 验签失败
                    // pay paypal notify valid SignMsg is failed.
                    return Content("Success");
                }
            }
            catch (Exception ex)
            {
                //paypal notify, ex: ex.ToString()

            }
            return Content("Success");
        }

至此,PayPal对接创建请求支付,同步回传处理,异步回传处理逻辑代码示例完成,大家可以试一试了

参考PayPal支付流程介绍
上一篇

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值