【微信小程序】实现支付

前言

都干到小程序了,那支付也避开不了,直接上号!

准备

首先必须接入微信支付,成为商家号,步骤可参考其他教程微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式

流程

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3

网上方法千千万,就看能否找到属于你的另一半

以下是我的另一半,直接上代码

后端代码:

1、下单:

    @PostMapping("/jsApiOrder")
    @ApiOperation("JSAPI下单")
    @Functional(visible = Visible.SIGNUP)
    public ResultInfo<SortedMap<String, String>> jsApiOrder(@Validated @RequestBody OrderDto orderDto, HttpServletRequest request) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        AuthParam authParam = this.getLoginUser(request);
        return new ResultInfo<>(wxPayService.jsApiOrder(orderDto, authParam));
    }
   //以下参数获取方法看准备步骤
    @Value("${wx.pay.appId}")
    private String appId;
    @Value("${wx.pay.merchantId}")
    private String merchantId;
    //商户证书序列号
    @Value("${wx.pay.merchantSerialNumber}")
    private String merchantSerialNumber;
    //商户APIv3密钥
    @Value("${wx.pay.apiV3Key}")
    private String apiV3Key;
    //支付通知地址
    @Value("${wx.pay.payNotifyUrl}")
    private String payNotifyUrl;
    //wx private key
    private String WX_PRIVATE_KEY = "";
    public static String privateKeyPath = "apiclient_key.pem";

    //读取微信支付的私钥,我直接复制出来放在resources下
    @PostConstruct
    private void init() {
        ClassPathResource classPathResource = new ClassPathResource(privateKeyPath);
        try (InputStream inputStream = classPathResource.getInputStream()) {
            StringBuilder privateKeyContent = new StringBuilder();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = inputStream.read(buffer)) != -1) {
                privateKeyContent.append(new String(buffer, 0, len));
            }
            WX_PRIVATE_KEY = privateKeyContent.toString().replaceAll("\\n", "").trim();
        } catch (IOException e) {
            throw new IllegalArgumentException("WeiXin Private Key Read Err:" + e.getMessage());
        }
        log.info("WeiXin Private Key Read Success !!!");
    }
    
    /**
     * 微信支付
     */
    @Override
    @Transactional
    public SortedMap<String, String> jsApiOrder(OrderDto orderDto, AuthParam authParam) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //判断是否有待支付订单
        if(authParam != null){
            String memberId = authParam.getUserId();
            synchronized (memberId.intern()) {
                Integer count = this.ordersMapper.selectByNotOrder1(memberId);
                if(count > 0){
                    throw ErrCodeMsgEnum.SERVER_UPDATE_ERROR.err("有订单未支付,请先支付再继续下单");
                }
            }
        }
        //创建 PrepayRequest 对象并设置金额和付款人信息
        PrepayRequest request = new PrepayRequest();
        SortedMap<String, String> params = new TreeMap<>();
        Amount amount = new Amount();

        // 获取下单金额---需要去获取你当前下单的商品价格
        String creationId = orderDto.getCreationId();
        Creation creation = this.creationMapper.selectByCreationId(creationId);
        Double creationPrice = creation.getCreationPrice();
        // 将金额从元转换为分
        int totalFeeInFen = (int) (creationPrice * 100);
        amount.setTotal(totalFeeInFen);
        amount.setCurrency("CNY");
        Payer payer = new Payer();
        //获取openId
        String memberId = authParam.getUserId();
        MemberInfo memberInfo = this.memberInfoMapper.selectInfoMemberId(memberId);
        if(memberInfo != null){
            payer.setOpenid(memberInfo.getOpenPid());
        }

        request.setAmount(amount);
        request.setPayer(payer);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription(orderDto.getDescription());
        //设置支付结果通知URL 和订单号
        request.setNotifyUrl(payNotifyUrl);
        request.setOutTradeNo(generateOrderNumber("这个地方就是你订单编号的前缀,看怎么起"));
        //调用微信支付系统 prepay 方法生成预支付订单
        PrepayResponse response = getJsapiService().prepay(request);

        //生成支付签名
        WechatPaySign sign = sign(response.getPrepayId());

        //构建返回参数
        params.put("trans_no", generateOrderNumber("C2M"));
        params.put("appId", appId);
        params.put("nonceStr", sign.getNonceStr());
        params.put("package", "prepay_id=" + sign.getPrepayId());
        params.put("signType", "RSA");
        params.put("timeStamp", sign.getTimeStamp());
        params.put("paySign", sign.getSign());


        //存储订单信息到数据库
        Order order = new Order();
        //这里就根据自己的表结构来了哈,还是轻松的
    
        return params;
    }

     //获取支付签名
    private WechatPaySign sign(String prepayId) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        //生成三十位随机数
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 30; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        String nonceStr = sb.toString();//随机字符串

        String packageStr = "prepay_id=" + prepayId;

        // 不能去除'.append("\n")',否则失败
        String signStr = appId + "\n" +
                timeStamp + "\n" +
                nonceStr + "\n" +
                packageStr + "\n";

        byte[] message = signStr.getBytes(StandardCharsets.UTF_8);

        Signature sign = Signature.getInstance("SHA256withRSA");

        sign.initSign(PemUtil.loadPrivateKeyFromString(WX_PRIVATE_KEY));

        sign.update(message);
        String signStrBase64 = Base64.getEncoder().encodeToString(sign.sign());
        WechatPaySign wechatPaySign = new WechatPaySign();
        wechatPaySign.setPrepayId(prepayId);
        wechatPaySign.setTimeStamp(timeStamp);
        wechatPaySign.setNonceStr(nonceStr);
        wechatPaySign.setPackageStr(packageStr);
        wechatPaySign.setSign(signStrBase64);
        return wechatPaySign;
    }

    /**
     * 创建小程序支付服务
     *
     * @return
     */
    private JsapiService getJsapiService() {
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(merchantId)
                        .privateKey(WX_PRIVATE_KEY)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();
        config.createSigner().getAlgorithm();
        return new JsapiService.Builder().config(config).build();
    }

    /**
     * 生成订单编号
     *
     * @param prefix 订单编号前缀
     * @return 生成的订单编号
     */
    private synchronized String generateOrderNumber(String prefix) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String timestamp = dateFormat.format(new Date());

        String key = String.format(Const.CacheKey.ORDER_NO, timestamp.substring(0, 8));
        long number = this.redisCache.incr(key, 1);
        this.redisCache.expire(key, DateUtils.getTomorrowSeconds());
        String randomNum = String.format("%06d", number);
        return prefix + timestamp + randomNum;
    }

2、重新支付

这种情况就是用户下单后没有支付,那就需要重新拉取支付了

@PostMapping("/order/pay")
    @ApiOperation("微信支付")
    @Functional(visible = Visible.SIGNUP)
    public ResultInfo<Map<String,String>> orderPay(@RequestParam("orderNumber") String orderNumber) {
        return new ResultInfo<>(wxPayService.wxPay(orderNumber));
    }
@Override
    public Map<String, String> wxPay(String orderNumber) {
        Order orderInfo = ordersMapper.selectByOrderNo(orderNumber);
        Assert.notNull(orderInfo, "订单信息不存在");
        Assert.isTrue(orderInfo.getOrderStatus().equals(OrderStatusEnum.WAIT_PAY.getCode()),
                "订单状态异常,刷新后再试");
        try {
            //生成微信支付签名信息
            WechatPaySign  wxPaySign = sign(orderInfo.getPrepayId());
            //返回结果
            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("appId", appId);
            resultMap.put("nonceStr", wxPaySign.getNonceStr());
            resultMap.put("package", "prepay_id=" + wxPaySign.getPrepayId());
            resultMap.put("signType", "RSA");
            resultMap.put("timeStamp", wxPaySign.getTimeStamp());
            resultMap.put("paySign", wxPaySign.getSign());
            return resultMap;
        } catch (NoSuchAlgorithmException|InvalidKeyException|SignatureException e) {
            log.error("", e);
            throw ErrCodeMsgEnum.WECHAT_PAY_ERROR.err();
        }
    }

3、回调

@PostMapping("/callback")
    @ApiOperation("微信回调")
    @Functional(visible = Visible.FREEDOM)
    public ResultInfo<ResponseEntity> callback(HttpServletRequest request){
        return new ResultInfo<>(wxPayService.callback(request));
    }
@Transactional
    public ResponseEntity callback(HttpServletRequest request) {
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                .nonce(request.getHeader("Wechatpay-Nonce"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .signature(request.getHeader("Wechatpay-Signature"))
                .body(getBodyUTF8(request))
                .build();
        log.debug("wxPay---------------requestParam:"+requestParam.toString());

        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(WX_PRIVATE_KEY)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();

        // 创建 NotificationParser 并解析请求参数
        NotificationParser parser = new NotificationParser(config);
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        String outTradeNo = transaction.getOutTradeNo();
        log.debug("--------------订单号:"+outTradeNo);

        Order orderInfo = ordersMapper.selectByOrderNo(outTradeNo);
        Assert.notNull(orderInfo, "订单信息不存在");
        //log.debug("--------------orderInfo:"+orderInfo);
        //修改状态这里


        return ResponseEntity.status(HttpStatus.OK).build();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值