ASP.NET Core2.0 環境下MVC模式的支付寶PC網站支付接口-沙箱環境開發測試


1.新建.NET Core web項目

2.Controllers-Models-Views 分三個大部分

3.下載安裝最新sdk

官方的SDK以及Demo都還是.NET Framework的,根據官方文檔說明新建網站后還是需要引用官方SDK的源碼,

在這里直接使用網上一位朋友的用.NET Standard 2.0 進行實現了支付寶服務端SDK,Alipay.AopSdk.Core(github:https://github.com/stulzq/Alipay.AopSdk.Core) ,支持.NET CORE 2.0。

為了使用方便以直接使用Nuget下載安裝,直接使用集成的SDK即可,道理和官網支付寶demo一個樣。

通過Nuget安裝:Install-Package Alipay.AopSdk.Core

4.首先要配置支付寶商戶信息 在這里使用的是沙箱賬號

新建一個配置類基本不用但是后續代碼還是可以方便使用。

Config.cs

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Alipay.PCPayment
{
    public class Config
    {
        // 應用ID,您的APPID 沙箱
        public static string AppId= "2016********3";
        /// <summary>
        /// 合作商戶uid 沙箱
        /// </summary>
        public static string Uid= "208**********2";
       
        // 支付寶網關 沙箱地址
        public static string Gatewayurl="https://openapi.alipaydev.com/gateway.do";
        // 支付寶網關 生產地址
        // public static string Gatewayurl = "https://openapi.alipay.com/gateway.do";

          /// <summary>
        /// 異步通知 處理支付寶接口通知返回 獲取是否是支付寶服務器發來的請求的驗證結果
        /// </summary>
        /// <param name="notifyId">通知驗證ID</param>
        /// <returns>驗證結果</returns>
        public static async Task<string> VerifyNotifyAsync(string notifyId)
        {
            return await SendAsync(Uid, notifyId);
        }
        /// <summary>
        ///  //支付寶消息驗證地址
        /// </summary>
        private const string API_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

        /// <summary>
        /// 獲取是否是支付寶服務器發來的請求的驗證結果
        /// </summary>
        /// <param name="partner">partner 合作身份ID</param>
        /// <param name="notify_id">通知驗證ID</param>
        /// <returns>驗證結果</returns>
        public static async Task<string> SendAsync(string partner, string notify_id)
        {
            string strResult;
            string verifyUrl = API_URL + "partner=" + partner + "&notify_id=" + notify_id;
            //獲取遠程服務器ATN結果,驗證是否是支付寶服務器發來的請求
            try
            {
                using (var client = new HttpClient())
                {

                    //client.Timeout = 120000;
                    var response = await client.GetAsync(verifyUrl);
                    if (response.IsSuccessStatusCode)
                    {
                        byte[] data = await response.Content.ReadAsByteArrayAsync();
                         Encoding.UTF8.GetString(data);
                        return strResult= "true";
                    }

                }
            }
            catch (Exception exp)
            {
                strResult = "錯誤信息:" + exp.Message;
            }
            return string.Empty;
        }
 public static ContentResult Response_Success(string msg = null)
        {
            return new ContentResult
            {
                Content = msg ?? "success"
            };
        }
        public static ContentResult ResponseFail(string msg = null)
        {
            return new ContentResult
            {
                Content = msg ?? "fail"
            };
        }
} }

 5. 添加一個控制器 PayController

using System;
using System.Collections.Generic;
using Alipay.AopSdk.AspnetCore;
using Alipay.AopSdk.Core;
using Alipay.AopSdk.Core.Domain;
using Alipay.AopSdk.Core.Request;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Alipay.Demo.PCPayment.Interfaces;
using Microsoft.Extensions.Logging;
namespace Alipay.PCPayment.Controllers
{   
    /// <summary>
    /// PC網站支付
    /// </summary>
    public class PayController : Controller
    {
        private readonly IAlipayService _alipayService;
        private readonly IAccounts _IAccounts;
        private readonly ILogger _logger;
        public PayController(IAlipayService alipayService,  ILogger<PayController> logger)
        {
            _alipayService = alipayService;
            
            _logger = logger;
        }
        //_alipayService.Execute();

        #region 發起支付

        public IActionResult Index()
        {
            return View();
        }
        /// <summary>
        /// 發起支付請求
        /// </summary>
        /// <param name="tradeno">外部訂單號,商戶網站訂單系統中唯一的訂單號</param>
        /// <param name="subject">訂單名稱</param>
        /// <param name="totalAmout">付款金額</param>
        /// <param name="itemBody">商品描述</param>
        /// <returns></returns>
        [HttpPost]
        public void PayRequest(string tradeno, string subject, string totalAmout, string itemBody)
        {   
        //    DefaultAopClient client = new DefaultAopClient(Config.Gatewayurl, Config.AppId, Config.PrivateKey, "json", "2.0",
        //Config.SignType, Config.AlipayPublicKey, Config.CharSet, false);
            // 組裝業務參數model
            AlipayTradePagePayModel model = new AlipayTradePagePayModel
            {
                Body = itemBody,
                Subject = subject,
                TotalAmount = totalAmout,
                OutTradeNo = tradeno,
                ProductCode = "FAST_INSTANT_TRADE_PAY"
            };

            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            // 設置同步回調地址 可以是調試模式地址 並非公網或域名地址 Pay走的是控制器的
            request.SetReturnUrl("http://190.120.120.01:110/Pay/Callback");
            // 設置異步通知接收地址 必須是公網或域名地址 Pay走的是控制器的方法
            request.SetNotifyUrl("http://50.200.50.10:110/Pay/AlipayNotify");
            // 將業務model載入到request
            request.SetBizModel(model);
            var response = _alipayService.SdkExecute(request);
            Console.WriteLine($"訂單支付發起成功,訂單號:{tradeno}");
            //跳轉支付寶支付 支付網關地址
            Response.Redirect(Config.Gatewayurl + "?" + response.Body);
        }
        #endregion
支付異步回調通知 使用異步通知來獲取支付結果,異步通知即支付寶主動請求我們提供的地址,我們根據請求數據來校驗,獲取支付結果。
#region 支付異步回調通知 /// <summary> /// 異步通知即支付寶主動請求我們提供的地址,我們根據請求數據來校驗,獲取支付結果。 /// 支付異步回調通知 需配置域名 因為是支付寶主動post請求這個action 所以要通過域名訪問或者公網ip /// </summary>//public async Task<IActionResult> AlipayNotify([FromForm]Dictionary<string,string> NotifyArray) public async Task<IActionResult> AlipayNotify() { /* 實際驗證過程建議商戶添加以下校驗。 1、商戶需要驗證該通知數據中的out_trade_no是否為商戶系統中創建的訂單號, 2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創建時的金額), 3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email) */ Dictionary<string, string> NotifyArray = GetRequestPost(); //通知驗證ID string notifyId = NotifyArray["notify_id"]; try { if (NotifyArray.Count != 0) { //驗簽以及驗證合作伙伴ID bool flag = _alipayService.RSACheckV1(NotifyArray); if (await Config.VerifyNotifyAsync(notifyId) == "true" && flag) { //交易狀態 if (NotifyArray["trade_status"] == "TRADE_FINISHED" || NotifyArray["trade_status"] == "TRADE_SUCCESS") { if (NotifyArray["app_id"] == Config.AppId) { // 修改支付信息以及狀態 //return await UpdateAliPayAsyn(NotifyArray); } } await Response.WriteAsync("success"); } else { await Response.WriteAsync("fail"); } } } catch (Exception e) { _logger.LogError("Alipay notify fail, {0}", e); } return View(); //string msg = null; //return new ContentResult //{ // Content = msg ?? "fail" //}; } /// <summary> /// 更新支付寶支付結果信息 /// 判斷該筆訂單是否已經做過處理 ///如果沒有做過處理,根據訂單號(out_trade_no)在商戶的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序 ///請務必判斷請求時的total_amount與通知時獲取的total_fee為一致的 ///如果有做過處理,不執行商戶的業務程序 /// </summary> /// <param name="dict"></param> /// <returns></returns> //private async Task<ContentResult> UpdateAliPayAsyn(Dictionary<string, string> dict) //{ // //獲取支付的訂單號 // string msg = null; // var orderNO = await accountsOrder.GetOrderAsync(dict["out_trade_no"]); // if (orderNO == null || !accountsOrder.ReadyOrderStatus(orderNO)) // { // _logger.LogInformation("充值訂單號不存在"); // // return new ContentResult // { // Content = msg ?? "fail" // }; // } // //if (!EqualAmountAliPay(order.PayPrice, dict["total_amount"])) // //{ // // return AliPay.ResponseFail("訂單金額不匹配"); // //} // ////更新訂單支付通知結果 // //if (await accountsOrder.UpdateOrderAsync(order)) // //{ // // await accountsOrder.SaveAliPayNotifyDataAsync(dict); // // _logger.LogInformation("[支付寶]支付成功,系統於 " + dtStartTime.ToString() + " 接收到請求,於 " + dict["notify_time"] + " 完成處理,交易流水號:" + dict["trade_no"] + ",交易單號:" + dict["out_trade_no"], "支付寶日志"); // // return AliPay.ResponseSuccess(); // //} // //else // //{ // // _logger.LogInformation("[支付寶]訂單號:" + dict["out_trade_no"] + "狀態:" + dict["trade_status"], "支付寶日志"); // // if (dict["trade_status"] == "TRADE_CLOSED") // // { // // return AliPay.ResponseSuccess(); // // } // // else // // { //成功 // // if (order.PayStatus == 1) // // { // // _logger.LogInformation("[支付寶]已支付過:" + order.RechargeOrderNO, "支付寶日志"); // // return AliPay.ResponseSuccess(); // // } // // else // // //等待支付 // // { // // _logger.LogInformation("[支付寶]未支付:" + order.RechargeOrderNO, "支付寶日志"); // // return AliPay.ResponseFail("等待支付"); // // } // // } // return new ContentResult // { // Content = msg ?? "fail" // }; // } #endregion

同步回調 同步回調即支付成功跳轉回商戶網站

        #region 支付同步回調

        /// <summary>
        /// 支付同步回調  同步回調即支付成功跳轉回商戶網站
        /// </summary>
        [HttpGet]
        public  IActionResult  Callback()
        {
            /* 實際驗證過程建議商戶添加以下校驗。
            1、商戶需要驗證該通知數據中的out_trade_no是否為商戶系統中創建的訂單號,
            2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創建時的金額),
            3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email)
            4、驗證app_id是否為該商戶本身。
            */
            Dictionary<string, string> sArray = GetRequestGet();
            if (sArray.Count != 0)
            {
                bool flag = _alipayService.RSACheckV1(sArray);
                if (flag)
                {
                    Console.WriteLine($"同步驗證通過,訂單號:{sArray["out_trade_no"]}");
                    ViewData["PayResult"] = "同步驗證通過";
                    Response.Redirect("http://190.120.120.01:110/");
                }
                else
                {
                    Console.WriteLine($"同步驗證失敗,訂單號:{sArray["out_trade_no"]}");
                    ViewData["PayResult"] = "同步驗證失敗";
                }
            }
            return View();
        }

        #endregion

       #region 解析請求參數

        private Dictionary<string, string> GetRequestGet()
        {
            Dictionary<string, string> sArray = new Dictionary<string, string>();

            ICollection<string> requestItem = Request.Query.Keys;
            foreach (var item in requestItem)
            {
                sArray.Add(item, Request.Query[item]);

            }
            return sArray;

        }
        /// <summary>
        /// 獲取返回的請求結果
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, string> GetRequestPost()
        {
            Dictionary<string, string> sArray = new Dictionary<string, string>();

            ICollection<string> requestItem = Request.Form.Keys;
            foreach (var item in requestItem)
            {
                sArray.Add(item, Request.Form[item]);

            }
            return sArray;

        }

        #endregion

       }
} }

 7.支付訂單信息頁面

Index.cshtml 支付請求action POST

@{
    ViewData["Title"] = "PC網站支付";
}
<h2>PC網站支付</h2>
<div class="row">
    <div class="col-sm-12" s>
        <form  asp-action="PayRequest" method="post" class="form-horizontal" role="form">
            <div class="form-group">
                <label for="tradeno" class="control-label col-sm-2">商戶訂單號:</label>
                <div class="col-sm-10">
                    <input type="text" name="tradeno" class="form-control" id="tradeno" value=""/>
                </div>
            </div>
            
            <div class="form-group">
                <label for="subject" class="control-label col-sm-2">訂單名稱:</label>
                <div class="col-sm-10">
                    <input type="text" name="subject" class="form-control" id="subject" value="iPhone X" />
                </div>
            </div>
            
            <div class="form-group">
                <label for="totalAmout" class="control-label col-sm-2">付款金額:</label>
                <div class="col-sm-10">
                    <input type="number" min="0.01" name="totalAmout" class="form-control" id="totalAmout" value="99.99" />
                </div>
            </div>
            
            <div class="form-group">
                <label for="itemBody" class="control-label col-sm-2">商品描述:</label>
                <div class="col-sm-10">
                    <input type="text"  name="itemBody" class="form-control" id="itemBody" value="蘋果手機" />
                </div>
            </div>
            
            <div class="form-group">
                <div class="col-sm-10 col-sm-offset-2">
                    <button class="btn btn-success btn-block">付款</button>
                    <p class="help-block text-center">如果您點擊“付款”按鈕,即表示您同意該次的執行操作。</p>
                </div>
                
            </div>
        </form>
    </div>
    
</div>

<script>
    function GetDateNow() {
        var vNow = new Date();
        var sNow = "";
        sNow += String(vNow.getFullYear());
        sNow += String(vNow.getMonth() + 1);
        sNow += String(vNow.getDate());
        sNow += String(vNow.getHours());
        sNow += String(vNow.getMinutes());
        sNow += String(vNow.getSeconds());
        sNow += String(vNow.getMilliseconds());
        document.getElementById("tradeno").value =  sNow;
    }
    GetDateNow();
</script>

 8.配置系統啟動項目信息

Startup.cs

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            Console.WriteLine(Configuration["Alipay:AlipayPublicKey"]);
            services.AddAlipay(options =>
            {
                options.AlipayPublicKey = Configuration["Alipay:AlipayPublicKey"];
                options.AppId = Configuration["Alipay:AppId"];
                options.CharSet = Configuration["Alipay:CharSet"];
                options.Gatewayurl = Configuration["Alipay:Gatewayurl"];
                options.PrivateKey = Configuration["Alipay:PrivateKey"];
                options.SignType = Configuration["Alipay:SignType"];
                options.Uid = Configuration["Alipay:Uid"];
            }).AddAlipayF2F();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
           
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
          
            
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

appsettings.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Error"
    }
  },
  "WriteTo": [
    "LiterateConsole",
    {
      "Name": "RollingFile",
      "Args": { "pathFormat": "logs\\log-{Date}.txt" }
    }
  ],
    "Alipay": {
    "AlipayPublicKey": "/fVCQx+B+++++HLB7K9yTNoBWBGsOsNpTiErj2wqdyOp8KVSp/5P1",
    "AppId": "2016******03",
    "CharSet": "UTF-8",
    "Gatewayurl": "https://openapi.alipaydev.com/gateway.do",
    "PrivateKey": "/eQ1ykzA5hecyw4K/+/pIFjLm/M/+/vj0gy+eqabgVUjyOLDuEc"\",": null,
    "SignType": "RSA2",
    "Uid": "208********2"
  }
}

9、支付演示
支付請求頁面

 

跳轉到支付寶支付網關沙箱地址

 拿起手機APP沙箱版的進行掃碼支付 或者進行沙箱賬號買家賬戶登錄支付

支付成功提示頁面

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM