【Java】微信支付

https://www.bilibili.com/video/BV1nW421R7qJ

小程序前端官方文档:https://developers.weixin.qq.com/miniprogram/dev/api/payment/wx.requestPayment.html

服务器端官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml

申请并绑定微信支付

微信支付商户平台:https://pay.weixin.qq.com/index.php/core/home/login

对于商家来说,想要开通微信支付,必须要去“微信支付商户平台”注册,然后把需要的资料提交上去,经过审核通过,你就开通了微信支付功能。

企业申请资料:

1、营业执照:彩色扫描件或数码照片

2、组织机构代码证:彩色扫描件或数码照片,若已三证合一,则无需提供

3、对公银行账户:包含开户行省市信息,开户账号

4、法人身份证:彩色扫描件或数码照片

支付接口编写

官方文档地址:https://github.com/wechatpay-apiv3/wechatpay-java

导入依赖

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
</dependency>

common-account.yaml 配置文件
异步回调地址必须外网能够访问,本地可以使用内网穿透。

wx:
  v3pay:
    #小程序微信公众平台appId
    appid: wxcc651fcbab275e33
    #商户号
    merchantId: 1631833859
    #商户API私钥路径
    privateKeyPath: /root/daijia/apiclient_key.pem
    #商户证书序列号
    merchantSerialNumber: 4AE80B52EBEAB2B96F68E02510A42801E952E889
    #商户APIV3密钥
    apiV3key: 84dba6dd51cdaf779e55bcabae564b53
    #异步回调地址
    notifyUrl: http://139.198.127.41:8600/payment/wxPay/notify

配置类


import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix="wx.v3pay") //读取节点
@Data
public class WxPayV3Properties {

    private String appid;
    /** 商户号 */
    public String merchantId;
    /** 商户API私钥路径 */
    public String privateKeyPath;
    /** 商户证书序列号 */
    public String merchantSerialNumber;
    /** 商户APIV3密钥 */
    public String apiV3key;
    /** 回调地址 */
    private String notifyUrl;

    @Bean
    public RSAAutoCertificateConfig getConfig(){
        return new RSAAutoCertificateConfig.Builder()
                        .merchantId(this.getMerchantId())
                        .privateKeyFromPath(this.getPrivateKeyPath())
                        .merchantSerialNumber(this.getMerchantSerialNumber())
                        .apiV3Key(this.getApiV3key())
                        .build();

    }
}

WxPayController

@Operation(summary = "创建微信支付")
@PostMapping("/createJsapi")
public Result<WxPrepayVo> createWxPayment(@RequestBody PaymentInfoForm paymentInfoForm) {
    return Result.ok(wxPayService.createWxPayment(paymentInfoForm));
}

WxPayService

WxPrepayVo createWxPayment(PaymentInfoForm paymentInfoForm);

WxPayServiceImpl

@Override
public WxPrepayVo createWxPayment(PaymentInfoForm paymentInfoForm) {
   try {
      PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, paymentInfoForm.getOrderNo()));
      if(null == paymentInfo) {
         paymentInfo = new PaymentInfo();
         BeanUtils.copyProperties(paymentInfoForm, paymentInfo);
         paymentInfo.setPaymentStatus(0);
         paymentInfoMapper.insert(paymentInfo);
      }

      // 构建service
      JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();

      // request.setXxx(val)设置所需参数,具体参数可见Request定义
      PrepayRequest request = new PrepayRequest();
      Amount amount = new Amount();
      amount.setTotal(paymentInfoForm.getAmount().multiply(new BigDecimal(100)).intValue());
      request.setAmount(amount);
      request.setAppid(wxPayV3Properties.getAppid());
      request.setMchid(wxPayV3Properties.getMerchantId());
      //string[1,127]
      String description = paymentInfo.getContent();
      if(description.length() > 127) {
         description = description.substring(0, 127);
      }
      request.setDescription(paymentInfo.getContent());
      request.setNotifyUrl(wxPayV3Properties.getNotifyUrl());
      request.setOutTradeNo(paymentInfo.getOrderNo());

      //获取用户信息
      Payer payer = new Payer();
      payer.setOpenid(paymentInfoForm.getCustomerOpenId());
      request.setPayer(payer);
       
      //是否指定分账,不指定不能分账
      SettleInfo settleInfo = new SettleInfo();
      settleInfo.setProfitSharing(true);
      request.setSettleInfo(settleInfo);

      // 调用下单方法,得到应答
      // response包含了调起支付所需的所有参数,可直接用于前端调起支付
      PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
      log.info("微信支付下单返回参数:{}", JSON.toJSONString(response));

      WxPrepayVo wxPrepayVo = new WxPrepayVo();
      BeanUtils.copyProperties(response, wxPrepayVo);
      wxPrepayVo.setTimeStamp(response.getTimeStamp());
      return wxPrepayVo;
   } catch (Exception e) {
      e.printStackTrace();
      throw new GuiguException(ResultCodeEnum.WX_CREATE_ERROR);
   }
}

Feign接口

WxPayFeignClient

/**
 * 创建微信支付
 * @param paymentInfoForm
 * @return
 */
@PostMapping("/payment/wxPay/createWxPayment")
Result<WxPrepayVo> createWxPayment(@RequestBody PaymentInfoForm paymentInfoForm);

乘客端web接口

OrderController

@Operation(summary = "创建微信支付")
@GuiguLogin
@PostMapping("/createWxPayment")
public Result<WxPrepayVo> createWxPayment(@RequestBody CreateWxPaymentForm createWxPaymentForm) {
   Long customerId = AuthContextHolder.getUserId();
   createWxPaymentForm.setCustomerId(customerId);
   return Result.ok(orderService.createWxPayment(createWxPaymentForm));
}

OrderService

WxPrepayVo createWxPayment(CreateWxPaymentForm createWxPaymentForm);

OrderServiceImpl

@Autowired
private DriverInfoFeignClient driverInfoFeignClient;

@Autowired
private CustomerInfoFeignClient customerInfoFeignClient;

@Autowired
private WxPayFeignClient wxPayFeignClient;

@Override
public WxPrepayVo createWxPayment(CreateWxPaymentForm createWxPaymentForm) {
    //1.获取订单支付相关信息
    OrderPayVo orderPayVo = orderInfoFeignClient.getOrderPayVo(createWxPaymentForm.getOrderNo(), createWxPaymentForm.getCustomerId()).getData();
    //判断是否在未支付状态
    if (orderPayVo.getStatus().intValue() != OrderStatus.UNPAID.getStatus().intValue()) {
        throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
    }

    //2.获取乘客微信openId
    String customerOpenId = customerInfoFeignClient.getCustomerOpenId(orderPayVo.getCustomerId()).getData();

    //3.获取司机微信openId
    String driverOpenId = driverInfoFeignClient.getDriverOpenId(orderPayVo.getDriverId()).getData();

    //4.封装微信下单对象,微信支付只关注以下订单属性
    PaymentInfoForm paymentInfoForm = new PaymentInfoForm();
    paymentInfoForm.setCustomerOpenId(customerOpenId);
    paymentInfoForm.setDriverOpenId(driverOpenId);
    paymentInfoForm.setOrderNo(orderPayVo.getOrderNo());
    paymentInfoForm.setAmount(orderPayVo.getPayAmount());
    paymentInfoForm.setContent(orderPayVo.getContent());
    paymentInfoForm.setPayWay(1);
    WxPrepayVo wxPrepayVo = wxPayFeignClient.createWxPayment(paymentInfoForm).getData();
    return wxPrepayVo;
}

支付结果通知

  • 方法一:主动进行微信支付查询
@Override
public Boolean queryPayStatus(String orderNo) {
   // 构建service
   JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();

   QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
   queryRequest.setMchid(wxPayV3Properties.getMerchantId());
   queryRequest.setOutTradeNo(orderNo);

   try {
      Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
      log.info(JSON.toJSONString(transaction));
      if(null != transaction && transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
         //更改订单状态
         this.handlePayment(transaction);
         return true;
      }
   } catch (ServiceException e) {
      // API返回失败, 例如ORDER_NOT_EXISTS
      System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
   }
   return false;
}


//如果支付成功,调用其他方法实现支付后处理逻辑
    public void handlePayment(Transaction transaction) {

        //1 更新支付记录,状态修改为 已经支付
        //订单编号
        String orderNo = transaction.getOutTradeNo();
        //根据订单编号查询支付记录
        LambdaQueryWrapper<PaymentInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PaymentInfo::getOrderNo,orderNo);
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(wrapper);
        //如果已经支付,不需要更新
        if(paymentInfo.getPaymentStatus() == 1) {
            return;
        }
        paymentInfo.setPaymentStatus(1);
        paymentInfo.setOrderNo(transaction.getOutTradeNo());
        paymentInfo.setTransactionId(transaction.getTransactionId());
        paymentInfo.setCallbackTime(new Date());
        paymentInfo.setCallbackContent(JSON.toJSONString(transaction));
        paymentInfoMapper.updateById(paymentInfo);

        //2 发送端:发送mq消息,传递 订单编号
        //  接收端:获取订单编号,完成后续处理
        rabbitService.sendMessage(MqConst.EXCHANGE_ORDER,
                MqConst.ROUTING_PAY_SUCCESS,
                orderNo);
    }
  • 方法二:微信平台异步通知
    腾讯服务器会异步回调我们提供给其的接口 (进行支付接口的时候,配置了接口路径了 request.setNotifyUrl(wxPayV3Properties.getNotifyUrl());)

WxPayController

@Operation(summary = "微信支付异步通知接口")
@PostMapping("/notify")
public Map<String,Object> notify(HttpServletRequest request) {
    try {
        wxPayService.wxnotify(request);

        //返回成功
        Map<String,Object> result = new HashMap<>();
        result.put("code", "SUCCESS");
        result.put("message", "成功");
        return result;
    } catch (Exception e) {
        e.printStackTrace();
    }

    //返回失败
    Map<String,Object> result = new HashMap<>();
    result.put("code", "FAIL");
    result.put("message", "失败");
    return result;
}

WxPayService

void wxnotify(HttpServletRequest request);

WxPayServiceImpl

@Autowired
private RabbitService rabbitService;

@Transactional
@Override
public void wxnotify(HttpServletRequest request) {
   //1.回调通知的验签与解密
   //从request头信息获取参数
   //HTTP 头 Wechatpay-Signature
   // HTTP 头 Wechatpay-Nonce
   //HTTP 头 Wechatpay-Timestamp
   //HTTP 头 Wechatpay-Serial
   //HTTP 头 Wechatpay-Signature-Type
   //HTTP 请求体 body。切记使用原始报文,不要用 JSON 对象序列化后的字符串,避免验签的 body 和原文不一致。
   String wechatPaySerial = request.getHeader("Wechatpay-Serial");
   String nonce = request.getHeader("Wechatpay-Nonce");
   String timestamp = request.getHeader("Wechatpay-Timestamp");
   String signature = request.getHeader("Wechatpay-Signature");
   String requestBody = RequestUtils.readData(request);
   log.info("wechatPaySerial:{}", wechatPaySerial);
   log.info("nonce:{}", nonce);
   log.info("timestamp:{}", timestamp);
   log.info("signature:{}", signature);
   log.info("requestBody:{}", requestBody);

   //2.构造 RequestParam
   RequestParam requestParam = new RequestParam.Builder()
         .serialNumber(wechatPaySerial)
         .nonce(nonce)
         .signature(signature)
         .timestamp(timestamp)
         .body(requestBody)
         .build();


   //3.初始化 NotificationParser
   NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
   //4.以支付通知回调为例,验签、解密并转换成 Transaction
   Transaction transaction = parser.parse(requestParam, Transaction.class);
   log.info("成功解析:{}", JSON.toJSONString(transaction));
   if(null != transaction && transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
      //5.处理支付业务
      this.handlePayment(transaction);
   }
}

public void handlePayment(Transaction transaction) {
   PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, transaction.getOutTradeNo()));
   if (paymentInfo.getPaymentStatus() == 1) {
      return;
   }

   //更新支付信息
   paymentInfo.setPaymentStatus(1);
   paymentInfo.setOrderNo(transaction.getOutTradeNo());
   paymentInfo.setTransactionId(transaction.getTransactionId());
   paymentInfo.setCallbackTime(new Date());
   paymentInfo.setCallbackContent(JSON.toJSONString(transaction));
   paymentInfoMapper.updateById(paymentInfo);
   // 表示交易成功!

   // 后续更新订单状态! 使用消息队列!
   rabbitService.sendMessage(MqConst.EXCHANGE_ORDER, MqConst.ROUTING_PAY_SUCCESS, paymentInfo.getOrderNo());
}

rabbitmq异步

rabbitmq:微信支付成功之后,还需要进行一些其他的后续操作(更新数据库的表什么的)。我们可以使用消息队列,进行异步操作,保证数据的最终一致性。先将支付成功的结果返回给用户

编写支付成功后的方法-发送端

  • 在handlePayment方法编写
//如果支付成功,调用其他方法实现支付后处理逻辑
public void handlePayment(Transaction transaction) {

    //1 更新支付记录,状态修改为 已经支付
    //订单编号
    String orderNo = transaction.getOutTradeNo();
    //根据订单编号查询支付记录
    LambdaQueryWrapper<PaymentInfo> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(PaymentInfo::getOrderNo,orderNo);
    PaymentInfo paymentInfo = paymentInfoMapper.selectOne(wrapper);
    //如果已经支付,不需要更新
    if(paymentInfo.getPaymentStatus() == 1) {
        return;
    }
    paymentInfo.setPaymentStatus(1);
    paymentInfo.setOrderNo(transaction.getOutTradeNo());
    paymentInfo.setTransactionId(transaction.getTransactionId());
    paymentInfo.setCallbackTime(new Date());
    paymentInfo.setCallbackContent(JSON.toJSONString(transaction));
    paymentInfoMapper.updateById(paymentInfo);

    //2 发送端:发送mq消息,传递 订单编号
    //  接收端:获取订单编号,完成后续处理
    rabbitService.sendMessage(MqConst.EXCHANGE_ORDER,
            MqConst.ROUTING_PAY_SUCCESS,
            orderNo);
}

编写支付成功后的方法-接收端

@Component
public class PaymentReceiver {

    @Autowired
    private WxPayService wxPayService;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_PAY_SUCCESS,durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_ORDER),
            key = {MqConst.ROUTING_PAY_SUCCESS}
    ))
    public void paySuccess(String orderNo, Message message, Channel channel) {
        wxPayService.handleOrder(orderNo);
    }
}
//支付成功后续处理
@Override
public void handleOrder(String orderNo) {
    //1 远程调用:更新订单状态:已经支付
    
    //2 远程调用:获取系统奖励,打入到司机账户
    
    //3 TODO 其他
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值