package com.ruoyi.web.controller.front; import com.alibaba.fastjson.JSONObject; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result; import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.core.content.VistorContextHolder; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.enums.ECardPayStatus; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ShortMessageService; import com.ruoyi.voluntary.domain.BBusiEcardPrice; import com.ruoyi.voluntary.domain.BBusiPaymentOrders; import com.ruoyi.voluntary.service.IBBusiEcardPriceService; import com.ruoyi.voluntary.service.IBBusiPaymentOrdersService; import com.ruoyi.web.service.WeixinPayService; import com.ruoyi.web.util.IosVerifyUtil; import eu.bitwalker.useragentutils.UserAgent; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.*; @Api(tags = "前端 电子卡业务") @RestController @RequestMapping("front/ecard") @Slf4j public class FrontECardController extends BaseController { @Autowired private IBBusiEcardPriceService ecardPriceService; @Autowired private IBBusiPaymentOrdersService paymentOrdersService; @Autowired private ShortMessageService shortMessageService; @Autowired private WeixinPayService weixinPayService; @Autowired private WxPayService wxService; @Autowired private ISysConfigService sysConfigService; private static final String SUCCESS = "SUCCESS"; private static final String cardDesc = "在线购卡"; public static final String description = "VIP会员"; // @Value("${wxpay.pay.callback}") // private String payCallback; @Value("${wxpay.mchid}") private String mchId; @Value("${wxpay.key}") private String mchKey; // @Value("${wxpay.appid}") // private String appId = WxMaConfiguration.getAppId(); public String getMchKey() { return this.mchKey; } /** * { "amount": { "currency": "CNY", "payer_currency": "CNY", "payer_total": 1, "total": 1 }, "appid": "wx7b9ff116a897456a", "attach": "", "bank_type": "OTHERS", "mchid": "1629698256", "out_trade_no": "test_order_t1", "payer": { "openid": "o8uY95w2o4T-xq6e8tjKTDRr2tFg" }, "promotion_detail": [], "success_time": "2022-08-23T23:55:35+08:00", "trade_state": "SUCCESS", "trade_state_desc": "支付成功", "trade_type": "NATIVE", "transaction_id": "4200001565202208236598567532" } * @return */ @ApiOperation(value = "5 重新生成二维码") @GetMapping("/regenerateQrCode") public AjaxResult regenerateQrCode(@RequestParam Long orderId) { BBusiPaymentOrders order = paymentOrdersService.selectBBusiPaymentOrdersById(orderId); String codeUrl = StringUtils.EMPTY; if(StringUtils.isBlank(order.getQrcodeId())){ try { codeUrl = weixinPayService.createOrderPayUrl(order.getPhonenumber()+"_"+orderId, order.getPayFee(), "单招"+cardDesc, null); order.setQrcodeId(codeUrl); paymentOrdersService.updateBBusiPaymentOrders(order); } catch (Exception e) { weixinPayService.processWxPayFail(orderId, null, e.getMessage()); logger.error("QrCodeId: ", e); } }else { codeUrl = order.getQrcodeId(); } String qrCode = weixinPayService.encodeQrCode(codeUrl); JSONObject result = new JSONObject(); result.put("qrCode",qrCode); result.put("orderId",orderId); result.put("isPaySuccess",ECardPayStatus.isSuccess(order.getStatus())); return AjaxResult.success(result); } @ApiOperation(value = "4 获取订单状态") @GetMapping("/getOrderPayStatus") public AjaxResult getOrderPayStatus(@RequestParam Long orderId) { BBusiPaymentOrders order = paymentOrdersService.selectBBusiPaymentOrdersById(orderId); JSONObject result = new JSONObject(); result.put("isPaySuccess",ECardPayStatus.isSuccess(order.getStatus())); result.put("isPayFailed",ECardPayStatus.isFaild(order.getStatus())); result.put("isUnPaid",ECardPayStatus.isUnPaid(order.getStatus())); return AjaxResult.success(result); } @Operation(summary = "查询订单") @GetMapping("/queryOrder") public WxPayOrderQueryV3Result queryOrder(@RequestParam(required = false) String transactionId, @RequestParam(required = false) String outTradeNo) throws Exception { WxPayOrderQueryV3Result result = wxService.queryOrderV3(transactionId, outTradeNo); if("NOTPAY".equals(result.getTradeState())) { Integer timeout = NumberUtils.toInt(sysConfigService.selectConfigByKey("pay.order.timeout"), 60); Date timeoutDate = DateUtils.addSeconds(new Date(), -timeout); BBusiPaymentOrders poCond = new BBusiPaymentOrders(); poCond.setOutTradeNo(outTradeNo); poCond.setTransactionId(transactionId); List orders = paymentOrdersService.selectBBusiPaymentOrdersList(poCond); if(CollectionUtils.isEmpty(orders) || orders.get(0).getCreateTime().before(timeoutDate)) { result.setTradeState("CLOSED"); result.setTradeStateDesc("超时关闭"); } } return result; } @ApiOperation(value = "3 支付通知(回调)") @PostMapping("/payResult") public AjaxResult wxPayResult(HttpServletRequest request, @RequestHeader("wechatpay-serial") String serial, @RequestHeader("wechatpay-signature-type") String signatureType, @RequestHeader("wechatpay-timestamp") String timestamp, @RequestHeader("wechatpay-nonce") String nonce, @RequestHeader("wechatpay-signature") String signature, @RequestBody String json) { logger.error("----------------------开始处理微信支付回调--------------"); // String json = readBody(req); try { weixinPayService.processWxPayResult(serial, signatureType, timestamp, nonce, signature, json); } catch (Exception e) { logger.error("处理微信回调失败: " + signature + "\n" + json, e); return AjaxResult.error("解析异常"); } return AjaxResult.success(); } @ApiOperation("02 测试支付") @GetMapping(value = "checktest") public AjaxResult checkTest() { weixinPayService.checkWxPay(); return AjaxResult.success(); } @ApiOperation("02 测试支付") @GetMapping(value = "paytest") public AjaxResult paytest(String id, Integer amount, @RequestParam(defaultValue = "false") boolean h5, HttpServletRequest request) { try { String os = UserAgent.parseUserAgentString(request.getHeader("User-Agent")).getOperatingSystem().getName(); String ip = IpUtils.getIpAddr(request); WeixinPayService.PayOrderSceneInfo sceneInfo = h5 ? new WeixinPayService.PayOrderSceneInfo(ip, os) : null; String codeUrl = weixinPayService.createOrderPayUrl("test_pay_" + id, amount, "Test" + id, sceneInfo); return AjaxResult.success(weixinPayService.encodeQrCode(codeUrl)); } catch (Exception e) { logger.error("QrCodeId: ", e); return AjaxResult.error(e.getMessage()); } } @ApiOperation("02 下单生成地址") @PostMapping(value = "createOrder") public AjaxResult createOrder(HttpServletRequest request, Double totalFee, @RequestParam(required = false) String type) throws WxPayException { if (null==totalFee){ AjaxResult.error("金额不能为空"); } if(StringUtils.isBlank(type)) { type = "h5"; } SysUser u = SecurityUtils.getLoginUser().getUser(); Integer totalFee2 = totalFee.intValue(); BBusiPaymentOrders insertOrder = new BBusiPaymentOrders(); insertOrder.setCode(IdUtils.simpleUUID()); insertOrder.setType(cardDesc); // insertOrder.setOutTime(ecardPrice.getOutTime()); int num = 1; insertOrder.setNum(num); insertOrder.setPrice(totalFee2); insertOrder.setFee(totalFee2); insertOrder.setTotalFee(totalFee2*num); insertOrder.setPayFee(totalFee2*num); insertOrder.setBody(cardDesc+"_"+description+"_" + type); insertOrder.setPhonenumber(u.getPhonenumber()); insertOrder.setCustomerCode(String.valueOf(u.getUserId())); insertOrder.setYear(String.valueOf(Calendar.getInstance().get(Calendar.YEAR))); //状态(-2:已退费,-1:支付失败,0:无效,1:未支付,2:已支付) insertOrder.setStatus(ECardPayStatus.unpaid.getValue()); insertOrder.setCreateTime(new Date()); paymentOrdersService.insertBBusiPaymentOrders(insertOrder); Long orderId = insertOrder.getId(); if("ios".equals(type)) { Map data = new HashMap<>(); data.put("orderId", String.valueOf(orderId)); return AjaxResult.success(data); } WxPayUnifiedOrderV3Request order = new WxPayUnifiedOrderV3Request(); // Integer totalFee = 1; String notifyUrl = sysConfigService.selectConfigByKey("pay.callback.url") + "/front/ecard/payResult"; String outTradeNo = u.getPhonenumber() + "_" + orderId + "_" + u.getUserId() +"_"+System.currentTimeMillis(); if(outTradeNo.length() > 30) { outTradeNo = outTradeNo.substring(0,20) + outTradeNo.substring(outTradeNo.length() - 10); } order.setDescription(description).setOutTradeNo(outTradeNo); order.setNotifyUrl(notifyUrl).setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(totalFee2)); WxPayUnifiedOrderV3Request.SceneInfo sceneInfo = new WxPayUnifiedOrderV3Request.SceneInfo(); sceneInfo.setPayerClientIp(IpUtils.getIpAddr(request)); WxPayUnifiedOrderV3Request.H5Info h5Info = new WxPayUnifiedOrderV3Request.H5Info(); String os = UserAgent.parseUserAgentString(request.getHeader("User-Agent")).getOperatingSystem().getName(); h5Info.setType(os); sceneInfo.setH5Info(h5Info); order.setSceneInfo(sceneInfo); String h5Url = wxService.createOrderV3(TradeTypeEnum.H5, order); String prepayId = StringUtils.substringBetween(h5Url, "prepay_id=", "&package="); BBusiPaymentOrders upOrder = new BBusiPaymentOrders(); upOrder.setPrepayId(prepayId); upOrder.setDetail(h5Url); upOrder.setOutTradeNo(outTradeNo); upOrder.setId(orderId); paymentOrdersService.updateBBusiPaymentOrders(upOrder); Map data = new HashMap<>(); data.put("h5url", h5Url); data.put("outTradeNo", outTradeNo); return AjaxResult.success(data); } @ApiOperation("02 下单生成二维码") @GetMapping(value = "prepayCard") public AjaxResult prepayCard(Long ecardPayId, String mobile, String code) { Pair pair = prepayCard(ecardPayId, mobile, code, null); if(null == pair) { return AjaxResult.error("验证码校验失败"); } String qrCode = weixinPayService.encodeQrCode(pair.getRight()); JSONObject result = new JSONObject(); result.put("qrCode",qrCode); result.put("orderId",pair.getLeft()); return AjaxResult.success(result); } private Pair prepayCard(Long ecardPayId, String mobile, String code, WeixinPayService.PayOrderSceneInfo sceneInfo) { boolean isSuccess = shortMessageService.checkCode(mobile, code); if(!isSuccess){ return null; } //生成订单 BBusiEcardPrice ecardPrice = ecardPriceService.selectBBusiEcardPriceById(ecardPayId); //未支付订单,不要多次生成 BBusiPaymentOrders order= paymentOrdersService.selectBBusiPaymentOrdersNoPaid(mobile); Boolean isInsert = false; if(null==order){ order=new BBusiPaymentOrders(); isInsert = true; } order.setCode(IdUtils.simpleUUID()); order.setType(cardDesc); //cardId在回调中处理 // order.setCardId(1L); // order.setCardNo(""); order.setOutTime(ecardPrice.getOutTime()); int num = 1; order.setNum(num); order.setFee(ecardPrice.getPrice()); order.setTotalFee(ecardPrice.getPrice()*num); order.setPayFee(ecardPrice.getPrice()*num); order.setBody(cardDesc); order.setPhonenumber(mobile); order.setYear(String.valueOf(ecardPrice.getYear())); //状态(-2:已退费,-1:支付失败,0:无效,1:未支付,2:已支付) order.setStatus(ECardPayStatus.unpaid.getValue()); if(isInsert){ paymentOrdersService.insertBBusiPaymentOrders(order); }else { paymentOrdersService.updateBBusiPaymentOrders(order); } Long orderId = order.getId(); // TODO 调用微信支付生成二维码返回给前端 try { String codeUrl = weixinPayService.createOrderPayUrl(mobile+"_"+orderId+"_"+System.currentTimeMillis(), order.getPayFee(), "金鲤志愿"+cardDesc, sceneInfo); order.setQrcodeId(codeUrl); } catch (Exception e) { weixinPayService.processWxPayFail(orderId, null, e.getMessage()); logger.error("QrCodeId: ", e); } order.setPrepayId("");//预支付ID order.setTransactionId("");//交易ID paymentOrdersService.updateBBusiPaymentOrders(order); return Pair.of(orderId, order.getQrcodeId()); } @ApiOperation("01 获取电子卡价格") @Anonymous @GetMapping(value = "getEcardPrices") public AjaxResult getEcardPrices() { BBusiEcardPrice cond = new BBusiEcardPrice(); cond.setLocation(VistorContextHolder.getLocation()); cond.setExamType(VistorContextHolder.getExamType().name()); return AjaxResult.success(ecardPriceService.selectBBusiEcardPriceList(cond)); } @PostMapping("/iosVerifyResult") public AjaxResult iosPay(@RequestBody String payload) { log.info("苹果内购校验开始,base64校验体:{}", payload); JSONObject reqData = JSONObject.parseObject(payload); String receiptData; if(null == reqData || StringUtils.isBlank(receiptData = reqData.getString("receipt"))) { return error("苹果验证失败,返回数据为空"); } Long orderId = reqData.getLong("orderId"); String transactionId = reqData.getString("transId"); //线上环境验证 String verifyResult = IosVerifyUtil.buyAppVerify(receiptData, 1); if (verifyResult == null) { return error("苹果验证失败,返回数据为空"); } else { log.info("线上,{}苹果平台返回JSON:{}", orderId, verifyResult); JSONObject appleReturn = JSONObject.parseObject(verifyResult); String states = appleReturn.getString("status"); //无数据则沙箱环境验证 if ("21007".equals(states)) { verifyResult = IosVerifyUtil.buyAppVerify(receiptData, 0); log.info("沙盒环境,{}苹果平台返回JSON:{}", orderId, verifyResult); appleReturn = JSONObject.parseObject(verifyResult); states = appleReturn.getString("status"); } log.info("苹果平台返回值:appleReturn" + appleReturn); // 前端所提供的收据是有效的 验证成功 if (states.equals("0")) { String receipt = appleReturn.getString("receipt"); JSONObject returnJson = JSONObject.parseObject(receipt); String inApp = returnJson.getString("in_app"); List inApps = JSONObject.parseArray(inApp, HashMap.class); if (!org.apache.commons.collections.CollectionUtils.isEmpty(inApps)) { ArrayList transactionIds = new ArrayList(); for (HashMap app : inApps) { transactionIds.add((String) app.get("transaction_id")); } //交易列表包含当前交易,则认为交易成功 if (transactionIds.contains(transactionId)) { //处理业务逻辑 weixinPayService.processPaySuccess(orderId, transactionId, "SUCCESS", "ios", ""); log.info("交易成功,处理订单:{}",orderId); return success("交易成功"); } return error("当前交易不在交易列表中"); } return error("未能获取获取到交易列表"); } else { weixinPayService.processPaySuccess(orderId, transactionId, "FAILURE", "ios", states); return error("支付失败,错误码:" + states); } } } // https://blog.csdn.net/Arhhhhhhh/article/details/130082795?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-130082795-blog-121291537.235^v43^pc_blog_bottom_relevance_base6&spm=1001.2101.3001.4242.1&utm_relevant_index=2 // https://developer.apple.com/documentation/appstoreserverapi/get-v2-refund-lookup-_transactionid_ }