学支付的无聊日子

正值青春的年纪,却过着每天学习的无聊生活。

一 支付的种类

1 微信支付

官方文档

1.1 小程序支付

产品介绍文档

  1. 用户通过分享或扫描二维码进入商户小程序,用户选择购买,完成选购流程;
  2. 调起微信支付控件,用户开始输入支付密码;
  3. 密码验证通过,支付成功。商户后台得到支付成功的通知;

  4. 返回商户小程序,显示购买成功;

  5. 微信支付公众号下发支付凭证。

(1)生成支付订单

   

(2)用户付款

付款

    @Login
    @PostMapping("/microAppPayOrder")
    @ApiOperation("小程序付款")
    public R microAppPayOrder(@RequestBody PayOrderForm form, @RequestHeader HashMap header) {
        ValidatorUtils.validateEntity(form);
        String token = header.get("token").toString();
        Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = form.getOrderId();

        UserEntity user = new UserEntity();
        user.setUserId(userId);
        QueryWrapper wrapper = new QueryWrapper(user);
        long count = userService.count(wrapper);
        //判断用户是否存在
        if (count == 0) {
            return R.error("用户不存在");
        }
        //获取用户账号的openid字符串
        String openId = userService.getOne(wrapper).getOpenId();

        //判断用户是否拥有这个订单,并且订单是未支付状态
        OrderEntity order = new OrderEntity();
        order.setUserId(userId.intValue());
        order.setId(orderId);
        order.setStatus(1);
        wrapper = new QueryWrapper(order);
        count = orderService.count(wrapper);
        if (count == 0) {
            return R.error("不是有效的订单");
        }
        //验证购物券是否有效
        //验证团购活动是否有效
        //查询订单详情信息
        order = new OrderEntity();
        order.setId(orderId);
        wrapper = new QueryWrapper(order);
        order = orderService.getOne(wrapper); //订单对象
        String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + ""; //订单金额

        try {
            //向微信平台发出请求,创建支付订单
            WXPay wxPay = new WXPay(myWXPayConfig);
            HashMap map = new HashMap();
            map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
            map.put("body", "订单备注");
            map.put("out_trade_no", order.getCode()); //商品订单流水号
            map.put("total_fee", amount); //订单金额
            map.put("spbill_create_ip", "127.0.0.1"); //客户端IP
            map.put("notify_url", "http://66931e50.nat1.nsloop.com/renren-fast/app/wx/recieveMessage"); //通知回调地址
            map.put("trade_type", "JSAPI");  //调用接口类型
            map.put("openid", openId); //用户授权

            Map<String, String> result = wxPay.unifiedOrder(map); //创建支付订单
            String prepayId = result.get("prepay_id"); //获取支付订单ID
//            System.out.println(prepayId);
            if (prepayId != null) {
                //更新本地商品订单信息,但是不更新商品订单状态
                order.setPrepayId(prepayId); //保存支付订单ID
                order.setPaymentType(1); //支付类型
                UpdateWrapper updateWrapper = new UpdateWrapper();
                updateWrapper.eq("id", order.getId());
                orderService.update(order, updateWrapper);

                //对返回给小程序的数据生成数字签名
                map.clear();
                map.put("appId", appId);
                String timeStamp = new Date().getTime() + "";
                map.put("timeStamp", timeStamp);
                String nonceStr = WXPayUtil.generateNonceStr();
                map.put("nonceStr", nonceStr);
                map.put("package", "prepay_id=" + prepayId);
                map.put("signType", "MD5");
                String paySign = WXPayUtil.generateSignature(map, key); //生成数字签名
                return R.ok().put("package", "prepay_id=" + prepayId)
                        .put("timeStamp", timeStamp)
                        .put("nonceStr", nonceStr)
                        .put("paySign", paySign);
            } else {
                return R.error("支付订单创建失败");
            }


        } catch (Exception e) {
            e.printStackTrace();
            return R.error("微信支付模块故障");
        }

    }

接收回调信息

    @ApiOperation("接收消息通知")
    @RequestMapping("/recieveMessage")
    public void recieveMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("utf-8");
        Reader reader = request.getReader();
        BufferedReader buffer = new BufferedReader(reader);
        String line = buffer.readLine();
        StringBuffer temp = new StringBuffer();
        while (line != null) {
            temp.append(line);
            line = buffer.readLine();
        }
        buffer.close();
        reader.close();
        String xml=temp.toString();
        if(WXPayUtil.isSignatureValid(xml,key)){
            Map<String, String> map = WXPayUtil.xmlToMap(temp.toString());
            String resultCode = map.get("result_code");
            String returnCode = map.get("return_code");
            if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)) {
                String outTradeNo = map.get("out_trade_no");
                UpdateWrapper wrapper = new UpdateWrapper();
                wrapper.eq("code", outTradeNo);
                wrapper.set("status", 2);
                // 更新订单支付状态
                orderService.update(wrapper);
                response.setCharacterEncoding("utf-8");
                response.setContentType("application/xml");
                Writer writer = response.getWriter();
                BufferedWriter bufferedWriter = new BufferedWriter(writer);
                bufferedWriter.write("<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>");
                bufferedWriter.close();
                writer.close();
            }
        }
        else{
            response.sendError(500,"数字签名异常");
        }

    }

如果微信平台出现技术故障,支付模块正常,但是消息模块出了问题,那么就会出现支付成功,但是消息发布出去。商户系统自然也就收不到支付结果的通知了。

如果其中一端出现了网络故障,商户系统都不能收到消息通知。

商户系统接收不到支付结果的时候,需要主动去查询支付的结果,到底成功了还是失败了?商户系统什么时候应该发起主动查询呢?难道还要写个定时程序吗?

当然不需要。当小程序上面提示支付成功以后,那么小程序就像商户系统发出 Ajax 请求,告诉商户系统我已经收到付款成功的通知啦,你也去查询一下,这时候商户系统才需要发起主动查询。在小程序上面的SUCCESS回调函能执行,就意味着支付成功了。我们在SUCCESS回调函数里面向商户系统发起Ajax请求即可。商户平台接收到小程序的请求之后,根据支付订单 ID 去查询微信平台,如果已经付款成功,就修改本地商品订单为已支付状态。

主动查询支付结果

    @Login
    @PostMapping("/updateOrderStatus")
    @ApiOperation("更新商品订单状态")
    public R updateOrderStatus(@RequestBody UpdateOrderStatusForm form,
                               @RequestHeader HashMap header) {
        ValidatorUtils.validateEntity(form);
        String token = header.get("token").toString();
        int userId = Integer.parseInt(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = form.getOrderId();
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setUserId(userId);
        orderEntity.setId(orderId);
        QueryWrapper wrapper = new QueryWrapper(orderEntity);
        int count = orderService.count(wrapper);
        if (count == 0) {
            return R.error("用户与订单不匹配");
        }
        orderEntity = orderService.getOne(wrapper);
        String code = orderEntity.getCode();
        HashMap map = new HashMap();
        map.put("appid", appId);
        map.put("mch_id", mchId);
        map.put("out_trade_no", code);
        map.put("nonce_str", WXPayUtil.generateNonceStr());
        try {
            String sign = WXPayUtil.generateSignature(map, key);
            map.put("sign", sign);
            WXPay wxPay = new WXPay(myWXPayConfig);
            Map<String, String> result = wxPay.orderQuery(map);
            String returnCode = result.get("return_code");
            String resultCode = result.get("result_code");
            if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
                String tradeState = result.get("trade_state");
                if ("SUCCESS".equals(tradeState)) {
                    UpdateWrapper updateWrapper = new UpdateWrapper();
                    updateWrapper.eq("code", code);
                    updateWrapper.set("status", 2);
                    updateWrapper.set("payment_type",1);
                    orderService.update(updateWrapper);
                    return R.ok("订单状态已修改");
                } else {
                    return R.ok("订单状态未修改");
                }
            }
            return R.ok("订单状态未修改");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("查询支付订单失败");
        }
    }

1.2 微信 Native 支付

产品介绍文档

  1. 商户根据微信支付的规则,为不同商品生成不同的二维码,展示在各种场景,用于用户扫描购买;
  2. 用户使用微信“扫一扫”扫描二维码后,获取商品支付信息;
  3. 用户确认支付,输入支付密码;
  4. 支付完成后会提示用户支付成功,商户后台得到支付成功的通知。

(1)生成支付订单

生成支付订单

    @Login
    @PostMapping("/nativePayOrder")
    @ApiOperation("native付款")
    public R nativePayOrder(@RequestBody PayOrderForm form, @RequestHeader HashMap header) {
        ValidatorUtils.validateEntity(form);
        String token = header.get("token").toString();
        Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = form.getOrderId();
        UserEntity user = new UserEntity();
        user.setUserId(userId);
        QueryWrapper wrapper = new QueryWrapper(user);
        long count = userService.count(wrapper);
        if (count == 0) {
            return R.error("用户不存在");
        }

        OrderEntity order = new OrderEntity();
        order.setUserId(userId.intValue());
        order.setId(orderId);
        order.setStatus(1);
        wrapper = new QueryWrapper(order);
        count = orderService.count(wrapper);
        if (count == 0) {
            return R.error("不是有效的订单");
        }
        //验证购物券是否有效
        //验证团购活动是否有效

        order = new OrderEntity();
        order.setId(orderId);
        wrapper = new QueryWrapper(order);
        order = orderService.getOne(wrapper);
        //向微信平台发出请求,创建支付订单
        String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";

        try {
            WXPay wxPay = new WXPay(myWXPayConfig);
            HashMap map = new HashMap();
            map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
            map.put("body", "订单备注");
            map.put("out_trade_no", order.getCode());
            map.put("total_fee", amount);
            map.put("spbill_create_ip", "127.0.0.1");
            map.put("notify_url", "https://127.0.0.1/test");
            map.put("trade_type", "NATIVE");
            String sign=WXPayUtil.generateSignature(map,key);
            map.put("sign",sign);
            Map<String, String> result = wxPay.unifiedOrder(map);
            String prepayId = result.get("prepay_id");
//            System.out.println(prepayId);
            String codeUrl=result.get("code_url");
            if (prepayId != null) {
                order.setPrepayId(prepayId);
                UpdateWrapper updateWrapper = new UpdateWrapper();
                updateWrapper.eq("id", order.getId());
                orderService.update(order, updateWrapper);
                return R.ok().put("codeUrl", codeUrl);
            } else {
                return R.error("支付订单创建失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("微信支付模块故障");
        }
    }

生成二维码

    @GetMapping("/qrcode")
    public void qrcode(HttpServletRequest request,HttpServletResponse response) throws Exception{
        String codeUrl=request.getParameter("codeUrl");
        if(codeUrl!=null&&codeUrl.length()>0){
            QrConfig qrConfig=new QrConfig();
            qrConfig.setWidth(250);
            qrConfig.setHeight(250);
            qrConfig.setMargin(2);
            OutputStream out=response.getOutputStream();
            QrCodeUtil.generate(codeUrl,qrConfig,"jpg",out);
            out.close();
        }
    }

(2)用户付款

网站或售货机与微信是两个平台,无法接收微信平台的支付结果的通知,需要前端定时主动查询订单支付结果

@Login
    @PostMapping("/searchOrderStatus")
    @ApiOperation("查询支付订单状态")
    public R searchOrderStatus(@RequestBody SearchOrderStatusForm form,
                               @RequestHeader HashMap header){
        ValidatorUtils.validateEntity(form);
        String token = header.get("token").toString();
        int userId = Integer.parseInt(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = form.getOrderId();
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setUserId(userId);
        orderEntity.setId(orderId);
        QueryWrapper wrapper = new QueryWrapper(orderEntity);
        int count = orderService.count(wrapper);
        if (count == 0) {
            return R.error("用户与订单不匹配");
        }
        orderEntity = orderService.getOne(wrapper);
        String code = orderEntity.getCode();
        HashMap map = new HashMap();
        map.put("appid", appId);
        map.put("mch_id", mchId);
        map.put("out_trade_no", code);
        map.put("nonce_str", WXPayUtil.generateNonceStr());
        try {
            String sign = WXPayUtil.generateSignature(map, key);
            map.put("sign", sign);
            WXPay wxPay = new WXPay(myWXPayConfig);
            Map<String, String> result = wxPay.orderQuery(map);
            String returnCode = result.get("return_code");
            String resultCode = result.get("result_code");
            if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
                String tradeState = result.get("trade_state");
                if ("SUCCESS".equals(tradeState)) {
                    UpdateWrapper updateWrapper = new UpdateWrapper();
                    updateWrapper.eq("code", code);
                    updateWrapper.set("status", 2);
                    updateWrapper.set("payment_type",1);
                    orderService.update(updateWrapper);
                    return R.ok("订单状态已修改");
                } else {
                    return R.ok("订单状态未修改");
                }
            }
            return R.ok("订单状态未修改");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("查询支付订单失败");
        }
    }

1.3 微信扫码支付

介绍文档

步骤1:用户选择付款码支付,付款码打开路径:微信->“我”->“支付”->“收付款”;

步骤2:收银员在商户系统操作生成支付订单,用户确认支付金额;

步骤3:商户收银员用扫码设备扫描用户的条码/二维码,商户收银系统提交支付;

步骤4:微信支付后台系统收到支付请求,根据验证密码规则判断是否验证用户的支付密码,不需要验证密码的交易直接发起扣款,需要验证密码的交易会弹出密码输入框。支付成功后微信端会弹出成功页面,支付失败会弹出错误提示。

    @Login
    @PostMapping("/scanCodePayOrder")
    @ApiOperation("付款码收款")
    public R scanCodePayOrder(@RequestBody ScanCodePayOrderForm form, @RequestHeader HashMap header) {
        ValidatorUtils.validateEntity(form);
        String token = header.get("token").toString();
        Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = form.getOrderId();
        UserEntity user = new UserEntity();
        user.setUserId(userId);
        QueryWrapper wrapper = new QueryWrapper(user);
        long count = userService.count(wrapper);
        if (count == 0) {
            return R.error("用户不存在");
        }

        OrderEntity order = new OrderEntity();
        order.setUserId(userId.intValue());
        order.setId(orderId);
        order.setStatus(1);
        wrapper = new QueryWrapper(order);
        count = orderService.count(wrapper);
        if (count == 0) {
            return R.error("不是有效的订单");
        }
        //验证购物券是否有效
        //验证团购活动是否有效

        order = new OrderEntity();
        order.setId(orderId);
        wrapper = new QueryWrapper(order);
        order = orderService.getOne(wrapper);
        //向微信平台发出请求,创建支付订单
        String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";

        try {
            WXPay wxPay = new WXPay(myWXPayConfig);
            HashMap map = new HashMap();
            map.put("appid",appId);
            map.put("mch_id",mchId);
            map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
            map.put("body", "订单备注");
            map.put("out_trade_no", order.getCode());
            map.put("total_fee", amount);
            map.put("spbill_create_ip", "127.0.0.1");
            map.put("auth_code",form.getAuthCode());
            String sign=WXPayUtil.generateSignature(map,key);
            map.put("sign",sign);
            Map<String, String> result = wxPay.microPay(map);
            String returnCode = result.get("return_code");
            String resultCode = result.get("result_code");
            if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
                String prepayId = result.get("transaction_id");
                order.setPrepayId(prepayId);
                order.setStatus(2);
                order.setPaymentType(1);
                UpdateWrapper updateWrapper = new UpdateWrapper();
                updateWrapper.eq("id", order.getId());
                orderService.update(order, updateWrapper);
                return R.ok("付款成功");
            }
            else {
                return R.error("付款失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("微信支付模块故障");
        }
    }

二 开发中遇到的支付问题

问题1 用户已支付,然后取消订单,没有退款

1.1 问题梳理分析

订单号:SA012104120809590003430563,订单创建时间:2021-04-12 08:09:59,订单支付时间:2021-04-12 08:14:00,订单取消时间:2021-04-12 08:25:12。由于支付过程为异步且和第三方存在交互,存在订单状态更新延时的情况,而在用户取消的一些特殊场景(通过小程序日志分析,该用户在支付完成后跳转到订单页,订单仍然是未支付状态,而此时用户可以取消订单;一般场景应该是支付完成跳转到订单页,订单为已支付,而此时用户是不可以取消订单的),未对取消操作做完整的校验和拦截,导致用户在支付的情况下取消订单,但确未返回现金的情况。

1.2 同样问题订单查询

通过系统查询发现同样的类型单有16单,涉及总金额 1383 元;

1.3 问题修复方案

(1)经过运营,研发等必要核查,将金额退回给用户;
(2)系统对于取消订单操作做必要的前置校验,加入判断订单是否已经支付,已经支付则前置拦截并且让客户感知。(备注:支付完成的订单走的售后流程)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值