1. 程式人生 > >Core3.1 微信v3 JSAPI支付

Core3.1 微信v3 JSAPI支付

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 = $"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 = $"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 = Convert.FromBase64String(privateKey);
            using CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob);
            using RSACng rsa = new RSACng(cngKey);
            byte[] data = 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 = 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 = 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 = 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 = JsonConvert.DeserializeObject<ResourceASC>(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 = 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

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