小程序秒杀--java对接微信小程序支付

小程序秒杀-JAVA对接微信小程序付款

微信小程序支付文档.

准备事项

appId(小程序分配)
小程序密钥(小程序配置界面获取)
商户号
api密钥(商家后台自己设置)

支付流程图

在这里插入图片描述

开发工具和环境

idea+jdk1.8+mysql 5.7 + spring cloud

JAVA代码

Controller

package cn.nhdc.cloud.modules.mx.controller;

import cn.nhdc.cloud.common.utils.JwtUtil;
import cn.nhdc.cloud.modules.customer.model.entity.Customer;
import cn.nhdc.cloud.modules.customer.service.ICustomerService;
import cn.nhdc.cloud.modules.mx.model.vo.MxOrderVo;
import cn.nhdc.cloud.modules.mx.service.OrderHandleService;
import cn.nhdc.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author Asa
 * @createDate 2020-11-28
 */

@Slf4j
@RestController
@RequestMapping("/order/handle")
public class OrderHandleController {
   

    @Resource
    private OrderHandleService handleService;
    @Resource
    private ICustomerService customerService;

    /**
     * 提交订单
     * @param mxOrderVo 下单信息
     */
    @PostMapping("/submitOrder")
    public Map<String, String> save(@RequestBody @Validated MxOrderVo mxOrderVo,
                                    HttpServletRequest request) {
   
        checkReq(request);
        mxOrderVo.setUserId(JwtUtil.getUserId());
//        //TODO
//        mxOrderVo.setUserId(1291198124810846209L);
        Customer customer = customerService.getById(mxOrderVo.getUserId());
        if (customer == null) {
   
            throw new BusinessException("用户不存在,请重新登录后重试!");
        }
       return handleService.submitOrder(mxOrderVo, customer, request);
    }

    private void checkReq(HttpServletRequest request) {
   
        String userAgent = request.getHeader("user-agent").toLowerCase();
        //非微信客户端不能参与
        if(userAgent.indexOf("micromessenger") == -1){
   
            throw new BusinessException("参数错误!");
        }
    }

    /**
     * 微信回调函数
     */
    @PostMapping("/notify")
    public void notify(HttpServletRequest request,HttpServletResponse response) {
   
        handleService.notifyOrder(request,response);
    }

    /**
     *  查询订单状态
     */
    @GetMapping("/query")
    public Boolean query(String sn) {
   
        return handleService.query(sn);
    }


    /**
     * 待付款下提交订单
     * @param id
     * @return
     */
    @GetMapping("/reSubmitOrder")
    public Map<String, Object> reSubmitOrder(@RequestParam("id") Long id) {
   
        return handleService.reSubmitOrder(id);
    }

}

Service:
订单业务逻辑

package cn.nhdc.cloud.modules.mx.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.nhdc.cloud.common.enums.*;
import cn.nhdc.cloud.common.utils.JwtUtil;
import cn.nhdc.cloud.modules.customer.model.entity.Customer;
import cn.nhdc.cloud.modules.mx.model.vo.MxOrderVo;
import cn.nhdc.cloud.modules.mx.model.vo.MxProductDto;
import cn.nhdc.cloud.modules.mx.service.AsyncMxOrderService;
import cn.nhdc.cloud.modules.mx.service.IMxProductService;
import cn.nhdc.cloud.modules.mx.service.IMxUserDataService;
import cn.nhdc.cloud.modules.mx.service.OrderHandleService;
import cn.nhdc.cloud.modules.order.mapper.ProductOrderMapper;
import cn.nhdc.cloud.modules.order.model.entity.ProductOrder;
import cn.nhdc.cloud.modules.pay.service.IWxPayService;
import cn.nhdc.cloud.modules.pay.wxsdk.WXPayConstants;
import cn.nhdc.cloud.modules.pay.wxsdk.WXPayUtil;
import cn.nhdc.cloud.modules.product.service.ISnService;
import cn.nhdc.common.exception.BusinessException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.klock.annotation.Klock;
import org.springframework.boot.autoconfigure.klock.model.LockTimeoutStrategy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Asa
 * @createDate 2020-11-28
 */
@Slf4j
@Service("orderHandleService")
public class OrderHandleServiceImpl implements OrderHandleService {
   

    @Resource
    private IMxProductService mxProductService;
    @Resource
    private AsyncMxOrderService asyncMxOrderService;
    @Resource
    private ISnService snService;
    @Resource
    private ProductOrderMapper productOrderMapper;
    @Resource
    private IWxPayService wxPayService;
    @Resource
    private IMxUserDataService userDataService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Klock(name="xwh:mall:mxOrder", keys = {
   "#mxOrderVo.mxProductId"}, lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST)
    public Map<String, String> submitOrder(MxOrderVo mxOrderVo, Customer customer, HttpServletRequest request) {
   
        mxOrderVo.setUserId(customer.getId());
        MxProductDto dbProductInfo = mxProductService.getInfoById(mxOrderVo.getMxProductId(), customer.getId(),
                MxPublishStatusEnum.PUBLISHED, OrderStatusEnum.CANCEL);
        //检查是否能秒杀
        checkCanMx(mxOrderVo, dbProductInfo);
        //设置订单基本信息
        ProductOrder order = getProductOrder(mxOrderVo, dbProductInfo);
        mxProductService.updateMxProductRemainNum(mxOrderVo);
        //生成微信支付所需参数
        Map<String, String> wxResult = wxPayService.generateOrder(order, customer.getOpenId(),request);
        //更新商品信息
        asyncMxOrderService.saveInfo(mxOrderVo, order, wxResult);
        return wxResult;
    }

    private ProductOrder getProductOrder(MxOrderVo mxOrderVo, MxProductDto dbProductInfo) {
   
        ProductOrder order = new ProductOrder();
        BeanUtils.copyProperties(mxOrderVo, order);
        //当前时间
        Date dateTime = new Date();
        //设置商品单号
        String sn = dbProductInfo.getSn();
        order.setProductSn(sn);
        order.setRemark(mxOrderVo.getRemark());
        //设置单号
        BigDecimal multiply = dbProductInfo.getPrice().multiply(BigDecimal.valueOf(mxOrderVo.getQuantity()));
        order.setSn(snService.generateOrder(dateTime));
        order.setScore(dbProductInfo.getPrice());
        //存入类型
        order.setBelongTo(BelongToEnum.SEC_KILL);
        order.setTotalScore(multiply);
        order.setStatus(OrderStatusEnum.UN_PAYMENT.getStatus());
        //设置商品信息
        order.setProductId(dbProductInfo.getProductId());
        order.setProductName(dbProductInfo.getName());
        order.setProductImg(StringUtils.isNotBlank(dbProductInfo.getImages()) ? dbProductInfo.getImages().split(",")[0] : null);
        //设置用户
        order.setCustomerId(mxOrderVo.getUserId());
        return order;
    }

    private void checkCanMx(MxOrderVo mxOrderVo, MxProductDto dbProductInfo) {
   
        if (dbProductInfo == null){
   
            throw new BusinessException("商品秒杀已结束!");
        }
        if(dbProductInfo.getMxStartTime().compareTo(new Date()) == 1){
   
            throw new BusinessException("该商品秒杀还未开始!");
        }
        if (dbProductInfo.getRemainQuantity() <= 0){
   
            throw new BusinessException("没有库存了,请联系客服人员!");
        }
        if (dbProductInfo.getPrice().compareTo(BigDecimal.ZERO)<=0){
   
            throw new BusinessException("商品价格出问题了,请联系客服人员!");
        }
        int num = dbProductInfo.getTotal() == null ? 0 : dbProductInfo.getTotal();
        num = num + mxOrderVo.getQuantity();
        if (dbProductInfo.getLimitNum().compareTo(num) == -1) {
   
            throw new BusinessException("下单数量超过限购数量!");
        }
    }

    @Override
    public void notifyOrder(HttpServletRequest request, HttpServletResponse response) {
   
        Map<String, String> map = getWXCallbackParamStr(request, response);
        if(CollectionUtil.isEmpty(map)){
   
            wxPayService.respToWx(response, "参数出错");
            return;
        }
        if (WXPayConstants.FAIL.equals(map.get("return_code"))){
   
            log.error("微信支付回调结果失败! {}", JSONObject.toJSONString(map));
            return;
        }

        //处理支付结果
        if (WXPayConstants.SUCCESS.equals(map.get("result_code"))) {
   
            doNotifyPayedSuccess(response, map);
        } else {
   
            String wxRespStr = JSONObject.toJSONString(map);
            log.error("微信支付回调结果失败! {}", wxRespStr);
            //商户订单号
            String ordersSn = map.get("out_trade_no");
            //处理业务逻辑
            ProductOrder order = productOrderMapper.getOrderBySn(ordersSn);
            //微信端订单号
            String transactionId = map.get("transaction_id");
            //更新状态信息
            userDataService.updateUserOrderStatusByOrderId(order.getId(), OrderStatusEnum.UN_PAYMENT, WxPayStatusEnum.FAIL,
                    transactionId, wxRespStr);
            //通知微信服务器已经支付成功
            wxPayService.respToWx(response, null);
        }

    }

    private void doNotifyPayedSuccess(HttpServletResponse response, Map<String, String> map) {
   
        //验证签名是否正确
        boolean isOK = wxPayService.checkCallbackSign(response, map);
        if(!isOK){
   
            return;
        }
        //商户订单号
        String ordersSn = map.get("out_trade_no");
        //处理业务逻辑
        ProductOrder order = productOrderMapper.getOrderBySn(ordersSn);
        if (order == null) {
   
            log.error("微信支付回调失败!商品不存在, {}", JSONObject.toJSONString(map));
            wxPayService.respToWx(response, "商品不存在");
        }
        // 因为微信回调可能多次,所以当第一次回调成功了,那么我们就不再执行逻辑了
        if(!OrderStatusEnum.UN_PAYMENT.getStatus().equals(order.getStatus())){
   
            wxPayService.respToWx(response, null);
            return;
        }

        //实际支付的订单金额:单位 分
        String totalFee = map.get("total_fee");
        //将分转换成元-实际支付金额:元
        BigDecimal amountPay = (new BigDecimal(totalFee).divide(new BigDecimal("100"))).setScale(2);
        //根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等
        if (amountPay.compareTo(order.getTotalScore()) != 0) {
   
            log.error("微信支付回调失败!金额不一致, {}", JSONObject.toJSONString(map));
            wxPayService.respToWx(response, "金额不一致");
            return;
        }

        //微信端订单号
        String transactionId = map.get("transaction_id");
        //更新状态信息
        userDataService.updateUserOrderStatusByOrderId(order.getId(), OrderStatusEnum.UN_DELIVER, WxPayStatusEnum.SUCCESS,
                transactionId, null);
        productOrderMapper.updateOrderStatusById(order.getId(), OrderStatusEnum.UN_DELIVER);
        //通知微信服务器已经支付成功
        wxPayService.respToWx(response, null);
    }

    private Map<String, String> getWXCallbackParamStr(HttpServletRequest request, HttpServletResponse response) {
   
        StringBuilder sb = new StringBuilder();
        try {
   
            BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
            String line = null;
            while ((line = br.readLine()) != null) {
   
                sb.append(line);
            }
            IoUtil.close(br);
        } catch (IOException e) {
   
            log.error("解析微信回调入参出错", e);
        }
        String notifyXml = sb.toString();
        log.info("接收到的报文:{}", notifyXml);
        if(StringUtils.isEmpty(notifyXml)){
   
            wxPayService.respToWx(response, "参数出错");
            return null;
        }
        //sb为微信返回的xml
        Map<String, String> map = null;
        try {
   
            map = WXPayUtil.xmlToMap(notifyXml);
        } catch (Exception e) {
   
            log.error("解析微信回调入参出错", e);
        }
        return map;
    }

    @SneakyThrows
    @Override
    public Boolean query(String orderSn) {
   
        ProductOrder order = productOrderMapper.getOrderBySn(orderSn);
        if (ObjectUtils.isEmpty(order)){
   
            throw new BusinessException("订单不存在,订单编号"+orderSn);
        }
        //异步回调结束不需要再去查询微信订单
        if (!order.getStatus().equals(OrderStatusEnum.UN_PAYMENT.getStatus())){
   
            return  true;
        }

        Map<String, String> reqData = new HashMap<>();
        reqData.put("out_trade_no", order.getSn());
        Map<String, String> wxResult = wxPayService.orderQuery(reqData);
        String trade_state = wxResult.get("trade_state");
        if(TradeStateEnum.FAIL.getStatus().equals(trade_state)
            || TradeStateEnum.USERPAYING.getStatus().equals(trade_state)){
   
            return false;
        } else if(TradeStateEnum.SUCCESS.getStatus().equals(trade_state)){
   
            //更新状态信息
            userDataService.updateUserOrderStatusByOrderId(order.getId(), OrderStatusEnum.UN_DELIVER,
                    WxPayStatusEnum.SUCCESS, wxResult.get("transaction_id"), null);
            productOrderMapper.updateOrderStatusById(order.getId(), OrderStatusEnum.UN_DELIVER);
            return true;
        }
        //TODO 其他状态
        return  true;

    }

    @Override
    public Map<String, Object> reSubmitOrder(Long id) {
   
        Long userId = JwtUtil.getUserId();
        String wxTempData = userDataService.getWxTempData(id, userId);
        if(StringUtils.isEmpty(wxTempData)){
   
            return Collections.emptyMap();
        }
        return JSONObject.parseObject(wxTempData);
    }

}

微信调用逻辑: --支付 回调 退款 查询

package cn.nhdc.cloud.modules.pay.service.Impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.nhdc.cloud.config.WXPayConfig;
import cn.nhdc.cloud.modules.mx.model.entity.MxUserData;
import cn.nhdc.cloud.modules.order.model.entity.ProductOrder;
import cn.nhdc.cloud.modules.pay.service.IWxPayService;
import cn.nhdc.cloud.modules.pay.wxsdk.MyWxPayRequest;
import cn.nhdc.cloud.modules.pay.wxsdk.WXPayConstants;
import cn.nhdc.cloud.modules.pay.wxsdk.WXPayUtil;
import cn.nhdc.common.exception.BusinessException;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author Asa
 * @since 2020-11-28 16:33:08
 */
@Slf4j
@Service("wxPayService")
public class WxPayServiceImpl implements IWxPayService {
   
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值