这阵子由于项目需要,需要对公众号的点餐程序添加微信支付与退款功能!想起大二上学期第一次做微信支付时的绝望,现在还记忆犹新。
这一次做微信支付相关的业务,是基于框架来做。如果有做过微信支付的同学,可能你会遇到各种各样懵逼的问题,最有趣的是返回的数据不告诉你大小还是小写,坑是多的吐血。
开发之前,我们需要有企业资质才可以做。如果没有的同学自己想办法!
pom.xml 文件
需要在 pom.xml 加入以下依赖!
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>3.0.0</version>
</dependency>
application.yml 文件
将 application.yml 中修改自己商户平台的信息,以下 keyPath 是证书,可以在微信支付后台下载,指定相关目录即可。
wx:
pay:
appId: #微信公众号或者小程序等的appid
mchId: #微信支付商户号
mchKey: #微信支付商户密钥
keyPath: # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
tradeType: JSAPI #JSAPI--公众号支付 NATIVE--原生扫码支付 APP--app支付
假设我们将证书放到 resources 目录下,那写成以下形式即可。
keyPath: classpath:证书名
还需要准备两个配置文件
WechatPayConfig 文件
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @author Gentle
* @date 2019/05/05
* 微信支付信息注入bean 中
*/
@Component
public class WeChatPayConfig {
@Autowired
private WeChatPayProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayConfig payConfig() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(this.properties.getAppId());
payConfig.setMchId(this.properties.getMchId());
payConfig.setMchKey(this.properties.getMchKey());
payConfig.setKeyPath(this.properties.getKeyPath());
payConfig.setTradeType(this.properties.getTradeType());
payConfig.setNotifyUrl(this.properties.getNotifyUrl());
return payConfig;
}
@Bean
public WxPayService wxPayService(WxPayConfig payConfig) {
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
微信支付接口的 Controller
import com.gentle.config.WeChatPayProperties;
import com.gentle.entity.ReturnPayInfoVO;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
/**
* @author : Gentle
* @date : 2019/5/17 12:11
* @description:
*/
@RestController
@RequestMapping(value = "/api/client/pay/")
public class WeChatPayController {
@Autowired
private WxPayService wxPayService;
@Autowired
WeChatPayProperties weChatPayProperties;
/**
* 此处处理订单信息,构建订单数据。
*
* 将构建好的支付参数返回到前端,前端调起微信支付
* @return
*/
@GetMapping(value = "weChatPay")
public ReturnPayInfoVO weChatPay() {
/**
* 处理内部业务,校验订单等
*/
final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
//调起支付的人的 openId
.openid("openId")
//订单编号
.outTradeNo("我们系统内部订单号")
//订单金额
.totalFee(yuanToFee(new BigDecimal(100)))
//商品描述
.body("订单信息")
//获取本地IP
.spbillCreateIp(InetAddress.getLoopbackAddress().getHostAddress())
//回调的 URL 地址
.notifyUrl("http://我们的域名/api/client/pay/weChatPayNotify")
.build();
WxPayUnifiedOrderResult wxPayUnifiedOrderResult =null;
try {
wxPayUnifiedOrderResult = wxPayService.unifiedOrder(wxPayUnifiedOrderRequest);
} catch (WxPayException e) {
e.printStackTrace();
throw new RuntimeException("微信支付调起失败!");
}
//组合参数构建支付
Map<String, String> paySignInfo = new HashMap<>(5);
String timeStamp = createTimestamp();
String nonceStr = String.valueOf(System.currentTimeMillis());
paySignInfo.put("appId", weChatPayProperties.getAppId());
paySignInfo.put("nonceStr", nonceStr);
paySignInfo.put("timeStamp", timeStamp);
paySignInfo.put("signType", "MD5");
paySignInfo.put("package", "prepay_id=" + wxPayUnifiedOrderResult.getPrepayId());
String paySign = SignUtils.createSign(paySignInfo, "MD5", weChatPayProperties.getMchKey(), false);
//组合支付参数
ReturnPayInfoVO returnPayInfoVO = new ReturnPayInfoVO();
returnPayInfoVO.setAppId(weChatPayProperties.getAppId());
returnPayInfoVO.setNonceStr(nonceStr);
returnPayInfoVO.setPaySign(paySign);
returnPayInfoVO.setSignType("MD5");
returnPayInfoVO.setPrepayId(wxPayUnifiedOrderResult.getPrepayId());
returnPayInfoVO.setTimeStamp(timeStamp);
return returnPayInfoVO;
}
/**
*
* @param xmlData 微信返回的流
* @return
*/
@RequestMapping(value = "weChatPayNotify",method = {RequestMethod.GET,RequestMethod.POST})
public String weChatNotify(@RequestBody String xmlData){
try {
final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
//这里是存储我们发起支付时订单号的信息,所以取出来
notifyResult.getOutTradeNo();
/**
* 系统内部业务,修改订单状态之类的
*/
//成功后回调微信信息
return WxPayNotifyResponse.success("回调成功!");
} catch (WxPayException e) {
e.printStackTrace();
return WxPayNotifyResponse.fail("回调有误!");
}
}
/**
* 1 块钱转为 100 分
* 元转分
*
* @param bigDecimal 钱数目
* @return 分
*/
private int yuanToFee(BigDecimal bigDecimal) {
return bigDecimal.multiply(new BigDecimal(100)).intValue();
}
/**
* 时间
*
* @return 时间戳
*/
private String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
上述代码便是位置支付的调起信息和支付成功后回调的处理逻辑了,可以对代码进行适当的修改,便可使用。
微信退款实现:
以下便是微信退款的实现,代码也相对简单,修改以下便可使用,当然要各种参数校验等,否则可能会出现被人恶意调起的可能!
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
/**
* @author : Gentle
* @date : 2019/5/17 12:38
* @description:
*/
@RestController
@RequestMapping(value = "/api/client/refund/")
@Slf4j
public class WeChatRefundController {
private static final String REFUND_SUCCESS = "SUCCESS";
@Autowired
private WxPayService wxPayService;
@PostMapping(value = "weChatRefund")
public String refund() {
//申请退款
WxPayRefundRequest refundInfo = WxPayRefundRequest.newBuilder()
//订单号
.outTradeNo("自己系统订单号")
//退款订单号
.outRefundNo("自己系统订单号")
//金额
.totalFee(yuanToFee(new BigDecimal(100)))
//退款金额
.refundFee(yuanToFee(new BigDecimal(100)))
//todo 回调地址
.notifyUrl("http://我们系统的域名/api/client/refund/refundNotify")
.build();
WxPayRefundResult wxPayRefundResult;
try {
wxPayRefundResult = wxPayService.refund(refundInfo);
//判断退款信息是否正确
if (REFUND_SUCCESS.equals(wxPayRefundResult.getReturnCode())
&& REFUND_SUCCESS.equals(wxPayRefundResult.getResultCode())) {
/**
* 系统内部业务逻辑
*/
return "正在退款中。。";
}
} catch (WxPayException e) {
log.error("微信退款接口错误信息= {}", e);
}
return "退款失败";
}
/**
* 仅支持一次性退款,多次退款需要修改逻辑
* @param xmlData 微信返回的流数据
* @return
*/
@RequestMapping(value = "refundNotify",method = {RequestMethod.GET,RequestMethod.POST})
public String refundNotify(@RequestBody String xmlData) {
WxPayRefundNotifyResult wxPayRefundNotifyResult;
try {
wxPayRefundNotifyResult = wxPayService.parseRefundNotifyResult(xmlData);
} catch (WxPayException e) {
log.error("退款失败,失败信息:{}", e);
return WxPayNotifyResponse.fail("退款失败");
}
//判断你返回状态信息是否正确
if (REFUND_SUCCESS.equals(wxPayRefundNotifyResult.getReturnCode())) {
WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();
//判断退款状态
if (REFUND_SUCCESS.equals(reqInfo.getRefundStatus())) {
//内部订单号
String outTradeNo = reqInfo.getOutTradeNo();
/**
* 一、可能会重复回调,需要做防重判断
* 二、处理我们系统内部业务,做修改订单状态,释放资源等!
*/
return WxPayNotifyResponse.success("退款成功!");
}
}
return WxPayNotifyResponse.fail("回调有误!");
}
/**
* 1 块钱转为 100 分
* 元转分
*
* @param bigDecimal 钱数目
* @return 分
*/
private int yuanToFee(BigDecimal bigDecimal) {
return bigDecimal.multiply(new BigDecimal(100)).intValue();
}
/**
* 时间
*
* @return 时间戳
*/
private String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
案例相关代码已经发布到 GitHub 和码云上,如有兴趣可以下载学习!
GitHub:
https://github.com/LuckyToMeet-Dian-N/WeChat-Demo/tree/master/wechat-pay-demo
码云:
https://gitee.com/reway_wen/WeChat-Demo/tree/master/wechat-pay-demo
总结:
上述便是微信支付和微信退款的实现!代码不难,只是开发资质倒是个问题。如果有资质的话,那这篇文章可能给到您一些帮助。