欢迎光临
我们一直在努力

c# .net 实现微信支付jsapi v3接口(JSAPI下单)

ziyoukeji阅读(62)

本文章记录一下通过.net 实现微信支付调用jsapi v3接口  统一下意接口

接口地址 :

https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi

需要引用

using System.Security.Cryptography;

 

发起请求、加密、签名代码如下

 /// <summary>
    /// 
    /// </summary>
    /// <param name="url">微信的接口地址</param>
    /// <param name="postData">post请求的数据,json格式 </param>
    /// <param name="privateKey">apiclient_key.pem中的内容,不要-----BEGIN PRIVATE KEY-----  -----END PRIVATE KEY-----</param>
    /// <param name="merchantId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param>
    /// <param name="serialNo">商户证书号</param>
    /// <returns></returns>
    public string postJson(string url, string postData, string privateKey, string merchantId, string serialNo)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "application/json;charset=UTF-8";
        request.UserAgent = "faloo.com/WebRequest";
        request.Accept = "application/json";

        string Authorization = GetAuthorization(url, "POST", postData, privateKey, merchantId, serialNo);
        request.Headers.Add("Authorization", Authorization);
        byte[] paramJsonBytes;
        paramJsonBytes = System.Text.Encoding.UTF8.GetBytes(postData);
        request.ContentLength = paramJsonBytes.Length;
        Stream writer;
        try
        {
            writer = request.GetRequestStream();
        }
        catch (Exception)
        {
            writer = null;
            Console.Write("连接服务器失败!");
        }
        writer.Write(paramJsonBytes, 0, paramJsonBytes.Length);
        writer.Close();
        HttpWebResponse response;
        try
        {
            response = (HttpWebResponse)request.GetResponse();
        }
        catch (WebException ex)
        {
            response = ex.Response as HttpWebResponse;
        }
        Stream resStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(resStream);
        string text = reader.ReadToEnd();
        return text;
    }

    //获取HTTP头的授权信息
    /// <summary>
    /// 
    /// </summary>
    /// <param name="url">微信的接口地址</param>
    /// <param name="method">请求的方式GET,POST,PUT</param>
    /// <param name="jsonParame">post请求的数据,json格式  ,get时传空</param>
    /// <param name="privateKey">apiclient_key.pem中的内容,不要-----BEGIN PRIVATE KEY-----  -----END PRIVATE KEY-----</param>
    /// <param name="merchantId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param>
    /// <param name="serialNo">商户证书号</param>
    /// <returns></returns>
    protected string GetAuthorization(string url, string method, string jsonParame, string privateKey, string merchantId, string serialNo)
    {
        var uri = new Uri(url);
        string urlPath = uri.PathAndQuery;
        string nonce = Guid.NewGuid().ToString();
        var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
        //数据签名     HTTP请求方法\n接口地址的url\n请求时间戳\n请求随机串\n请求报文主体\n
        method = string.IsNullOrEmpty(method) ? "" : method;
        string message = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", method, urlPath, timestamp, nonce, jsonParame);
        string signTxt = Sign(message, privateKey);



        //Authorization和格式
        string authorzationTxt = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"",
            merchantId,
            nonce,
            timestamp,
            serialNo,
            signTxt
            );
        return authorzationTxt;
    }

    protected string Sign(string message, string privateKey)
    {
        byte[] keyData = Convert.FromBase64String(privateKey);
        using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
        using (RSACng rsa = new RSACng(cngKey))
        {
            byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
            return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
        }
    }

 

 

调用接口代码

string merchantId = "";   //商户号
        string serialNo = "";  //证书编号
        string privateKey = @""; // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- 亦不包括结尾的-----END PRIVATE KEY-----

        StringBuilder str = new StringBuilder();
        str.Append("{");
        str.AppendFormat("	\"mchid\": \"{0}\",", merchantId);
        str.AppendFormat("	\"out_trade_no\": \"{0}_{1}\",", DateTime.Now.ToString("yyyyMMddHHmmss"), UserID);
        str.Append("	\"appid\": \"wx3ec325cc846b0202\",");
        str.Append("	\"description\": \"描述如某某充值\",");
        str.Append("	\"notify_url\": \"https://xxx.xxx.com/WXAppPayNotify.aspx\",");  //支付成功后的通知地址 
        str.Append("	\"amount\": {");
        str.AppendFormat("		\"total\": {0},", 1);  //支付金额
        str.Append("		\"currency\": \"CNY\"");
        str.Append("	},");
        str.Append("	\"payer\": {");
        str.Append("		\"openid\": \""+ UserThirdId + "\"");
        str.Append("	}");
        str.Append("}");
        

        string transactionsResponse = postJson("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", str.ToString(), privateKey, merchantId, serialNo);

 

 

 

.net 微信小程序支付成功后通知解密

ziyoukeji阅读(39)

本篇文章记录一下微信小程序支付功能,支付成功后接收通知时的解决代码

1、准备工作,需要引入以下

必须引dll  BouncyCastle.Crypto.dll

using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Engines;
using System.Text;
using Org.BouncyCastle.Crypto.Parameters;

 

2、获取post传过来的body文本

            System.IO.Stream s = HttpContext.Current.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();
            var str = builder.ToString();
//转换成对象实体
WxPayNotifyModel wxPayNotify = Newtonsoft.Json.JsonConvert.DeserializeObject<WxPayNotifyModel>(ReadStr);
//开始解密
AesGcmDecryptByBouncyCastle("这里是商户平台上设置的APIv3密钥", wxPayNotify.resource.nonce, wxPayNotify.resource.ciphertext, wxPayNotify.resource.associated_data)

 

3、c# 加解密相关代码,  支付通知里只需要解密的方法  AesGcmDecryptByBouncyCastle

/// <summary>
    /// 使用BouncyCastle进行AEAD_AES_256_GCM 解密
    /// </summary>
    /// <param name="key">key:32位字符</param>
    /// <param name="nonce">随机串12位</param>
    /// <param name="cipherData">密文(Base64字符)</param>
    /// <param name="associatedData">附加数据可能null</param>
    /// <returns></returns>
    static string AesGcmDecryptByBouncyCastle(string key, string nonce, string cipherData, string associatedData)
    {
        var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

        var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
        var parameters = new AeadParameters(
            new KeyParameter(Encoding.UTF8.GetBytes(key)),
            128,  //128 = 16 * 8 => (tag size * 8)
            Encoding.UTF8.GetBytes(nonce),
            associatedBytes);
        gcmBlockCipher.Init(false, parameters);

        var data = Convert.FromBase64String(cipherData);
        var plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];

        var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
        gcmBlockCipher.DoFinal(plaintext, length);
        return Encoding.UTF8.GetString(plaintext);
    }

    /// <summary>
    /// 使用BouncyCastle进行AEAD_AES_256_GCM 加密
    /// </summary>
    /// <param name="key">key32位字符</param>
    /// <param name="nonce">随机串12位</param>
    /// <param name="plainData">明文</param>
    /// <param name="associatedData">附加数据可能null</param>
    /// <returns></returns>
    static string AesGcmEncryptByBouncyCastle(string key, string nonce, string plainData, string associatedData)
    {
        var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

        var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
        var parameters = new AeadParameters(
            new KeyParameter(Encoding.UTF8.GetBytes(key)),
            128, //128 = 16 * 8 => (tag size * 8)
            Encoding.UTF8.GetBytes(nonce),
            associatedBytes);
        gcmBlockCipher.Init(true, parameters);

        var data = Encoding.UTF8.GetBytes(plainData);
        var cipherData = new byte[gcmBlockCipher.GetOutputSize(data.Length)];

        var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, cipherData, 0);
        gcmBlockCipher.DoFinal(cipherData, length);
        return Convert.ToBase64String(cipherData);
    }

 

4、需要的实体代码

/// <summary>
/// 微信支付结果回调通知实体
/// </summary>
public class WxPayNotifyModel
{
    /// <summary>
    /// 通知的唯一ID
    /// </summary>
    public string id { set; get; }

    /// <summary>
    /// 通知创建时间,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒。
    /// </summary>
    public string create_time { set; get; }

    /// <summary>
    /// 通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS
    /// </summary>
    public string event_type { set; get; }

    /// <summary>
    /// 通知的资源数据类型,支付成功通知为encrypt-resource
    /// </summary>
    public string resource_type { set; get; }

    /// <summary>
    /// 通知资源数据,json格式
    /// </summary>
    public WxPayResourceModel resource { set; get; }

    /// <summary>
    /// 回调摘要
    /// </summary>
    public string summary { set; get; }
}

/// <summary>
/// 微信支付回调通知结果resource实体
/// </summary>
public class WxPayResourceModel
{
    /// <summary>
    /// 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM
    /// </summary>
    public string algorithm { set; get; }

    /// <summary>
    /// Base64编码后的开启/停用结果数据密文
    /// </summary>
    public string ciphertext { set; get; }

    /// <summary>
    /// 附加数据
    /// </summary>
    public string associated_data { set; get; }

    /// <summary>
    /// 原始回调类型,为transaction
    /// </summary>
    public string original_type { set; get; }

    /// <summary>
    /// 加密使用的随机串
    /// </summary>
    public string nonce { set; get; }
}

/// <summary>
/// 微信支付回调通知结果解密实体
/// </summary>
public class WxPayResourceDecryptModel
{
    /// <summary>
    /// 直连商户申请的公众号或移动应用appid
    /// </summary>
    public string appid { set; get; }

    /// <summary>
    /// 商户的商户号,由微信支付生成并下发。
    /// </summary>
    public string mchid { set; get; }

    /// <summary>
    /// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。特殊规则:最小字符长度为6
    /// </summary>
    public string out_trade_no { set; get; }

    /// <summary>
    /// 微信支付系统生成的订单号。
    /// </summary>
    public string transaction_id { set; get; }

    /// <summary>
    /// 交易状态,枚举值:
    /// SUCCESS:支付成功
    /// REFUND:转入退款
    /// NOTPAY:未支付
    /// CLOSED:已关闭
    /// REVOKED:已撤销(付款码支付)
    /// USERPAYING:用户支付中(付款码支付)
    /// PAYERROR:支付失败(其他原因,如银行返回失败)
    /// ACCEPT:已接收,等待扣款
    /// </summary>
    public string trade_state { get; set; }

    /// <summary>
    /// 交易状态描述
    /// </summary>
    public string trade_state_desc { get; set; }

    /// <summary>
    /// 支付者信息
    /// </summary>
    public WxPayerResourceDecryptModel payer { set; get; }

}

/// <summary>
/// 支付用户信息实体
/// </summary>
public class WxPayerResourceDecryptModel
{
    /// <summary>
    /// 用户在直连商户appid下的唯一标识。
    /// </summary>
    public string openid { get; set; }
}

 

异常以及解决方案: 错误:mac check in GCM failed   这个异常不是代码错误是,是解密失败。 特别特别要检查商户平台上设置的APIv3密钥是否正确。检查解密函数中参数是否正确。

注,只要代码编译通过就不会出异常

“/”应用程序中的服务器错误。
mac check in GCM failed
说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: Org.BouncyCastle.Crypto.InvalidCipherTextException: mac check in GCM failed

 

 

 

微信小程序开发工具取消默认打开上一次的项目

ziyoukeji阅读(48)

本篇文章记录一下哪如何设置启动时默认打开上一次的项目的问题

我们使用小程序开发工具的时候,每次一启动,总是打开上一次的项目,总是需要关了再重新打开,那么如何不默认打开上一次项目呢?看下图操作

第一步:打开设置— 通用设置

第二步:去掉打开最后一次修改项目的勾

js数组交差获取所有组合,实现商品多规格或sku的组合

ziyoukeji阅读(59)

本偏文章记录一下用js实现对数组交差获取数组有多少种组合,如商品的中sku或商品规格的组合一样。

在商品中经常有sku的设计 , 如颜色/尺寸等 。 后台控制时需霜选择  需要设置 如:白色_m号 10个  白色XL号 20个等 。

看效果

这里我是对数据进行交叉求出颜色规格的所有组合情况:

先上一段代码

function isArray(obj) {
                if (typeof obj == "object" && obj.constructor === Array) {
                    return true;
                }
                return false;
            }

            function group(arr) {
                if (arr.length === 1) {
                    return arr.shift(); //树最底层的叶节点直接返回
                }
                let start_tree = arr.shift(); //递归取出每一层的所有可能与下层拼接
                let tree = group(arr);
                let skuArr = []
                start_tree.forEach((item, index) => {
                    tree.forEach((item_t, index_t) => {
                        item_t = isArray(item_t) ? item_t : [item_t]
                        skuArr.push([...[item], ...item_t]);
                    })
                })
                return skuArr;
            }

            //示例:
            let arr = [["iphone6", "iphone7"], ["白色", "黑色"], ["64G", "128G"]];
            group(arr);



运行结果如下
0: (3) ["iphone6", "白色", "64G"]
1: (3) ["iphone6", "白色", "128G"]
2: (3) ["iphone6", "黑色", "64G"]
3: (3) ["iphone6", "黑色", "128G"]
4: (3) ["iphone7", "白色", "64G"]
5: (3) ["iphone7", "白色", "128G"]
6: (3) ["iphone7", "黑色", "64G"]
7: (3) ["iphone7", "黑色", "128G"]

 

这样就组合好了  剩下根据自己的业务去存储的数据库就可以了。

 

支付宝小程序关闭保存文件自动编译并刷新模拟器的问题

ziyoukeji阅读(72)

本篇文章记录一下支付宝小程序如何关闭保存文件时自动编译并刷新模拟器的问题

最近开发支付宝小程序,并这保存文件自动编译并刷新模拟器的问题所苦恼,百度又查不到方便,难道就我一人觉得难受?

我翻了好久的官方文档终于找到了解决方案。如下图

 

问题解决,如有帮助点个赞吧

解决ios所有浏览器切换背景时不能及时生效的问题

ziyoukeji阅读(86)

最近再做小说网站,小说阅读页有一个切换背景的功能,我们的背景都是图片,苹果手机切换背景时发现背景色没有变化,或偶尔变化,或多点几次后发生变化。最想了很多解决方案最终找到了解决方案

这是一篇没有代码的解决方案,只能说思路。

原来切换背景时我都是用js切换body的class   值  用不同的class切换 background-image的值

以上方式在苹果就会有问题

解决方案:

我的项目中有5个背景

我把5个背景都单独拿出来 不用body呈现了 , 用5个div去呈现  这里还是用background-image

页面加载时先把div加载出来不过css 样式  opacity: 0;   这个属性让元素透明了 (结果是元素已经完全加载完  只不过是透明的你看不见   不能用display: none😉

当切换背景时只需要把opacity  变成1 如  opacity :1

好了问题解决了

微信小程序css控制iphone底部距离横杠的间距

ziyoukeji阅读(120)

本篇文章记录一下,  做微信小程序时因苹果x以后的手机底部有一个横杠  我们做底部悬浮时会和横杠重叠 的解决方案

原来还需要用js去判断机型来控制样式,现在不用了.直接用wxss控制就好了,直接上代码

padding-bottom: calc(env(safe-area-inset-bottom)/2);

加上面上的样式就会在有横杠的手机上元素会向上移动一段距离

解决IE11以下Array没有find的方法

ziyoukeji阅读(125)

本篇文章记录一下ie11以下array数组不支持find方法的解决方案

扩展find方法
if(!Array.prototype.find){
    Array.prototype.find = function(callback) {
        return callback && (this.filter(callback)|| [])[0];
    };
}

调用demo

//demo
var s = [{"name":"001"},{"name":"002"},{"name":"003"}];
s.find(function(a){
  return a.name=='001'
});

 

display:inline-block的间隙问题和解决办法

ziyoukeji阅读(247)

本篇文章记录一下标签设置display:inline-block后并排存在间隙问题和解决办法

解决方案  给父元素设置 font-size:0  就可以了

/*样式*/
.htb_tags{font-size:0}
.htb_tags a{display: inline-block;}

/*html代码*/
<div class="htb_tags">
                <a href="" title="">摊牌</a>
                <a href="" title="">大唐</a>
                <a href="" title="">西游</a>
                <a href="" title="">摊牌</a>
                <a href="" title="">大唐</a>
                <a href="" title="">西游</a>
                <a href="" title="">摊牌</a>
            </div>

自由技术,自由自在

项目合作联系我们