电商收付通系列④,图片预上传,生成MediaID

简介: 部分微信支付业务指定商户需要使用图片上传 API来上报图片信息,从而获得必传参数的值:图片MediaID。即电商收付通接口有涉及到需要传图片的参数,不可以直接传图片文件,均需要通过指定的图片上传接口获取MediaID,再把MediaID传给相应的字段。比如二级商户进件接口需要上传营业执照,字段是business_license_copy,那么需要预先生成MediaID,将MediaID的值传business_license_copy。

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);

image.png

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();

image.png

5.获取结果验签

//打印返回头信息
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、发送请求

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<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;
  }

image.png

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

image.png

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

上一篇:电商收付通系列③,对微信应答或回调进行签名验证

相关文章
|
6月前
|
安全 API Windows
支付系统13------支付系统的资料在技术库里的在线支付当中,怎样获取微信平台证书那?怎样获取微信平台证书那?第一步打开我们的微信支付平台的文档中心
支付系统13------支付系统的资料在技术库里的在线支付当中,怎样获取微信平台证书那?怎样获取微信平台证书那?第一步打开我们的微信支付平台的文档中心
办公技巧 | 快速批量制作N张奖状,再也不用加班了!
演示视频中的word文档使用的是16开奖状尺寸,内容的位置已经和奖状对应好,直接可以打印使用。如果你需要,也可以下载下去看看。
205 0
wordpress电商独立站收款方式,电商独立站支付选哪几种?
WordPress是一款非常受欢迎的建站平台,许多人都选择用它来搭建自己的个人博客或商业网站。在建立一个WordPress独立站的同时,如何设置收款方式也是非常重要的。
wordpress电商独立站收款方式,电商独立站支付选哪几种?
|
JSON 算法 安全
金润·核验通-运营商在网状态接口文档
运营商在网状态接口介绍:提供用户手机号,核验用户手机在网状态正常、停机、销号、在网但不可用等状态 更新时间:实时 接口类型:API接口 数据优势:直连官方数据,合法合规、权威、精确 数据安全:仅确认在网状态,保护个人信息安全 计费方式:核验计费,详情请咨询
金润·核验通-运营商在网状态接口文档
|
JSON 算法 安全
金润·核验通-运营商在网时长接口文档
运营商在网时长接口介绍:提供用户手机号,核验用户在运营商激活手机号正常使用至今的时长 更新时间:实时 接口类型:API接口 数据优势:直连官方数据,合法合规、权威、精确 数据安全:仅提供用户在网时长,保护个人信息安全 计费方式:核验计费,详情请咨询
金润·核验通-运营商在网时长接口文档
金润·高速通-车辆信息查验接口文档
车辆信息查验接口介绍:查询指定人员/企业(ETC开户人/车辆所有人)指定时间内所有车辆的车牌号、车型、轴数及ETC卡信息 更新时间:实时 接口类型:API接口 数据优势:直连交通部路网中心,合法合规、权威、精确 数据安全:只查验车辆信息,保护个人信息隐私 计费方式:核验计费,详情请咨询
金润·高速通-车辆信息查验接口文档
|
容灾
|
小程序 开发者
电商收付通,商户进件,上传身份证、营业执照自动识别相关信息
二级商户进件的时候,需要提交的资料不少,有一个繁琐的地方就是,不管选择哪种主体类型,都需要上传身份证人像面、身份证国徽面、身份证姓名、身份证号码、身份证居住地址、身份证开始时间和身份证结束时间这些要素。
262 0
电商收付通,商户进件,上传身份证、营业执照自动识别相关信息
|
数据可视化 安全
物流运输logo在线设计制作:三分钟教你搞定
今天标小智来从物流logo图标、物流logo字体等多个角度分享物流运输logo设计干货,并为你准备了简单好看的交通物流logo设计案例模板,一起看看看吧!
物流运输logo在线设计制作:三分钟教你搞定
|
移动开发 安全 小程序
电商收付通系统,可视化进件二级商户
传统的电商平台都是在用户确认收货后再在一定的周期内结算给商户,就是采取平台先收款的方式,但这样不仅给电商平台带来税务的问题,还影响商户的资金安全。有了电商收付通,就不存在这样的问题了。
299 0
电商收付通系统,可视化进件二级商户