最新版微信小程序支付,退款问题实现与总结(保姆级)

1.了解微信支付逻辑

说白了就是我们去调用微信官方提供的接口,但是这些接口有很多繁琐的参数,还需要计算签名等等一系列恶心的步骤,详情可以看微信官网,我们可以用微信官方提供的sdk,也可以用github上的三方sdk,本文以GitHub上大佬binaryWang的微信支付sdk演示微信支付的实现

前言

首先我们作为后端,需要像前端提供一个接口,前端通过调用这个接口唤起下单,在这个接口中,后端回去调用微信下单接口,这时候微信会给我们返一个预支付id,我们通过这个预支付的id,

这个预支付id是唤起下单的一个必填参数,咱们只需要计算出唤起支付所需要的参数,然后传给前端,前端就可以调用这个唤起支付接口,这样,输入密码的数字轮盘就出来了

注意事项:

sdk的版本可能会影响sign的计算,导致输入支付密码后,微信提示sign鉴权失败,这里的sdk大佬们都在维护的,只需要更新到最新版本即可

2.微信支付需要的参数

mchKey:新版本更新后叫做v2密钥,作用是处理签名信息解密,在微信支付商户平台申请,为32为密钥,自行设定,需要绑定商户的手机号进行短信验证和操作码验证

keyPath: classpath:wxcert/apiclient_cert.p12

#apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径.

privateKeyPath: classpath:wxcert/apiclient_key.pem

#apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径.

privateCertPath: classpath:wxcert/apiclient_cert.pem

需要在api管理这里设置证书,在指引处可以下载证书

注:密钥,是开发者自己生成,生成后配置到商户平台即可

下载证书工具;下载后,双击“WXCertUtil.exe”文件,选择安装路径后,点击申请证书

也可通过以下链接下载证书工具:

windows版本 :https://wx.gtimg.com/mch/files/WXCertUtil.exe

mac版本 :https://wx.gtimg.com/mch/files/WXCertUtil.dmg

3.实现微信支付

导入依赖,并配置wx所需参数

        <!--微信支付-->
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
            <version>4.6.0</version>
        </dependency>

订单支付

    /**
     * 订单微信支付
     * @param paymentForm
     * @return
     */
    @Override
    public <T> T payOrder(OrderPaymentForm paymentForm,Long memberId) {
        //获取订单号
        String orderSn = paymentForm.getOrderSn();
        //通过该订单号查询订单对象
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderCode, orderSn));
        log.info("appid---"+paymentForm.getAppId());
        Assert.isTrue(order!=null,"该订单信息不存在");
        Assert.isTrue(OrderStatusEnum.UNPAID.getValue().equals(order.getState()), "订单不可支付,请检查订单状态");
        RLock lock = redissonClient.getLock(OrderConstants.ORDER_LOCK_PREFIX + order.getOrderCode());
        try {
            lock.lock();
            T result;
            result =  (T) wxJsapiPay(paymentForm.getAppId(), order.getOrderCode(), order.getAmount(),memberId);
            return result;
        } finally {
            //释放锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }

掉起订单支付

   /**
     * 微信支付调起
     *
     * @param appId         微信小程序ID
     * @param orderSn       订单编号
     * @param paymentAmount 支付金额
     * @return 微信支付调起参数
     */
    private WxPayUnifiedOrderV3Result.JsapiResult wxJsapiPay(String appId, String orderSn, BigDecimal paymentAmount,Long memberId) {
        //价格转换为分
        Long longValueAmount = BigDecimal2Long(paymentAmount);

        //获取用户OpenId
        String memberOpenId = memberService.getById(memberId).getOpenid();

        WxPayUnifiedOrderV3Request wxRequest = new WxPayUnifiedOrderV3Request()
                .setAppid(appId)//小程序appId
                .setOutTradeNo(orderSn)//设置订单号
                .setAmount(new WxPayUnifiedOrderV3Request
                        .Amount()
                        .setTotal(Math.toIntExact(longValueAmount)).
                        setCurrency(wxPayProperties.getCurrency())
                )//设置金额
                .setPayer(
                        new WxPayUnifiedOrderV3Request.Payer()
                                .setOpenid(memberOpenId)
                )//设置付款人
                .setDescription("赅买-订单编号:" + orderSn)
                .setNotifyUrl(wxPayProperties.getPayNotifyUrl());

        WxPayUnifiedOrderV3Result.JsapiResult jsapiResult;
        try {
            jsapiResult = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, wxRequest);
        } catch (WxPayException e) {
            log.error(e.getMessage(), e);
            throw new BizException(e.getMessage()+"微信统一下单异常");
        }
        log.info("----------------------"+jsapiResult);
        return jsapiResult;
    }

返回值

实现效果

在用户输入密码成功后,这块微信就支付成功了,这时候我们需要暴露给微信一个开发的回调接口,微信这边会传入一些加密参数,我们拿到参数后调用api将这些参数解密,最后可以对订单进行一些信息的校验以及状态的更新等操作

4.实现微信退款

具体方式与支付差不多,但这块调用微信退款后,前端只需掉我们的接口,会自动完成退款,我们需要按照微信官方的返回规范,退款成功sucsess,更新订单信息

注:如果微信这块没有接收到返回成功的信息,还会按照一定策略(频繁度从高到底)不断调用我们提供的回调接口,具体调用方式请看微信小程序官方退款接口文档

退款--入参(订单编号,退款金额,退款原因)

   /**
     * 退款
     */
    public <T> T refundOrder(RefundForm refundForm){
        Assert.isTrue(refundForm.getReason()!=null,"退款备注不能为空");
        //判断该订单是否未付款,或已取消
                //获取商户订单号
                String orderCode = refundForm.getOrderCode();
                //转金额
                Long orderRundAmount = BigDecimal2Long(refundForm.getRefundAmount());
                //获取订单信息
                Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderCode, orderCode));
                //获取微信退款单号
                String wxPayCode = order.getWxPayCode();

                //订单金额
                Long orderAmount = BigDecimal2Long(order.getAmount());
                Assert.isTrue(order !=null,"查询不到该订单信息,无法退款");
                Assert.isTrue(order.getState()==OrderStatusEnum.PAID.getValue() || order.getState()==OrderStatusEnum.REBATES.getValue(),"该订单尚未完成支付或已取消,无法退款");
                Assert.isTrue(orderRundAmount.intValue()>0,"退款金额必须大于0");
                Assert.isTrue(orderRundAmount<=orderAmount,"退款金额必须小于等于订单金额");
                OrderRefundForm.Amount amount = OrderRefundForm.Amount.builder().refund(refundForm.getRefundAmount())//退款金额
                        .total(order.getAmount()).build(); //订单金额

                OrderRefundForm orderRefundForm = OrderRefundForm.builder().amount(amount)
                        .out_refund_no(generateTradeNo(SecurityUtils.getUserId())) //退款单号
                        .reason(refundForm.getReason()==null?"":refundForm.getReason())
                        .transaction_id(wxPayCode)
                        .build();
                T result = (T) wxJsapiRefund(orderRefundForm);
                return result;
    }

掉起退款

    /**
     * 订单退款掉起
     * @param orderRefundForm 退款请求对象 memberId
     * @return
     */
    private WxPayRefundV3Result wxJsapiRefund(OrderRefundForm orderRefundForm) {
        //退款金额
        Long longValueRefundAmount = BigDecimal2Long(orderRefundForm.getAmount().getRefund());
        //订单总金额
        Long longValueOrderAmount = BigDecimal2Long(orderRefundForm.getAmount().getTotal());
        //通过订单号查询商品id
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getWxPayCode, orderRefundForm.getTransaction_id()));
        appOrderMapper.updateOrderRefundReason(orderRefundForm.getReason(),order.getId()); // 更新退款原因
        //如果是自动退款

        WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request().
                setTransactionId(orderRefundForm.getTransaction_id()).
                setAmount(new WxPayRefundV3Request.Amount().
                        setTotal(Math.toIntExact(longValueOrderAmount))
                        .setRefund(Math.toIntExact(longValueRefundAmount))
                        .setCurrency(wxPayProperties.getCurrency())).
                setOutRefundNo(orderRefundForm.getOut_refund_no()).
                setNotifyUrl(wxPayProperties.getRefundNotifyUrl()).
                setReason(orderRefundForm.getReason()==null?"":orderRefundForm.getReason());
        WxPayRefundV3Result refundV3Result;
        try {
            refundV3Result =  wxPayService.refundV3(wxPayRefundV3Request);
            log.info("掉起退款接口成功");
        } catch (WxPayException e) {
            throw new BizException("退款失败"+e);
        }
        return refundV3Result;
    }

退款也是一样的,需要暴露回调接口,退款成功后,微信会退款给商户,并且我们在回调接口处理订单状态等信息

实现效果:

总结:

微信支付的下单和调起需要前后端的配合与联调,我们需要根据业务需求灵活的处理更新订单的信息,再支付完成之后,在回调接口中校验支付金额与订单金额是否相同等信息,校验通过后才更新订单为已付款状态,,现在我们就实现类微信支付与退款功能了,本文没有写到回调接口的书写,如有需要可留言

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值