电商项目-微信支付(三)支付回调处理

一、 需求分析

在完成支付后,修改订单状态为已支付,并记录订单日志。

二、 代码实现思路

应用技术:分布式架构,Feign远程调用,Oauth,Wxpay ,Vue, Axios,Eureka , Rabbitmq
(1)接受微信支付平台的回调信息(xml)

<xml><appid><![CDATA[wx8397f8696b538317]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1473426802]]></mch_id>
<nonce_str><![CDATA[c6bea293399a40e0a873df51e667f45a]]></nonce_str>
<openid><![CDATA[oNpSGwbtNBQROpN_dL8WUZG3wRkM]]></openid>
<out_trade_no><![CDATA[1553063775279]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[DD4E5DF5AF8D8D8061B0B8BF210127DE]]></sign>
<time_end><![CDATA[20190320143646]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[NATIVE]]></trade_type>
<transaction_id><![CDATA[4200000248201903206581106357]]></transaction_id>
</xml>

(2)收到通知后,调用查询接口查询订单。

(3)如果支付结果为成功,则调用修改订单状态和记录订单日志的方法。

三、 代码实现

3.1 内网映射工具

在请求统一下单接口时,有个参数notify_url ,这个是回调地址,也就是在支付成功后微信支付会自动访问这个地址,通知业务系统支付完成。但这个地址必须是互联网可以访问的(也就是有域名的)

那么如何测试呢?我们可以借助一个工具 EchoSite 内网映射工具

(1)打开网址: 注册用户,登录到控制台下载客户端。

(2)买一个域名(可以用1个月),点击域名端口—抢注域名

(3)使用软件echosite ,添加config.yml

#   这是你的 EchoSite 购买域名的服务器标志
server_addr: cross.echosite.cn:4443
trust_host_root_certs: falseechosite_id: 17799998888
echosite_token: $2y$10$bY081mt/2KAFJLJXrsSNHe3f.6SM.SGfXgu1gG7aJvmPMPN9BTqrS
​
#   以下是你需要开启的通道,只能开启属于你的域名通道
#   以下分别是 http 和 https 以及 tcp 协议的示例
tunnels:
  name1:
    subdomain: "shangcheng"
    proto:
      http: 127.0.0.1:9010

然后在echosite目录中输入以下命令

echosite -config=config.yml start-all

这样你购买的域名就映射到127.0.0.1:9011上了。

怎么才能验证域名是否映射到你的计算机上了呢?

WxPayController新增notifyLogic方法

/**
 * 回调
 */
@RequestMapping("/notify")
public void notifyLogic(){
    System.out.println("支付成功回调。。。。");
}

测试:

1)通过本地方式访问该接口

2)通过域名形式访问该接口

3.2 接收回调信息

步骤(1)修改支付微服务配置文件

wxpay:
  notify_url: http://weizhaohui.cross.echosite.cn/wxpay/notify #回调地址

步骤(2)修改WxPayServiceImpl ,引入

@Value( "${wxpay.notify_url}" )
private String notifyUrl;

步骤(3)修改WxPayServiceImpl 的nativePay方法

map.put("notify_url",notifyUrl);//回调地址

测试: 重新生成订单并支付。可以发现控制台在不断的触发支付回调通知。这是为什么呢?

注意

1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起10次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。

3.3 回调消息接收并转换

微信支付平台发送给回调地址的数据是二进制流,我们需要提取二进制流转换为字符串,这个字符串就是xml格式。

步骤(1)在shangcheng_common工程添加工具类ConvertUtils。(资源提供:资源\类文件\工具类)

/**
 * 转换工具类
 */
public class ConvertUtils {/**
     * 输入流转换为xml字符串
     * @param inputStream
     * @return
     */
    public static String convertToString(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inputStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inputStream.close();
        String result  = new String(outSteam.toByteArray(), "utf-8");
        return result;
    }
}

步骤(2)修改notifyLogic方法

/**
 * 回调
 */
@RequestMapping("/notify")
public void notifyLogic(HttpServletRequest request, HttpServletResponse response) throws IOException {
    System.out.println("支付成功回调。。。。");
    try {
        //输入流转换为xml字符串
        String xml = ConvertUtils.convertToString( request.getInputStream() );
        System.out.println(xml);//给微信支付一个成功的响应
        response.setContentType("text/xml");
        String data = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        response.getWriter().write(data);
        
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

测试后,在控制台看到输出的消息

<xml><appid><![CDATA[wx8397f8696b538317]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1473426802]]></mch_id>
<nonce_str><![CDATA[c6bea293399a40e0a873df51e667f45a]]></nonce_str>
<openid><![CDATA[oNpSGwbtNBQROpN_dL8WUZG3wRkM]]></openid>
<out_trade_no><![CDATA[1553063775279]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[DD4E5DF5AF8D8D8061B0B8BF210127DE]]></sign>
<time_end><![CDATA[20190320143646]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[NATIVE]]></trade_type>
<transaction_id><![CDATA[4200000248201903206581106357]]></transaction_id>
</xml>

我们可以将此xml字符串,转换为map,提取其中的out_trade_no(订单号),根据订单号修改订单状态。

3.4 查询订单验证通知

在这里插入图片描述

步骤(1)WxPayService新增方法定义

/**
 * 查询订单
 * @param orderId
 * @return
 */
Map queryOrder(String orderId);

步骤(2)WxPayServiceImpl实现方法

@Override
public Map queryOrder(String orderId) {
    Map map=new HashMap(  );
    map.put( "out_trade_no", orderId );
    try {
        return  wxPay.orderQuery( map );
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

步骤(3)修改notifyLogic方法

@Autowired
private RabbitTemplate rabbitTemplate;/**
 * 回调
 */
@RequestMapping("/notify")
public void notifyLogic(HttpServletRequest request, HttpServletResponse response) throws IOException {
    System.out.println("支付成功回调。。。。");
    try {
        //输入流转换为xml字符串
        String xml = ConvertUtils.convertToString( request.getInputStream() );
        System.out.println(xml);
        //解析
        Map<String, String> map = WXPayUtil.xmlToMap( xml );
        //查询订单
        if("SUCCESS".equals(map.get( "result_code" )  )){  //如果返回的结果是成功
            Map result = wxPayService.queryOrder( map.get( "out_trade_no" ) );
            System.out.println("查询订单返回结果:"+result);//如果查询结果是成功发送到mq
            if("SUCCESS".equals( result.get( "result_code" ) )){
                Map m=new HashMap();
                m.put( "orderId",result.get( "out_trade_no" ) );
                m.put( "transactionId",result.get( "transaction_id" ));
                rabbitTemplate.convertAndSend( "","order_pay", JSON.toJSONString(m) );//如果成功,给微信支付一个成功的响应
                response.setContentType("text/xml");
                String data = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                response.getWriter().write(data);
            }
        }else {
            System.out.println(map.get( "err_code_des" ));//错误信息描述
        }
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

(4)rabbitmq中添加order_pay队列

3.5 修改订单状态

步骤(1)com.shangcheng.order.listener包下创建OrderPayListener

@Component
@RabbitListener(queues = "order_pay")
public class OrderPayListener {@Autowired
    private OrderService orderService;/**
     * 更新支付状态
     * @param message
     */
    @RabbitHandler
    public void updatePayStatus(String message){
        System.out.println("接收到消息:"+message);
        Map map = JSON.parseObject( message, Map.class );
        orderService.updatePayStatus( (String)map.get("orderId"), (String)map.get("transactionId") );
    }
}

步骤(2)OrderService接口新增方法定义

/**
 * 修改订单状态为已支付
 * @param orderId
 * @param transactionId
 */
void updatePayStatus(String orderId,String transactionId);

步骤(3)OrderServiceImpl新增方法实现

@Autowired
private OrderLogMapper orderLogMapper;@Override
public void updatePayStatus(String orderId, String transactionId) {
    Order order = orderMapper.selectByPrimaryKey(orderId);
    if(order!=null  && "0".equals(order.getPayStatus())){  //存在订单且状态为0
        order.setPayStatus("1");
        order.setOrderStatus("1");
        order.setUpdateTime(new Date());
        order.setPayTime(new Date());
        order.setTransactionId(transactionId);//微信返回的交易流水号
        orderMapper.updateByPrimaryKeySelective(order);
        //记录订单变动日志
        OrderLog orderLog=new OrderLog();
        orderLog.setId( idWorker.nextId()+"" );
        orderLog.setOperater("system");// 系统
        orderLog.setOperateTime(new Date());//当前日期
        orderLog.setOrderStatus("1");
        orderLog.setPayStatus("1");
        orderLog.setRemarks("支付流水号"+transactionId);
        orderLog.setOrderId(order.getId());
        orderLogMapper.insert(orderLog);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值