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開發接入前準備 //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 = "//open.weixin.qq.com/connect/oauth2/authorize?";
private const string Oauth = "//api.weixin.qq.com/sns/oauth2/access_token?";
private const string GetUserInfo = "//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 = $"//打碼/#/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, $"//打碼/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;////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的還是自己來。裏面的各種術語花里花哨的感覺 哈哈。可能是現在的我心裏太多的浮躁了,還是要慢慢靜下心來看。本文出處魏楊楊博客園

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

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