package com.ishop.mobile.api; import com.iplatform.base.ArgumentsConstants; import com.iplatform.base.PlatformRuntimeException; import com.iplatform.base.VariableConstants; import com.iplatform.base.WechatConstants; import com.iplatform.base.util.PlatformRSAUtils; import com.ishop.merchant.Constants; import com.ishop.merchant.OrderConstants; import com.ishop.merchant.PayConstants; import com.ishop.merchant.util.PayUtils; import com.ishop.mobile.BaseApi; import com.ishop.mobile.util.WechatUtils; import com.ishop.model.po.EbOrder; import com.ishop.model.po.EbUser; import com.ishop.model.po.EbUserConfig; import com.ishop.model.request.OrderPayRequest; import com.ishop.model.response.OrderPayResultResponse; import com.ishop.model.response.PayConfigResponse; import com.ishop.model.vo.WechatOrderVo; import com.ishop.model.vo.WxPayJsResultVo; import com.ishop.model.wechat.AttachVo; import com.ishop.model.wechat.CreateOrderH5SceneInfoDetailVo; import com.ishop.model.wechat.CreateOrderH5SceneInfoVo; import com.walker.infrastructure.utils.DateUtils; import com.walker.infrastructure.utils.JsonUtils; import com.walker.infrastructure.utils.MD5; import com.walker.infrastructure.utils.StringUtils; import com.walker.pay.Order; import com.walker.pay.PayEngineManager; import com.walker.pay.PayStatus; import com.walker.pay.exception.OrderException; import com.walker.pay.response.OrderStatusResponsePay; import com.walker.pay.wechat.v2.H5ResponsePay; import com.walker.web.ResponseValue; import com.walker.web.WebUserAgent; import com.walker.web.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/front/pay") public class PayApi extends BaseApi { // 使用支付引擎完成微信等第三方支付工作。2023-08-10 private PayEngineManager payEngineManager; @Autowired public PayApi(PayEngineManager payEngineManager){ this.payEngineManager = payEngineManager; } /** * 查询微信订单结果 * @param orderNo * @return * @date 2023-08-30 */ @RequestMapping(value = "/query/wechat/pay/result/{orderNo}", method = RequestMethod.GET) public ResponseValue searchWechatPayOrder(@PathVariable(value = "orderNo") String orderNo){ if(StringUtils.isEmpty(orderNo)){ return ResponseValue.error("orderNo必须输入"); } EbOrder order = this.getOrderService().queryOrder(orderNo); if(order == null){ return ResponseValue.error("订单不存在,orderId={}" + orderNo); } if (order.getCancelStatus() > OrderConstants.ORDER_CANCEL_STATUS_NORMAL) { return ResponseValue.error("订单已取消"); } OrderStatusResponsePay responsePay = this.payEngineManager.searchOrderStatus(String.valueOf(order.getId())); if(responsePay == null){ logger.error("未查询到微信订单状态,orderNo = " + orderNo); return ResponseValue.success(false); } if(!responsePay.getStatus()){ logger.error("查询微信订单状态失败:" + responsePay.getMessage()); return ResponseValue.success(false); } if(responsePay.getPayStatus() != PayStatus.Success){ logger.error("查询微信订单状态【未成功】:" + responsePay.getMessage()); return ResponseValue.success(false); } return ResponseValue.success(true); } /** * 订单支付,发起预订单。 * @return * @date 2023-07-12 */ @RequestMapping(value = "/payment", method = RequestMethod.POST) public ResponseValue payment(@RequestBody OrderPayRequest request){ if(request == null || StringUtils.isEmpty(request.getOrderNo())){ return ResponseValue.error("预订单信息不存在"); } logger.debug("支付方式 = " + request.getPayType()); String orderNo = request.getOrderNo(); EbOrder order = this.getOrderService().queryOrder(orderNo); if(order == null){ return ResponseValue.error("订单不存在"); } if (order.getCancelStatus().intValue() > OrderConstants.ORDER_CANCEL_STATUS_NORMAL) { return ResponseValue.error("订单已取消"); } if (order.getPaid().intValue() == 1) { return ResponseValue.error("订单已支付"); } if (order.getStatus().intValue() > OrderConstants.ORDER_STATUS_WAIT_PAY) { return ResponseValue.error("订单状态异常"); } // 2023-09-09,检查商品是否虚拟商品,如果无需发货,直接修改订单状态为:已完成。 // boolean isAutoShippingDone = true; // List productIds = this.getOrderService().queryOrderProductIds(orderNo); // if(StringUtils.isEmptyList(productIds)){ // throw new IllegalStateException("未查询到订单明细商品,orderNo=" + orderNo); // } // int templateId = 0; // for(long productId : productIds){ // templateId = this.getProductCache().get(productId).getTempId(); // if(templateId != Constants.SHIPPING_TEMPLATE_ID_NO_SEND){ // isAutoShippingDone = false; // break; // } // } // 余额支付 long userId = this.getCurrentUserId(); EbUser user = this.getUserRegCache().get(userId); if (request.getPayType().equals(PayConstants.PAY_TYPE_YUE)) { if(user.getNowMoney().doubleValue() < order.getPayPrice().doubleValue()){ return ResponseValue.error("账户余额不足"); } if(!this.checkPayPass(request.getPayPass(), userId)){ return ResponseValue.error("支付密码错误"); } } OrderPayResultResponse response = new OrderPayResultResponse(); response.setOrderNo(order.getOrderNo()); response.setPayType(request.getPayType()); response.setPayChannel(request.getPayChannel()); // 根据支付类型进行校验,更换支付类型 EbOrder updateOrder = new EbOrder(order.getId()); updateOrder.setPayType(request.getPayType()); updateOrder.setPayChannel(request.getPayChannel()); updateOrder.setOrderNo(order.getOrderNo()); updateOrder.setUpdateTime(DateUtils.getDateTimeNumber()); logger.debug("当前支付渠道,payChannel = {}", request.getPayChannel()); // 余额支付 if (request.getPayType().equals(PayConstants.PAY_TYPE_YUE)) { // 2023-09-09 if(PayUtils.isAutoShippingDone(orderNo)){ // 当余额支付是,虚拟商品自动完成订单,无需发货 updateOrder.setStatus(OrderConstants.ORDER_STATUS_COMPLETE); logger.debug("虚拟商品,自动发货完成"); } else { order.setStatus(OrderConstants.ORDER_STATUS_WAIT_SHIPPING); } this.getPayService().execPayYue(updateOrder, order.getPayPrice().doubleValue(), userId, VariableConstants.TOKEN_SECRET, user); this.getUserRegCache().update(user); // 更新用户缓存 response.setStatus(true); logger.debug("余额支付订单成功"); } else if (request.getPayType().equals(PayConstants.PAY_TYPE_WE_CHAT)) { // 微信支付,调用微信预下单,返回拉起微信支付需要的信息 // String userOpenId = this.getCurrentUser().getWx_open_id(); // logger.debug("userOpenId={}", userOpenId); // // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // 2023-08-28 不知道为啥openId字符串会带双引号, // // 目前没找到原因,先直接剔除,后面要找到(初步估计是redis取出来带的) // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // if(StringUtils.isNotEmpty(userOpenId) && userOpenId.indexOf("\"") >= 0){ // userOpenId = userOpenId.replaceAll("\"", StringUtils.EMPTY_STRING); // } // //~~~~~~~~~~~~~~~~~~~~~~~ end ~~~~~~~~~~~~~~~~~~~~~~ WxPayJsResultVo vo = null; if(request.getPayChannel().equals(PayConstants.PAY_CHANNEL_WECHAT_PUBLIC) || request.getPayChannel().equals(PayConstants.PAY_CHANNEL_H5)){ // 微信公众号、H5支付 String userOpenId = PayUtils.filterUserOpenId(this.getCurrentUser().getWx_open_id()); logger.debug("userOpenId={}", userOpenId); if(StringUtils.isEmpty(userOpenId)){ return ResponseValue.error("您还未授权(绑定)微信登录,无法完成微信支付"); } vo = this.wechatH5Order(order, userOpenId); } else if (request.getPayChannel().equals(PayConstants.PAY_CHANNEL_WECHAT_MINI)) { // 微信小程序支付 String userMiniOpenId = PayUtils.filterUserOpenId(this.getCurrentUser().getWx_union_id()); logger.debug("userMiniOpenId={}", userMiniOpenId); if(StringUtils.isEmpty(userMiniOpenId)){ return ResponseValue.error("您还未授权(绑定小程序)微信登录,无法完成微信支付"); } vo = this.wechatMiniOrder(order, userMiniOpenId); } else { throw new UnsupportedOperationException("还未实现微信其他支付方式:" + request.getPayChannel()); } response.setStatus(true); response.setJsConfig(vo); logger.debug("微信预下单成功"); } else if(request.getPayType().equals(PayConstants.PAY_TYPE_ALI_PAY)){ throw new UnsupportedOperationException("暂未实现支付宝支付"); } else { throw new UnsupportedOperationException("不支持的支付方式:" + request.getPayType()); } return ResponseValue.success(response); } private boolean checkPayPass(String payPass, long userId){ EbUserConfig config = this.getUserRegConfigCache().get(userId); if(StringUtils.isEmpty(config.getPayPwd())){ throw new PlatformRuntimeException("用户尚未设置支付密码"); } try{ String originPassword = PlatformRSAUtils.getAesDecryptValue(payPass); if(this.matchesPassword(originPassword, config.getPayPwd())){ return true; } logger.error("支付密码输入错误,originPassword={}", originPassword); return false; } catch (Exception ex){ throw new PlatformRuntimeException("解析支付密码错误:" + ex.getMessage(), ex); } } private WxPayJsResultVo wechatMiniOrder(EbOrder order, String userMiniOpenId){ String apiDomain = this.getArgumentVariable(Constants.CONFIG_KEY_API_URL).getStringValue(); String siteName = this.getArgumentVariable(Constants.CONFIG_KEY_SITE_NAME).getStringValue(); String signKey = this.getArgumentVariable(WechatConstants.WECHAT_PAY_MINI_KEY).getStringValue(); String attach = PayConstants.PAY_SERVICE_TYPE_ORDER + "," + order.getUid(); // final WebUserAgent webUserAgent = this.getCurrentWebUserAgent(); double payPriceFen = order.getPayPrice().doubleValue() * 100; Order platformPayOrder = PayUtils.acquirePlatformOrderRoutineWechatV2((long)payPriceFen , order.getId(), siteName, attach, "127.0.0.1", apiDomain + PayConstants.WX_PAY_NOTIFY_API_URI, userMiniOpenId); return this.acquireWechatNormalPayResultVo(platformPayOrder, signKey); } private WxPayJsResultVo wechatH5Order(EbOrder order, String userOpenId){ String apiDomain = this.getArgumentVariable(Constants.CONFIG_KEY_API_URL).getStringValue(); String siteName = this.getArgumentVariable(Constants.CONFIG_KEY_SITE_NAME).getStringValue(); String signKey = this.getArgumentVariable(WechatConstants.WECHAT_PAY_PUBLIC_KEY).getStringValue(); String attach = PayConstants.PAY_SERVICE_TYPE_ORDER + "," + order.getUid(); // final WebUserAgent webUserAgent = this.getCurrentWebUserAgent(); double payPriceFen = order.getPayPrice().doubleValue() * 100; // 2023-08-28 注意:这里使用 webUserAgent.getIp()总是导致签名报错,暂时ip不管。 Order platformPayOrder = PayUtils.acquirePlatformOrderH5WechatV2((long)payPriceFen , order.getId(), siteName, attach, "127.0.0.1", apiDomain + PayConstants.WX_PAY_NOTIFY_API_URI, userOpenId); // logger.debug(platformPayOrder.toString()); // H5ResponsePay responsePay = null; // try { // responsePay = (H5ResponsePay) this.payEngineManager.generatePrepareOrder(platformPayOrder); // } catch (OrderException e) { // throw new PlatformRuntimeException("发起微信预订单错误:" + e.getMessage() + ", orderId=" + e.getOrderId(), e); // } // if(!responsePay.getStatus()){ // throw new PlatformRuntimeException("微信支付H5订单返回错误:" + responsePay.getMessage(), null); // } // // Map map = new HashMap<>(); // map.put("appId", responsePay.getAppId()); // map.put("nonceStr", responsePay.getAppId()); // map.put("package", "prepay_id=" + responsePay.getPrepayId()); // map.put("signType", MD5.MD5_NAME); // map.put("timeStamp", String.valueOf(System.currentTimeMillis()/1000)); // // WxPayJsResultVo vo = new WxPayJsResultVo(); // vo.setAppId(responsePay.getAppId()); // vo.setNonceStr(responsePay.getAppId()); // vo.setPackages("prepay_id=" + responsePay.getPrepayId()); // vo.setSignType(MD5.MD5_NAME); // vo.setTimeStamp(String.valueOf(System.currentTimeMillis()/1000)); // vo.setMwebUrl(responsePay.getCodeUrl()); // try { // vo.setPaySign(WechatUtils.getSign(map, signKey)); // } catch (Exception e) { // throw new PlatformRuntimeException("设置返回值签名错误:" + e.getMessage(), e); // } // return vo; return this.acquireWechatNormalPayResultVo(platformPayOrder, signKey); } private WxPayJsResultVo acquireWechatNormalPayResultVo(Order platformPayOrder, String signKey){ H5ResponsePay responsePay = null; try { responsePay = (H5ResponsePay) this.payEngineManager.generatePrepareOrder(platformPayOrder); } catch (OrderException e) { throw new PlatformRuntimeException("发起微信预订单错误:" + e.getMessage() + ", orderId=" + e.getOrderId(), e); } if(!responsePay.getStatus()){ throw new PlatformRuntimeException("微信支付H5订单返回错误:" + responsePay.getMessage(), null); } Map map = new HashMap<>(); map.put("appId", responsePay.getAppId()); map.put("nonceStr", responsePay.getAppId()); map.put("package", "prepay_id=" + responsePay.getPrepayId()); map.put("signType", MD5.MD5_NAME); map.put("timeStamp", String.valueOf(System.currentTimeMillis()/1000)); WxPayJsResultVo vo = new WxPayJsResultVo(); vo.setAppId(responsePay.getAppId()); vo.setNonceStr(responsePay.getAppId()); vo.setPackages("prepay_id=" + responsePay.getPrepayId()); vo.setSignType(MD5.MD5_NAME); vo.setTimeStamp(String.valueOf(System.currentTimeMillis()/1000)); vo.setMwebUrl(responsePay.getCodeUrl()); try { vo.setPaySign(WechatUtils.getSign(map, signKey)); } catch (Exception e) { throw new PlatformRuntimeException("设置返回值签名错误:" + e.getMessage(), e); } // order.setOutTradeNo(responsePay); return vo; } @Deprecated private WechatOrderVo acquireWechatOrderVo(EbOrder order, String openid) throws Exception{ WechatOrderVo vo = new WechatOrderVo(); final WebUserAgent webUserAgent = this.getCurrentWebUserAgent(); String domain = this.getArgumentVariable(Constants.CONFIG_KEY_SITE_URL).getStringValue(); String siteName = this.getArgumentVariable(Constants.CONFIG_KEY_SITE_NAME).getStringValue(); String apiDomain = this.getArgumentVariable(Constants.CONFIG_KEY_API_URL).getStringValue(); AttachVo attachVo = new AttachVo(PayConstants.PAY_SERVICE_TYPE_ORDER, order.getUid()); vo.setAppid(this.getArgumentVariable(WechatConstants.WECHAT_PUBLIC_APPID).getStringValue()); vo.setMch_id(this.getArgumentVariable(WechatConstants.WECHAT_PAY_PUBLIC_MCHID).getStringValue()); vo.setNonce_str(UUID.randomUUID().toString().replace("-", "")); vo.setSign_type(MD5.MD5_NAME); // 因商品名称在微信侧超长更换为网站名称 vo.setBody(siteName); vo.setAttach(JsonUtils.objectToJsonString(attachVo)); vo.setOut_trade_no(order.getOrderNo()); // 系统订单号 double fenTotal = order.getPayPrice()*100; // 元转为分 vo.setTotal_fee((long)fenTotal); vo.setSpbill_create_ip(webUserAgent.getIp()); vo.setNotify_url(apiDomain + PayConstants.WX_PAY_NOTIFY_API_URI); switch (order.getPayChannel()) { case PayConstants.PAY_CHANNEL_H5: vo.setTrade_type(PayConstants.WX_PAY_TRADE_TYPE_H5); vo.setOpenid(null); break; case PayConstants.PAY_CHANNEL_WECHAT_APP_IOS: case PayConstants.PAY_CHANNEL_WECHAT_APP_ANDROID: vo.setTrade_type(PayConstants.WX_PAY_TRADE_TYPE_APP); vo.setOpenid(null); break; default: vo.setTrade_type(PayConstants.WX_PAY_TRADE_TYPE_JS); vo.setOpenid(openid); } CreateOrderH5SceneInfoVo createOrderH5SceneInfoVo = new CreateOrderH5SceneInfoVo( new CreateOrderH5SceneInfoDetailVo(domain,siteName)); vo.setScene_info(JsonUtils.objectToJsonString(createOrderH5SceneInfoVo)); String voJson = JsonUtils.objectToJsonString(vo); String sign = WechatUtils.getSign(voJson, this.getArgumentVariable(WechatConstants.WECHAT_PAY_PUBLIC_KEY).getStringValue()); vo.setSign(sign); return vo; } /** * 获取支付配置 * @return */ @RequestMapping(value = "/get/config", method = RequestMethod.GET) public ResponseValue getPayConfig(){ String payWxOpen = this.getArgumentVariable(PayConstants.CONFIG_PAY_WECHAT_OPEN).getStringValue(); String yuePayStatus = this.getArgumentVariable(PayConstants.CONFIG_YUE_PAY_STATUS).getStringValue(); String aliPayStatus = this.getArgumentVariable(PayConstants.CONFIG_ALI_PAY_STATUS).getStringValue(); PayConfigResponse response = new PayConfigResponse(); response.setYuePayStatus(ArgumentsConstants.CONFIG_FORM_SWITCH_OPEN.equals(yuePayStatus)); response.setPayWechatOpen(ArgumentsConstants.CONFIG_FORM_SWITCH_OPEN.equals(payWxOpen)); response.setAliPayStatus(ArgumentsConstants.CONFIG_FORM_SWITCH_OPEN.equals(aliPayStatus)); if (ArgumentsConstants.CONFIG_FORM_SWITCH_OPEN.equals(yuePayStatus)) { EbUser user = this.getCurrentEbUser(); response.setUserBalance(user.getNowMoney()); // 2023-08-07,是否存在支付密码 EbUserConfig config = this.getUserRegConfigCache().get(user.getId()); response.setPayPass(StringUtils.isNotEmpty(config.getPayPwd())); } return ResponseValue.success(response); } }