7 接入微信

7.1 接入分析

惠民支付平台是将各各常用的第三方支付渠道统一为一个支付通道,前边实现了C扫B支付宝支付的流程,下边接入 微信支付,根据接入支付宝的流程分析接入微信需要实现的如下:

1、支付入口

顾客扫码进入支付入口,根据客户端类型判断是微信还是支付宝,是支付宝则直接进入收银台,如果是微信则需要 首先获取openid,再进入收银台。

2、立即支付

点击立即支付调用微信的统一下单接口,若下单成功则唤起微信客户端开始支付。 3、获取支付结果

调用微信的“支付结果查询”接口获取支付结果。

7.2 支付入口

7.2.1 获取openid接口

参考:惠民支付-第3章-微信支付接入指南.pdf

7.2.2 获取微信授权码

用户进入支付入口,判断客户端类型如果是微信则获取微信授权码。 根据获取openid的流程得知,第一步获取微信授权码,这里需要生成获取微信授权码的URL,由页面重定向即可。 1、在nacos中交易服务的主配置文件中添加如下参数:

weixin: 
 oauth2RequestUrl: "https://open.weixin.qq.com/connect/oauth2/authorize" 
 oauth2CodeReturnUrl: "http://xfc.nat300.top/transaction/wx‐oauth‐code‐return" 
 oauth2Token: "https://api.weixin.qq.com/sns/oauth2/access_token"

2、在交易服务PayController中添加获取微信授权码方法。

@Value("${weixin.oauth2RequestUrl}") 
private String wxOAuth2RequestUrl; 

@Value("${weixin.oauth2CodeReturnUrl}") 
private String wxOAuth2CodeReturnUrl; 
/** 
\* 获取微信授权码
*
\* @param order 订单对象 
\* @return
\* @throws Exception
*/
@Override 
public String getWXOAuth2Code(PayOrderDTO order) throws BusinessException { 
    //将订单信息封装到state参数中 
    String state = EncryptUtil.encodeUTF8StringBase64(JSON.toJSONString(order)); 
    //应用id 
    String appId = order.getAppId(); 
    //服务类型 
    String channel = order.getChannel(); 
    //获取微信支付渠道参数,根据应用、服务类型、支付渠道查询支付渠道参数 
    PayChannelParamDTO payChannelParamDTO = 
        payChannelService.queryParamByAppPlatformAndPayChannel(appId, 
                                                               channel, "WX_JSAPI"); 
    if(payChannelParamDTO == null){ 
        throw new BusinessException(CommonErrorCode.E_300007); 
    } 
    //支付渠道参数 
    String payParam = payChannelParamDTO.getParam(); 
    WXConfigParam wxConfigParam = JSON.parseObject(payParam, WXConfigParam.class); 

    try { 
        String url = String 
            .format("%s?appid=%s&scope=snsapi_base&state=%s&redirect_uri=%s", 
                    wxOAuth2RequestUrl, wxConfigParam.getAppId(), 
                    state, URLEncoder.encode(wxOAuth2CodeReturnUrl, "utf‐8")); 
        log.info("微信生成授权码url:{}",url); 
        return "redirect:" + url; 
    } catch (UnsupportedEncodingException e) { 
        e.printStackTrace(); 
        return "forward:/pay‐page‐error";//生成获取授权码链接失败 
    } 
}

2、支付入口调用微信授权码获取方法

@RequestMapping(value = "/pay‐entry/{ticket}") 
public String payEntry(@PathVariable("ticket") String ticket,HttpServletRequest request) { 
    ... 
        BrowserType browserType = BrowserType.valueOfUserAgent(request.getHeader("user‐agent")); 
    switch (browserType) { 
        case ALIPAY: //直接跳转收银台pay.html 
            return "forward:/pay‐page?" + toParamsString(order); 
        case WECHAT: //获取授权码(待实现) 
            return transactionService.getWXOAuth2Code(order); 
        default: 
    } 
    ... 
}

7.2.3 微信授权码回调接口

授权码获取成功后微信会将授权码传入授权码回调URL,在授权码回调接口中实现获取openid。

7.2.3.1 接口定义

1、在PayController中定义微信授权码回调接口

@ApiOperation("微信授权码回调") 
@GetMapping("/wx‐oauth‐code‐return") 
public String wxOAuth2CodeReturn(@RequestParam String code, @RequestParam String state) { 
    //获取openid 
    //重定向到支付确认页面 

}

2、在TransactionService中定义获取openid方法

/**
\* 获取微信openid
*
\* @param code 授权id
\* @param appId 应用id,用于获取微信支付的参数
\* @return openid
*/
public String getWXOAuthOpenId(String code, String appId);
7.2.3.2 接口实现

1、添加RestTemplate配置

使用RestTemplate发起http请求,在huiminpay-transaction-service工程的pom.xml中添加依赖:

<!‐‐ okhttp3 ‐‐>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
</dependency>

在TransactionBootstrap中注入RestTemplate

@Bean 
public RestTemplate restTemplate() { 
    return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); 
}

2、添加获取openid地址配置

在nacos中向交易服务添加获取openid地址配置,如下:

weixin: 
 oauth2Token: "https://api.weixin.qq.com/sns/oauth2/access_token"

3、getWXOAuthOpenId接口实现

/** 
\* 获取微信openid
\* @param code 授权id 
\* @param appId 应用id,用于获取微信支付的参数
\* @param channel 服务类型,用于获取微信支付的参数
\* @return openid
*/
@Override 
public String getWXOAuthOpenId(String code, String appId) { 

    //获取微信支付渠道参数,根据应用、服务类型、支付渠道查询支付渠道参数 
    PayChannelParamDTO payChannelParamDTO = 
        payChannelService.queryParamByAppPlatformAndPayChannel(appId, "huimin_c2b", "WX_JSAPI"); 
    if(payChannelParamDTO == null){ 
        throw new BusinessException(CommonErrorCode.E_300007); 
    } 
    //支付渠道参数 
    String payParam = payChannelParamDTO.getParam(); 
    WXConfigParam wxConfigParam = JSON.parseObject(payParam, WXConfigParam.class); 
    //密钥 
    String appSecret = wxConfigParam.getAppSecret(); 
    //获取openid地址 
    String url = String.format("%s?appid=%s&secret=%s&code=%s&grant_type=authorization_code", 
                               oauth2Token,wxConfigParam.getAppId(), wxConfigParam.getAppSecret(), code); 
    ResponseEntity<String> exchange = restTemplate.exchange(tokenUrl, HttpMethod.GET, null, String.class); 
    String response = exchange.getBody(); 
    return JSONObject.parseObject(response).getString("openid"); 
}

4、微信授权码回调接口实现

@ApiOperation("微信授权码回调") 
@GetMapping("/wx‐oauth‐code‐return") 
public String wxOAuth2CodeReturn(@RequestParam String code, @RequestParam String state) { 
    //将之前state中保存的订单信息读取出来 
    PayOrderDTO payOrderDTO = JSON.parseObject(EncryptUtil.decodeUTF8StringBase64(state), PayOrderDTO.class); 
    //应用id 
    String appId = payOrderDTO.getAppId(); 
    //获取openid 
    String openId = transactionService.getWXOAuthOpenId(code,appId); 
    try { 
        //将订单信息转成query参数的形式拼接起来 
        String orderInfo = ParseURLPairUtil.parseURLPair(payOrderDTO); 
        //返回所有的query参数到支付确认页面 
        return String.format("forward:/pay‐page?openId=%s&%s", openId, orderInfo); 
    } catch (Exception e) { 
        e.printStackTrace(); 
        return "forward:/pay‐page‐error"; 
    } 
}

7.2.4 测试

1、生成门店c扫b的二维码

2、打开内网穿透工具

3、打开模拟器,使用微信扫码

4、观察程序输出日志,确认openid是否生成成功,支付确认页面是否正常打开