MVC 微信支付

微信支付方式有好幾種,俺研究了跟本身須要的兩種,即:JS API網頁支付和Native原生支付,這兩個名詞實在是有目的難懂。JS API網頁支付:個人理解是在微信瀏覽器裏面能夠調用微信支付控件的支付方式;Native 原生支付則是在其餘瀏覽器裏面經過掃描二維碼進行支付的方式。相關技術文檔和術語有不少是地球人比較難理解的,須要試驗,我把個人試驗成功貼一下。javascript

第一步,首先在公衆帳號配置微信支付,以下圖,並把微信號添加到測試白名單。建議測試目錄裏面的文字所有小寫,騰訊竟然要求網址的大小寫也要匹配。php

image

第二步,統一下單。就是在微信支付服務後臺生成預支付交易單,返回正確的預支付交易標識後再按掃碼、JSAPI、APP等不一樣場景生成交易串調起支付。html

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using Zmm.Wx.HttpUtility;

namespace Zmm.Wx.MP.TenPay
{
    public class UnifierOrderResult
    {
        public string return_code { get; set; }
        public string return_msg { get; set; }
        public string appid { get; set; }
        public string mch_id { get; set; }
        public string device_info { get; set; }
        public string nonce_str { get; set; }
        public string sign { get; set; }
        public string result_code { get; set; }
        public string err_code { get; set; }
        public string err_code_des { get; set; }
        public string trade_type { get; set; }
        public string prepay_id { get; set; }
        public string code_url { get; set; }

        public override string ToString()
        {
            List<string> items = new List<string>();
            var props = this.GetType().GetProperties();
            foreach (var prop in props)
            {
                var val = prop.GetValue(this);
                if (val != null && !string.IsNullOrEmpty(val.ToString()))
                    items.Add(prop.Name + "=" + val);
            }
            return string.Join(";", items);
        }
        class RequirePara
        {
            public string appid { get; set; }
            public string mch_id { get; set; }
            public string nonce_str { get; set; }
            public string body { get; set; }
            public string detail { get; set; }
            public string attach { get; set; }
            public string out_trade_no { get; set; }
            public int total_fee { get; set; }
            public string spbill_create_ip { get; set; }
            public string notify_url { get; set; }
            public string trade_type { get; set; }
            public string product_id { get; set; }
            public string openid { get; set; }
            public string GetXml()
            {
                List<string> items = new List<string>();
                var props = this.GetType().GetProperties();
                foreach (var prop in props)
                {
                    var val = prop.GetValue(this);
                    if (val != null && !string.IsNullOrEmpty(val.ToString()))
                        items.Add(prop.Name + "=" + val);
                }
                items.Sort();
                StringBuilder sb = new StringBuilder();
                foreach (var item in items)
                    sb.Append(item + "&");
                sb.Append("key=keyvalue");  
                string sign = MD5Util.GetMD5(sb.ToString()).ToUpper();
                return string.Format("<xml>{0}<sign>{1}</sign></xml>", ToXml(), sign);
            }

            string ToXml()
            {
                List<string> items = new List<string>();
                var props = this.GetType().GetProperties();
                foreach (var prop in props)
                {
                    var val = prop.GetValue(this);
                    if (val != null && !string.IsNullOrEmpty(val.ToString()))
                        items.Add(string.Format("<{0}>{1}</{0}>", prop.Name, val));
                }
                items.Sort();
                return string.Join("", items);

            }

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="body">商品描述</param>
        /// <param name="detail">商品詳情</param>
        /// <param name="attach">附加數據,在查詢API和支付通知中原樣返回,該字段主要用於商戶攜帶訂單的自定義數據</param>
        /// <param name="out_trade_no">商戶系統內部的訂單號,32個字符內、可包含字母, 其餘說明見商戶訂單號</param>
        /// <param name="total_fee">訂單總金額,只能爲整數,單位爲分</param>
        /// <param name="spbill_create_ip">APP和網頁支付提交用戶端ip,Native支付填調用微信支付API的機器IP。</param>
        /// <param name="notify_url">接收微信支付異步通知回調地址</param>
        /// <param name="trade_type">交易類型,取值以下:JSAPI,NATIVE,APP,WAP</param>
        /// <param name="product_id">trade_type=NATIVE,此參數必傳。此id爲二維碼中包含的商品ID,商戶自行定義。  </param>
        /// <param name="openid">trade_type=JSAPI,此參數必傳,用戶在商戶appid下的惟一標識。</param>
        /// <returns></returns>
        public static UnifierOrderResult Get(string body, string detail, string attach, string out_trade_no, int total_fee, string spbill_create_ip, string notify_url, string trade_type, string product_id, string openid)
        {

            const string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

            RequirePara para = new RequirePara()
            {
                appid = "appid",
                mch_id = "partnerid",
                nonce_str = DateTime.Now.Ticks.ToString(),
                body = body,
                detail = detail,
                attach = attach,
                out_trade_no = out_trade_no,
                total_fee = total_fee,
                spbill_create_ip = spbill_create_ip,
                notify_url = notify_url,
                trade_type = trade_type,
                product_id = product_id,
                openid = openid
            };



            string retval = HttpUtility.RequestUtility.HttpPost(url, para.GetXml());
            UnifierOrderResult ret = new UnifierOrderResult();
            ret.FillEntityWithXml(retval);
            return ret;
        }
    }

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Zmm.Wx.MP.TenPay
{
    public class TenpayData
    {
        const string appId = "appid";
        public string nonceStr { get; set; }
        public string package { get; set; }
        const string signType = "MD5";
        public string timeStamp { get; set; }
        public string paySign { get; set; }

        public void Execute(string prepay_id)
        {
            nonceStr = DateTime.Now.Ticks.ToString();
            package = "prepay_id=" + prepay_id;
            
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString();

            var str = string.Format("appId={0}&nonceStr={1}&package={2}&signType={3}&timeStamp={4}&key=154f138bf4407a9c07ac7240a2b7135f", appId, nonceStr, package, signType, timeStamp);
            
             
            paySign = MD5Util.GetMD5(str);


        }


    }
}
using System;
using System.Security.Cryptography;
using System.Text;

namespace Zmm.Wx.MP.TenPay
{
    /// <summary>
    /// MD5Util 的摘要說明。
    /// </summary>
    public class MD5Util
    {
        public MD5Util()
        {
            //
            // TODO: 在此處添加構造函數邏輯
            //
        }

        /** 獲取大寫的MD5簽名結果 */
        public static string GetMD5(string encypStr)
        {
            string retStr;
            MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();

            //建立md5對象
            byte[] inputBye;
            byte[] outputBye;

            //使用GB2312編碼方式把字符串轉化爲字節數組.
            try
            {
                inputBye = Encoding.UTF8.GetBytes(encypStr);
            }
            catch 
            {
                inputBye = Encoding.GetEncoding("GB2312").GetBytes(encypStr);
            }
            outputBye = m5.ComputeHash(inputBye);

            retStr = System.BitConverter.ToString(outputBye);
            retStr = retStr.Replace("-", "").ToUpper();
            return retStr;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Zmm.Wx.MP.TenPay
{
    public class OrderResult
    {
        //Help:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_7
        /// <summary>
        /// 返回狀態碼 SUCCESS/FAIL 此字段是通訊標識,非交易標識,交易是否成功須要查看result_code來判斷 
        /// </summary>
        public string return_code { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string return_msg { get; set; }
        public string appid { get; set; }
        public string mch_id { get; set; }
        public string device_info { get; set; }
        public string nonce_str { get; set; }
        public string sign { get; set; }
        public string result_code { get; set; }
        public string err_code { get; set; }
        public string err_code_des { get; set; }
        public string openid { get; set; }
        public string is_subscribe { get; set; }
        public string trade_type { get; set; }
        public string bank_type { get; set; }
        public string CMC { get; set; }
        public string total_fee { get; set; }
        public string fee_type { get; set; }
        public string cash_fee { get; set; }
        public string cash_fee_type { get; set; }
        public string coupon_fee { get; set; }
        public string coupon_count { get; set; }
        //public string coupon_batch_id_$n {get;set;}
        //public string coupon_id_$n {get;set;}
        //public string coupon_fee_$n {get;set;}
        public string transaction_id { get; set; }
        public string out_trade_no { get; set; }
        public string attach { get; set; }
        public string time_end { get; set; }

        public override string ToString()
        {
            List<string> items = new List<string>();
            var props = this.GetType().GetProperties();
            foreach (var prop in props)
            {
                var val = prop.GetValue(this);
                if (val != null && !string.IsNullOrEmpty(val.ToString()))
                    items.Add(prop.Name + "=" + val);
            }
            return string.Join(";", items);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Zmm.Wx.MP.TenPay
{
    public static class XmlToClass
    {
        public static void FillEntityWithXml<T>(this T entity, string xml) where T : /*MessageBase*/ class, new()
        {

            XElement xe = XElement.Parse(xml);
            entity.FillEntityWithXml(xe);

        }
        public static void FillEntityWithXml<T>(this T entity, XElement xe) where T : /*MessageBase*/ class, new()
        {
            entity = entity ?? new T();

            var props = entity.GetType().GetProperties();
            foreach (var prop in props)
            {
                var propName = prop.Name;
                if (xe.Element(propName) != null)
                {
                    prop.SetValue(entity, xe.Element(propName).Value);
                }
            }

        }
    }
}

Action以下:前端

public ActionResult Pay()
      {
          var ret = Zmm.Wx.MP.TenPay.UnifierOrderResult.Get("衢州旅遊特惠卡", "DetailTest", "AttachTest", DateTime.Now.Ticks + "1", 1, "218.244.138.166", "http://www.xuduovip.com/wzf/home/notify", "JSAPI", "Id1", "oLcmxt0gWL5nhdsgPAYB2iBM-IMs");java

          if (ret.return_code == "FAIL")
          {
              return Content(ret.return_msg);
          }
          if (ret.result_code == "FAIL")
          {
              return Content(ret.err_code + "," + ret.err_code_des);
          }api

          Zmm.Wx.MP.TenPay.TenpayData data = new Wx.MP.TenPay.TenpayData();
          data.Execute(ret.prepay_id);
          return View(data);數組

      }瀏覽器

      public ActionResult Notify()
      {微信

          XElement xe = XElement.Load(Request.InputStream);
          OrderResult ret = new Wx.MP.TenPay.OrderResult();
          ret.FillEntityWithXml(xe);
          Zmm.BLL.AppLogService.Info("Notify:" + ret);app

          return Content(@"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
      }

view 以下:

@model Zmm.Wx.MP.TenPay.TenpayData
@{
    ViewBag.Title = "Pay";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Pay</h2>

<script language="javascript" type="text/javascript">
    function onBridgeReady() {
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                "appId": "appid",     //公衆號名稱,由商戶傳入
                "timeStamp": "@Model.timeStamp",         //時間戳,自1970年以來的秒數
                "nonceStr": "@Model.nonceStr", //隨機串
                "package": "@Model.package",
                "signType": "MD5",         //微信簽名方式:
                "paySign": "@Model.paySign"  //微信簽名
            },
            function (res) {
                alert("callback:" + res.err_msg);
                if (res.err_msg == "get_brand_wcpay_request:ok") { }     // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回    ok,但並不保證它絕對可靠。
            }
        );
    }
    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();
    }

</script>

jsapi方式測試成功,主要打開微信網頁的時候必定要所有小寫。

native方式:

action:

var ret = Zmm.Wx.MP.TenPay.UnifierOrderResult.Get("衢州旅遊特惠卡", "DetailTest", "AttachTest", DateTime.Now.Ticks + "1", 20000, "218.244.138.166", "http://www.xuduovip.com/wzf/home/notify", "NATIVE", "Id1", "oLcmxt0gWL5nhdsgPAYB2iBM-IMs");
          ViewBag.Code = Zmm.Common.QRCode.Create(ret.code_url);
          return View();

view:

@{
    ViewBag.Title = "測試網頁";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>公衆號Native支付測試網頁</h2>
<div class="WCPay">
    <a href="@Url.Action("Pay", new {showwxpaytitle=1})">點擊提交可體驗微信支付</a>
    <img src="@ViewBag.Code" />
</div>

二維碼生成類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZXing;
using ZXing.QrCode;
using System.Drawing;
using System.IO;
namespace Zmm.Common
{
    public class QRCode
    {
        public static string Create(string s)
        {
            var options = new QrCodeEncodingOptions
              {
                  DisableECI = true,
                  CharacterSet = "UTF-8",
                  Width = 300,
                  Height = 300
              };
            var writer = new BarcodeWriter();
            writer.Format = BarcodeFormat.QR_CODE;
            writer.Options = options;


            var image = writer.Write(s);

            MemoryStream ms = new MemoryStream(); //新建內存流
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            return "data:image/jpeg;base64," + Convert.ToBase64String(ms.GetBuffer());
        }
    }
}

native測試成功。

 

亂寫,湊合着看。寫文檔確實比較麻煩,也理解騰訊的那些兄弟了。

相關文章
相關標籤/搜索