1、介绍
部分微信支付业务指定商户需要使用图片上传 API来上报图片信息,从而获得必传参数的值:图片MediaID。即电商收付通接口有涉及到需要传图片的参数,不可以直接传图片文件,均需要通过指定的图片上传接口获取MediaID,再把MediaID传给相应的字段。比如二级商户进件接口需要上传营业执照,字段是business_license_copy,那么需要预先生成MediaID,将MediaID的值传给business_license_copy。
2、Authorization签名
文档地址:
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/chapter3_1.shtml
注意:参与签名计算的请求主体为meta的json串:{ "filename": "file.jpg", "sha256": "hjkahkjsjkfsjk78687dhjahdajhk" },文档说商户上传的媒体图片的名称,商户自定义,必须以JPG、BMP、PNG为后缀,亲测JPEG也可以上传成功
待签名串:
代码语言:javascript复制//时间戳
String timestamp = Long.toString(System.currentTimeMillis()/1000);
//随机数
String nonce_str = UuidUtils.randomUUID();
//图片文件
String filePath ="图片位置.jpg";//文件路径
File file = new File(filePath);
String filename = file.getName();//文件名
String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256值
//拼签名串
StringBuilder sb =new StringBuilder();
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":"").append(filename).append("","sha256":"").append(fileSha256).append(""}").append("n");
System.out.println("签名原串:" sb.toString());
计算签名sign
代码语言:javascript复制//计算签名
String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKeyFile)));
System.out.println("签名sign值:" sign);
拼接Authorization
代码语言:javascript复制//拼装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);
3、添加HTTP头
代码语言:javascript复制//接口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);
4.添加body
代码语言:javascript复制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();
5.获取结果验签
代码语言:javascript复制//接收返回
//打印返回头信息
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();
System.out.println("responseCode:" responseCode);
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(SignUtils.v3VerifyRSA(ss.toString(), Base64.decodeBase64(Wsign.getBytes()), "微信支付平台证书.pem")) {
System.out.println("签名验证成功");
}else {
System.out.println("签名验证失败");
}
6、发送请求
代码语言:javascript复制private static JSONObject uploadMedia() {
try {
// 换行符
String LINE_END ="rn";
String PREFIX ="--";
// 定义数据分隔线
String BOUNDARY = "----------" System.currentTimeMillis();
//商户号
String mchid ="商户号";
//证书序列号
String serial_no ="证书序列号";
//商户私钥
String rsaPrivateKeyFile = "商户私钥";
//时间戳
String timestamp = Long.toString(System.currentTimeMillis()/1000);
//随机数
String nonce_str = UuidUtils.randomUUID();
//图片文件
String filePath ="图片位置.jpg";//文件路径
File file = new File(filePath);
String filename = file.getName();//文件名
String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256值
//拼签名串
StringBuilder sb =new StringBuilder();
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":"").append(filename).append("","sha256":"").append(fileSha256).append(""}").append("n");
System.out.println("签名原串:" sb.toString());
//计算签名
String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKeyFile)));
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();
System.out.println("responseCode:" responseCode);
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(SignUtils.v3VerifyRSA(ss.toString(), Base64.decodeBase64(Wsign.getBytes()), "微信支付平台证书.pem")) {
System.out.println("签名验证成功");
}else {
System.out.println("签名验证失败");
}
return JSONObject.parseObject(rescontent);
} catch (Exception e) {
System.out.println("发送POST请求异常!" e);
e.printStackTrace();
}
return null;
}
7、测试
代码语言:javascript复制签名原串:
POST
/v3/merchant/media/upload
1585302190
20962176ac337f69cbb1548ada1fe448
{"filename":"图片位置.jpg","sha256":"75e6d36f9e4d5738bfafbccb298924108022cb89fa6fb94c58c877f83753174f"}
签名sign值:nnIQQbWurSmAxiS6gA6vPH6iaJSBbdJ8luvz97 8vRDfF9Z3SDtYMR79mMbyl2QhGYI1sERT3hlDone2HGvd3RnEHl8pmtOp8TiYoQfrTu1WpCKvjCdQJDnqkSSgVy5JBgR9J2in2G21pox5RTtRSktGMVBFAn3wcJuFjbtFkqMlt3bLP3/JTzvB1vGxHnuiZu8Lp8gDqY541U4IMr KKtQlahsPKgNGhh5B9iocGyB0wNyjJkgtBic11YGjMTAvVeJYW3ZQmt6aIbyvHDyYb0taXWoH2lEGPIpsPOx7Myy8OWcvbM Ze1uuEX L7su vXl1TALSuDgoEsUTelRr/w==
authorization值:WECHATPAY2-SHA256-RSA2048 mchid="1900000109",nonce_str="20962176ac337f69cbb1548ada1fe448",signature="nnIQQbWurSmAxiS6gA6vPH6iaJSBbdJ8luvz97 8vRDfF9Z3SDtYMR79mMbyl2QhGYI1sERT3hlDone2HGvd3RnEHl8pmtOp8TiYoQfrTu1WpCKvjCdQJDnqkSSgVy5JBgR9J2in2G21pox5RTtRSktGMVBFAn3wcJuFjbtFkqMlt3bLP3/JTzvB1vGxHnuiZu8Lp8gDqY541U4IMr KKtQlahsPKgNGhh5B9iocGyB0wNyjJkgtBic11YGjMTAvVeJYW3ZQmt6aIbyvHDyYb0taXWoH2lEGPIpsPOx7Myy8OWcvbM Ze1uuEX L7su vXl1TALSuDgoEsUTelRr/w==",timestamp="1585302190",serial_no="678C5D9A1FDBAC2C1291C65ADB22FDC2942F9CAE"
接口返回头信息:
Keep-Alive:[timeout=8]
null:[HTTP/1.1 200 OK]
Wechatpay-Timestamp:[1585302193]
Server:[nginx]
X-Content-Type-Options:[nosniff]
Connection:[keep-alive]
Date:[Fri, 27 Mar 2020 09:43:13 GMT]
Wechatpay-Serial:[911AFE1DC9C13FCCE6414B17A4927800A15A2E44]
Wechatpay-Nonce:[5f1e613ac12d8d3fd62144b402c09ce5]
Wechatpay-Signature:[GKc2009HtQD4Ld8AP8o/vmyRAO6C9kcCfpce90NL6eX44ov6qTNS25DYzh8GFqybq6ZBoSzZ2QZNFBD2NOCIcuUZ06d32ZtpdiVh7eOMRb8pRjO R2DMHzm44ApU/KmGQlo8PlZm SU1unDFeEtKJxL1b1ih4ndORqJeSXBQET1yCifB yuic4hLOXMjk849F pUg0M579t 8y8JM 7vPMTMenr7JdH2UihBiIKtQURTcW59QuDkeeomK/n2737e/MeXxZxOqkHqGNzWYiuI/gcAlcgNWSJ96KqD9J/pZPsJDEXnIu1408Q0BHJLeAyLquawtI9Mzxg1 8K64sOCL7==]
Cache-Control:[no-cache, must-revalidate]
Content-Length:[122]
Content-Language:[zh-CN]
Request-ID:[bnd0ak]
Content-Type:[application/json; charset=utf-8]
responseCode:200
图片上传成功:{"media_id":"VrvwA0PTBrBCnLiawyLnWFiF9pthhuBlt1FgxnXw80jOftpw9Nx-ujy185eULCvkESSdw702IHXFKAhaONWQpUkOfSsawMoOK4XuUrPNtNY"}
17:43:13.527 [main] INFO com.smartMap.media.common.weixin.ecommercepay.common.SignUtils - v3VerifyRSA result:签名验证成功
签名验证成功
上一篇:电商收付通系列③,对微信应答或回调进行签名验证