微信支付api v2的使用


朋友叫我幫忙開發個demo,讓他可以在此基礎上進一步開發, 因此研究了一下微信支付。 經了解,api v3沒有.net的sdk, 后來下載了v2的.net sdk后發現並不能正常使用, 因此選擇了第三方庫來實現。
選擇的庫也是最有名氣的senparc。經了解調試以后發現senparc所謂的Tenpay.V3類竟然對應的是官方的api v2。 因此才有了本文這個標題。

基本信息了解

  • 安裝senparc時除了安裝基礎庫,還要單獨安裝它的tenpay。
  • 網上senparc的使用教程都講的不是很清楚,有一篇雖講清楚了但是沒有很好的發揮全部功能(作者吃飽了還自己計算簽名?)
  • 同支付寶一樣,微信支付也是先下單(生成預訂單id)再調起支付(需要之前生成的預訂單id和計算簽名)。只不過前端代碼有些區別,而且阿里有完整可用的sdk。
  • senparc的文檔:http://doc.weixin.senparc.com/html/N_Senparc_Weixin_TenPay_V3.htm
  • 官方說v2可以用md5加密。 而v3只能是RSA。

付款的實現

  • 這次給他寫的demo里,沒有分離前后端,而是直接通過code頁生成前端代碼,aspx頁就直接運行它了。
  • 是我自己用的話,應該是封裝一個api負責返回prepay_id、paySign等參數給前端頁面用。 前端頁面在下文。
  • 用到的幾個核心方法都是senparc的, 參數見文檔

首先是后端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Senparc.Weixin.TenPay.V3;

public partial class wxpay_xiadan_Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
		//商品信息和購買人
		string body = Request.QueryString["body"];
		string outTradeNo = Request.QueryString["outTradeNo"];
		string _totalFee = Request.QueryString["totalFee"];
		int totalFee = Convert.ToInt32(_totalFee);
		string openid = Request.QueryString["openid"];

		//通知地址(騰訊會調用這個url,通過這個url來更新我方的訂單信息)
		string notifyUrl = "https://***********";

		//計算簽名的信息
		string appId = "wx9b*******";
		string mchId = "1608*****";
		string ts = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000) + "";
		string key = "z***********************";
		string nonceStr = "e61463f8efa94090b1f366cccfbbb444";

		//准備預訂單的請求數據
		TenPayV3UnifiedorderRequestData unifiedorderRequestData = new TenPayV3UnifiedorderRequestData(
			appId,
			mchId,
			body,
			outTradeNo,
			totalFee,
			Request.ServerVariables["REMOTE_ADDR"],
			notifyUrl,
			0,
			openid,
			key,
			nonceStr,
			null,
			null,
			null,
			null,
			null,
			"CNY",
			null,
			null,
			false,
			null
		);

		//請求下預訂單
		UnifiedorderResult unifiedorderResult = TenPayV3.Unifiedorder(
			unifiedorderRequestData,
			10000
		);

		// Response.Write("<br>prepay_id: " + unifiedorderResult.prepay_id);
		// Response.Write("<br>prepay_id: " + unifiedorderResult.err_code + unifiedorderResult.err_code_des);

		//下預訂單失敗
		if (!String.IsNullOrEmpty(unifiedorderResult.err_code))
        {
			tishi.Text=("發生錯誤: " + unifiedorderResult.err_code);
		}
		else //下預訂單成功
        {
			tishi.Text = "請在彈出窗口中完成付款。 完成后請返回上一頁。";

			//生成簽名
			string paySign = TenPayV3.GetJsPaySign(
				appId,
				ts,
				nonceStr,
				"prepay_id=" + unifiedorderResult.prepay_id,
				key,
				"md5"
			);

			//把生成簽名的原參數(除了key)和算出的簽名給前端。 前端自己再拼js代碼來拉起付款。
			vars.Text = @"
			<script>
				var appId='" + appId + @"';
				var ts='" + ts + @"';
				var nonceStr='" + nonceStr + @"';
				var prepay_id='" + unifiedorderResult.prepay_id + @"';
				var paySign='" + paySign + @"';
			</script>
			";

		}


	}
}

然后是前端(注意很多代碼是騰訊規定必須這么寫的,比如WeixinJSBridge.invoke這塊

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="wxpay_xiadan_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<asp:Literal id="vars" runat="server" />
<script>
    function onBridgeReady() {
        WeixinJSBridge.invoke('getBrandWCPayRequest', {
            "appId": appId,     //公眾號ID,由商戶傳入     
            "timeStamp": ts,     //時間戳,自1970年以來的秒數     
            "nonceStr": nonceStr,      //隨機串     
            "package": "prepay_id=" + prepay_id,
            "signType": "RSA",     //微信簽名方式:     
            "paySign": paySign //微信簽名 
        },
            function (res) {
                if (res.err_msg == "get_brand_wcpay_request:ok") {
                    // 使用以上方式判斷前端返回,微信團隊鄭重提示:
                    //res.err_msg將在用戶支付成功后返回ok,但並不保證它絕對可靠。
                }
            });
    }

    function startPay() {

        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();
        }
    }

    startPay();
</script>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Literal id="tishi" runat="server" />
            
        </div>
    </form>
</body>
</html>

接收通知

  • v2接收到的通知是xml格式,而v3是json
  • 除了格式區別, 還有就是v3的json中一部分是加密的。而v2只需驗簽,內容不加密。
  • BouncyCastle暫時忽略,是給v3解密數據用的
  • 這份通知接收的代碼, 原型的senparc官方demo(比如怎么驗簽和判斷是否訂單付款是否成功,以及最后返回xml給微信)

具體代碼

using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Dapper;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using System.Configuration;
using Senparc.Weixin.TenPay.V3;



public partial class yyt_wxpay_Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        

        try
        {
            ResponseHandler resHandler = new ResponseHandler(null);

            string return_code = resHandler.GetParameter("return_code");
            string return_msg = resHandler.GetParameter("return_msg");
            string key = "z8r***************";

            bool paySuccess = false;
            resHandler.SetKey(key);

            //驗證請求是否從微信發過來(安全)並設定成功或失敗的標識
            if (resHandler.IsTenpaySign() && return_code.ToUpper() == "SUCCESS")
            {
                paySuccess = true;
            }
            else
            {
                paySuccess = false;
                rt.Text="驗簽失敗";
            }

            if (paySuccess)
            {

                #region 記錄到數據庫
                try
                {
                    //此處寫更新自己訂單數據的代碼

                    //返回給騰訊看
                    string xml = string.Format(@"<xml>
    <return_code><![CDATA[{0}]]></return_code>
    <return_msg><![CDATA[{1}]]></return_msg>
    </xml>", return_code, return_msg);
                    rt.Text = xml;

                }
                catch(Exception err)
                {
                    rt.Text = err.ToString();
                }
                #endregion


            }
            else
            {
            }


        }
        catch (Exception ex)
        {
            rt.Text=ex.ToString();
        }

        
    }

    //這個class備用,v3需要它來解密數據
    public class AesGcm
    {
        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 = "z8*************";

        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);
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM