java 微信支付前后端代码(小程序+java)

微信小程序支付--业务流程:
1.小程序发起订单,支付
2.后端程序封装数据调用微信接口发起预支付,微信返回预支付数据到小程序
3.小程序弹出支付界面输入密码确认支付,调用微信支付接口支付
4.微信支付成功调用后端外网的回调接口,回调接口接收到微信数据,解析读取数据后调用微信接口通知微信已收到回调
5.更新系统表的订单状态已支付
6.小程序端1秒刷新一次调用后端接口,查看订单状态是否支付成功,支付成功显示支付成功。

1.下订单及支付接口
package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.;
import com.sky.entity.
;
import com.sky.exception.AddressBookBusinessException;
import com.sky.exception.OrderBusinessException;
import com.sky.exception.ShoppingCartBusinessException;
import com.sky.mapper.*;
import com.sky.result.PageResult;
import com.sky.service.OrderService;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderStatisticsVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**

  • 订单
    */
    @Service
    @Slf4j
    public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;
    @Autowired
    private WeChatPayUtil weChatPayUtil;

    /**

    • 用户下单

    • @param ordersSubmitDTO

    • @return
      */
      @Transactional
      public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
      //异常情况的处理(收货地址为空、购物车为空)
      AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
      if (addressBook == null) {
      throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
      }

      Long userId = BaseContext.getCurrentId();
      ShoppingCart shoppingCart = new ShoppingCart();
      shoppingCart.setUserId(userId);

      //查询当前用户的购物车数据
      List shoppingCartList = shoppingCartMapper.list(shoppingCart);
      if (shoppingCartList == null || shoppingCartList.size() == 0) {
      throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
      }

      //构造订单数据
      Orders order = new Orders();
      BeanUtils.copyProperties(ordersSubmitDTO,order);
      order.setPhone(addressBook.getPhone());
      order.setAddress(addressBook.getDetail());
      order.setConsignee(addressBook.getConsignee());
      order.setNumber(String.valueOf(System.currentTimeMillis()));
      order.setUserId(userId);
      order.setStatus(Orders.PENDING_PAYMENT);
      order.setPayStatus(Orders.UN_PAID);
      order.setOrderTime(LocalDateTime.now());

      //向订单表插入1条数据
      orderMapper.insert(order);

      //订单明细数据
      List orderDetailList = new ArrayList<>();
      for (ShoppingCart cart : shoppingCartList) {
      OrderDetail orderDetail = new OrderDetail();
      BeanUtils.copyProperties(cart, orderDetail);
      orderDetail.setOrderId(order.getId());
      orderDetailList.add(orderDetail);
      }

      //向明细表插入n条数据
      orderDetailMapper.insertBatch(orderDetailList);

      //清理购物车中的数据
      shoppingCartMapper.deleteByUserId(userId);

      //封装返回结果
      OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
      .id(order.getId())
      .orderNumber(order.getNumber())
      .orderAmount(order.getAmount())
      .orderTime(order.getOrderTime())
      .build();

      return orderSubmitVO;
      }

    /**

    • 订单支付

    • @param ordersPaymentDTO

    • @return
      */
      public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
      // 当前登录用户id
      Long userId = BaseContext.getCurrentId();
      User user = userMapper.getById(userId);

      //调用微信支付接口,生成预支付交易单
      JSONObject jsonObject = weChatPayUtil.pay(
      ordersPaymentDTO.getOrderNumber(), //商户订单号
      new BigDecimal(0.01), //支付金额,单位 元
      "苍穹外卖订单", //商品描述
      user.getOpenid() //微信用户的openid
      );

      if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
      throw new OrderBusinessException("该订单已支付");
      }

      OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
      vo.setPackageStr(jsonObject.getString("package"));

      return vo;
      }

    /**

    • 支付成功,修改订单状态

    • @param outTradeNo
      */
      public void paySuccess(String outTradeNo) {

      // 根据订单号查询订单
      Orders ordersDB = orderMapper.getByNumber(outTradeNo);

      // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
      Orders orders = Orders.builder()
      .id(ordersDB.getId())
      .status(Orders.TO_BE_CONFIRMED)
      .payStatus(Orders.PAID)
      .checkoutTime(LocalDateTime.now())
      .build();

      orderMapper.update(orders);
      }

}

2.回调相关方法:
package com.sky.controller.notify;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.properties.WeChatProperties;
import com.sky.service.OrderService;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**

  • 支付回调相关接口
    */
    @RestController
    @RequestMapping("/notify")
    @Slf4j
    public class PayNotifyController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private WeChatProperties weChatProperties;

    /**

    • 支付成功回调

    • @param request
      */
      @RequestMapping("/paySuccess")
      public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
      //读取数据
      String body = readData(request);
      log.info("支付成功回调:{}", body);

      //数据解密
      String plainText = decryptData(body);
      log.info("解密后的文本:{}", plainText);

      JSONObject jsonObject = JSON.parseObject(plainText);
      String outTradeNo = jsonObject.getString("out_trade_no");//商户平台订单号
      String transactionId = jsonObject.getString("transaction_id");//微信支付交易号

      log.info("商户平台订单号:{}", outTradeNo);
      log.info("微信支付交易号:{}", transactionId);

      //业务处理,修改订单状态、来单提醒
      orderService.paySuccess(outTradeNo);

      //给微信响应
      responseToWeixin(response);
      }

    /**

    • 回调后读取数据
    • @param request
    • @return
    • @throws Exception
      */
      private String readData(HttpServletRequest request) throws Exception {
      BufferedReader reader = request.getReader();
      StringBuilder result = new StringBuilder();
      String line = null;
      while ((line = reader.readLine()) != null) {
      if (result.length() > 0) {
      result.append("\n");
      }
      result.append(line);
      }
      return result.toString();
      }

    /**

    • 数据解密

    • @param body

    • @return

    • @throws Exception
      */
      private String decryptData(String body) throws Exception {
      JSONObject resultObject = JSON.parseObject(body);
      JSONObject resource = resultObject.getJSONObject("resource");
      String ciphertext = resource.getString("ciphertext");
      String nonce = resource.getString("nonce");
      String associatedData = resource.getString("associated_data");

      AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
      //密文解密
      String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
      nonce.getBytes(StandardCharsets.UTF_8),
      ciphertext);

      return plainText;
      }

    /**

    • 收到回调后给微信响应
    • @param response
      */
      private void responseToWeixin(HttpServletResponse response) throws Exception{
      response.setStatus(200);
      HashMap<Object, Object> map = new HashMap<>();
      map.put("code", "SUCCESS");
      map.put("message", "SUCCESS");
      response.setHeader("Content-type", ContentType.APPLICATION_JSON.toString());
      response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8));
      response.flushBuffer();
      }
      }

3.微信支付工具类

package com.sky.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.properties.WeChatProperties;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

/**

  • 微信支付工具类
    */
    @Component
    public class WeChatPayUtil {

    //微信支付下单接口地址
    public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    //申请退款接口地址
    public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";

    @Autowired
    private WeChatProperties weChatProperties;

    /**

    • 获取调用微信接口的客户端工具对象

    • @return
      */
      private CloseableHttpClient getClient() {
      PrivateKey merchantPrivateKey = null;
      try {
      //merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题
      merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
      //加载平台证书文件
      X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath())));
      //wechatPayCertificates微信支付平台证书列表。你也可以使用后面章节提到的“定时更新平台证书功能”,而不需要关心平台证书的来龙去脉
      List wechatPayCertificates = Arrays.asList(x509Certificate);

       WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
               .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
               .withWechatPay(wechatPayCertificates);
      
       // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
       CloseableHttpClient httpClient = builder.build();
       return httpClient;
      

      } catch (FileNotFoundException e) {
      e.printStackTrace();
      return null;
      }
      }

    /**

    • 发送post方式请求

    • @param url

    • @param body

    • @return
      */
      private String post(String url, String body) throws Exception {
      CloseableHttpClient httpClient = getClient();

      HttpPost httpPost = new HttpPost(url);
      httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
      httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
      httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
      httpPost.setEntity(new StringEntity(body, "UTF-8"));

      CloseableHttpResponse response = httpClient.execute(httpPost);
      try {
      String bodyAsString = EntityUtils.toString(response.getEntity());
      return bodyAsString;
      } finally {
      httpClient.close();
      response.close();
      }
      }

    /**

    • 发送get方式请求

    • @param url

    • @return
      */
      private String get(String url) throws Exception {
      CloseableHttpClient httpClient = getClient();

      HttpGet httpGet = new HttpGet(url);
      httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
      httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
      httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());

      CloseableHttpResponse response = httpClient.execute(httpGet);
      try {
      String bodyAsString = EntityUtils.toString(response.getEntity());
      return bodyAsString;
      } finally {
      httpClient.close();
      response.close();
      }
      }

    /**

    • jsapi下单

    • @param orderNum 商户订单号

    • @param total 总金额

    • @param description 商品描述

    • @param openid 微信用户的openid

    • @return
      */
      private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
      JSONObject jsonObject = new JSONObject();
      jsonObject.put("appid", weChatProperties.getAppid());
      jsonObject.put("mchid", weChatProperties.getMchid());
      jsonObject.put("description", description);
      jsonObject.put("out_trade_no", orderNum);
      jsonObject.put("notify_url", weChatProperties.getNotifyUrl());

      JSONObject amount = new JSONObject();
      amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
      amount.put("currency", "CNY");

      jsonObject.put("amount", amount);

      JSONObject payer = new JSONObject();
      payer.put("openid", openid);

      jsonObject.put("payer", payer);

      String body = jsonObject.toJSONString();
      return post(JSAPI, body);
      }

    /**

    • 小程序支付

    • @param orderNum 商户订单号

    • @param total 金额,单位 元

    • @param description 商品描述

    • @param openid 微信用户的openid

    • @return
      */
      public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
      //统一下单,生成预支付交易单
      String bodyAsString = jsapi(orderNum, total, description, openid);
      //解析返回结果
      JSONObject jsonObject = JSON.parseObject(bodyAsString);
      System.out.println(jsonObject);

      String prepayId = jsonObject.getString("prepay_id");
      if (prepayId != null) {
      String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
      String nonceStr = RandomStringUtils.randomNumeric(32);
      ArrayList list = new ArrayList<>();
      list.add(weChatProperties.getAppid());
      list.add(timeStamp);
      list.add(nonceStr);
      list.add("prepay_id=" + prepayId);
      //二次签名,调起支付需要重新签名
      StringBuilder stringBuilder = new StringBuilder();
      for (Object o : list) {
      stringBuilder.append(o).append("\n");
      }
      String signMessage = stringBuilder.toString();
      byte[] message = signMessage.getBytes();

       Signature signature = Signature.getInstance("SHA256withRSA");
       signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))));
       signature.update(message);
       String packageSign = Base64.getEncoder().encodeToString(signature.sign());
      
       //构造数据给微信小程序,用于调起微信支付
       JSONObject jo = new JSONObject();
       jo.put("timeStamp", timeStamp);
       jo.put("nonceStr", nonceStr);
       jo.put("package", "prepay_id=" + prepayId);
       jo.put("signType", "RSA");
       jo.put("paySign", packageSign);
      
       return jo;
      

      }
      return jsonObject;
      }

    /**

    • 申请退款

    • @param outTradeNo 商户订单号

    • @param outRefundNo 商户退款单号

    • @param refund 退款金额

    • @param total 原订单金额

    • @return
      */
      public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception {
      JSONObject jsonObject = new JSONObject();
      jsonObject.put("out_trade_no", outTradeNo);
      jsonObject.put("out_refund_no", outRefundNo);

      JSONObject amount = new JSONObject();
      amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
      amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
      amount.put("currency", "CNY");

      jsonObject.put("amount", amount);
      jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl());

      String body = jsonObject.toJSONString();

      //调用申请退款接口
      return post(REFUNDS, body);
      }
      }

posted @ 2024-02-15 10:45  大树2  阅读(2)  评论(0编辑  收藏  举报