All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.mingsoft.pay.action.web.WeixinPayAction Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2012-present 铭软科技(mingsoft.net)
 * 本软件及相关文档文件(以下简称“软件”)的版权归 铭软科技 所有
 * 遵循 铭软科技《服务协议》中的《保密条款》
 */


package net.mingsoft.pay.action.web;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
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.WxPayMwebOrderResult;
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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.mp.enums.WxMpApiUrl;
import net.mingsoft.base.entity.ResultData;
import net.mingsoft.base.exception.BusinessException;
import net.mingsoft.basic.util.BasicUtil;
import net.mingsoft.mdiy.util.ConfigUtil;
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 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.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 铭软科技 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 (StringUtils.isEmpty(pay.getAppId()) && StringUtils.isEmpty(pay.getKey()) && StringUtils.isEmpty(pay.getSecret()) && StringUtils.isEmpty(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 (StringUtils.isNotEmpty(pay.getAttach())) { wpuoRequest.setAttach(pay.getAttach()); } WxPayService wxService = WeixinPayAction.buildPayService(weixinPayConfig, pay.getNotifyUrl()); LOG.debug("开始判断支付类型:{}",pay.getType()); if (PayBean.Type.WEIXIN_APP.equals(pay.getType())) { LOG.debug("支付类型WEIXIN_APP"); //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())) { LOG.debug("支付类型WEIXIN"); // 公众号支付 LOG.debug("开始判断是否公众号isMobile:{}",pay.isMobile()); if (pay.isMobile()) { LOG.debug("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); // 设置支付回调地址 wpuoRequest.setNotifyUrl(pay.getNotifyUrl()); LOG.debug("开始判断openId"); if (StringUtils.isNotEmpty(pay.getWeixinOpenId())) { //获取接口传递的openid LOG.debug("存在 getWeixinOpenId"); wpuoRequest.setOpenid(pay.getWeixinOpenId()); } else { // 如果没有正常获取到用户的信息 LOG.debug("code {}",code); if (StringUtils.isEmpty(code)) { return ResultData.build().error(this.getResString("get.people.fail")); } //微信授权地址 LOG.debug("前往授权的参数: appid:{}、secret:{}、code:{}",appid,secret,code); String json = HttpUtil.get(String.format(WxMpApiUrl.OAuth2.OAUTH2_ACCESS_TOKEN_URL.getPrefix()+WxMpApiUrl.OAuth2.OAUTH2_ACCESS_TOKEN_URL.getPath(), appid, secret, code)); LOG.debug("授权返回json数据:{}",json); JSONObject jsonObject = new JSONObject(json); // 错误信息容错处理 if (jsonObject.get("errcode")!=null){ LOG.debug("微信支付错误"); throw new BusinessException("创建订单失败,{}",jsonObject.get("errmsg")); } LOG.debug("用户openid:{}",jsonObject.get("openid").toString()); //获取网页授权的openid wpuoRequest.setOpenid(jsonObject.get("openid") + ""); } WxPayMpOrderResult wpuoReuslt = wxService.createOrder(wpuoRequest); Map mapPay = new HashMap(); mapPay.put("wxAppId", weixinPayConfig.get("payNo")); mapPay.put("timeStamp", wpuoReuslt.getTimeStamp()); mapPay.put("nonceStr", wpuoReuslt.getNonceStr()); mapPay.put("package", wpuoReuslt.getPackageValue()); mapPay.put("signType", "MD5"); LOG.debug("wpuoReuslt {}",JSONUtil.toJsonStr(wpuoReuslt)); mapPay.put("sign", wpuoReuslt.getPaySign()); LOG.debug("支付参数:"+HttpUtil.toParams(mapPay)); // 支付价格需展示 mapPay.put("price",pay.getOrderPrice()); // 标题 mapPay.put("orderName",pay.getOrderName()); // 支付成功跳转页面 mapPay.put("returnUrl",pay.getReturnUrl()); // TODO: 2023/7/17 ?号拼接后的数据都是经过微信平台处理之后的数据,就算用户篡改数据也不会导致支付出问题,自定义页面中通过${}获取?后面的参数 response.sendRedirect(pay.getPage() + "?" + HttpUtil.toParams(mapPay)); } else { LOG.debug("no isMobile"); // 扫码支付 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())) { LOG.debug("支付类型WEIXIN_H5"); //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.debug("H5支付失败: "+ex.getReturnMsg()); throw new BusinessException(ex.getReturnCode()+ex.getErrCode()); } } 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 = JSONUtil.toBean(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.debug("微信回调结果异常,异常原因 {}",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"))) { List list = JSONUtil.toList(weixinPayConfig.get("payResource"),Map.class); payConfig.setKeyPath(BasicUtil.getRealPath(list.get(0).get("path").toString())); // 设置文件路径 } payConfig.setSignType(SignType.MD5); wxService.setConfig(payConfig); return wxService; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy