折腾了一天,终于把这个图片接口(https://api.mch.weixin.qq.com/v3/merchant/media/upload)调通了。看到社区里面有不少小伙伴遇到了一样的问题,这里简单分享一下我的测试代码。因为我是用java开发的,所以下面内容的代码示例都是java的。
水平有限,如有错漏之处,请多多指教。
public class uploadFileTest { public static void main(String[] args) { // TODO Auto-generated method stub try { // 换行符 String LINE_END = "\r\n" ; String PREFIX = "--" ; // 定义数据分隔线 String BOUNDARY = "" ; //商户号 String mchid = "" ; //证书序列号 String serial_no = "" ; //商户私钥( 拷贝apiclient_key.pem文件里-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----之间的内容) String rsaPrivateKeyFile = "" ; //微信支付平台公钥文件 String rsaPublicKey = "" ; //时间戳 String timestamp = Long.toString(System.currentTimeMillis()/ 1000 ); //随机数 String nonce_str = "" ; //图片文件 String filePath = "" ; //文件路径 File file = new File(filePath); String filename = file.getName(); //文件名 String fileSha256 = DigestUtils.sha256Hex( //文件sha256值 //拼签名串 StringBuffer sb = new StringBuffer(); sb.append( "POST" ).append( "\n" ); sb.append( "/v3/merchant/media/upload" ).append( "\n" ); sb.append(timestamp).append( "\n" ); sb.append(nonce_str).append( "\n" ); sb.append( "{\"filename\":\"" +filename+ "\",\"sha256\":\"" +fileSha256+ "\"}" ).append( "\n" ); System.out.println( "签名原串:" +sb.toString()); //计算签名 String sign = new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKey))); System.out.println( "签名sign值:" +sign); //拼装http头的Authorization内容 String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" +mchid+ "\",nonce_str=\"" +nonce_str+ "\",signature=\"" +sign+ "\",timestamp=\"" +timestamp+ "\",serial_no=\"" +serial_no+ "\"" ; System.out.println( "authorization值:" +authorization); //接口URL URL url = new URL( "https://api.mch.weixin.qq.com/v3/merchant/media/upload" ); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 设置为POST conn.setRequestMethod( "POST" ); // 发送POST请求必须设置如下两行 conn.setDoOutput( true ); conn.setDoInput( true ); conn.setUseCaches( false ); // 设置请求头参数 conn.setRequestProperty( "Charsert" , "UTF-8" ); conn.setRequestProperty( "Accept" , "application/json" ); conn.setRequestProperty( "Content-Type" , "multipart/form-data; boundary=" + BOUNDARY); conn.setRequestProperty( "Authorization" , authorization); DataOutputStream dos = new DataOutputStream(conn.getOutputStream()); //拼装请求内容第一部分 StringBuilder strSb = new StringBuilder(); strSb.append(PREFIX).append(BOUNDARY).append(LINE_END) .append( "Content-Disposition: form-data; name=\"meta\";" + LINE_END) .append( "Content-Type: application/json; " + LINE_END) .append(LINE_END) // 空行 .append( "{\"filename\":\"" +filename+ "\",\"sha256\":\"" +fileSha256+ "\"}" ) .append(LINE_END); dos.write(strSb.toString().getBytes());
dos.flush(); //拼装请求内容第二部分 StringBuilder fileSbStart = new StringBuilder(); fileSbStart.append(PREFIX).append(BOUNDARY).append(LINE_END) .append( "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename+ "\";" + LINE_END) .append( "Content-Type: image/jpeg" + LINE_END) .append(LINE_END); // 空行 dos.write(fileSbStart.toString().getBytes());
dos.flush(); //文件二进制内容 InputStream is = new FileInputStream(file); byte [] buffer = new byte [ 1024 ]; int len = 0 ; while ((len = is.read(buffer)) != - 1 ){ dos.write(buffer, 0 ,len); } is.close(); //拼装请求内容结尾 StringBuilder fileSbEnd = new StringBuilder(); fileSbEnd.append(LINE_END) .append(PREFIX).append(BOUNDARY).append(PREFIX) .append(LINE_END); dos.write(fileSbEnd.toString().getBytes());
dos.flush(); dos.close(); //接收返回 //打印返回头信息 System.out.println( "接口返回头信息:" ); Map<String, List<String>> responseHeader = conn.getHeaderFields(); for (Map.Entry<String, List<String>> entry : responseHeader.entrySet()) { System.out.println(entry.getKey()+ ":" + entry.getValue()); } //打印返回内容 int responseCode = conn.getResponseCode(); String rescontent = "" ; if ((responseCode+ "" ).startsWith( "2" )){ //成功 rescontent = new String(InputStreamTOByte(conn.getInputStream())); System.out.println( "图片上传成功:" +rescontent); } else { //失败 rescontent = new String(InputStreamTOByte(conn.getErrorStream())); System.out.println( "图片上传失败:" +rescontent); } //验证微信支付返回签名 String Wtimestamp = responseHeader.get( "Wechatpay-Timestamp" ).get( 0 ); String Wnonce = responseHeader.get( "Wechatpay-Nonce" ).get( 0 ); String Wsign = responseHeader.get( "Wechatpay-Signature" ).get( 0 ); //拼装待签名串 StringBuffer ss = new StringBuffer(); ss.append(Wtimestamp).append( "\n" ); ss.append(Wnonce).append( "\n" ); ss.append(rescontent).append( "\n" ); //验证签名 if (verifyRSA(ss.toString(), Base64.decodeBase64(Wsign.getBytes()), rsaPublicKeyFile)) { System.out.println( "签名验证成功" ); } else { System.out.println( "签名验证失败" ); } } catch (Exception e) { System.out.println( "发送POST请求异常!" + e); e.printStackTrace(); } } } |
可以使用httpClient 实现文件流的传输吗?
图片上传失败:{"code":"PARAM_ERROR","message":"商户没有权限"}
上传图片的接口功能也要去微信官方开通权限吗?
上传成功后怎么或者到客户端上显示呢?https://pay.weixin.qq.com/files/media/get?media_id=xxxxx。获取不到啊,需要验证吗
我用的这套代码为什么签名错误?我私钥没问题呀
{
"code": "SIGN_ERROR",
"detail": {
"detail": {
"issue": "sign not match"
},
"field": "signature",
"location": "authorization",
"sign_information": {
"method": "POST",
"sign_message_length": 175,
"truncated_sign_message": "POST\n/v3/merchant/media/upload\n1574050808\n3dbf238498594cec83ab1e9f9fee45e8\n{\"filena\n",
"url": "/v3/merchant/media/upload"
}
},
"message": "错误的签名,验签失败"
}
{ "filename": "filea.jpg", "sha256": "hjkahkjsjkfsjk78687dhjahdajhk" }
修改了一下签名和验签的方法
public
static
byte
[] InputStreamTOByte(InputStream in)
throws
IOException{
int
BUFFER_SIZE =
4096
;
ByteArrayOutputStream outStream =
new
ByteArrayOutputStream();
byte
[] data =
new
byte
[BUFFER_SIZE];
int
count = -
1
;
while
((count = in.read(data,
0
,BUFFER_SIZE)) != -
1
)
outStream.write(data,
0
, count);
data =
null
;
byte
[] outByte = outStream.toByteArray();
outStream.close();
return
outByte;
}
public
static
byte
[] signRSA(String data, String priKey)
throws
Exception {
//签名的类型
Signature sign = Signature.getInstance(
"SHA256withRSA"
);
//读取商户私钥,该方法传入商户私钥证书的内容即可
byte
[] keyBytes = Base64.decodeBase64(priKey);
PKCS8EncodedKeySpec keySpec =
new
PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(
"RSA"
);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sign.initSign(privateKey);
sign.update(data.getBytes(
"UTF-8"
));
return
sign.sign();
}
public
static
boolean
verifyRSA(String data,
byte
[] sign, String pubKey)
throws
Exception{
if
(data ==
null
|| sign ==
null
|| pubKey ==
null
){
return
false
;
}
CertificateFactory cf = CertificateFactory.getInstance(
"X.509"
);
FileInputStream in =
new
FileInputStream(pubKey);
Certificate c = cf.generateCertificate(in);
in.close();
PublicKey publicKey = c.getPublicKey();
Signature signature = Signature.getInstance(
"SHA256WithRSA"
);
signature.initVerify(publicKey);
signature.update(data.getBytes(
"UTF-8"
));
return
signature.verify(sign);
}
--接口文档1:获取商户号授权状态接口
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_4.shtml
--接口文档2:提交申请单API
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_1.shtml
--接口文档3:撤销申请单API
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_2.shtml
--接口文档4:查询申请单审核结果API
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_3.shtml
--接口文档5:获取平台证书列表
https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu
--接口文档6:图片上传
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/download/图片上传.pdf
--重点信息:
--开发过程密钥、apiv3、jdk、微信提供的httpClient jar
https://wechatpay-api.gitbook.io/wechatpay-api-v3/
--微信提供的jar 自用需要修改一些部分
https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
--test 大佬实现的地址
--解释
https://developers.weixin.qq.com/community/develop/doc/000ca6c41e8e581c3f1932f3051404
--httpClient 4.5.9的实现
https://developers.weixin.qq.com/community/develop/doc/000ae8e8904a38ac4019bb24651404
更新了JDK,但是还是不行,读出来的秘钥全是0,第一次接接口,弄疯了,谢谢你
你好我的代码总是报错,说秘钥格式不对,我是从这个路径加载秘钥的,谢谢。
更新了JDK,但是还是不行,读出来的秘钥全是0,第一次接接口,弄疯了,谢谢你
请求报文里面送的:
网站上加出来的:
微信返回的:
JDK已经换成:1.8.0.221 版本了