Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
1. 实现v3上传图片功能
2. 将接口获取到的证书保存到PayConfig中,以后再对敏感信息加密时会使用到
  • Loading branch information
zhouyongshen committed Aug 7, 2020
commit f9eed3a34a26655819f79ed147ed628c238cb23d
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.github.binarywang.wxpay.bean.media;

import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;

/**
* 媒体文件上传返回结果对象
* @author zhouyongshen
*/
@NoArgsConstructor
@Data
public class ImageUploadResult {

public static ImageUploadResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, ImageUploadResult.class);
}
/**
* 媒体文件标识 Id
*
* 微信返回的媒体文件标识Id。
* 示例值:6uqyGjGrCf2GtyXP8bxrbuH9-aAoTjH-rKeSl3Lf4_So6kdkQu4w8BYVP3bzLtvR38lxt4PjtCDXsQpzqge_hQEovHzOhsLleGFQVRF-U_0
*
*/
@SerializedName("media_id")
private String mediaId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder;
import com.github.binarywang.wxpay.v3.auth.AutoUpdateCertificatesVerifier;
import com.github.binarywang.wxpay.v3.auth.PrivateKeySigner;
import com.github.binarywang.wxpay.v3.auth.WxPayCredentials;
import com.github.binarywang.wxpay.v3.auth.WxPayValidator;
import com.github.binarywang.wxpay.v3.auth.*;
import com.github.binarywang.wxpay.v3.util.PemUtils;
import jodd.util.ResourcesUtil;
import lombok.Data;
Expand Down Expand Up @@ -153,6 +150,12 @@ public class WxPayConfig {
private String httpProxyUsername;
private String httpProxyPassword;

/**
* v3接口下证书检验对象,通过改对象可以获取到X509Certificate,进一步对敏感信息加密
* 文档见 https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi
*/
private Verifier verifier;

/**
* 返回所设置的微信支付接口请求地址域名.
*
Expand Down Expand Up @@ -297,14 +300,20 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {

try {
PrivateKey merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);

AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));


CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create()
.withMerchant(mchId, certSerialNo, merchantPrivateKey)
.withWechatpay(Collections.singletonList(PemUtils.loadCertificate(certInputStream)))
.withValidator(new WxPayValidator(new AutoUpdateCertificatesVerifier(
new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8))))
.withValidator(new WxPayValidator(verifier))
.build();
this.apiV3HttpClient = httpClient;
this.verifier=verifier;

return httpClient;
} catch (Exception e) {
throw new WxPayException("v3请求构造异常!", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.binarywang.wxpay.service;

import com.github.binarywang.wxpay.bean.media.ImageUploadResult;
import com.github.binarywang.wxpay.exception.WxPayException;

import java.io.File;
import java.io.IOException;

/**
* <pre>
* 微信支付通用媒体接口.
* </pre>
*
* @author zhouyongshen
*/
public interface MerchantMediaService {
/**
* <pre>
* 通用接口-图片上传API
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/chapter3_1.shtml
* 接口链接:https://api.mch.weixin.qq.com/v3/merchant/media/upload
* </pre>
*
* @param imageFile 需要上传的图片文件
* @return ImageUploadResult 微信返回的媒体文件标识Id。示例值:6uqyGjGrCf2GtyXP8bxrbuH9-aAoTjH-rKeSl3Lf4_So6kdkQu4w8BYVP3bzLtvR38lxt4PjtCDXsQpzqge_hQEovHzOhsLleGFQVRF-U_0
* @throws WxPayException the wx pay exception
*/
ImageUploadResult imageUploadV3(File imageFile) throws WxPayException, IOException;


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import org.apache.http.client.methods.HttpPost;

import java.io.File;
import java.net.URI;
Expand Down Expand Up @@ -65,6 +66,16 @@ public interface WxPayService {
*/
String postV3(String url, String requestStr) throws WxPayException;

/**
* 发送post请求,得到响应字符串.
*
* @param url 请求地址
* @param httpPost 请求信息
* @return 返回请求结果字符串 string
* @throws WxPayException the wx pay exception
*/
String postV3(String url, HttpPost httpPost) throws WxPayException;

/**
* 发送get V3请求,得到响应字符串.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.github.binarywang.wxpay.service.impl;

import com.github.binarywang.wxpay.bean.media.ImageUploadResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.MerchantMediaService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.WechatPayUploadHttpPost;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

/**
* 微信支付-媒体文件上传service
* @author zhouyongshen
*/
@Slf4j
@RequiredArgsConstructor
public class MerchantMediaServiceImpl implements MerchantMediaService {

private final WxPayService payService;

@Override
public ImageUploadResult imageUploadV3(File imageFile) throws WxPayException,IOException {
String url = String.format("%s/v3/merchant/media/upload", this.payService.getPayBaseUrl());

try (FileInputStream s1 = new FileInputStream(imageFile)) {
String sha256 = DigestUtils.sha256Hex(s1);
try (InputStream s2 = new FileInputStream(imageFile)) {
WechatPayUploadHttpPost request = new WechatPayUploadHttpPost.Builder(URI.create(url))
.withImage(imageFile.getName(), sha256, s2)
.build();
String result = this.payService.postV3(url, request);
return ImageUploadResult.fromJson(result);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,36 @@ public String postV3(String url, String requestStr) throws WxPayException {

}

@Override
public String postV3(String url, HttpPost httpPost) throws WxPayException {

httpPost.setConfig(RequestConfig.custom()
.setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout())
.setConnectTimeout(this.getConfig().getHttpConnectionTimeout())
.setSocketTimeout(this.getConfig().getHttpTimeout())
.build());

CloseableHttpClient httpClient = this.createApiV3HttpClient();
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
//v3已经改为通过状态码判断200 204 成功
int statusCode = response.getStatusLine().getStatusCode();
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) {
this.log.info("\n【请求地址】:{}\n【响应数据】:{}", url, responseString);
return responseString;
} else {
//有错误提示信息返回
JsonObject jsonObject = GsonParser.parse(responseString);
throw new WxPayException(jsonObject.get("message").getAsString());
}
} catch (Exception e) {
this.log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage());
throw new WxPayException(e.getMessage(), e);
} finally {
httpPost.releaseConnection();
}
}

@Override
public String getV3(URI url) throws WxPayException {
CloseableHttpClient httpClient = this.createApiV3HttpClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import jodd.http.net.SSLSocketHttpConnectionProvider;
import jodd.http.net.SocketHttpConnectionProvider;
import jodd.util.Base64;
import org.apache.http.client.methods.HttpPost;

/**
* 微信支付请求实现类,jodd-http实现.
Expand Down Expand Up @@ -65,6 +66,11 @@ public String postV3(String url, String requestStr) throws WxPayException {
return null;
}

@Override
public String postV3(String url, HttpPost httpPost) throws WxPayException {
return null;
}

@Override
public String getV3(URI url) throws WxPayException {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.github.binarywang.wxpay.v3;

import java.io.IOException;
import org.apache.http.client.methods.HttpUriRequest;

import org.apache.http.client.methods.HttpRequestWrapper;

public interface Credentials {

String getSchema();

String getToken(HttpUriRequest request) throws IOException;
String getToken(HttpRequestWrapper request) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
Expand All @@ -12,6 +13,7 @@
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.execchain.ClientExecChain;
import org.apache.http.util.EntityUtils;
Expand Down Expand Up @@ -43,11 +45,11 @@ protected void convertToRepeatableResponseEntity(CloseableHttpResponse response)
}
}

protected void convertToRepeatableRequestEntity(HttpUriRequest request) throws IOException {
if (request instanceof HttpEntityEnclosingRequestBase) {
HttpEntity entity = ((HttpEntityEnclosingRequestBase) request).getEntity();
if (entity != null && !entity.isRepeatable()) {
((HttpEntityEnclosingRequestBase) request).setEntity(newRepeatableEntity(entity));
protected void convertToRepeatableRequestEntity(HttpRequestWrapper request) throws IOException {
if (request instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
if (entity != null) {
((HttpEntityEnclosingRequest) request).setEntity(new BufferedHttpEntity(entity));
}
}
}
Expand All @@ -64,15 +66,16 @@ public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request

private CloseableHttpResponse executeWithSignature(HttpRoute route, HttpRequestWrapper request,
HttpClientContext context, HttpExecutionAware execAware) throws IOException, HttpException {
HttpUriRequest newRequest = RequestBuilder.copy(request.getOriginal()).build();
convertToRepeatableRequestEntity(newRequest);
// 上传类不需要消耗两次故不做转换
if (!(request.getOriginal() instanceof WechatPayUploadHttpPost)) {
convertToRepeatableRequestEntity(request);
}
// 添加认证信息
newRequest.addHeader("Authorization",
credentials.getSchema() + " " + credentials.getToken(newRequest));
request.addHeader("Authorization",
credentials.getSchema() + " " + credentials.getToken(request));

// 执行
CloseableHttpResponse response = mainExec.execute(
route, HttpRequestWrapper.wrap(newRequest), context, execAware);
CloseableHttpResponse response = mainExec.execute(route, request, context, execAware);

// 对成功应答验签
StatusLine statusLine = response.getStatusLine();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.github.binarywang.wxpay.v3;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;

import java.io.InputStream;
import java.net.URI;
import java.net.URLConnection;

public class WechatPayUploadHttpPost extends HttpPost {

private String meta;

private WechatPayUploadHttpPost(URI uri, String meta) {
super(uri);

this.meta = meta;
}

public String getMeta() {
return meta;
}

public static class Builder {

private String fileName;
private String fileSha256;
private InputStream fileInputStream;
private ContentType fileContentType;
private URI uri;

public Builder(URI uri) {
this.uri = uri;
}

public Builder withImage(String fileName, String fileSha256, InputStream inputStream) {
this.fileName = fileName;
this.fileSha256 = fileSha256;
this.fileInputStream = inputStream;

String mimeType = URLConnection.guessContentTypeFromName(fileName);
if (mimeType == null) {
// guess this is a video uploading
this.fileContentType = ContentType.APPLICATION_OCTET_STREAM;
} else {
this.fileContentType = ContentType.create(mimeType);
}
return this;
}

public WechatPayUploadHttpPost build() {
if (fileName == null || fileSha256 == null || fileInputStream == null) {
throw new IllegalArgumentException("缺少待上传图片文件信息");
}

if (uri == null) {
throw new IllegalArgumentException("缺少上传图片接口URL");
}

String meta = String.format("{\"filename\":\"%s\",\"sha256\":\"%s\"}", fileName, fileSha256);
WechatPayUploadHttpPost request = new WechatPayUploadHttpPost(uri, meta);

MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
entityBuilder.setMode(HttpMultipartMode.RFC6532)
.addBinaryBody("file", fileInputStream, fileContentType, fileName)
.addTextBody("meta", meta, ContentType.APPLICATION_JSON);

request.setEntity(entityBuilder.build());
request.addHeader("Accept", ContentType.APPLICATION_JSON.toString());

return request;
}
}
}
Loading