Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.mingsoft.pay.action.web.WeixinPayAction Maven / Gradle / Ivy
/**
* Copyright (c) 2012-present 铭软科技(mingsoft.net)
* 本软件及相关文档文件(以下简称“软件”)的版权归 铭软科技 所有
* 遵循 铭软科技《服务协议》中的《保密条款》
*/
package net.mingsoft.pay.action.web;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import me.chanjar.weixin.mp.enums.WxMpApiUrl;
import net.mingsoft.base.entity.ResultData;
import net.mingsoft.basic.exception.BusinessException;
import net.mingsoft.basic.util.SpringUtil;
import net.mingsoft.mdiy.util.ConfigUtil;
import net.mingsoft.pay.ann.MPayNotifyLogAnn;
import net.mingsoft.pay.util.PayUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants.SignType;
import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.binarywang.wxpay.util.SignUtils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.mp.api.WxMpService;
import net.mingsoft.basic.util.BasicUtil;
import net.mingsoft.pay.action.BaseAction;
import net.mingsoft.pay.bean.PayBean;
import net.mingsoft.pay.biz.IPayLogBiz;
import net.mingsoft.pay.constant.Const;
import net.mingsoft.pay.entity.PayLogEntity;
import net.mingsoft.pay.entity.PayLogEntity.LogStatusEnum;
import net.mingsoft.pay.entity.PayLogEntity.LogTypeEnum;
import springfox.documentation.annotations.ApiIgnore;
/**
* 铭软科技 Copyright: Copyright (c) 2014 - 2015
*
* 微信支付接口
* @author 铭飞开发 Comments:微信支付 Create Date:2015-1-19 Modification history:
*/
@Api(tags = {"前端-支付模块接口"})
@Controller
@RequestMapping("/mpay/weixin")
public class WeixinPayAction extends BaseAction {
/**
* 交易记录
*/
@Autowired
private IPayLogBiz payLogBiz;
/**
*
* 支付
*
* pay
* pay参数包含字段信息参考:
* notifyUrl:(可选)接口异步请求地址,绝对地址
* orderNo:订单编号
* orderName:订单标题
* orderPrice:订单价格
* orderDesc:订单描述
* page:微信支付调用时使用,微信支付实现通过接口获取支付的凭证,再将凭证填充到page页面再发起支付
*
*
* 公众号支付:调用模版:(需要两个页面才能完成)
* 页面一
var payNextPay = "{ms:global.host/}/people/order-pay.do?orderId="+this.orderDetail.id; //支付一个页面
$.ajax({
type: "POST",
url: "{ms:global.host/}/mpay/pay/gateway.do",
data: "type=weixin&orderNo="+this.orderDetail.orderNo+"&page="+encodeURIComponent(payNextPay),
success: function(msg){
location.href=msg.resultMsg;
}
});
*
* 页面二
* //微信公众号支付
* //https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"{appId/}", //公众号名称,由商户传入
"timeStamp":"{timeStamp/}", //时间戳,自1970年以来的秒数
"nonceStr":"{nonceStr/}", //随机串
"package":"{package/}",
"signType":"MD5", //微信签名方式:
"paySign":"{sign/}" //微信签名
},
function(res){
if (res.err_msg == "get_brand_wcpay_request:ok") {
//alert("微信支付成功!");
location.href="{ms:global.host/}/people/order-success.do";
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
//alert("用户取消支付!");
} else {
alert("支付失败!");
}
}
);
* 扫码支付
*
* H5支付
*
*
*
*/
@ApiOperation(value = "微信支付接口,只提供网关调用/mpay/pay/gateway")
@RequestMapping(value = "/pay", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public ResultData pay(HttpServletRequest request, HttpServletResponse response) throws WxPayException, IOException {
PayBean pay = (PayBean) request.getAttribute("pay");
Map weixinPayConfig = new HashMap<>();
// 判断是否是公众号支付
if (ObjectUtil.isNull(pay.getAppId()) && ObjectUtil.isNull(pay.getKey()) && ObjectUtil.isNull(pay.getSecret())
&& ObjectUtil.isNull(pay.getMchId())) {
weixinPayConfig = ConfigUtil.getMap(Const.WEIXIN_PAY_CONFIG_NAME);
} else {
//当有多个公众号时,前端传递公众号支付信息
weixinPayConfig.put("payNo",pay.getAppId());
weixinPayConfig.put("payKey",pay.getKey());
weixinPayConfig.put("paySecret",pay.getSecret());
weixinPayConfig.put("payPartner",pay.getMchId());
}
StringBuffer sb = new StringBuffer();
// 该订单需要支付的价格(注订单价格不能有小数)
int priceStr = (int) (Double.valueOf(pay.getOrderPrice()) * 100);
WxPayUnifiedOrderRequest wpuoRequest = new WxPayUnifiedOrderRequest();
// 订单描述
wpuoRequest.setBody(pay.getOrderDesc());
// 商户订单号,为了保证微信支付可以反复重复支付,需要在原有的订单好基础上再做随机处理,在回调方法处需要还原原始订单号
wpuoRequest.setOutTradeNo(pay.getOrderNo());
// 支付价格
wpuoRequest.setTotalFee(priceStr);
// 终端IP(必须)
wpuoRequest.setSpbillCreateIp(this.getHostIp());
//判断是否有其他信息参数
if (ObjectUtil.isNotNull(pay.getAttach())) {
wpuoRequest.setAttach(pay.getAttach());
}
WxPayService wxService = WeixinPayAction.buildPayService(weixinPayConfig, pay.getNotifyUrl());
if (PayBean.Type.WEIXIN_APP.equals(pay.getType())) {
//APP支付
wpuoRequest.setTradeType(TradeType.APP);
WxPayAppOrderResult wpuoReuslt = null;
wpuoReuslt = wxService.createOrder(wpuoRequest);
return ResultData.build().success(wpuoReuslt);
} else if (PayBean.Type.WEIXIN.equals(pay.getType())) {
// 公众号支付
if (pay.isMobile()) {
if (StringUtils.isEmpty(pay.getPage())) {
return ResultData.build().error(this.getResString("pay.weixin.page"));
}
//用户同意授权就可以获得
String code = request.getParameter("code");
String appid = weixinPayConfig.get("payNo");
String secret = weixinPayConfig.get("paySecret");
wpuoRequest.setTradeType(TradeType.JSAPI);
if (ObjectUtil.isNotNull(pay.getWeixinOpenId())) {
//获取接口传递的openid
wpuoRequest.setOpenid(pay.getWeixinOpenId());
} else {
// 如果没有正常获取到用户的信息
if (ObjectUtil.isNull(code)) {
return ResultData.build().error(this.getResString("get.people.fail"));
}
//微信授权地址
String json = HttpUtil.get(String.format(WxMpApiUrl.OAuth2.OAUTH2_ACCESS_TOKEN_URL.toString(), appid, secret, code));
JSONObject jsonObject = new JSONObject(json);
//获取网页授权的openid
wpuoRequest.setOpenid(jsonObject.get("openid") + "");
}
WxPayMpOrderResult wpuoReuslt = wxService.createOrder(wpuoRequest);
Map mapPay = new HashMap();
mapPay.put("appId", weixinPayConfig.get("payNo"));
mapPay.put("timeStamp", wpuoReuslt.getTimeStamp());
mapPay.put("nonceStr", wpuoReuslt.getNonceStr());
mapPay.put("package", wpuoReuslt.getPackageValue());
mapPay.put("signType", "MD5");
mapPay.put("sign", SignUtils.createSign(mapPay, "MD5", weixinPayConfig.get("payKey"), null));
response.sendRedirect(pay.getPage() + "?" + HttpUtil.toParams(mapPay));
} else {
// 扫码支付
wpuoRequest.setTradeType(TradeType.NATIVE);
wpuoRequest.setNotifyUrl(pay.getNotifyUrl());
wpuoRequest.setProductId(pay.getOrderNo());
WxPayNativeOrderResult wpuoReuslt = wxService.createOrder(wpuoRequest);
// 只返回二维码图片地址
return ResultData.build().success(wpuoReuslt.getCodeUrl());
}
} else if (PayBean.Type.WEIXIN_H5.equals(pay.getType())) {
//H5支付
wpuoRequest.setTradeType(TradeType.MWEB);
wpuoRequest.setNotifyUrl(pay.getNotifyUrl());
wpuoRequest.setProductId(pay.getOrderNo());
try {
WxPayMwebOrderResult wpuoReuslt = wxService.createOrder(wpuoRequest);
response.sendRedirect(wpuoReuslt.getMwebUrl() + "&redirect_url=" + HttpUtil.encodeParams(pay.getReturnUrl(), StandardCharsets.UTF_8));
} catch (WxPayException ex) {
LOG.info("H5支付失败");
return ResultData.build().error(ex.getErrCodeDes());
}
}
LOG.info("支付类型错误pay.getType(){}", pay.getType());
return ResultData.build().error(this.getResString("err.error", this.getResString("pay.type")));
}
/**
* 刷卡支付
* @param
* @param request
* @param response
*/
@ApiOperation(value = "刷卡支付接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "type", value = "支付类型", required = true, paramType = "query"),
@ApiImplicitParam(name = "orderPrice", value = "订单价格", required = true, paramType = "query"),
@ApiImplicitParam(name = "orderDesc", value = "订单描述", required = true, paramType = "query"),
@ApiImplicitParam(name = "authCode", value = "微信扫码支付授权码", required = true, paramType = "query"),
@ApiImplicitParam(name = "orderNo", value = "订单编号,不传时会自动生成", required = false, paramType = "query"),
})
@RequestMapping(value = "/micropay", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public ResultData micropay(PayBean pay, HttpServletRequest request,
HttpServletResponse response) {
Map weixinPayConfig = ConfigUtil.getMap(Const.WEIXIN_PAY_CONFIG_NAME);
WxPayService wxService = WeixinPayAction.buildPayService(weixinPayConfig, pay.getNotifyUrl());
// 获取支付实体
WxPayMicropayRequest micropayRequest = new WxPayMicropayRequest();
micropayRequest.setBody(pay.getOrderDesc());
micropayRequest.setOutTradeNo(pay.getOrderNo());
micropayRequest.setSpbillCreateIp(this.getHostIp());
micropayRequest.setTotalFee((int) (Double.parseDouble(pay.getOrderPrice()) * 100));
micropayRequest.setAuthCode(pay.getAuthCode());
//获取订单数据
PayLogEntity payLog = new PayLogEntity();
payLog.setOrderNo(pay.getOrderNo());
PayLogEntity newPayLog = (PayLogEntity) payLogBiz.getEntity(payLog);
if (newPayLog == null) {
throw new BusinessException(this.getResString("order.no.exist"));
}
WxPayMicropayResult wpmResult = null;
try {
//开始支付
wpmResult = wxService.micropay(micropayRequest);
//不需要密码直接支付成功
if (wpmResult.getResultCode().equalsIgnoreCase("SUCCESS")) {
if (newPayLog.getLogStatus().equals(LogStatusEnum.UN_PAY.toString())
&& newPayLog.getLogMoney() == Double.parseDouble(wpmResult.getTotalFee() + "") / 100) { // 判断存在的订单是否已经支付成功,如果没有支付成功就进行支付操作
newPayLog.setLogStatus(LogStatusEnum.PAY.toString());
newPayLog.setLogTransactionId(wpmResult.getTransactionId());
newPayLog.setLogPayType(PayBean.Type.WEIXIN);
newPayLog.setLogDate(new Date());
newPayLog.setUpdateDate(new Date());
newPayLog.setLogType(LogTypeEnum.OUTCOME.toString());
payLogBiz.updateEntity(newPayLog);
this.LOG.info("out_trade_no: " + wpmResult.getOutTradeNo() + " refund SUCCESS!");
return ResultData.build().success();
}
}
} catch (WxPayException e) {
//需要用户输入密码
int remainingTimeMs = 60 * 1000;
while (true) {
try {
WxPayOrderQueryResult queryResult = wxService.queryOrder("", pay.getOrderNo());
String state = queryResult.getTradeState();
if (state.equals("SUCCESS")) {
//支付成功
if (StringUtils.equals(newPayLog.getLogStatus(), LogStatusEnum.UN_PAY.toString())
&& newPayLog.getLogMoney() == Double.valueOf(queryResult.getTotalFee()) / 100) { // 判断存在的订单是否已经支付成功,如果没有支付成功就进行支付操作
newPayLog.setLogStatus(LogStatusEnum.PAY.toString());
newPayLog.setLogTransactionId(queryResult.getTransactionId());
newPayLog.setLogPayType(PayBean.Type.WEIXIN);
newPayLog.setLogDate(new Date());
newPayLog.setUpdateDate(new Date());
newPayLog.setLogType(LogTypeEnum.OUTCOME.toString());
payLogBiz.updateEntity(newPayLog);
this.LOG.info("out_trade_no: " + queryResult.getOutTradeNo() + " refund SUCCESS!");
return ResultData.build().success();
}
break;
}
if (queryResult.getResultCode().equals("SUCCESS")) {
if (state.equals("SYSTEMERROR") || state.equals("PAYERROR") || state.equals("USERPAYING")) {
remainingTimeMs = remainingTimeMs - 5 * 1000;
if (remainingTimeMs > 0) {
Thread.sleep(5 * 1000);
continue;
} else {
break;
}
} else {
break;
}
} else {
break;
}
} catch (WxPayException | InterruptedException ex) {
ex.printStackTrace();
}
}
}
return ResultData.build().error();
}
/**
* 支付回调
* 必须配合订单模块使用
*
* @param request
* @param response
*/
@ApiOperation(value = "微信支付回调")
@RequestMapping(value = "/notify", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public ResultData notify(HttpServletRequest request, HttpServletResponse response) {
// 获取支付实体
WxPayService payService = WeixinPayAction.buildPayService(ConfigUtil.getMap(Const.WEIXIN_PAY_CONFIG_NAME), null);
try {
synchronized (this) {
String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
WxPayOrderNotifyResult result = payService.parseOrderNotifyResult(xmlResult);
Map weixinPayConfig = ConfigUtil.getMap(Const.WEIXIN_PAY_CONFIG_NAME);
WxPayService wxService = WeixinPayAction.buildPayService(weixinPayConfig, "");
result.checkResult(wxService, result.getSign(), true);
if (result.getResultCode().equalsIgnoreCase("SUCCESS")) { // 支付成功
String orderNo = result.getOutTradeNo().split("\\|")[0]; // 需要通过分割特殊符合还原原始订单号
String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
// 自己处理订单的业务逻辑,需要判断订单是否已经支付过,否则可能会重复调用
// 根据订单ID查询订单信息
PayLogEntity payLog = new PayLogEntity();
payLog.setOrderNo(orderNo);
PayLogEntity newPayLog = (PayLogEntity) payLogBiz.getEntity(payLog);
if (newPayLog != null) {
if (StringUtils.equals(newPayLog.getLogStatus(), LogStatusEnum.UN_PAY.toString())
&& newPayLog.getLogMoney() == Double.parseDouble(totalFee)) { // 判断存在的订单是否已经支付成功,如果没有支付成功就进行支付操作
HashMap map = JSON.parseObject(URLDecoder.decode(result.getAttach()), HashMap.class);
newPayLog.setLogStatus(LogStatusEnum.PAY.toString());
newPayLog.setLogPayType(PayBean.Type.WEIXIN);
newPayLog.setLogTransactionId(result.getTransactionId());
newPayLog.setLogDate(new Date());
newPayLog.setUpdateDate(new Date());
newPayLog.setLogType(LogTypeEnum.OUTCOME.toString());
newPayLog.setPeopleId(map.get(Const.Attach.USER_ID).toString());
payLogBiz.updateEntity(newPayLog);
this.LOG.info("out_trade_no{},Attach " ,result.getOutTradeNo() ,result.getAttach());
if(StringUtils.isNotBlank(result.getAttach())) {
PayUtil.callPayNotfiy(newPayLog, URLDecoder.decode(result.getAttach()));
}
}
}
return ResultData.build().success(newPayLog);
}
}
} catch (Exception e) {
LOG.error("微信回调结果异常,异常原因 {}",e.getMessage());
// return WxPayNotifyResponse.fail(e.getMessage());
}
return ResultData.build().error(this.getResString("pay.notify.fail"));
}
/**
* 构建微信支付service
*
* @param notifiyUrl
* 异步回调方法
* @return
*/
public static WxPayService buildPayService(Map weixinPayConfig, String notifiyUrl) {
// 创建支付模块到配置
WxPayService wxService = new WxPayServiceImpl();
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(weixinPayConfig.get("payNo"));// 微信应用ID
payConfig.setMchId(weixinPayConfig.get("payPartner"));// 商户号
// 检查是否自定义了支付回调方法
if (StringUtils.isEmpty(notifiyUrl)) {
payConfig.setNotifyUrl(BasicUtil.getApp().getAppHostUrl() + Const.WEIXIN_NOTIFY_URL);// 接收微信支付成功通知(必须)
} else {
payConfig.setNotifyUrl(notifiyUrl);// 接收微信支付成功通知(必须)
}
payConfig.setMchKey(weixinPayConfig.get("payKey"));
if (StringUtils.isNotEmpty(weixinPayConfig.get("payResource"))) {
payConfig.setKeyPath(BasicUtil.getRealPath(weixinPayConfig.get("payResource"))); // 设置文件路径
}
payConfig.setSignType(SignType.MD5);
wxService.setConfig(payConfig);
return wxService;
}
}