微信支付

微信支付

微信支付网址

1 选择支付类型

在这里插入图片描述

选择扫码支付模式二

在这里插入图片描述

2 选择统一订单管理

在这里插入图片描述
有微信支付需要的请求参数

3 下载WeChatPay中的java SDK

在这里插入图片描述

4 编写WXPayConfig和IWXPayDomain实现类

WXPayConfig实现类

package com.github.wxpay.sdk;

import lombok.Data;

import java.io.InputStream;


@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();
    }
}

IWXPayDomain实现类

package com.github.wxpay.sdk;
import org.apache.http.conn.ConnectTimeoutException;

import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by blaketang on 2017/6/16.
 */
public class WXPayDomainSimpleImpl implements IWXPayDomain {
    private WXPayDomainSimpleImpl(){}
    private static class WxpayDomainHolder{
        private static IWXPayDomain holder = new WXPayDomainSimpleImpl();
    }
    public static IWXPayDomain instance(){
        return WxpayDomainHolder.holder;
    }

    public synchronized void report(final String domain, long elapsedTimeMillis, final Exception ex) {
        DomainStatics info = domainData.get(domain);
        if(info == null){
            info = new DomainStatics(domain);
            domainData.put(domain, info);
        }

        if(ex == null){ //success
            if(info.succCount >= 2){    //continue succ, clear error count
                info.connectTimeoutCount = info.dnsErrorCount = info.otherErrorCount = 0;
            }else{
                ++info.succCount;
            }
        }else if(ex instanceof ConnectTimeoutException){
            info.succCount = info.dnsErrorCount = 0;
            ++info.connectTimeoutCount;
        }else if(ex instanceof UnknownHostException){
            info.succCount = 0;
            ++info.dnsErrorCount;
        }else{
            info.succCount = 0;
            ++info.otherErrorCount;
        }
    }

    public synchronized DomainInfo getDomain(final WXPayConfig config) {
        DomainStatics primaryDomain = domainData.get(WXPayConstants.DOMAIN_API);
        if(primaryDomain == null ||
                primaryDomain.isGood()) {
            return new DomainInfo(WXPayConstants.DOMAIN_API, true);
        }

        long now = System.currentTimeMillis();
        if(switchToAlternateDomainTime == 0){   //first switch
            switchToAlternateDomainTime = now;
            return new DomainInfo(WXPayConstants.DOMAIN_API2, false);
        }else if(now - switchToAlternateDomainTime < MIN_SWITCH_PRIMARY_MSEC){
            DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2);
            if(alternateDomain == null ||
                alternateDomain.isGood() ||
                alternateDomain.badCount() < primaryDomain.badCount()){
                return new DomainInfo(WXPayConstants.DOMAIN_API2, false);
            }else{
                return new DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        }else{  //force switch back
            switchToAlternateDomainTime = 0;
            primaryDomain.resetCount();
            DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2);
            if(alternateDomain != null)
                alternateDomain.resetCount();
            return new DomainInfo(WXPayConstants.DOMAIN_API, true);
        }
    }

    static class DomainStatics {
        final String domain;
        int succCount = 0;
        int connectTimeoutCount = 0;
        int dnsErrorCount =0;
        int otherErrorCount = 0;

        DomainStatics(String domain) {
            this.domain = domain;
        }
        void resetCount(){
            succCount = connectTimeoutCount = dnsErrorCount = otherErrorCount = 0;
        }
        boolean isGood(){ return connectTimeoutCount <= 2 && dnsErrorCount <= 2; }
        int badCount(){
            return connectTimeoutCount + dnsErrorCount * 5 + otherErrorCount / 4;
        }
    }
    private final int MIN_SWITCH_PRIMARY_MSEC = 3 * 60 * 1000;  //3 minutes
    private long switchToAlternateDomainTime = 0;
    private Map<String, DomainStatics> domainData = new HashMap<String, DomainStatics>();
}

5 将SDK install 到本地仓库

在用DOSS命令进行install

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

用这个命令主要是为了跳过测试,用maven中的install也可以

6 在工程里引入wxpay坐标

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

选择WXPay中的方法

在这里插入图片描述

在这使用统一下单方法

/**
     * 作用:统一下单<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());
}

请求参数在微信提供的文档里有;但是我们只需要APPID 商家Id 秘钥 回调URL 支付类型 即可,这些都写进yml文件里了,

7 在yml文件里配置APPId 商家Id 秘钥 回调URL 支付类型

ly:
  pay:
    wx:
      appID: *************
      mchID: ***********
      key: *****************
      payType: NATIVE

8 编写配置类,将属性配置到配置类中

把yml文件中的参数读取到WXPayConfigImpl中,再由此构建WXPay

package com.leyou.order.config;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfigImpl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PayConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "ly.pay.wx")
    public WXPayConfigImpl payConfig(){
        return new WXPayConfigImpl();
    }

    /**
     * 注册WXPay对象
     * @param payConfig 支付相关配置
     * @return WXPay对象
     * @throws Exception 连结WX失败时用到
     */
    @Bean
    public WXPay wxPay(WXPayConfigImpl payConfig) throws Exception {
        return new WXPay(payConfig);
    }
}

9 发送带有订单数据的请求给微信支付,返回值为支付的URL

我们先初始化WXPay对象,并注入到Spring容器中:

controller

 @GetMapping("/url/{id}")
    public ResponseEntity<String> getUrl(@PathVariable("id") Long orderId){
        return ResponseEntity.ok(orderService.getUrl(orderId));
    }

service

 @Autowired
 private TbOrderService orderService;
 @Autowired
 private PayHelper payHelper;
 @Autowired
 private StringRedisTemplate redisTemplate;
 private String payUrlKey = "ly:pay:orderid:";
    public String getUrl(Long orderId) {
        //先去redis中查询url有没有,有直接返回,没有取微信获得
        String url = redisTemplate.opsForValue().get(payUrlKey + orderId);
        if (!StringUtils.isEmpty(url)){
            return url;
        }
        TbOrder order = orderService.getById(orderId);
        if (order==null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //订单状态不为1 不能去支付
        if (order.getStatus().intValue() != OrderStatusEnum.INIT.value().intValue()){
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }

        Long actualFee = 1L;//order.getActualFee();
        String payUrl = payHelper.createOrder(orderId, actualFee, "乐优商城");
        //得到url后 存入到redis中,设置时间为2小时,key为当前用户的userId
        redisTemplate.opsForValue().set(payUrlKey+orderId,payUrl,2, TimeUnit.HOURS);
        return payUrl;
    }

在这用到了PayHelper工具类,
1 准备微信支付要是的数据,用Map来传输
2 调用unifiedOrder方法获得支付的连接

package com.leyou.order.utils;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfigImpl;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

@Slf4j
@Component
public class PayHelper {

    @Autowired
    private WXPay wxPay;

    @Autowired
    private WXPayConfigImpl payConfig;

    public String createOrder(Long orderId, Long totalPay, String desc){
        Map<String, String> data = new HashMap<>();
        // 商品描述
        data.put("body", desc);
        // 订单号
        data.put("out_trade_no", orderId.toString());
        //金额,单位是分
        data.put("total_fee", totalPay.toString());
        //调用微信支付的终端IP
        data.put("spbill_create_ip", "127.0.0.1");
        //回调地址
        data.put("notify_url", payConfig.getNotifyUrl());
        // 交易类型为扫码支付
        data.put("trade_type", payConfig.getPayType());

        // 利用wxPay工具,完成下单
        Map<String, String> result = null;
        try {
            result = wxPay.unifiedOrder(data);
        } catch (Exception e) {
            log.error("【微信下单】创建预交易订单异常失败", e);
            throw new RuntimeException("微信下单失败", e);
        }
        // 校验业务状态
        checkResultCode(result);

        // 下单成功,获取支付链接
        String url = result.get("code_url");
        if (StringUtils.isBlank(url)) {
            throw new RuntimeException("微信下单失败,支付链接为空");
        }
        return url;
    }

    public void checkResultCode(Map<String, String> result) {
        // 检查业务状态
        String resultCode = result.get("result_code");
        if ("FAIL".equals(resultCode)) {
            log.error("【微信支付】微信支付业务失败,错误码:{},原因:{}", result.get("err_code"), result.get("err_code_des"));
            throw new RuntimeException("【微信支付】微信支付业务失败");
        }
    }

    /**
     * 验证签名
     * @param result 微信回调返回的结果
     * @throws Exception
     */
    public void isValidSign(Map<String,String> result) throws Exception {
        boolean bool1 = WXPayUtil.isSignatureValid(result, payConfig.getKey(), WXPayConstants.SignType.MD5);
        boolean bool2 = WXPayUtil.isSignatureValid(result, payConfig.getKey(), WXPayConstants.SignType.HMACSHA256);
        if (!bool1 && !bool2){
            throw new RuntimeException("【微信支付回调】签名有误");
        }
    }
}

10 前端根据URL生成二维码

生成二维码用的是二维码生成插件qrious
将qrcode.js引入到前端工程中
在支付页面引入js
写一个div用于展示二维码
用返回的url生成二维码

  // 生成付款链接
          ly.http.get("/order/url/" + id).then(resp => {
            new QRCode(document.getElementById("qrImage"), {
              text: resp.data,
              width: 250,
              height: 250,
              colorDark: "#000000",
              colorLight: "#ffffff",
              correctLevel: QRCode.CorrectLevel.H
            });

11 编写用户支付成功后,接收微信返回支付结果的方法

controller

package com.leyou.order.controller;

import com.leyou.order.service.impl.PayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

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

/**
 * - 微信给这个发送请求,返回支付结果
 * - 请求方式:官方文档虽然没有明说,但是测试得出是POST请求
 * - 请求路径:我们之前指定的notify_url的路径是:/pay/wx/notify
 * - 请求参数:是xml格式数据,包括支付的结果和状态
 * - 返回结果:也是xml,表明是否成功
 */
@Slf4j
@RestController
@RequestMapping("/pay")
public class PayController {
    @Autowired
    private PayService payService;

    @PostMapping(value = "/wx/notify",produces = {"application/xml"})
    public Map<String,String> hello(@RequestBody Map<String,String> result){
        //拿到结果判断金额,修改状态
        log.info("[支付回调] 接收微信回调,结果:{}",result);
        payService.handleNotify(result);

        //返回信息
        Map<String,String> msg = new HashMap<>();
        msg.put("return_code","SUCCESS");
        msg.put("return_msg","OK");
        return msg;

    }


}

service

package com.leyou.order.service.impl;

import com.github.wxpay.sdk.WXPay;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.order.entity.TbOrder;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.service.TbOrderService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;

@Service
public class PayService {
    @Autowired
    private WXPay wxPay;
    @Autowired
    private TbOrderService orderService;
    @Transactional
    public void handleNotify(Map<String, String> result) {
        //判断的result_code是否为success
        if (result.get("result_code") == null || result.get("result_code").equals("SUCCESS")){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        //判断支付结果通知中的sign是否有效
        try {
            boolean flag = wxPay.isPayResultNotifySignatureValid(result);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        //1 获取返回的订单id
        String tradeNo = result.get("out_trade_no");
        if (StringUtils.isEmpty(tradeNo)){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        //2 获取返回的总金额
        String totalFee = result.get("total_fee");
        if (StringUtils.isEmpty(totalFee)){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        Long total = Long.valueOf(totalFee);

        //3 获取数据库订单金额
        TbOrder tbOrder = orderService.getById(Long.valueOf(tradeNo));
        Long actualFee = tbOrder.getActualFee();
        //4 返回金额和数据库金额做比对
        //现在是测试,所以不能使用下面的判断
       /* if (!total.equals(actualFee)){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }*/
        //5 保证订单的状态为未付款
        if (tbOrder.getStatus().longValue() != OrderStatusEnum.INIT.value()){
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }
        //6 修改订单状态
        TbOrder tbOrder1 = new TbOrder();
        tbOrder1.setOrderId(Long.valueOf(tradeNo).longValue());
        tbOrder1.setStatus(OrderStatusEnum.PAY_UP.value());
        boolean b = orderService.updateById(tbOrder1);
        if (!b){
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }

    }


}

12 定时查询支付状态

在这里插入图片描述
后端:
controller

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

    @GetMapping("/state/{id}")
    public ResponseEntity<Integer> queryState(@PathVariable("id") Long id){
        return ResponseEntity.ok(orderService.queryState(id));
    }

service

 public Integer queryState(long id) {
        TbOrder tbOrder = orderService.getById(id);
        if (tbOrder == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return tbOrder.getStatus();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值