官網鏈接
商戶可以通過以下指引入駐成為小微商戶:
-
步驟一:服務商通過申請入駐接口提交你的小微商戶入駐申請
-
步驟二:服務商通過查詢申請狀態接口查詢你的申請結果(建議提交申請后5分鍾再進行查詢)
-
步驟三:小微商戶本人簽署協議(只能本人微信號掃碼簽約)
-
提醒:你可能需要的開戶銀行對照表、省市區編碼可前往對照表查看
根據官網鏈接的指引,點開申請入駐頁面,可以看到需要證書,需要加密,需要簽名 所以先從證書相關開始
1.注冊並下載安裝證書
首先進入官網登錄下,點擊->賬戶中心->api安全 ,下載API證書,注意:下載之后如果有升級證書按鈕直接點擊升級,否則后續調用接口會失敗 ;下載之后再服務器上安裝,密碼默認為商戶號
- 安裝完成后設置APIv3密鑰和api秘鑰,否則調用證書不能成功
申明:本案例參考博客園前輩(我們的家鄉)的文章傳送門 ;Github地址 WxPayAPI.dll地址
簽名
1、簽名算法
(簽名校驗工具)
簽名生成的通用步驟如下:
第一步,設所有發送或者接收到的數據為集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特別注意以下重要規則:
◆ 參數名ASCII碼從小到大排序(字典序);
◆ 如果參數的值為空不參與簽名;
◆ 參數名區分大小寫;
◆ 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值作校驗。
◆ 微信接口可能增加字段,驗證簽名時必須支持增加的擴展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,並對stringSignTemp進行MD5運算,再將得到的字符串所有字符轉換為大寫,得到sign值signValue。
◆ key設置路徑:微信商戶平台(pay.weixin.qq.com)-->賬戶設置-->API安全-->密鑰設置
具體說明查看官網文檔
- 下載WxPayAPIc#項目,找到
WxPayData.cs
類,修改CalcHMACSHA256Hash
方法
private string CalcHMACSHA256Hash(string plaintext, string salt)
{
string result = "";
var enc = Encoding.UTF8; //官網下載為 Encoding.Default,需要修改為UTF8;
byte[]
baText2BeHashed = enc.GetBytes(plaintext),
baSalt = enc.GetBytes(salt);
System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray()).ToUpper();//這里需要轉換為大寫
}
模型類
public class weixinConfig
{
//=======【基本信息設置】=====================================
/* 微信公眾號信息配置
* APPID:綁定支付的APPID(必須配置)
* MCHID:商戶號(必須配置)
* KEY:商戶支付密鑰,參考開戶郵件設置(必須配置),請妥善保管,避免密鑰泄露
* APPSECRET:公眾帳號secert(僅JSAPI支付的時候需要配置),請妥善保管,避免密鑰泄露
*/
public static string AppID
{
get;set;
}
public static string MchID { get; set;}
/// <summary>
/// api秘鑰 支付秘鑰
/// </summary>
public static string Key { get; set; }
public static string AppSecret
{
get;set;
}
//=======【證書路徑設置】=====================================
/* 證書路徑,注意應該填寫絕對路徑(僅退款、撤銷訂單時需要)
* 1.證書文件不能放在web服務器虛擬目錄,應放在有訪問權限控制的目錄中,防止被他人下載;
* 2.建議將證書文件名改為復雜且不容易猜測的文件
* 3.商戶服務器要做好病毒和木馬防護工作,不被非法侵入者竊取證書文件。
*/
public static string SSlCertPath { get; set; }
public static string DerCertPath { get; set; }
public static string SSlCertPassword
{
get; set;
}
/// <summary>
/// apiv3秘鑰
/// </summary>
/// <value>
/// The cert key.
/// </value>
///
public static string CertKey { get; set; }
/// <summary>
/// 主要用來平台證書解密成密鑰
/// </summary>
/// <value>
/// The encrypt certificate.
/// </value>
public static encrypt_certificate encrypt_certificate { get; set; }
/// <summary>
/// 平台證書序列號
/// </summary>
/// <value>
/// The serial no.
/// </value>
public static string serial_no { get; set; }
}
public class data_certificates//: result
{
public List<data_certificate> data { get; set; }
}
public class data_certificate
{
public string serial_no { get; set; }
public string effective_time { get; set; }
public string expire_time { get; set; }
public encrypt_certificate encrypt_certificate { get; set; }
}
/// <summary>
/// 證書相關
/// </summary>
public class encrypt_certificate
{
public string algorithm { get; set; }
public string nonce { get; set; }
public string associated_data { get; set; }
public string ciphertext { get; set; }
}
獲取證書序列號
string url = "https://api.mch.weixin.qq.com/risk/getcertficates";
WxPayDataToXiaoWei inputObj = new WxPayDataToXiaoWei();
inputObj.SetValue("mch_id", weixinConfig.MchID);
inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));
inputObj.SetValue("sign_type", "HMAC-SHA256");
inputObj.SetValue("sign", inputObj.MakeSign(weixinConfig.Key));
var respones = WeixinXiaoweiService.Post(inputObj.ToXml(), url, false, true, 1000);
try
{
//獲取證書序列號
inputObj = new WxPayDataToXiaoWei();
inputObj.FromXml(respones);
if (inputObj.GetValue("return_code").ToString() == "SUCCESS"
&& inputObj.GetValue("result_code").ToString() == "SUCCESS")
{
var certificates = inputObj.GetValue("certificates").ToString();//獲取證書信息
var data = JsonConvert.DeserializeObject<dynamic>(certificates);
var obj = JsonConvert.DeserializeObject<List<dynamic>>(data.data.ToString());
weixinConfig.serial_no = obj[0].serial_no.ToString();//證書序列號
weixinConfig.encrypt_certificate = JsonConvert.DeserializeObject<encrypt_certificate>(obj[0].encrypt_certificate.ToString());
}
return ProtocolManager.GetPackage(PackageReturnCode.OK, "獲取成功!");
}
catch (Exception ex)
{
LogHelper.Error("獲取證書失敗:" + ex.ToString());
return ProtocolManager.GetPackage(PackageReturnCode.Fail, "獲取失敗!");
}
說明: sign簽名方法是
inputObj.MakeSign(weixinConfig.Key)
,//注:key為商戶平台設置的密鑰key;WeixinXiaoweiService.Post(inputObj.ToXml(), url, false, true, 1000);
這個方法是下載的微信官方Demo中的HttpService類中,下面是修改的
public static string Post(string body, string url, bool isUseCert,bool isxml, int timeout)
{
string result = "";//返回結果
HttpWebRequest request = null;
HttpWebResponse response = null;
Stream reqStream = null;
try
{
//設置最大連接數
ServicePointManager.DefaultConnectionLimit = 200;
//設置https驗證方式
if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(CheckValidationResult);
}
/***************************************************************
* 下面設置HttpWebRequest的相關屬性
* ************************************************************/
request = (HttpWebRequest)WebRequest.Create(url);
request.UserAgent = USER_AGENT;
request.Method = "POST";
request.Timeout = timeout * 1000;
//設置代理服務器
//WebProxy proxy = new WebProxy(); //定義一個網關對象
//proxy.Address = new Uri(WxPayConfig.PROXY_URL); //網關服務器端口:端口
//request.Proxy = proxy;
//設置POST的數據類型和長度
request.ContentType = isxml? "text/xml": "application/json";
byte[] data = System.Text.Encoding.UTF8.GetBytes(body);
request.ContentLength = data.Length;
//是否使用證書
if (isUseCert)
{
// X509Certificate2 cert = new X509Certificate2(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"WxXiaoWeiCert\apiclient_cert.p12"), weixinConfig.SSlCertPassword);
X509Certificate2 cert = new X509Certificate2(weixinConfig.SSlCertPath,weixinConfig.SSlCertPassword);
request.ClientCertificates.Add(cert);
LogHelper .Debug("WxPayApi", "PostXml used cert");
}
//往服務器寫入數據
reqStream = request.GetRequestStream();
reqStream.Write(data, 0, data.Length);
reqStream.Close();
//獲取服務端返回
response = (HttpWebResponse)request.GetResponse();
//獲取服務端返回數據
StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
result = sr.ReadToEnd().Trim();
sr.Close();
}
catch (System.Threading.ThreadAbortException e)
{
LogHelper.Error("HttpService", "Thread - caught ThreadAbortException - resetting.");
LogHelper.Error("Exception message: {0}", e.Message);
System.Threading.Thread.ResetAbort();
}
catch (WebException e)
{
LogHelper.Error("HttpService", e.ToString());
if (e.Status == WebExceptionStatus.ProtocolError)
{
LogHelper.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
LogHelper.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
}
throw new Exception(e.ToString());
}
catch (Exception e)
{
LogHelper.Error("HttpService", e.ToString());
throw new Exception(e.ToString());
}
finally
{
//關閉連接和流
if (response != null)
{
response.Close();
}
if (request != null)
{
request.Abort();
}
}
return result;
}