Web整合微信支付

08、微信支付:扫码支付流程

1)介绍

微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
在这里插入图片描述
然后选中v2版本
在这里插入图片描述
我们选择开发文档,而后进入选择页面:

https://pay.weixin.qq.com/wiki/doc/api/index.html
在这里插入图片描述
选择native支付,就是扫码支付:
在这里插入图片描述
此处我们使用模式二来开发:

2)开发流程

模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。

商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url;

商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。

注意:code_url有效期为2小时,过期后扫码不能再发起支付。

流程图:
在这里插入图片描述
这里我们把商户(我们)要做的事情总结一下:

  • 1、商户生成订单
  • 2、商户调用微信下单接口,获取code_url支付链接
  • 3、商户将链接生成二维码图片,展示给用户;
  • 4、支付结果通知:
    • 微信异步通知商户支付结果,商户告知微信支付接收情况
    • 商户如果没有收到通知,可以调用接口,查询支付状态
  • 5、如果支付成功,发货,修改订单状态

在前面的业务中,我们已经完成了:

  • 1、生成订单

接下来,我们需要做的是:

  • 2、调用微信下单接口,生成支付链接。
  • 3、根据链接生成二维码图片
  • 4、支付成功后修改订单状态

09、微信支付:统一下单API请求参数说明

按照上面的步骤分析,第一步是要生成支付链接。我们查看下微信官方文档

在微信支付文档中,可以查询到下面的信息:

请求路径

POST请求 URL地址:https://pay.weixin.qq.com/static/product/product_index.shtml

请求参数

字段名变量名必填类型示例值描述
公众账号IDappidString(32)wxd678efh56微信支付分配的公众账号ID
商户号mch_idString(32)1230000109微信支付分配的商户号
随机字符串nonce_strString(32)5K8264ILT随机字符串,长度要求在32位以内。推荐随机数生成算法
签名signString(32)C380BEC2B通过签名算法计算得出的签名值,详见签名生成算法
商品描述bodyString(128)乐优手机商品简单描述,该字段请按照规范传递,具体请见参数规定
商户订单号out_trade_noString(32)20150806125商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。详见商户订单号
标价金额total_feeInt88订单总金额,单位为分,详见支付金额
终端IPspbill_create_ipString(16)123.12.12.123APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
通知地址notify_urlString(256)http://www.weixin.qq.com/wxpay/pay.php异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型trade_typeString(16)JSAPIJSAPI 公众号支付;NATIVE 扫码支付;APP APP支付说明详见参数规定

这些参数大致分成3类:

  • appid、mch_id、spbill_create_ip、notify_url、trade_type:是商家自己的信息或固定数据,可以提前配置,因此无需每次请求单独配置,而是统一设置好即可,
  • nonce_str、sign:是为了保证数据安全而添加的验证数据,根据算法去生成,每次请求自动生成即可。
  • body、out_trade_no、total_fee:订单相关信息,需要我们自己填写。

10、微信支付:微信SDK介绍

1)下载

虽然请求参数比较复杂,但官方已经提供了SDK,供我们使用:
在这里插入图片描述
微信没有提供maven仓库坐标,因此我们必须下载使用,建议使用课前资料中,我提供给大家的SDK,其中做了一些必要的设置:
在这里插入图片描述

2)WXPay工具

微信SDK提供了一个统一的微信支付工具类:WXPay
在这里插入图片描述
其中包含这样一些方法:

com.github.wxpay.sdk.WXPay类下提供了对应的方法:

方法名说明
microPay刷卡支付
unifiedOrder统一下单
orderQuery查询订单
reverse撤销订单
closeOrder关闭订单
refund申请退款
refundQuery查询退款
downloadBill下载对账单
report交易保障
shortUrl转换短链接
authCodeToOpenid授权码查询openid
  • 注意:
    • 参数为Map<String, String>对象,返回类型也是Map<String, String>
    • 方法内部会将参数转换成含有appidmch_idnonce_strsign_typesign的XML
    • 通过HTTPS请求得到返回数据后会对其做必要的处理(例如验证签名,签名错误则抛出异常)

我们主要关注其中的unifiedOrder方法,统一下单:

/**
     * 作用:统一下单<br>
     * 场景:公共号支付、扫码支付、APP支付
     * @param reqData 向wxpay post的请求数据
     * @return API返回数据
     * @throws Exception
     */
public Map<String, String> unifiedOrder(Map<String, String> reqData) throws Exception {
    return this.unifiedOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
}

这里的请求参数是:Map<String, String> reqData,就是官方API说明中的请求参数了,不过并不需要我们填写所有参数,而只需要下面的:

  • body:商品描述
  • out_trade_no:订单编号
  • total_fee:订单应支付金额
  • spbill_create_ip:设备IP
  • notify_url:回调地址
  • trade_type:交易类型

剩下的:appidmch_idnonce_strsign_typesign参数都有WXPay对象帮我们设置,那么问题来了:这些参数数据WXPay是怎么拿到的呢?

其中,

  • nonce_str:是随机字符串,因此由WXPay随机生成,
  • sign_type:是签名算法,由WXPay指定,默认是HMACSHA256;
  • sign:是签名,有签名算法结合密钥加密而来,因此这里的关键是密钥:key
  • appid、mch_id是商家信息,需要配置

也就是说,这例需要配置的包括:appid、mch_id、密钥key。这些从哪里来呢?

看下WXPay的构造函数:

public WXPay(final WXPayConfig config) throws Exception {
    this(config, null, true, false);
}

这里需要一个WXPayConfig对象,显然是配置对象。

3)WXPayConfig配置

WXPay依赖于WXPayConfig进行配置,那么WXPayConfig是什么呢?

看下源码中的关键部分:

public abstract class WXPayConfig {
    /**
     * 获取 App ID
     *
     * @return App ID
     */
    abstract String getAppID();
    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    abstract String getMchID();
    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    abstract String getKey();
    
    // 。。。省略
}

这不就是WXPay中需要配置的3个属性嘛,当我们实现这个类,并且给出其中的值,把WXPayConfig传递给WXPay时,WXPay就会获取到这些数据:
在这里插入图片描述
当我们利用WXPay发送请求时,WXPay就会帮我们封装到请求参数中:
在这里插入图片描述
而在我提供给大家的SDK中,就编写了一个WXPayConfig的实现:

package com.github.wxpay.sdk;

import lombok.Data;

import java.io.InputStream;

/**
 * @author 黑马程序员
 */
@Data
public class WXPayConfigImpl extends WXPayConfig {
    /**
     * 公众账号ID
     */
    private String appID;
    /**
     * 商户号
     */
    private String mchID;
    /**
     * 生成签名的密钥
     */
    private String key;
    /**
     * 支付回调地址
     */
    private String notifyUrl;
    /**
     * 支付方式
     */
    private String payType;

    public InputStream getCertStream(){
        return null;
    }

    public IWXPayDomain getWXPayDomain(){
        return WXPayDomainSimpleImpl.instance();
    }
}

将来我们只需要new出这个实现类对象,并且给这3个参数赋值即可。

11、微信支付:打包微信SDK

首先,把我提供的SDK打包并安装到本地的maven仓库,方便在项目中使用。

直接对SDK进行打包,在项目maven中执行如下命令:

mvn source:jar install -Dmaven.test.skip=true

如图所示:
在这里插入图片描述
控制台效果:
在这里插入图片描述
然后进入本地仓库查看:
在这里插入图片描述

12、微信支付:把SDK整合到项目中

1)配置WXPay

在ly-order中引入坐标:

<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>3.0.9</version>
</dependency>

我们将这些WXPayConfig中的属性定义到application.yml中

ly:
  pay:
    wx:
      appID: wx8397f8696b538317
      mchID: 1473426802
      key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
      notifyUrl: http://api.leyou.com/api/pay/wx/notify
      payType: NATIVE

将这些属性注入到PayProperties中:

package com.leyou.order.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 *
 */
@Data
@Component
@ConfigurationProperties(prefix = "ly.pay.wx")
public class PayProperties {
    private String appId;
    private String mchId;
    private String key;
    private String notifyUrl;
    private String payType;
}

2)支付工具类

创建了配置类后,我们再来初始化WXPay对象,并注入到Spring容器中:

package com.leyou.order.config;

import com.github.wxpay.sdk.PayConfig;
import com.github.wxpay.sdk.WXPay;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 初始化支付对象
 */
@Configuration
public class PayConfiguration {

    @Bean
    public WXPay createWxPay(PayProperties payProps) throws Exception {
        PayConfig payConfig = new PayConfig();
        payConfig.setAppID(payProps.getAppId());
        payConfig.setMchID(payProps.getMchId());
        payConfig.setKey(payProps.getKey());
        return new WXPay(payConfig);
    }

}


我们定义支付工具类,完成后续操作:

package com.leyou.order.utils;

import com.github.wxpay.sdk.WXPay;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.order.config.PayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 支付工具类
 */
@Component
@Slf4j
public class PayHelper {
    @Autowired
    private PayProperties payProps;
    @Autowired
    private WXPay wxPay;

    /**
     * 对接微信支付获取二维码链接
     */
    public String getPayUrl(Long orderId,Long totalFee){
        // 请求参数:
        Map<String, String> data = new HashMap<String, String>();
        data.put("body", "乐优商城-支付订单");
        data.put("out_trade_no", orderId.toString());
        data.put("total_fee", totalFee.toString());
        data.put("spbill_create_ip", "123.12.12.123");
        data.put("notify_url", payProps.getNotifyUrl());
        data.put("trade_type", payProps.getPayType());  // 此处指定为扫码支付

        try {
            Map<String, String> resp = wxPay.unifiedOrder(data);

            //判断获取链接是否成功
            //获取return_code 和 result_code
            String returnCode =resp.get("return_code");
            String resultCode =resp.get("result_code");
            if(returnCode.equals("SUCCESS") && resultCode.equals("SUCCESS")){
                log.info("【申请微信支付链接】申请链接成功");
                return resp.get("code_url");
            }else{
                log.error("【申请微信支付链接】申请链接失败,原因:"+resp.get("return_msg"));
                throw new LyException(500,"【申请微信支付链接】申请链接失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【申请微信支付链接】申请链接失败,原因:"+e.getMessage());
            throw new LyException(500,"【申请微信支付链接】申请链接失败");
        }
    }

}


编写测试类:

package com.leyou;

import com.leyou.order.utils.PayHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = LyOrderApplication.class)
public class PayHelperTest {
    @Autowired
    private PayHelper payHelper;


    @Test
    public void testGetPayUrl(){
        String payUrl = payHelper.getPayUrl(1374555262857371650L,1L);
        System.out.println("支付链接:"+payUrl);
    }
}

03、微信支付:生成微信支付二维码

1)获取微信支付链接

在订单支付页面,会向后台发起请求,查询支付的URL地址:
在这里插入图片描述
我们需要编写controller,来实现这个功能:

  • 请求方式:GET
  • 请求路径:/order/url/{id}
  • 请求参数:id,订单的编号
  • 返回结果:url地址

代码如下:

OrderController:

  /**
     * 获取支付二维码链接
     */
    @GetMapping("/order/url/{id}")
    public ResponseEntity<String> getPayUrl(@PathVariable("id") Long id){
        String payUrl = orderService.getPayUrl(id);
        return ResponseEntity.ok(payUrl);
    }
    /**
     * 获取支付连接
     */
    @GetMapping("/order/url/{id}")
    public ResponseEntity<String> getPayUrl(@PathVariable("id") Long id){
        String payUrl = orderService.getPayUrl(id);
        return ResponseEntity.ok(payUrl);
    }

service,订单支付url的有效期是2小时,因此我们可以获取url后存入redis缓存:

  • 先检查redis是否已经有url,有则返回
  • 没有,则查询订单信息,校验订单状态是否为已经支付,是则抛出异常
  • 如果没有支付,调用PayHelper,生成url
  • 将url存入redis,设置有效期为2小时

在ly-common的常量类中指定支付链接在redis中存储的key的前缀

package com.leyou.common.constant;

public class LyConstants {

    ……

    /*支付链接在redis中存储的key的前缀*/
    public static final String PAY_URL_PRE = "PAY_URL_PRE";
}

service代码:

OrderService添加getPayUrl方法:

public String getPayUrl(Long id) {
        String payKey = LyConstants.PAY_URL_PRE+id;

        //1.从redis取出支付链接
        String payUrl = redisTemplate.opsForValue().get(payKey);

        //2.判断redis是否存在该订单的支付链接
        if(payUrl!=null){
            //4.如果存在支付链接,则直接取出返回即可
            return payUrl;
        }

        //根据订单ID查询订单
        Order order = orderMapper.selectById(id);

        //3.如果不存在支付链接,则微信下单API获取支付链接
        payUrl = payHelper.getPayUrl(id,order.getActualFee());

        //4. 把支付链接存入redis,设置有效期为3小时
        redisTemplate.opsForValue().set(payKey,payUrl,3, TimeUnit.HOURS);

        //5.返回生成的支付链接
        return payUrl;
    }

页面响应结果:
在这里插入图片描述
支付页面效果:
在这里插入图片描述

2)根据支付链接生成二维码【了解】

1.什么是二维码

二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。

二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。

2.二维码优势
  • 信息容量大, 可以容纳多达1850个大写字母或2710个数字或500多个汉字
  • 应用范围广, 支持文字,声音,图片,指纹等等…
  • 容错能力强, 即使图片出现部分破损也能使用
  • 成本低, 容易制作
3.二维码容错级别
  • L级(低) 7%的码字可以被恢复。
  • M级(中) 15%的码字可以被恢复。
  • Q级(四分)25%的码字可以被恢复。
  • H级(高)30% 的码字可以被恢复。
4.二维码生成插件qrious

qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,还可以将生成的二维码进行Base64编码。官网

qrious.js二维码插件的可用配置参数如下:

参数类型默认值描述
backgroundString“white”二维码的背景颜色。
foregroundString“black”二维码的前景颜色。
levelString“L”二维码的误差校正级别(L, M, Q, H)。
mimeString“image/png”二维码输出为图片时的MIME类型。
sizeNumber100二维码的尺寸,单位像素。
valueString“”需要编码为二维码的值

课前资料中给出的案例可以直接生成二维码:
在这里插入图片描述

5.生成二维码

我们把课前资料中的这个js脚本引入到项目中:
在这里插入图片描述
然后在页面引用:
在这里插入图片描述
页面定义一个div,用于展示二维码:
在这里插入图片描述
然后获取到付款链接后,根据链接生成二维码:
在这里插入图片描述
刷新页面,查看效果:
在这里插入图片描述
此时,客户用手机扫描二维码,可以看到付款页面。
在这里插入图片描述

04、微信支付:内网穿透

参考blog:Ngork内网穿透

支付以后,我们后台需要修改订单状态。我们怎么得知有没有支付成功呢?

在我们的请求参数中,有一个notify_url的参数,是支付的回调地址。当用户支付成功后,微信会主动访问这个地址,并携带支付结果信息。

那么,这个notify_url该怎么用呢?

1)什么是notify_url

参数中有一个非常重要的,叫做notify_url的:
在这里插入图片描述
基于上文的介绍我们知道,这个地址是在支付成功后的异步结果通知。官网介绍如下:

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。

所以,此处的地址必须是一个外网可访问地址,而且我们要定义好回调的处理接口。

http://api.leyou.com/api/pay/notify

2)内网穿透

此处我们肯定不能写:http://api.leyou.com/api/pay/notify,这个域名未经备案,是不被识别的。如何才能获取一个能够外网访问的域名呢?

我们可以通过内网穿透来实现,那么什么是内网穿透呢?
在这里插入图片描述

简单来说内网穿透的目的是:让外网能访问你本地的应用,例如在外网打开你本地http://127.0.0.1指向的Web站点。

在这里有一篇播客,详细介绍了几种内网穿透策略:一分钟了解内网穿透

这里我们使用一个免费的内网穿透工具:Natapp:[NATAPP官网](
在这里插入图片描述
详细教程在这里:一分钟的natapp快速新手教程
购买隧道:
在这里插入图片描述
在我的隧道中记录token:
在这里插入图片描述

下载软件:
在这里插入图片描述
注意,这里下载的只有软件,没有配置文件,启动的时候,需要手动指定authtoken,资料里面我给大家提供好了一个带有配置文件的软件:

在这里插入图片描述
解压:
在这里插入图片描述
启动后的样子:
在这里插入图片描述
比如此处,我使用的natapp得到的域名是:http://9xtfjr.natappfree.cc,并且我设置指向到127.0.0.1:10010位置,也就是我的网关服务。

3)配置回调地址

设置内网穿透地址到配置文件application.yml:

ly:
  pay:
    wx:
      appID: wx8397f8696b538317
      mchID: 1473426802
      key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
      notifyUrl: http://xn4agt.natappfree.cc/api/pay/wx/notify
      payType: NATIVE

WxPayConfigImpl中本来就有notifyURL属性,因此会被自动注入。

4)网关白名单

因为异步回调是微信来访问我们的,因此不应该对登录做校验,我们把这个地址配置到白名单,修改ly-gateway中的application.yml

ly:
  filter:
    allowPaths:
     ly:
  filter:
    allowPaths:
      - /api/pay

然后,将/api/pay映射到订单微服务:

        - id: pay-service   # 路由id,可以随意写
          # 代理的服务地址;lb表示负载均衡(从nacos中获取具体服务)
          uri: lb://order-service
          # 路由断言,可以配置映射路径
          predicates:
            - Path=/api/pay/**
          filters:
            # 表示过滤1个路径,2表示两个路径,以此类推
            - StripPrefix=2

5)订单微服务拦截器对微信的回调地址放开配置

package com.leyou.order.config;

import com.leyou.order.interceptor.UserInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * SpringMVC配置类
 */
@Configuration
public class MvcConfig implements WebMvcConfigurer{
    @Autowired
    private UserInterceptor userInterceptor;

    /**
     * 添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /**
         * addPathPatterns(): 配置拦截器拦截路径
         * excludePathPatterns(): 配置拦截器排除路径
         */
        registry.addInterceptor(userInterceptor).addPathPatterns("/**").excludePathPatterns("/wx/notify/**");

    }
}


05、微信支付:微信异步通知

先分析接口需要的四个数据:

  • 请求方式:官方文档虽然没有明说,但是测试得出是POST请求
  • 请求路径:我们之前指定的notify_url的路径是:/pay/wx/notify
  • 请求参数:是xml格式数据,包括支付的结果和状态
  • 返回结果:也是xml,表明是否成功

因为要接收xml格式数据,因此我们需要引入解析xml的依赖:

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

然后编写controller:

package com.leyou.order.controller;

import com.leyou.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * 支付回调接口
 */
@RestController  // @Controller + @ResponseBody
public class PayController {

    @Autowired
    private OrderService orderService;

    /**
     * 微信支付回调方法
     *  @ResponseBody的用法:
     *     1)没有导入jackson包的情况下,该注解直接返回响应体内容
     *     2)在导入jackson包的情况下,该注解可以把方法返回值对象转换为json字符串返回
     *     3)在导入xml包的情况下,该注解可以把方法返回值对象转换为xml字符串返回
     *
     *  注意: 在jackson包和xml包同时存在的情况下,默认转换为json,除非在@RequestMapping注解上面添加producers属性指定转换类型。
     */
    @PostMapping(value = "/wx/notify",produces = "application/xml")
    public Map<String,String> wxNotify(@RequestBody Map<String,String> paramsMap){
        orderService.wxNotify(paramsMap);

        //返回正确信息给微信
        Map<String,String>resultMap = new HashMap<>();
        resultMap.put("return_code","SUCCESS");
        resultMap.put("return_msg","OK");
        return resultMap;
    }


}


Service 代码:

service中需要完成下列代码;

  • 签名校验
  • 数据校验
    • 订单号码校验
    • 订单金额校验
  • 更新订单状态

OrderService类中:

    public void wxNotify(Map<String, String> paramsMap) {
        //1.获取订单号和支付金额
        Long orderId =  Long.valueOf(paramsMap.get("out_trade_no"));
        Long totalFee = Long.valueOf(paramsMap.get("total_fee"));

        //2.根据订单号查询订单
        Order order = orderMapper.selectById(orderId);

        //3.判断订单是否存在,如果不存在,抛出异常
        if(order==null){
            log.error("【支付回调】订单不存在!");
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }

        //4.判断支付金额是否一致
        if(order.getActualFee()!=totalFee){
            log.error("【支付回调】支付金额不一致!");
            throw new LyException(ExceptionEnum.PAY_FEE_NOT_ERROR);
        }

        //5.判断订单状态如果已经支付过了,无需支付了
        if(order.getStatus()!=1){
            log.error("【支付回调】订单已经支付,无需重复支付!");
            throw new LyException(ExceptionEnum.ORDER_HAS_PAY_ERROR);
        }

        //6.更新订单状态以及支付时间
        try {
            order.setStatus(2);
            order.setPayTime(new Date());
            orderMapper.updateById(order);

            log.info("【支付回调】订单状态更新成功");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【支付回调】订单状态更新失败");
        }


    }

06、微信支付:主动查询支付状态

当用户扫码支付成功,会自动调用回调接口,从而修改订单状态,完成订单支付。

但是,页面上并不知道支付是否成功。怎么办?

1)页面查询支付状态

因为不知道用户什么时候会支付,也不知道支付有没有成功,因此页面会采用定时任务,不断查询订单支付的状态:

 // 开启定时任务,查询付款状态
            const taskId = setInterval(() => {
              ly.http.get("/order/state/" + id)
                .then(resp => {
                  let i = resp.data;
                  if (i != 1) {
                    // 付款成功
                    clearInterval(taskId);
                    // 跳转到付款成功页
                    location.href = "/paysuccess.html?orderId=" + id;
                  }
                }).catch((e) => {
                alert("支付状态查询失败,请刷新页面重试。");
                clearInterval(taskId);
              })
            }, 3000);
            // 同时设置一个定时任务,10分钟后,终止查询,认为付款失败
            setTimeout(() => {
              clearInterval(taskId);
              location.href = "/payfail.html?orderId=" + id;
            }, 600000)
          });

每隔5秒就会查询一次,为了防止用户一直不支付的情况,又设置了一个定时任务,10分钟后跳转到支付失败页面。

2)支付状态查询接口

上面的查询请求 分析:

  • 请求方式:Get
  • 请求路径 :/state/{id}
  • 请求参数:订单id
  • 返回结果:1或者其它,1代表未支付,其它是已经支付

controller:

 /**
     * 查询订单状态
     */
    @GetMapping("/order/state/{id}")
    public ResponseEntity<Integer> checkState(@PathVariable("id") Long id){
        Integer state = orderService.checkState(id);
        return ResponseEntity.ok(state);
    }

service:

  public Integer checkState(Long id) {
        Order order = orderMapper.selectById(id);
        if(order==null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }

重启测试:
在这里插入图片描述

Memorial Day is 541 days
I miss you
xiaokeai

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值