微信支付v3版本NET v3/merchant/media/upload 图片上传

最近在写特约服务商进件的由于微信官方没有DEMO,导致踩了很多坑,特把自己经验分享给大家。

注意几点:

1、上传图片签名不是把所有body内容都进行签名,只需签名计算的请求主体为meta的json串:{ "filename": "filea.jpg", "sha256": "hjkahkjsjkfsjk78687dhjahdajhk" }

2、签名的是私钥, 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- 亦不包括结尾的-----END PRIVATE KEY-----

3、上传参数meta、file、文件名必须新增双引号

4、MultipartFormDataContent 必须添加头文件multipart/form-data,不然一直签名错误。

5、头文件必须添加"user-agent"、"application/json“

代码如下:

上传代码:

string private_key = @"商户私钥";
            string mchid = "商户号";
            string serialNo = "商户API证书序列号";
            string filePath = @"H:\1.jpg";
            string boundary = string.Format("--{0}", DateTime.Now.Ticks.ToString("x"));
            var sha256 = SHAFile.SHA256File(filePath);
            meta meta = new meta()
            {
                sha256 = sha256,
                filename = System.IO.Path.GetFileName(serialNo)
            };
            var json = JsonConvert.SerializeObject(meta);
            var httpHandler = new HttpHandler(mchid, serialNo, private_key, json);
            HttpClient client = new HttpClient(httpHandler);
            using (var requestContent = new MultipartFormDataContent(boundary))
            {
                requestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data"); //这里必须添加
                requestContent.Add(new StringContent(json, Encoding.UTF8, "application/json"), "\"meta\""); //这里主要必须要双引号
                var fileInfo = new FileInfo(filePath);
                using (var fileStream = fileInfo.OpenRead())
                {
                    var content = new byte[fileStream.Length];
                    fileStream.Read(content, 0, content.Length);
                    var byteArrayContent = new ByteArrayContent(content);
                    byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpg");
                    requestContent.Add(byteArrayContent, "\"file\"", "\"" + meta.filename + "\"");  //这里主要必须要双引号
                    using (var response = await client.PostAsync("https://api.mch.weixin.qq.com/v3/merchant/media/upload", requestContent)) //上传
                    using (var responseContent = response.Content)
                    {
                        string responseBody = await responseContent.ReadAsStringAsync(); //这里就可以拿到图片id了
                        return ResultHelper.QuickReturn(responseBody);
                    }
                }
            }

 

SHAFile 类:

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Cloud.Pay.Common
{
    public class SHAFile
    {
        /// 计算文件的 SHA256 值
        /// </summary>
        /// <param name="fileName">要计算 SHA256 值的文件名和路径</param>
        /// <returns>SHA256值16进制字符串</returns>
        public static string SHA256File(string fileName)
        {
            return HashFile(fileName, "sha256");
        }

        /// <summary>
        /// 计算文件的哈希值
        /// </summary>
        /// <param name="fileName">要计算哈希值的文件名和路径</param>
        /// <param name="algName">算法:sha1,md5</param>
        /// <returns>哈希值16进制字符串</returns>
        private static string HashFile(string fileName, string algName)
        {
            if (!System.IO.File.Exists(fileName))
                return string.Empty;

            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            byte[] hashBytes = HashData(fs, algName);
            fs.Close();
            return ByteArrayToHexString(hashBytes);
        }


        /// <summary>
        /// 字节数组转换为16进制表示的字符串
        /// </summary>
        private static string ByteArrayToHexString(byte[] buf)
        {
            string returnStr = "";
            if (buf != null)
            {
                for (int i = 0; i < buf.Length; i++)
                {
                    returnStr += buf[i].ToString("X2");
                }
            }
            return returnStr;
        }


        /// <summary>
        /// 计算哈希值
        /// </summary>
        /// <param name="stream">要计算哈希值的 Stream</param>
        /// <param name="algName">算法:sha1,md5</param>
        /// <returns>哈希值字节数组</returns>
        private static byte[] HashData(Stream stream, string algName)
        {
            HashAlgorithm algorithm;
            if (algName == null)
            {
                throw new ArgumentNullException("algName 不能为 null");
            }
            if (string.Compare(algName, "sha256", true) == 0)
            {
                algorithm = SHA256.Create();
            }
            else
            {
                if (string.Compare(algName, "md5", true) != 0)
                {
                    throw new Exception("algName 只能使用 sha256 或 md5");
                }
                algorithm = MD5.Create();
            }
            return algorithm.ComputeHash(stream);
        }
    }
}

 

 

meta类:

public class meta
    {
        public string filename { get; set; }

        public string sha256 { get; set; }
    }

 

头文件

using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Cloud.Pay.Common
{
    /// <summary>
    /// 头文件
    /// </summary>
    public class HttpHandler : DelegatingHandler
    {
        private readonly string merchantId;
        private readonly string serialNo;
        private readonly string privateKey;
        private readonly string json;
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="merchantId">商户号</param>
        /// <param name="merchantSerialNo">证书序列号</param>
        /// <param name="privateKey"> 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----        亦不包括结尾的-----END PRIVATE KEY-----</param>
        /// <param name="json">签名json数据,默认不需要传入,获取body内容,如传入签名传入参数上传图片时需传入</param>
        public HttpHandler(string merchantId, string merchantSerialNo, string privateKey, string json = "")
        {

            HttpClientHandler handler = new HttpClientHandler();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.SslProtocols = SslProtocols.Tls12;
            try
            {
                string certPath = System.IO.Path.Combine(Environment.CurrentDirectory, @"Cert\apiclient_cert.p12");
                handler.ClientCertificates.Add(new X509Certificate2(certPath, "1487076932",
                    X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet));
            }
            catch (Exception e)
            {
                throw new Exception("ca err(证书错误)");
            }
            handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
            handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
            InnerHandler = handler;
            this.merchantId = merchantId;
            this.serialNo = merchantSerialNo;
            this.privateKey = privateKey;
            this.json = json;
        }

        protected async override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            var auth = await BuildAuthAsync(request);
            string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
            request.Headers.Add("Authorization", value);
            request.Headers.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36");
            MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeader = new MediaTypeWithQualityHeaderValue("application/json");
            request.Headers.Accept.Add(mediaTypeWithQualityHeader);
            request.Headers.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8"));
            return await base.SendAsync(request, cancellationToken);
        }

        protected async Task<string> BuildAuthAsync(HttpRequestMessage request)
        {
            string method = request.Method.ToString();
            string body = "";
            if (method == "POST" || method == "PUT" || method == "PATCH")
            {
                if (!string.IsNullOrEmpty(json))
                    body = json;
                else
                {
                    var content = request.Content;
                    body = await content.ReadAsStringAsync();
                }
            }
            string uri = request.RequestUri.PathAndQuery;
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            string nonce = Guid.NewGuid().ToString("n");
            string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
            string signature = Sign(message);
            return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
        }

        protected string Sign(string message)
        {
            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));
            }
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
微信支付v3 版本相比较 v2 版本有了很多变化和升级,包括接口地址、签名方式、请求参数等等。在 Python 中对接微信支付 v3 接口,需要使用到官方提供的 SDK 和第三方库。 下面是一个简单的对接微信支付 v3 的示例代码: 1. 安装依赖库:需要安装 `wechatpay` 和 `requests` 两个库,可以通过 pip 命令进行安装: ```python pip install wechatpay requests ``` 2. 导入 SDK 和库: ```python import wechatpay import wechatpay.utils as utils import requests ``` 3. 配置商户证书和密钥: ```python merchant_id = '商户号' api_key = 'API密钥' cert_file = 'apiclient_cert.pem' key_file = 'apiclient_key.pem' ``` 4. 使用 SDK 创建支付订单: ```python url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi' nonce_str = utils.generate_random_string(32) timestamp = utils.get_current_timestamp() body = { "mchid": merchant_id, "appid": "应用ID", "description": "商品描述", "out_trade_no": "商户订单号", "amount": { "total": 1, "currency": "CNY" }, "payer": { "openid": "用户openid" } } headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': 'WECHATPAY2-SHA256-RSA2048 ' + wechatpay.get_authorization_header( method='POST', url=url, body=body, merchant_id=merchant_id, api_key=api_key, cert_file=cert_file, key_file=key_file, nonce_str=nonce_str, timestamp=timestamp ) } response = requests.post(url, headers=headers, json=body) ``` 5. 处理返回结果: ```python if response.status_code == 200: result = response.json() prepay_id = result.get('prepay_id') return_data = { 'appId': '应用ID', 'timeStamp': str(timestamp), 'nonceStr': nonce_str, 'package': 'prepay_id=' + prepay_id, 'signType': 'RSA', 'paySign': wechatpay.get_sha256withrsa_signature( timestamp + '\n' + nonce_str + '\n' + 'prepay_id=' + prepay_id + '\n', key_file=key_file ) } else: error_msg = response.json().get('message') return {'error': error_msg} ``` 以上是一个简单的微信支付 v3 对接示例,具体实现还需要根据自己的业务需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云打印-enuny

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值