前由: 最近做java的微信支付,查看微信支付sdk的maven仓库,发现最新一次更新在2017年3月16日,版本号停留在0.0.3:
<!-- https://mvnrepository.com/artifact/com.github.wxpay/wxpay-sdk -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
同时,微信官方SDK(下载地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1) 内容版本如下:
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.9</version>
官方SDK为3.0.9版本同样也是基于maven构建,只不过没有上传到maven的公共仓库,那么我们就可以自己动手打成jar包从而导入本地maven项目。
打包步骤:
- 修改配置文件WXPayConfig的抽象属性。原版的缺省(default)抽象属性只能在同一个package下可见,在不同package下面缺省属性是没有访问权限的,跨包必须改成公共public属性才可以。
public abstract class WXPayConfig {
/**
* 获取 App ID
*
* @return App ID
*/
public abstract String getAppID();
/**
* 获取 Mch ID
*
* @return Mch ID
*/
public abstract String getMchID();
/**
* 获取 API 密钥
*
* @return API密钥
*/
public abstract String getKey();
/**
* 获取商户证书内容
*
* @return 商户证书内容
*/
public abstract InputStream getCertStream();
/**
* HTTP(S) 连接超时时间,单位毫秒
*
* @return
*/
public int getHttpConnectTimeoutMs() {
return 6*1000;
}
/**
* HTTP(S) 读数据超时时间,单位毫秒
*
* @return
*/
public int getHttpReadTimeoutMs() {
return 8*1000;
}
/**
* 获取WXPayDomain, 用于多域名容灾自动切换
* @return
*/
public abstract IWXPayDomain getWXPayDomain();
/**
* 是否自动上报。
* 若要关闭自动上报,子类中实现该函数返回 false 即可。
*
* @return
*/
public boolean shouldAutoReport() {
return true;
}
/**
* 进行健康上报的线程的数量
*
* @return
*/
public int getReportWorkerNum() {
return 6;
}
/**
* 健康上报缓存消息的最大数量。会有线程去独立上报
* 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
*
* @return
*/
public int getReportQueueMaxSize() {
return 10000;
}
/**
* 批量上报,一次最多上报多个数据
*
* @return
*/
public int getReportBatchSize() {
return 10;
}
}
2.自行打包完成,安装本地jar包到本地仓库需要如下命令(注意不要换行):
mvn install:install-file -Dfile=E:\wxpay-sdk-3.0.9.jar -DgroupId=com.github.wxpay -DartifactId=wxpay-sdk -Dversion=3.0.9 -Dpackaging=jar
3.maven的pom.xml引入wxpay-sdk依赖。
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.9</version>
</dependency>
同时需要注意打包wxpay-sdk-3.0.9.jar
并没有把wxpay依赖的包打进去,我们还需引入wxpay-sdk
依赖的包:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
wxpay-sdk依赖的slf4j与spring-boot-starter-web
所依赖的的spring-boot-starter-logging
有冲突,请注意依赖排除,本人排除的是spring-boot-starter-logging
的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions><!-- 去掉springboot默认配置的日志 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
SDK使用:
1.微信支付yml配置文件
# 微信app支付
pay:
wxpay:
app:
appID: "xxxxx"
mchID: "xxxx"
key: "xxxxxxxxxxxxx"
#从微信商户平台下载的安全证书存放的路径、放在resources下面,切记一定要看看target目录下的class文件下有没有打包apiclient_cert.p12文件
certPath: static/cert/wxpay/apiclient_cert.p12
#微信支付成功的异步通知接口
#payNotifyUrl: ${server.service-domain}/wxPay/notify
payNotifyUrl: http://xxx.com/wxPay/notify
2.读取配置信息
@Component
@ConfigurationProperties(prefix = "pay.wxpay.app")
public class WxPayAppConfig extends WXPayConfig {
/**
* appID
*/
private String appID;
/**
* 商户号
*/
private String mchID;
/**
* API 密钥
*/
private String key;
/**
* API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
*/
private String certPath;
/**
* HTTP(S) 连接超时时间,单位毫秒
*/
private int httpConnectTimeoutMs = 8000;
/**
* HTTP(S) 读数据超时时间,单位毫秒
*/
private int httpReadTimeoutMs = 10000;
/**
* 微信支付异步通知地址
*/
private String payNotifyUrl;
/**
* 微信退款异步通知地址
*/
private String refundNotifyUrl;
/**
* 获取商户证书内容(这里证书需要到微信商户平台进行下载)
*
* @return 商户证书内容
*/
@Override
public InputStream getCertStream() {
InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);
return certStream;
}
public String getAppID() {
return appID;
}
public void setAppID(String appID) {
this.appID = appID;
}
public String getMchID() {
return mchID;
}
public void setMchID(String mchID) {
this.mchID = mchID;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getCertPath() {
return certPath;
}
public void setCertPath(String certPath) {
this.certPath = certPath;
}
public int getHttpConnectTimeoutMs() {
return httpConnectTimeoutMs;
}
public void setHttpConnectTimeoutMs(int httpConnectTimeoutMs) {
this.httpConnectTimeoutMs = httpConnectTimeoutMs;
}
public int getHttpReadTimeoutMs() {
return httpReadTimeoutMs;
}
public void setHttpReadTimeoutMs(int httpReadTimeoutMs) {
this.httpReadTimeoutMs = httpReadTimeoutMs;
}
public String getPayNotifyUrl() {
return payNotifyUrl;
}
public void setPayNotifyUrl(String payNotifyUrl) {
this.payNotifyUrl = payNotifyUrl;
}
public String getRefundNotifyUrl() {
return refundNotifyUrl;
}
public void setRefundNotifyUrl(String refundNotifyUrl) {
this.refundNotifyUrl = refundNotifyUrl;
}
public IWXPayDomain getWXPayDomain() {
IWXPayDomain iwxPayDomain = new IWXPayDomain() {
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
}
};
return iwxPayDomain;
}
}
3.通用返回结果集
/**
* @Description 通用返回结果集
* @Author
*/
public class ResultMap extends HashMap<String, Object> {
public ResultMap() {
put("state", true);
put("code", 0);
put("msg", "success");
}
public static ResultMap error(int code, String msg) {
ResultMap r = new ResultMap();
r.put("state", false);
r.put("code", code);
r.put("msg", msg);
return r;
}
public static ResultMap error(String msg) {
return error(HttpStatus.METHOD_NOT_ALLOWED.value(), msg);
}
public static ResultMap error() {
return error(HttpStatus.METHOD_NOT_ALLOWED.value(), "未知异常,请联系管理员");
}
public static ResultMap ok(String msg) {
ResultMap r = new ResultMap();
r.put("msg", msg);
return r;
}
public static ResultMap ok(Map<String, Object> par) {
ResultMap r = new ResultMap();
r.putAll(par);
return r;
}
public static ResultMap ok() {
return new ResultMap();
}
public ResultMap put(String key, Object value) {
super.put(key, value);
return this;
}
}
4.微信支付服务接口
/**
* 微信支付服务接口
*/
public interface WxPayService {
/**
* @Description: 微信支付统一下单
* @param orderNo: 订单编号
* @param amount: 实际支付金额
* @param body: 订单描述
* @Author:
* @return
*/
ResultMap unifiedOrder(String orderNo, double amount, String body) ;
/**
* @Description: 订单支付异步通知
* @param notifyStr: 微信异步通知消息字符串
* @Author:
* @return
*/
String notify(String notifyStr) throws Exception;
/**
* @Description: 退款
* @param orderNo: 订单编号
* @param amount: 实际支付金额
* @param refundReason: 退款原因
* @Author:
* @return
*/
ResultMap refund(String orderNo, double amount, String refundReason) throws Exception;
}
5.服务接口实现类
@Service
public class WxPayServiceImpl implements WxPayService {
private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
@Autowired
private WxPayAppConfig wxPayAppConfig;
@Override
public ResultMap unifiedOrder(String orderNo, double amount, String body) {
Map<String, String> returnMap = new HashMap<>();
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
try {
WXPay wxpay = new WXPay(wxPayAppConfig);
requestMap.put("body", body); // 商品描述
requestMap.put("out_trade_no", orderNo); // 商户订单号
requestMap.put("total_fee", String.valueOf((int)(amount*100))); // 总金额
//requestMap.put("spbill_create_ip", HttpContextUtils.getIpAddr()); // 终端IP
requestMap.put("trade_type", "APP"); // App支付类型
requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl()); // 接收微信支付异步通知回调地址
Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
for (String resultKey : resultMap.keySet()) {
logger.info("订单key:{}", resultMap.get(resultKey));
}
//获取返回码
String returnCode = resultMap.get("return_code");
String returnMsg = resultMap.get("return_msg");
//若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
if ("SUCCESS".equals(returnCode)) {
String resultCode = resultMap.get("result_code");
String errCodeDes = resultMap.get("err_code_des");
if ("SUCCESS".equals(resultCode)) {
responseMap = resultMap;
}
}
if (responseMap == null || responseMap.isEmpty()) {
return ResultMap.error("获取预支付交易会话标识失败");
}
// 3、签名生成算法
Long time = System.currentTimeMillis() / 1000;
String timestamp = time.toString();
returnMap.put("appid", wxPayAppConfig.getAppID());
returnMap.put("partnerid", wxPayAppConfig.getMchID());
returnMap.put("prepayid", responseMap.get("prepay_id"));
returnMap.put("noncestr", responseMap.get("nonce_str"));
returnMap.put("timestamp", timestamp);
returnMap.put("package", "Sign=WXPay");
returnMap.put("sign", WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey()));//微信支付签名
return ResultMap.ok().put("data", returnMap);
} catch (Exception e) {
logger.error("订单号:{},错误信息:{}", orderNo, e.getMessage());
return ResultMap.error("微信支付统一下单失败");
}
}
@Override
public String notify(String notifyStr) {
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
// 转换成map
Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
WXPay wxpayApp = new WXPay(wxPayAppConfig);
if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
String returnCode = resultMap.get("return_code"); //状态
String outTradeNo = resultMap.get("out_trade_no");//商户订单号
String transactionId = resultMap.get("transaction_id");
if (returnCode.equals("SUCCESS")) {
if (! StringUtils.isEmpty(outTradeNo)) {
/**
* 注意!!!
* 请根据业务流程,修改数据库订单支付状态,和其他数据的相应状态
*
*/
logger.info("微信手机支付回调成功,订单号:{}", outTradeNo);
xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlBack;
}
@Override
public ResultMap refund(String orderNo, double amount, String refundReason) throws Exception {
if(StringUtils.isEmpty(orderNo)){
return ResultMap.error("订单编号不能为空");
}
if(amount <= 0){
return ResultMap.error("退款金额必须大于0");
}
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
WXPay wxpay = new WXPay(wxPayAppConfig);
System.out.println(wxPayAppConfig.toString());
requestMap.put("out_trade_no", orderNo);
requestMap.put("out_refund_no", "xxxx自行手动生成");//商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
requestMap.put("total_fee", "订单总金额");
requestMap.put("refund_fee", String.valueOf((int)(amount*100)));//所需退款金额
requestMap.put("refund_desc", refundReason);
try {
responseMap = wxpay.refund(requestMap);
} catch (Exception e) {
e.printStackTrace();
}
for (String responseKey : responseMap.keySet()) {
logger.info("订单key:{}", responseMap.get(responseKey));
}
String return_code = responseMap.get("return_code"); //返回状态码
String return_msg = responseMap.get("return_msg"); //返回信息
if ("SUCCESS".equals(return_code)) {
String result_code = responseMap.get("result_code"); //业务结果
String err_code_des = responseMap.get("err_code_des"); //错误代码描述
if ("SUCCESS".equals(result_code)) {
//表示退款申请接受成功,结果通过退款查询接口查询
//修改用户订单状态为退款申请中或已退款。退款异步通知根据需求,可选
return ResultMap.ok("退款申请成功");
} else {
logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
return ResultMap.error(err_code_des);
}
} else {
logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
return ResultMap.error(return_msg);
}
}
}
6.微信支付对外REST接口(用到Swagger2)
@Api(tags = "微信支付接口管理")
@RestController
@RequestMapping("/wxPay")
public class WxPayController{
@Autowired
private WxPayService wxPayService;
private final Logger logger = LoggerFactory.getLogger(WxPayController.class);
/**
* 统一下单接口
*/
@ApiOperation(value = "统一下单", notes = "统一下单")
@GetMapping("/unifiedOrder")
public ResultMap unifiedOrder(
@ApiParam(value = "订单号") @RequestParam String orderNo,
@ApiParam(value = "订单金额") @RequestParam double amount,
@ApiParam(value = "商品名称") @RequestParam String body,
HttpServletRequest request) {
try {
// 1、验证订单是否存在
// 2、开始微信支付统一下单
ResultMap resultMap = wxPayService.unifiedOrder(orderNo, amount, body);
return resultMap;//系统通用的返回结果集,见文章末尾
} catch (Exception e) {
logger.error(e.getMessage());
return ResultMap.error("运行异常,请联系管理员");
}
}
/**
* 微信支付异步通知
*/
@RequestMapping(value = "/notify")
public String payNotify(HttpServletRequest request) {
InputStream is = null;
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
is = request.getInputStream();
// 将InputStream转换成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
xmlBack = wxPayService.notify(sb.toString());
} catch (Exception e) {
logger.error("微信手机支付回调通知失败:", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return xmlBack;
}
@ApiOperation(value = "退款", notes = "退款")
@PostMapping("/refund")
public ResultMap refund(@ApiParam(value = "订单号") @RequestParam String orderNo,
@ApiParam(value = "退款金额") @RequestParam double amount,
@ApiParam(value = "退款原因") @RequestParam(required = false) String refundReason) throws Exception {
return wxPayService.refund(orderNo, amount, refundReason);
}
}
来源:oschina
链接:https://my.oschina.net/u/4087795/blog/3215688