微信掃碼支付(Asp.Net MVC)

風靈使發表於2018-09-14

這裡的掃碼支付指的是PC網站上面使用微信支付,也就是官方的模式二,網站是Asp.net MVC,整理如下。(demo在最下方)

一、準備工作

使用的微信API中的統一下單方法,關鍵的引數是‘公眾賬號ID(appid)’,‘商戶號(mch_id)’和’商戶支付金鑰(KEY)‘,所以首先要有一個稽核過的公眾號,並開通支付功能,然後申請商戶,通過稽核後得到商戶號,也就是商戶平臺的登入名。商戶支付金鑰是用來簽名的,確保url不被篡改。進入商戶平臺後在API安全中設定,是一個32位的字串。
這裡寫圖片描述
有這三個引數後,還有一點要注意的是交易起始時間和交易結束時間的間隔應該在五分鐘以上2小時以內。不然獲取支付url的時候回報錯。

二、生成支付二維碼

有了上面的引數,接下來就是下載SDK: .net SDK及示例

可惜官方的這個示例一開始並不能執行正確。把相關dll引用MVC目錄下。並建立一個WxPayAPI資料夾把相關類複製過來。
這裡寫圖片描述

這裡寫圖片描述
然後將WxPayConfig中的相關引數設定成自己的引數,再修改GetPayUrl方法,

public string GetPayUrl(Order order,string ip)
        {
            if (order == null)
            {
                throw new ArgumentNullException("order");
            }

            var product = order.OrderItems.First();
            WxPayData data = new WxPayData();
            data.SetValue("appid", WxPayConfig.APPID);
            data.SetValue("mch_id", WxPayConfig.MCHID);
            // data.SetValue("device_info", "iphone4s");
            data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());
            data.SetValue("body", product.AttributeDescription);//商品描述
            data.SetValue("detail", product.AttributeDescription);//商品描述
            data.SetValue("attach", "北京分店");//附加資料
            data.SetValue("out_trade_no", order.TradeNumber);//隨機字串
           // data.SetValue("total_fee", Convert.ToInt32(order.OrderTotal * 100));//總金額
            data.SetValue("total_fee", 1);//總金額
            data.SetValue("spbill_create_ip",ip);//總金額
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始時間
            data.SetValue("time_expire", DateTime.Now.AddMinutes(30).ToString("yyyyMMddHHmmss"));//交易結束時間
            data.SetValue("goods_tag", "智慧嬰兒床");//商品標記
            data.SetValue("notify_url", "http://www.xxxx.com/Checkout/ResultNotify");//通知地址
            data.SetValue("trade_type", "NATIVE");//交易型別
            data.SetValue("product_id", product.ProductId);//商品ID  
            data.SetValue("sign", data.MakeSign());//簽名
            Logger.Info("獲得簽名" + data.GetValue("sign"));

            WxPayData result = WxPayApi.UnifiedOrder(data);//呼叫統一下單介面
            Logger.Info(result.ToJson());
            string url = result.GetValue("code_url").ToString();//獲得統一下單介面返回的二維碼連結
            Logger.Info("pay url:" + url);
            return url;
        }

TradeNumber是呼叫WxPayApi.GenerateOutTradeNo() 方法生成的,notify_url是使用者支付之後微信通知的地址。金額的單位是,只能傳int型或string型,decimal需要轉換一下。獲取url成功後,在負責支付的控制器中建立一個payment方法。用於顯示二維碼:

 public ActionResult Payment(string guid)
        {
            if(string.IsNullOrEmpty(guid)) 
                throw new ArgumentException("guide");

            var order = _orderService.GetOrderByGuid(new Guid(guid));var user = _workContext.CurrentUser;
            NativePay nativePay = new NativePay();
            string url2 = nativePay.GetPayUrl(order, user.LastIpAddress);
            ViewBag.QRCode = "/Checkout/MakeQRCode?data=" + HttpUtility.UrlEncode(url2);
            ViewBag.Order = order;

            return View();
        }

這裡只是返回了一個url,在頁面上:

<img src="@ViewBag.QRCode" class="qrcode"  />

後臺用的qrCodeEncoder生成二維碼。

  public FileResult MakeQRCode(string data)
        {
            if (string.IsNullOrEmpty(data)) 
                throw new ArgumentException("data");

            //初始化二維碼生成工具
            QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
            qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
            qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
            qrCodeEncoder.QRCodeVersion = 0;
            qrCodeEncoder.QRCodeScale = 4;

            //將字串生成二維碼圖片
            Bitmap image = qrCodeEncoder.Encode(data, Encoding.Default);

            //儲存為PNG到記憶體流  
            MemoryStream ms = new MemoryStream();
            image.Save(ms, ImageFormat.Jpeg);

            return File(ms.ToArray(), "image/jpeg");
        }

成功之後得到支付頁面:
這裡寫圖片描述

掃碼後跳出支付頁面:
這裡寫圖片描述

三、回撥

使用者支付之後,微信會給之前預留的介面(介面不能帶引數)發訊息, 網站在收到訊息後進行驗證和確認,確定之後再給微信發一個訊息。詳細引數和文件請看官方API

這裡還是把demo中的方法稍作改動放到了控制器裡面:

  public ActionResult ResultNotify()
        {
            //接收從微信後臺POST過來的資料
            Stream s = Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            s.Flush();
            s.Close();
            s.Dispose();

            Logger.Info(this.GetType()+ "Receive data from WeChat : " + builder);
            //轉換資料格式並驗證簽名
            WxPayData data = new WxPayData();
            try
            {
                data.FromXml(builder.ToString());
            }
            catch (WxPayException ex)
            {
                //若簽名錯誤,則立即返回結果給微信支付後臺
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", ex.Message);
                Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
                Response.Write(res.ToXml());
                Response.End();
            }
            Logger.Info(this.GetType()+ "Check sign success");


            ProcessNotify(data);

            return View();
        }
        public void ProcessNotify(WxPayData data)
        {
            WxPayData notifyData = data;

            //檢查支付結果中transaction_id是否存在
            if (!notifyData.IsSet("transaction_id"))
            {
                //若transaction_id不存在,則立即返回結果給微信支付後臺
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "支付結果中微信訂單號不存在");
                Logger.Error(this.GetType()+"The Pay result is error : " + res.ToXml());
                Response.Write(res.ToXml());
                Response.End();
            }

            string transaction_id = notifyData.GetValue("transaction_id").ToString();

            //查詢訂單,判斷訂單真實性
            if (!QueryOrder(transaction_id))
            {
                //若訂單查詢失敗,則立即返回結果給微信支付後臺
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "訂單查詢失敗");
                Logger.Error(this.GetType()+"Order query failure : " + res.ToXml());
                Response.Write(res.ToXml());
                Response.End();
            }
            //查詢訂單成功
            else
            {
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "SUCCESS");
                res.SetValue("return_msg", "OK");
                Logger.Info(this.GetType()+"order query success : " + res.ToXml());
                SetPaymentResult(data.GetValue("out_trade_no").ToString(), PaymentStatus.Paid);
                Response.Write(res.ToXml());
                Response.End();
            }
        }

收到確認後,我們要更新訂單的狀態:

  public void SetPaymentResult(string tradeno, PaymentStatus status)
        {
            Logger.Info("訂單號:"+tradeno);
            var order = _orderService.GetOrderByTradeNumber(tradeno);
            if (order != null)
            {
                order.PaymentStatus = status;
                if (status == PaymentStatus.Paid)
                {
                    order.PaidDate = DateTime.Now;
                }
                _orderService.UpdateOrder(order);
                Logger.Info("訂單:"+tradeno+"成功更新狀態為"+status);
            }
        }

然後在頁面上檢測訂單的狀態,確定成功後,跳轉頁面。
這裡寫圖片描述
在商戶平臺的後臺,我們可以查詢到:
這裡寫圖片描述
小結:主要過程就是這樣,因為不能本地除錯,打日誌除錯比較耗時,希望對你有幫助。接下來研究下退款(需要證照)。

demo 下載:http://files.cnblogs.com/files/stoneniqiu/WXPayDemo.zip

相關文章