微信支付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