电商收付通系列④,图片预上传,生成MediaID
发布于 4 年前 作者 vliang 2910 次浏览 来自 分享

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也可以上传成功

待签名串:

//时间戳
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

//计算签名
String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKeyFile)));
System.out.println("签名sign值:"+sign);


拼接Authorization

//拼装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头

//接口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

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.获取结果验签

//接收返回
//打印返回头信息
System.out.println("接口返回头信息:");
Map> responseHeader = conn.getHeaderFields();
for (Map.Entry> 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、发送请求

    private static JSONObject uploadMedia() {
try {

    // 换行符
    String LINE_END ="\r\n";
    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> responseHeader = conn.getHeaderFields();
    for (Map.Entry> 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、测试

签名原串:
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:签名验证成功
签名验证成功

谢谢阅读🤝,未完待续

回到顶部