主頁 > .NET開發 > Core3.1 微信v3 JSAPI支付

Core3.1 微信v3 JSAPI支付

2021-01-24 16:43:40 .NET開發

1、前言

  “小魏呀,這個微信支付還要多久?”,“快了快了老板,就等著最后一步了,,,”,“搞快點哈,就等著上線呢”,“.........” 

因公司業務需要微信支付,以前沒弄過花了幾天時間寫了一個微信v3的JSAPI支付,我滴個乖乖,差點今年小孩的奶粉就沒了,還好弄出來了,在這里面各種踩坑,在這里記錄一下,我開發的是微信公眾號上面拉起微信支付,后臺是Core3.1的介面,前端用的是Vue,后面是部署在CentOS上面的

2、寫代碼之前的準備

 你必須要有一個非個人性質的公眾號(服務號),還有一個微信商戶號,服務號申請地址,微信商戶號申請地址,具體的根據網站申請中按人家要求來就行了,個人建議把申請下來的公眾號里面的appid 、appsecret,微信商戶平臺,商戶號等資料保存在資料庫中,

3、公眾號、商戶號配置

    1)、公眾號JS安全域名

登錄公眾號在左手邊選單:公眾號設定---->功能設定------>JS安全域名----->設定,在里面可以連寫5個域名下載檔案上傳到服務器上面 域名要經過ICP備案,可以訪問到上面說的那個檔案就可以了,

core3.1Api 發布后你放根目錄是訪問不到的,在configure里面加上訪問靜態檔案 app.UseStaticFiles();然后在根目錄建一個檔案夾wwwroot 吧域名驗證需要的txt檔案丟進去  我是這么搞點,暫時沒有想到其他騷操作

這里有人要問了 這個設定了是干嘛的,以前我也不知道是干嘛的哈哈,總有一顆好奇的心想知道,現在想想個人理解這個JS安全域名就是一個驗證的機制吧,這里設定了加上微信服務號也有一個類似的,后面就可以呼叫JSAPI支付了,

    2)、

這個緊接著在JS安全域名后面  跟著設定一下就可以了 我部署在CentOS上面 看一下檔案夾目錄,還有一個檔案夾里面是是p12檔案 后面會提到

 

這個網頁授權意思就是后面要獲取到用戶的OpenId的時候 要通過這個域名授權,我們就能獲取到用戶的資訊,授權登錄這些配置,后面圖上還有一個HHhhjZj的檔案這個是商戶號上面設定的,

    3)、微信商戶號設定

在微信商戶平臺上面選擇產品中心---->開發配置,這里面設定支付目錄,我這里是設定的一個 ,我也不是申請商戶號的人 也沒有這個權限 ,上面的界面跟上面兩步驟差不多就不啰嗦了,

 

   4)、微信商戶號的key,V3key設定

這里不再重復 參考微信開發檔案  微信JSAPI開發接入前準備 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_1.shtml 慢慢來哦,設定秘鑰 我倒是自己想著(阿貓阿狗888666)AMAG66688這種來拼夠32位就可以了哈哈,

4、Core3.1后端代碼 詳解

前面這些弄好了只算搭好了環境 下面開始擼碼,微信支付的邏輯就是,獲取用戶的OpenId------->統一下單獲取payId-------------->拉起微信支付------------>支付回呼介面寫邏輯 

下面官網的

 

 

 

 

 

 

 

參考檔案  JSAPI支付

   1)、封裝微信請求類

這里我單獨封裝了一個微信支付的請求類,因為呼叫v3支付必須要符合APIV3介面規則 ,具體的在微信官方檔案看

using App.Common.Base;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace App.Common.HttpHelper
{
    /// <summary>
    /// 請求類封裝   別忘了 呼叫的時候添加 services.AddHttpClient() 呼叫都要加引數,物體類回傳沒有超時時間 
    /// 容器要添加
    /// </summary>
    public class HttpClientFactoryHelper
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private IWebHostEnvironment _webHostEnvironment;
        public HttpClientFactoryHelper(IHttpClientFactory httpClientFactory, IWebHostEnvironment webHostEnvironment)
        {
            _httpClientFactory = httpClientFactory;
            _webHostEnvironment = webHostEnvironment;
        }
       
        public void SaveLog(string text)
        {
            string thisTime = DateTime.Now.ToString("yyyyMMdd");
            var path = _webHostEnvironment.ContentRootPath + $"/ApiInterfaceErrorLog/";//絕對路徑
            string dirPath = Path.Combine(path, thisTime + "/");//絕對徑路 儲存檔案路徑的檔案夾
            if (!Directory.Exists(dirPath))//查看檔案夾是否存在
                Directory.CreateDirectory(dirPath);
            string splitLine = "============================下一條==============================";
            string timeNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            using StreamWriter file = new StreamWriter(dirPath + thisTime + ".txt", true);
            file.WriteLine(timeNow+text);
            file.WriteLine(splitLine);
        }
        #region //微信支付請求
        /// <summary>
        /// 微信請求Post
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="requestString">引數</param>
        /// <param name="privateKey">私有秘鑰 p12檔案</param>
        /// <param name="merchantId">商戶號</param>
        /// <param name="serialNo">商戶證書號</param>
        /// <returns></returns>
        public async Task<string> WeChatPostAsync(string url,string requestString, string privateKey, string merchantId, string serialNo)
        {
            try
            {
                var client = _httpClientFactory.CreateClient();
                var requestContent = new StringContent(requestString);
                requestContent.Headers.ContentType.MediaType = "application/json";
                var auth = BuildAuthAsync(url, requestString, privateKey, merchantId, serialNo,"POST");
                string value = https://www.cnblogs.com/w5942066/p/$"WECHATPAY2-SHA256-RSA2048 {auth}";
                client.DefaultRequestHeaders.Add("Authorization", value);
                client.DefaultRequestHeaders.Add("Accept", "application/json");
                client.DefaultRequestHeaders.Add("User-Agent", "WOW64");
                client.Timeout = TimeSpan.FromSeconds(60);
                var response = await client.PostAsync(url, requestContent);
                if (response.IsSuccessStatusCode)
                {
                    var result = await response.Content.ReadAsStringAsync();
                    return result;
                }
                else
                {
                    return $"介面【{url}】請求錯誤,錯誤代碼{response.StatusCode},錯誤原因{response.ReasonPhrase}具體的話========================================\n{JsonConvert.SerializeObject(response)}";
                }
            }
            catch(Exception ex)
            {
                SaveLog($"介面【{DateTime.Now +url}】請求錯誤,錯誤代碼{ex.Message}具體=============================================/n{ex.StackTrace}");
                return ex.Message + ex.StackTrace;
            }
        }
        /// <summary>
        /// 微信請求
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="requestString">引數</param>
        /// <param name="privateKey">私有秘鑰 p12檔案</param>
        /// <param name="merchantId">商戶號</param>
        /// <param name="serialNo">商戶證書號</param>
        /// <param name="method">Get或者Post</param>
        /// <returns></returns>
        public async Task<string> WeChatGetAsync(string url, string privateKey, string merchantId, string serialNo)
        {
            try
            {
                var client = _httpClientFactory.CreateClient();
                var auth = BuildAuthAsync(url, "", privateKey, merchantId, serialNo,"GET");
                string value = https://www.cnblogs.com/w5942066/p/$"WECHATPAY2-SHA256-RSA2048 {auth}";
                client.DefaultRequestHeaders.Add("Authorization", value);
                client.DefaultRequestHeaders.Add("Accept", "*/*");
                client.DefaultRequestHeaders.Add("User-Agent", "WOW64");
                client.Timeout = TimeSpan.FromSeconds(60);
                var response = await client.GetAsync(url);
                if (response.IsSuccessStatusCode)
                {
                    var result = await response.Content.ReadAsStringAsync();
                    return result;
                }
                else
                {
                    return $"介面【{url}】請求錯誤,錯誤代碼{response.StatusCode},錯誤原因{response.ReasonPhrase}";
                }
            }
            catch (Exception ex)
            {
                SaveLog($"介面【{DateTime.Now + url}】請求錯誤,錯誤代碼{ex.Message}具體=============================================/n{ex.StackTrace}");
                return ex.Message+ ex.StackTrace;
            }
        }
        protected string BuildAuthAsync(string url,string jsonParame ,string privateKey, string merchantId,string serialNo,string method="")
        {
            string body = jsonParame;
            var uri = new Uri(url);
            var urlPath = uri.PathAndQuery;
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            string nonce = Path.GetRandomFileName();

            string message = $"{method}\n{urlPath}\n{timestamp}\n{nonce}\n{body}\n";
            //string signature = Sign(message, privateKey);
            var path = _webHostEnvironment.WebRootPath + "/arsjkll/apiclient_cert.p12";
            string signature = Sign(message,path, "1601849569");
            return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
        }
        /// <summary>
        /// 簽名(CentOs 不支持 換了下面的)
        /// </summary>
        /// <param name="message">簽名內容</param>
        /// <param name="privateKey">秘鑰</param>
        /// <returns></returns>
        public string Sign(string message,string privateKey)
        {
            byte[] keyData =https://www.cnblogs.com/w5942066/p/ Convert.FromBase64String(privateKey);
            using CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob);
            using RSACng rsa = new RSACng(cngKey);
            byte[] data =https://www.cnblogs.com/w5942066/p/ System.Text.Encoding.UTF8.GetBytes(message);
            return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
        }
        /// <summary>
        /// 獲取簽名證書私鑰
        /// </summary>
        /// <param name="priKeyFile">證書檔案路徑</param>
        /// <param name="keyPwd">密碼</param>
        /// <returns></returns>
        private static RSA GetPrivateKey(string priKeyFile, string keyPwd)
        {
            var pc = new X509Certificate2(priKeyFile, keyPwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
            return (RSA)pc.PrivateKey;
        }
        /// <summary>
        //// <summary>
        /// 根據證書簽名資料   后面要做成配置在資料庫中
        /// </summary>
        /// <param name="data">要簽名的資料</param>
        /// <param name="certPah">證書路徑</param>
        /// <param name="certPwd">密碼</param>
        /// <returns></returns>
        public string Sign(string data, string certPah, string certPwd)
        {
            var rsa = GetPrivateKey(certPah, certPwd);

            var rsaClear = new RSACryptoServiceProvider();

            var paras = rsa.ExportParameters(true);
            rsaClear.ImportParameters(paras);

            var signData =https://www.cnblogs.com/w5942066/p/ rsa.SignData(Encoding.UTF8.GetBytes(data), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            return Convert.ToBase64String(signData);
        }
        #endregion

    }
    public class CustomerHttpException : Exception
    {
        public CustomerHttpException() : base()
        { }
        public CustomerHttpException(string message) : base(message)
        {
        }
    }
    public class ReturnData
    {
        /// <summary>
        /// 回傳碼
        /// </summary>
        public int Code { get; set; }
        /// <summary>
        /// 訊息
        /// </summary>
        public string Msg { get; set; }
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSuccess { get; set; }
        /// <summary>
        /// 回傳資料
        /// </summary>
        public object Data { get; set; }
        /// <summary>
        /// 成功默認
        /// </summary>
        /// <param name="data"></param>
        /// <param name="msg"></param>
        public static ReturnData ToSuccess(object data, string msg = "sussess")
        {
            var result = new ReturnData
            {
                Code = (int)HttpStatusCode.OK,
                IsSuccess = true,
                Msg = msg,
                Data = data
            };
            return result;

        }
        public static ReturnData ToFail(string msg)
        {
            var result = new ReturnData
            {
                Code = (int)HttpStatusCode.BadRequest,
                IsSuccess = false,
                Msg = msg
            };
            return result;
        }
        /// <summary>
        /// 例外
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="data"></param>
        public static ReturnData ToError(Exception ex, object data = https://www.cnblogs.com/w5942066/p/null)
        {
            var result = new ReturnData
            {
                Code = (int)HttpStatusCode.InternalServerError,
                IsSuccess = false,
                Msg = "例外" + ex.Message,
                Data = data
            };
            return result;
        }
        /// <summary>
        /// 未經授權
        /// </summary>
        /// <param name="data"></param>
        public static ReturnData ToNoToken(object data = https://www.cnblogs.com/w5942066/p/null)
        {
            var result = new ReturnData
            {
                Code = (int)HttpStatusCode.Forbidden,
                IsSuccess = false,
                Msg = "未經授權不能訪問",
                Data = data
            };
            return result;
        }
        public static ReturnData Instance(object data, string msg, int code)
        {
            var result = new ReturnData
            {
                Code = code,
                IsSuccess = false,
                Msg = msg,
                Data = data
            };
            return result;

        }
    }
}

Sign簽名官方給的只能在IIS上面運行 那是通過直接用私鑰簽名,我在CentOS上面不行

 以前在IIS上面也是 但是這個只要配置IIS應用程式池,把加載用戶組態檔改成true就可以了,CentOS上面就不行了,后來我還是把p12檔案放在了跟驗證域名的那個位置,通過讀取檔案獲取私鑰,這個問題搞了我2天,,,不能跨平臺,或者是我配置不對,后面有時間在研究,

    2)、獲取用戶的OpenId 

在用戶統一下單的時候需要用戶的OpenId就是這個用戶在這個公眾號下面的一個身份號碼,關沒關注獲取了就不會變,所以我就是沒呼叫統一下單之前就獲取了保存在資料庫中,統一下單的時候直接呼叫就可以了 參考連接  公眾號網頁授權

        private const string AuthorUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?";
        private const string Oauth = "https://api.weixin.qq.com/sns/oauth2/access_token?";
        private const string GetUserInfo = "https://api.weixin.qq.com/sns/userinfo?";
        public string GetAuthorizeUrl(string appId, string redirectUrl, string state = "state", string scope = "snsapi_userinfo", string responseType = "code")
        {
            redirectUrl = HttpUtility.UrlEncode(redirectUrl, System.Text.Encoding.UTF8);
            object[] args = new object[] { appId, redirectUrl, responseType, scope, state };
            return string.Format(AuthorUrl + "appid={0}&redirect_uri={1}&response_type={2}&scope={3}&state={4}#wechat_redirect", args);
        }
        private string GetOpenIdUrl(string appId, string secret, string code, string grantType = "authorization_code")
        {
            object[] args = new object[] { appId, secret, code, grantType };
            string requestUri = string.Format(Oauth + "appid={0}&secret={1}&code={2}&grant_type={3}", args);
            return requestUri;
        }
        public GetOpenIdDto GetOpenid(string appId, string secret, string code, string grantType = "authorization_code")
        {
            string requestUri = GetOpenIdUrl(appId, secret, code, grantType);
            var responseStr = _httpClientFactoryHelper.GetAsync(requestUri, null, 120).Result;
            var obj = JsonConvert.DeserializeObject<GetOpenIdDto>(responseStr);
            var getUserInfoUrl = GetUserInfo + $"access_token={obj.Access_Token}&openid={obj.OpenId}&lang=zh_CN";
            var responseUser = _httpClientFactoryHelper.GetAsync(getUserInfoUrl, null, 120).Result;
            SaveLog("OpenDetails", responseUser);
            var objUser = JsonConvert.DeserializeObject<GetOpenIdDto>(responseUser);
           
            
            return objUser;
        }

 

上面GetOpenId就是下面Api這里呼叫的

下面的介面地址就是/api/WeChatPay/SaveHospPatirntOpenId

 /// <summary>
        /// 儲存用戶所在公眾號下面的OpenId
        /// </summary>
        /// <param name="hospCode">醫院|公眾號編碼</param>
        /// <param name="userId">用戶Id(登錄的那個)</param>
        /// <param name="code">微信服務器回傳的code不用填</param>
        /// <returns>跳轉的returnUrl</returns>
        [HttpGet]
        public IActionResult SaveHospPatirntOpenId(string hospCode, int userId, string code = "")
        {
            var modelOpenId = _weChatPayService.IsSaveHospPatientOpenId(hospCode, userId);
            var model = _weChatPayService.GetHospInfo(hospCode);
            var modelNew = _weChatPayService.GetHospNewInfo(hospCode);
           // var path = _configuration.GetValue<string>("ReturnUrl");
            var returnUrl = $"http://打碼/#/subSite?hospCode={model.HospCode}&hospId={modelNew.Id}";
            //var returnUrl = HttpUtility.UrlEncode(returnUrltarget, System.Text.Encoding.UTF8);
            if (modelOpenId != null)
                return Redirect(returnUrl);
            else
            {
               
                if (string.IsNullOrEmpty(code))
                {
                    var redirectUrl = _weChatPayService.GetAuthorizeUrl(model.WxAppid, $"http://打碼/WeChatPay/SaveHospPatirntOpenId?hospCode={hospCode}&userId={userId}");
                    return Redirect(redirectUrl);
                }
                else
                {
                    //根據code和微信引數得到openid
                    var openIdModel = _weChatPayService.GetOpenid(model.WxAppid, model.WxAppsecret, code);
                    //業務處理保存到資料庫
                    var modelOId=_weChatPayService.HospPatirntOpenIdSaveAsync(hospCode, userId, openIdModel).Result;
                    
                    return Redirect(returnUrl);
                }
            }
        }

 

我這里的邏輯就是獲取過了直接資料庫獲取沒有獲取過的微信授權獲取,這里 如果用戶沒有授權實際上這個介面要訪問2次的 第一次code沒有值,第二次微信授權后通過redirect_uri帶著code回來就獲取到了用戶的OpenId資訊,

    3)、統一下單

                    var path = RequestUrl.TRANSACTIONS;//https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
                    var timeOut = DateTime.Now.AddMinutes(10);
                    var address = $"{model.Address}/api/WeChatPay/NotifySuccess";//這個是微信支付狀態回傳發到你的介面上的地址
                    var reqDto = new
                    {
                        appid = model.WxAppid,
                        mchid = model.WxMchid,
                        description = req.Desc,
                        out_trade_no = troNo,
                        //time_expire = timeOut,
                        attach = regOrderModel.RegId.ToString(),
                        notify_url = address,
                        amount = new
                        {
                            total = req.Total,
                            currency = "CNY"
                        },
                        payer = new
                        {
                            openid = modelOpenId.OpenId
                        }
                    };
                    var reqJson = JsonConvert.SerializeObject(reqDto);
                   
                    var ret = _httpClientFactoryHelper.WeChatPostAsync(path, reqJson, model.PrivateKey, model.WxMchid, model.CardNo).Result;
                    var result = JsonConvert.DeserializeObject<JsApiResult>(ret);
                    if (!string.IsNullOrEmpty(result.Prepay_Id))
                    {
                        //時間戳
                        DateTimeOffset dto = new DateTimeOffset(DateTime.Now);
                        var unixTime = dto.ToUnixTimeSeconds().ToString();
                        //亂數
                        var nonMun = Guid.NewGuid().ToString("N").Substring(0, 30);
                        var pck = "prepay_id=" + result.Prepay_Id;
                        //簽名
                        string message = $"{model.WxAppid}\n{unixTime}\n{nonMun}\n{pck}\n";
                        var pathfile = _webHostEnvironment.WebRootPath + "/arsjkll/apiclient_cert.p12";
                        string keyRsa = _httpClientFactoryHelper.Sign(message, pathfile, "密碼咯");
                        //var keyRsa = _httpClientFactoryHelper.Sign(message, model.PrivateKey);
                        //構建JSAPI拉取支付的引數 匿名引數
                        var requestParam = new
                        {
                            appId = model.WxAppid,
                            timeStamp = unixTime,
                            nonceStr = nonMun,
                            package = pck,
                            signType = "RSA",
                            paySign = keyRsa
                        };
                        return Result.ToSuccess(requestParam);
                    }
                    else
                    {
                        return Result.ToFail("prepay_id獲取失敗+++++++" + ret);
                    }

 

 上面統一下單獲取到prepay_id在構造JSAPI拉取微信支付的引數回傳到前端,簽名上面有代碼就不貼了,

    4)、支付回呼介面

 public Result NotifySuccess(NotifyDto ret)
        {
            SaveLog("NotifyParame", JsonConvert.SerializeObject(ret));
            //ResourceASC
            if (ret.Event_type == "TRANSACTION.SUCCESS")//支付成功
            {

                //解密資料報文
                var dataJson = AesGcmHelper.AesGcmDecrypt(ret.Resource.Associated_data, ret.Resource.Nonce, ret.Resource.Ciphertext);
                //轉換物件接受
                var data = https://www.cnblogs.com/w5942066/p/JsonConvert.DeserializeObject(dataJson);
                //獲取當前訂單記錄物體
              //自己的業務邏輯
            }
            else
            {
                SaveLog("NotifyFaile", JsonConvert.SerializeObject(ret));
            }
            return Result.ToSuccess("");
        }
/// <summary>
    /// 支付結果回呼接收引數
    /// </summary>
    public class NotifyDto
    {
        /// <summary>
        /// 通知ID通知的唯一ID  
        /// 示例值:EV-2018022511223320873
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// 通知創建時間  通知創建的時間,遵循rfc3339標準格式,格式為YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出現在字串中,表示time元素的開頭,HH:mm:ss.表示時分秒,TIMEZONE表示時區(+08:00表示東八區時間,領先UTC 8小時,即北京時間),例如:2015-05-20T13:29:35+08:00表示北京時間2015年05月20日13點29分35秒,
        /// 示例值:2015-05-20T13:29:35+08:00
        /// </summary>
        public string Create_time { get; set; }
        /// <summary>
        /// 通知型別  通知的型別,支付成功通知的型別為TRANSACTION.SUCCESS
        /// 示例值:TRANSACTION.SUCCESS
        /// </summary>
        public string Event_type { get; set; }
        /// <summary>
        /// 通知資料型別  通知的資源資料型別,支付成功通知為encrypt-resource
        /// 示例值:encrypt-resource
        /// </summary>
        public string Resource_type { get; set; }
        /// <summary>
        /// 通知資料 通知資源資料
        /// json格式,見示例
        /// </summary>
        public Resource Resource { get; set; }
        /// <summary>
        /// 回呼摘要 
        /// 示例值:支付成功
        /// </summary>
        public string Summary { get; set; }
    }

 解密類

 public class AesGcmHelper
    {
        private static string ALGORITHM = "AES/GCM/NoPadding";
        private static int TAG_LENGTH_BIT = 128;
        private static int NONCE_LENGTH_BYTE = 12;
        private static string AES_KEY = "v3秘鑰";

        public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
        {
            GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            AeadParameters aeadParameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)),
                128,
                Encoding.UTF8.GetBytes(nonce),
                Encoding.UTF8.GetBytes(associatedData));
            gcmBlockCipher.Init(false, aeadParameters);

            byte[] data =https://www.cnblogs.com/w5942066/p/ Convert.FromBase64String(ciphertext);
            byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
            int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
            gcmBlockCipher.DoFinal(plaintext, length);
            return Encoding.UTF8.GetString(plaintext);
        }
    }

 

支付回傳的我保存了日志分享出來看看

 

之前一直沒有調通,花了一天時間 找到了原因 統一下單的時候 里面不是謠傳一個引數  支付金錢 total  我攜程decmial型別了賦值 ,注意這里一定要int  單位是分數,支付提示 (系統繁忙、請稍后再試),一般這個錯誤就是引數不對,型別一定要跟官網對應起來

    5)、Vue前端呼叫拉起支付

<template>
  <div>
    <van-nav-bar
      left-text="回傳"
      left-arrow
      @click-left="clickLeft"
      title="支付訂單"
    />

    <div class="figure">
      <div>支付金額</div>
      <div class="money"><span>{{ total }}</span>
      </div>
      <div style="font-size: 14px">
        請在
        <van-count-down
          :time="countDownTime"
          format="mm分ss秒"
          style="display: inline"
          @finish="countDownFinish"
        />
        內支付完成,超時后訂單自動取消
      </div>
    </div>

    <van-radio-group v-model="radio">
      <van-cell-group>
        <van-cell v-for="el in payType" @click="chkCheck(el)">
          <template #right-icon>
            <svg class="icon" aria-hidden="true">
              <use :xlink:href="el.icon"></use>
            </svg>
            <span style="width: 80px; margin-left: 15px">{{ el.title }}</span>
            <van-radio style="margin-left: 60%" :name="el.name" />
          </template>
        </van-cell>
      </van-cell-group>
    </van-radio-group>

    <div style="margin: 16px">
      <van-button
        round
        block
        type="info"
        native-type="submit"
        @click="onSubmit"
        :disabled="!btnIsable"
      >
        {{ btnText }}
      </van-button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // cardNo: '',
      // desc: '',
      // hospCode: '',
      // pointId: '',//號點Id
      total: '',//支付費用
      countDownTime: 5 * 60 * 1000,//支付剩余毫秒數
      radio: '1',
      appId: '',
      timeStamp: '',
      nonceStr: '',
      package: '',
      signType: '',
      paySign: '',
      btnText: '立即支付',
      btnIsable: true,//支付按鈕是否可用,true:可用 false:不可用
      payType: [{
        icon: '#icon-weixinzhifu',
        title: '微信支付',
        name: '1',
      }],
    }
  },
  methods: {
    onSubmit() {
      let vm = this;
      let obj = {
        "appId": vm.appId,//公眾號名稱
        "timeStamp": vm.timeStamp,//時間戳,自1970年以來的秒數
        "nonceStr": vm.nonceStr,//隨機串
        "package": vm.package,
        "signType": vm.signType,
        "paySign": vm.paySign //簽名 
      };

      function onBridgeReady() {
        WeixinJSBridge.invoke('getBrandWCPayRequest', obj,
          function (res) {
            if (res.err_msg == "get_brand_wcpay_request:ok") {
              // 使用以上方式判斷前端回傳,微信團隊鄭重提示:
              //res.err_msg將在用戶支付成功后回傳ok,但并不保證它絕對可靠,
              vm.btnIsable = false;
              vm.closePage('支付成功');//后期跳轉到掛號記錄頁面
            }
          });
      }
      if (typeof WeixinJSBridge == "undefined") {
        if (document.addEventListener) {
          document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
        } else if (document.attachEvent) {
          document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
          document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
        }
      } else {
        onBridgeReady();
      }
    },
    chkCheck: (el) => {
      return el.name;
    },
    countDownFinish() {
      //倒計時結束銷號,不提交支付
      this.btnIsable = false;
      this.closePage('支付超時');
    },
    clickLeft() {
      this.$dialog.confirm({
        title: '提示',
        message: '支付尚未完成,是否繼續支付',
      })
        .then(() => {
          // on confirm
        })
        .catch(() => {
          this.$router.go(-1);
        });
    },
    getBaseData() {
      // 獲得支付資訊
      let vm = this;
      let p = vm.$route.params;
      if (JSON.stringify(p) != "{}") {
        this.appId = p.appId;
        this.total = p.fee;
        this.nonceStr = p.nonceStr;
        this.package = p.package;
        this.paySign = p.paySign;
        this.countDownTime = p.paymentDeadline - vm.$moment().valueOf();
        this.signType = p.signType;
        this.timeStamp = p.timeStamp;
      } else {
        this.closePage('無效請求');
      }
    },
    closePage(text, num = 5, route = 'home') {
      this.btnIsable = false;
      let lock = setInterval(() => {
        num--;
        this.btnText = `${text},${num}秒后關閉`;
        if (num == 0) {
          clearInterval(lock);
          this.$router.push({ path: route });
        }
      }, 1000);
    },
  },
  mounted() {
    this.getBaseData();
  }
}
</script>

<style>
.figure {
  background: #fff;
  text-align: center;
  margin: 10px auto;
  padding: 10px 0;
  color: gray;
}
.money {
  color: orange;
  font-size: 16px;
  margin: 15px auto;
}
.money span {
  font-size: 36px;
}
</style>

5、總結

紙上得來終覺淺,看著微信官網的,v3支付只有 爪哇 跟 派森 的sdk ,NET的還是自己來,里面的各種術語花里花哨的感覺 哈哈,可能是現在的我心里太多的浮躁了,還是要慢慢靜下心來看,本文出處魏楊楊博客園

原文鏈接我自己貼上https://www.cnblogs.com/w5942066/p/14313946.html

做個內心陽光的人,不憂傷,不心急,堅強、向上,靠近陽光,成為更好的自己,你不需要別人過多的稱贊,因為你自己知道自己有多好,內心的強大,永遠勝過外表的浮華,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/251288.html

標籤:.NET Core

上一篇:使用vs2019遠程除錯部署在linux系統中的.net core程式

下一篇:開發進階:Dotnet Core多路徑異步終止

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more