首先要理解微信支付的流程。需二次握手。
先把訂單信息,金額傳給微信,微信返回相應信息,再調用微信支付。
詳細的查看微信公眾號的相關資料
/// <summary>
/// 新建一個普通訂單
/// </summary>
/// <returns></returns>
public Entity NewOrder(Entity en)
{
string WeiXinOpenID = en.GetValue("WeiXinOpenID").TryString();
string AddressID = en.GetValue("AddressID").TryString();
string OrderMessage = en.GetValue("OrderMessage").TryString();
int OrderStatus = (int)OrderEnum.OrderStatus.待付款;
string CommodityList = en.GetValue("CommodityList").TryString();
string CouponRecordGuid = en.GetValue("CouponRecordGuid").TryString(); //優惠券
decimal ManorMoney = en.GetValue("ManorMoney").TryDecimal(); //庄園幣
string OrderGuid = System.Guid.NewGuid().ToString();
try
{
using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope())
{
#region 支付信息
string PayRecordID = System.Guid.NewGuid().TryString();
int PayStatus = (int)OrderEnum.PayStatus.未支付;
string OrderNum = OrderServiceDA.NewOrder(OrderGuid, WeiXinOpenID, AddressID, OriginalTotalPrice, SaleTotalPrice, OrderMessage, OrderStatus);
//添加支付記錄
OrderServiceDA.AddPayRecord(PayRecordID, SaleTotalPrice, WeiXinOpenID, OrderGuid, PayStatus);
decimal WeixinPayMoney = SaleTotalPrice - (ManorMoney + Couponvalue);
if (WeixinPayMoney > 0)
{
int PayType = (int)OrderEnum.PayType.微信支付;
OrderServiceDA.AddPayRecordWeixinDetail(PayRecordID, PayType, WeixinPayMoney, PayStatus);
OrderServiceDA.UpdatePayRecordPayType(PayRecordID, PayType);
WeixinPayService WeixinPayService = new WeixinPayService();
PaymentData payData = new PaymentData();
payData.SetValue("attach", OrderNum);
payData.SetValue("body", "微信在線支付");
payData.SetValue("out_trade_no", OrderNum);
payData.SetValue("trade_type", "JSAPI");
payData.SetValue("total_fee", (WeixinPayMoney * 100).ToInt32());//付款金額,單位分
payData.SetValue("openid", WeiXinOpenID);
PaymentData returnData = WeixinPayService.UnifiedOrder(payData);
string prepay_id = "";
string SignKey = "";
string TimeStamp = "";
string NonceStr = "";
string returnCode = returnData.GetValue("return_code").ToString();
if (returnCode.ToUpper().Equals("SUCCESS"))
{
#region 獲取支付簽名
//獲取支付ID
prepay_id = returnData.GetValue("prepay_id").ToString();
PaymentData jsApiParam = new PaymentData();
NonceStr = WeixinPayService.GenerateNonceStr();
TimeStamp = WeixinPayService.GenerateTimeStamp();
jsApiParam.SetValue("appId", WeixinPayService.AppId);
jsApiParam.SetValue("timeStamp", TimeStamp);
jsApiParam.SetValue("nonceStr", NonceStr);
jsApiParam.SetValue("package", "prepay_id=" + prepay_id);
jsApiParam.SetValue("signType", "MD5");
SignKey = jsApiParam.MakeSign(WeixinPayService.SignKey);
#endregion
}
else
{
string ErrorMessage = returnData.GetValue("return_msg").ToString();
FaultException exception = new FaultException(ErrorMessage);
throw (exception);
}
//保存信息,用於前端再進行調起支付。
OrderServiceDA.UpdateWeixinPayRecord(PayRecordID, prepay_id, SignKey, TimeStamp, NonceStr);
}
else
{
if (ManorMoney == 0)
{
int PayType = (int)OrderEnum.PayType.優惠券;
OrderServiceDA.UpdatePayRecordPayType(PayRecordID, PayType);
}
else
{
int PayType = (int)OrderEnum.PayType.庄園幣;
OrderServiceDA.UpdatePayRecordPayType(PayRecordID, PayType);
}
}
#endregion
scope.Complete();
}
}
catch (Exception ex)
{
Entity result = new Entity(new PropertyCollection());
result.AddSimple("Status", -10, typeof(int));
result.AddSimple("StatusText", ex.Message, typeof(string));
return result;
}
Entity resultEntity = new Entity(new PropertyCollection());
resultEntity.AddSimple("Status", 115, typeof(int));
resultEntity.AddSimple("StatusText", "操作成功.", typeof(string));
resultEntity.AddSimple("OrderGuid", OrderGuid, typeof(string));
return resultEntity;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TalentCloud.Common;
using TalentCloud.Base.Utils;
using TalentCloud.Agriculture.Services;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using TalentCloud.Common.Log;
namespace TalentCloud.Agriculture.Weixin.Services
{
public class WeixinPayService
{
/// <summary>
/// 微信分配的AppId
/// </summary>
public string AppId = TCConfigManager.GetConfig("WeixinPayAppid").TryToString();
/// <summary>
/// 公眾號的支付密鑰appsecret
/// </summary>
public string AppSecret = TCConfigManager.GetConfig("WeixinPayAppsecret").TryToString();
/// <summary>
/// 當前用戶IP
/// </summary>
public string Ip = WCFClientInfo.ClientIP;
/// <summary>
/// 商戶號
/// </summary>
public string MchId = TCConfigManager.GetConfig("MchId").TryToString();
/// <summary>
/// 簽名Key
/// </summary>
public string SignKey = TCConfigManager.GetConfig("SignKey").TryToString();
//=======【證書路徑設置】=====================================
/* 證書路徑,注意應該填寫絕對路徑(僅退款、撤銷訂單時需要)
*/
public string CertPath = TCConfigManager.GetConfig("certPath").TrySafeString();
/// <summary>
/// 證書密碼,windows上可以直接雙擊導入系統,導入過程中會提示輸入證書密碼,證書密碼默認為您的商戶ID
/// </summary>
public string CertPassword = TCConfigManager.GetConfig("certpsd").TrySafeString();
/// <summary>
/// 支付結果通知回調url,用於商戶接收支付結果
/// </summary>
public string NOTIFY_URL =TCConfigManager.GetConfig("WebSite").TryToString()+ "/Weixin/PayCallback.aspx";
/// <summary>
/// 生成隨機串,隨機串包含字母或數字 @return 隨機串
/// </summary>
/// <returns></returns>
public string GenerateNonceStr()
{
return Guid.NewGuid().ToString().Replace("-", "");
}
/// <summary>
/// 生成時間戳,標准北京時間,時區為東八區,自1970年1月1日 0點0分0秒以來的秒數
/// </summary>
/// <returns></returns>
public string GenerateTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// * 統一下單
///* @param WxPaydata inputObj 提交給統一下單API的參數
///* @param int timeOut 超時時間
///* @throws WxPayException
///* @return 成功時返回,其他拋異常
/// <returns></returns>
public PaymentData UnifiedOrder(PaymentData inputObj, int timeOut = 15)
{
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//檢測必填參數
if (!inputObj.IsSet("out_trade_no"))
{
throw new Exception("缺少統一支付接口必填參數out_trade_no!");
}
else if (!inputObj.IsSet("body"))
{
throw new Exception("缺少統一支付接口必填參數body!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new Exception("缺少統一支付接口必填參數total_fee!");
}
else if (!inputObj.IsSet("trade_type"))
{
throw new Exception("缺少統一支付接口必填參數trade_type!");
}
//關聯參數
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
{
throw new Exception("統一支付接口中,缺少必填參數openid!trade_type為JSAPI時,openid為必填參數!");
}
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
{
throw new Exception("統一支付接口中,缺少必填參數product_id!trade_type為JSAPI時,product_id為必填參數!");
}
//異步通知url未設置,則使用配置文件中的url
if (!inputObj.IsSet("notify_url"))
{
inputObj.SetValue("notify_url", NOTIFY_URL);//異步通知url
}
inputObj.SetValue("appid", AppId);//公眾賬號ID
inputObj.SetValue("mch_id", MchId);//商戶號
inputObj.SetValue("spbill_create_ip", Ip);//終端ip
inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串
//簽名
inputObj.SetValue("sign", inputObj.MakeSign(SignKey));
string xml = inputObj.ToXml();
string response = HttpService.Post(xml, url, false, timeOut, "", "");
PaymentData result = new PaymentData();
result.FromXml(response);
return result;
}
/// <summary>
/// 申請退款
/// </summary>
/// <param name="inputObj">提交給申請退款API的參數</param>
/// <param name="CertPath">證書路徑</param>
/// <param name="CertPassword">證書密碼</param>
/// <param name="timeOut">超時時間</param>
/// <returns></returns>
public PaymentData Refund(PaymentData inputObj, int timeOut = 6)
{
string url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
//檢測必填參數
if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
{
throw new Exception("退款申請接口中,out_trade_no、transaction_id至少填一個!");
}
else if (!inputObj.IsSet("out_refund_no"))
{
throw new Exception("退款申請接口中,缺少必填參數out_refund_no!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new Exception("退款申請接口中,缺少必填參數total_fee!");
}
else if (!inputObj.IsSet("refund_fee"))
{
throw new Exception("退款申請接口中,缺少必填參數refund_fee!");
}
else if (!inputObj.IsSet("op_user_id"))
{
throw new Exception("退款申請接口中,缺少必填參數op_user_id!");
}
inputObj.SetValue("appid", AppId);//公眾賬號ID
inputObj.SetValue("mch_id", MchId);//商戶號
inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//隨機字符串
inputObj.SetValue("sign", inputObj.MakeSign(SignKey));//簽名
string xml = inputObj.ToXml();
string response = HttpService.Post(xml, url, true, timeOut, CertPath, CertPassword);
//將xml格式的結果轉換為對象以返回
PaymentData result = new PaymentData();
result.FromXml(response);
return result;
}
/// <summary>
/// 企業付款接口( 開通條件:
/// 1、商戶號已入駐90日
/// 2、商戶號有30天連續正常交易
/// 登錄微信支付商戶平台-產品中心,開通企業付款。)
/// </summary>
/// <param name="inputObj">參數</param>
/// <param name="CertPath">證書路徑</param>
/// <param name="CertPassword">證書密碼</param>
/// <param name="timeOut">超時時間</param>
/// <returns></returns>
public PaymentData Transfers(PaymentData inputObj, int timeOut = 6)
{
string url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
#region 檢測必要參數
//檢測必填參數
if (!inputObj.IsSet("partner_trade_no") && !inputObj.IsSet("partner_trade_no"))
{
throw new Exception("企業付款接口中,缺少必填參數partner_trade_no!");
}
if (!inputObj.IsSet("openid"))
{
throw new Exception("企業付款接口中,缺少必填參數openid!");
}
if (!inputObj.IsSet("check_name"))
{
throw new Exception("企業付款接口中,缺少必填參數check_name!");
}
else
{
string checkName = inputObj.GetValue("check_name").ToString();
switch (checkName)
{
case "FORCE_CHECK":
case "OPTION_CHECK":
if (!inputObj.IsSet("check_name"))
{
throw new Exception("企業付款接口中,缺少必填參數re_user_name!");
}
break;
default:
break;
}
}
if (!inputObj.IsSet("amount"))
{
throw new Exception("企業付款接口中,缺少必填參數amount!");
}
if (!inputObj.IsSet("desc"))
{
throw new Exception("企業付款接口中,缺少必填參數desc!");
}
if (!inputObj.IsSet("spbill_create_ip"))
{
throw new Exception("企業付款接口中,缺少必填參數spbill_create_ip!");
}
#endregion
#region 添加公共參數
inputObj.SetValue("mch_appid", AppId);//公眾賬號ID
inputObj.SetValue("mchid", MchId);//商戶號
inputObj.SetValue("spbill_create_ip", Ip);//隨機字符串
inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//隨機字符串
inputObj.SetValue("sign", inputObj.MakeSign(SignKey));//簽名
#endregion
string xml = inputObj.ToXml();
var start = DateTime.Now;
string response = HttpService.Post(xml, url, true, timeOut, CertPath, CertPassword);
LogHelper.WriteFileLog("TransfersData", response);
PaymentData result = new PaymentData();
result.FromXml(response);
return result;
}
#region 微信發紅發,未事項,簽名失敗問題,未解決
/// <summary>
/// 微信紅包
/// </summary>
public class PayWeiXin
{
public string nonce_str { get; set; }
public string sign { get; set; }
public string mch_billno { get; set; }
public string mch_id { get; set; }
public string wxappid { get; set; }
public string nick_name { get; set; }
public string send_name { get; set; }
public string re_openid { get; set; }
public int total_amount { get; set; }
public int min_value { get; set; }
public int max_value { get; set; }
public int total_num { get; set; }
public string wishing { get; set; }
public string client_ip { get; set; }
public string act_id { get; set; }
public string act_name { get; set; }
public string remark { get; set; }
public string logo_imgurl { get; set; }
public string share_content { get; set; }
public string share_url { get; set; }
public string share_imgurl { get; set; }
}
/// <summary>
/// 調用微信支付接口,發送紅包
/// </summary>
/// <param name="payForWeiXin"></param>
/// <returns></returns>
public string PayRedBag(PaymentData inputObj)
{ ////商戶號
inputObj.SetValue("mch_id", MchId);
//商戶 appid
inputObj.SetValue("wxappid", AppId);
inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串
//簽名
inputObj.SetValue("sign", inputObj.MakeSign(SignKey));
string xml = inputObj.ToXml();
string result = string.Empty;
try
{
result = PostPage("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack", xml);
LogHelper.WriteFileLog("PayRedBagpostData", xml);
LogHelper.WriteFileLog("PayRedBag", result);
}
catch (Exception ex)
{
LogHelper.WriteFileLog("PayRedBagError", ex.Message);
}
return result;
}
/// <summary>
/// post微信請求
/// </summary>
/// <param name="posturl"></param>
/// <param name="postData"></param>
/// <returns></returns>
public string PostPage(string posturl, string postData)
{
Stream outstream = null;
Stream instream = null;
StreamReader sr = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
Encoding encoding = Encoding.UTF8;
byte[] data = encoding.GetBytes(postData);
// 准備請求...
try
{
//CerPath證書路徑
string certPath = TCConfigManager.GetConfig("certPath").TrySafeString();
//證書密碼
string password = TCConfigManager.GetConfig("certpsd").TrySafeString();
X509Certificate2 cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(certPath, password, X509KeyStorageFlags.MachineKeySet);
// 設置參數
request = WebRequest.Create(posturl) as HttpWebRequest;
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = true;
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = data.Length;
request.ClientCertificates.Add(cert);
outstream = request.GetRequestStream();
outstream.Write(data, 0, data.Length);
outstream.Close();
//發送請求並獲取相應回應數據
response = request.GetResponse() as HttpWebResponse;
//直到request.GetResponse()程序才開始向目標網頁發送Post請求
instream = response.GetResponseStream();
sr = new StreamReader(instream, encoding);
//返回結果網頁(html)代碼
string content = sr.ReadToEnd();
string err = string.Empty;
return content;
}
catch (Exception ex)
{
string err = ex.Message;
return string.Empty;
}
}
/// <summary>
/// Md5加密
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static String Encrypt(String s)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s);
bytes = md5.ComputeHash(bytes);
md5.Clear();
string ret = "";
for (int i = 0; i < bytes.Length; i++)
{
ret += Convert.ToString(bytes[i], 16).PadLeft(2, '0');
}
return ret.PadLeft(32, '0');
}
public string RandomStr(string str, int Length)
{
string result = string.Empty;
Random rd = new Random();
for (int i = 0; i < Length; i++)
{
result += str[rd.Next(str.Length)];
}
return result;
}
#endregion
}
}
//彈出微信支付頁面
WeixinJSBridge.invoke('getBrandWCPayRequest',
{
"appId": data.AppID, //公眾號名稱,由商戶傳入
"timeStamp": data.TimeStamp, //時間戳,自1970年以來的秒數
"nonceStr": data.nonceStr, //隨機串
"package": "prepay_id="+data.prepay_id,
"signType": "MD5", //微信簽名方式:
"paySign": data.SignKey //微信簽名
},
function (res) {
switch (res.err_msg) {
case "get_brand_wcpay_request:ok":
layer.open({
content: '充值成功',
skin: 'msg',
time: 2 //2秒后自動關閉
});
browserHistory.push('/WeChat/dist/member/wallet/');
break;
case "get_brand_wcpay_request:cancel":
break;
case "get_brand_wcpay_request:fail":
layer.open({
content: '充值失敗!',
skin: 'msg',
time: 2 //2秒后自動關閉
});
break;
default:
}
});
public partial class PayCallback : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
StreamReader reader = new StreamReader(Request.InputStream, Encoding.UTF8);
string postStr = reader.ReadToEnd();
reader.Close();
XmlDocument doc = new XmlDocument();
doc.LoadXml(postStr);
string ReturnCode = doc.SelectSingleNode("//result_code").InnerText;//獲取支付結果代碼
StringBuilder xmlStr = new StringBuilder();
LogHelper.WriteFileLog("PayCallback", ReturnCode);
if (ReturnCode.ToUpper().Equals("SUCCESS"))
{
string strOrderNo = doc.SelectSingleNode("//out_trade_no").InnerText;
//獲取微信支付流水號
string weChatTransaction_Id = doc.SelectSingleNode("//transaction_id").InnerText;
string WeiXinOpenID = doc.SelectSingleNode("//openid").InnerText;
try
{
//獲取訂單號
WeiXinService WeiXinService = new WeiXinService();
Entity result= WeiXinService.PayOrder(WeiXinOpenID, strOrderNo, weChatTransaction_Id);
string Status = result.GetValue("Status").ToString();
string StatusText = result.GetValue("StatusText").ToString();
LogHelper.WriteFileLog("Pay", WeiXinOpenID + "," + strOrderNo + "," + weChatTransaction_Id + "," + Status + "," + StatusText);
}
catch (Exception ex)
{
LogHelper.WriteFileLog("PayError", ex.Message + "," + WeiXinOpenID + "," + strOrderNo + "," + weChatTransaction_Id);
//記錄錯誤
throw ex;
}
#region 通知微信后台支付成功
Response.ContentType = "text/xml";
xmlStr.AppendLine("<xml>");
xmlStr.AppendFormat("<return_code><![CDATA[SUCCESS]]></return_code>");
xmlStr.AppendFormat("<return_msg><![CDATA[OK]]></return_msg>");
xmlStr.AppendFormat("</xml>");
#endregion
}
else
{
#region 通知微信后台支付失敗
Response.ContentType = "text/xml";
xmlStr.AppendLine("<xml>");
xmlStr.AppendFormat("<return_code><![CDATA[FAIL]]></return_code>");
xmlStr.AppendFormat("<return_msg><![CDATA[支付失敗]]></return_msg>");
xmlStr.AppendFormat("</xml>");
#endregion
}
Response.Write(xmlStr.ToString());
}
