流程概要

1、小程序传递订单参数调用后端的支付订单接口
2、后端接口调用微信支付系统后生成6个必要参数返回给小程序
3、小程序调用wx.requestPayment拉起微信支付
4、用户支付后,微信支付系统调用后端回调接口
5、后端回调接口对具体的业务逻辑进行处理

官方接口文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml

添加Maven依赖

<dependency>
     <groupId>com.github.javen205</groupId>
     <artifactId>IJPay-All</artifactId>
     <version>2.7.4</version>
</dependency>

平台证书的生成

平台证书是必须的,生成方法:
参考整理的另一篇笔记:https://blog.csdn.net/Yisermorn/article/details/127304729?spm=1001.2014.3001.5502

创建通用参数文件 weChatPay.properties

文件存放路径:src\main\resources\

#秘钥
v3.keyPath=opt/.../apiclient_key.pem
#CA证书 格式.pem
v3.certPath=opt/.../apiclient_cert.pem
#CA证书 格式.p12
v3.certP12Path=opt/.../apiclient_cert.p12
#平台证书路径
v3.platformCertPath=opy/.../wx_cert.pem

#服务商id/商户id
v3.mchId=
#自定义 api 秘钥
v3.apiKey=
#自定义 apiv3 秘钥
v3.apiKey3=
#项目域名
v3.domain=

#服务商/直连商户平台 关联的 公众号appid
v3.AppId=
#服务商/直连商户平台 关联的 公众号secret
v3.AppSecret=

创建通用参数类 WxPay

将properties文件中属性的值赋值到WxPay类中

@Data
@Component
@PropertySource("classpath:weChatPay.properties")
@ConfigurationProperties(prefix = "v3")
public class WxPay {

    private String keyPath;
    private String certPath;
    private String certP12Path;
    private String platformCertPath;
    private String mchId;
    private String apiKey;
    private String apiKey3;
    private String domain;
    private String AppId;
    private String AppSecret;


}

编写控制层代码

创建Controller类编写 获取微信支付唤起参数 接口


	/**
     * 获取微信支付唤起参数
     * orderNumber: 订单号码
     */
	@GetMapping(value = "/jsapiPay")
	public Object jsapiPay(String orderNumber){
        try {
            return wxPayService.jsapiPay(orderNumber);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



	/**
     * 微信支付的回调接收接口
     */
    @RequestMapping("/payNotify")
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        wxPayService.payNotify(request,response);
    }

编写服务层代码

略过service层方法接口,展示的是方法的实现


	@Override
    public Map<String, String> jsapiPay(String orderNumber) throws Exception{

        // 根据订单编号查询到订单记录,用户获取订单所需的金额之类的参数
        Order order = orderMapper.selectOneByOrderNumber(orderNumber);
        // OpenId
        String openId = ;
        // 设置订单金额 单位为分且最小为1
        Integer price = 1;
        //price = Integer.parseInt( order.getAmountMoney().multiply(new BigDecimal("100")).stripTrailingZeros().toString() );
        
        // 设置支付超时时间  时间格式:yyyy-MM-dd'T'HH:mm:ssXXX
        String timeExpire = Utils.rfc3339.format( new Date(System.currentTimeMillis()+1000*60*5) );
        


        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                         .setAppid( wxPay.getAppId() )
                         .setMchid( wxPay.getMchId() )
                         .setDescription( "描述" )
                         .setOut_trade_no( orderNumber )
                         .setTime_expire(timeExpire)
                         .setAttach( "附加数据" )
                         .setNotify_url(wxPay.getDomain().concat("/weChatPay/payNotify")) //回调地址
                         .setAmount(new Amount().setTotal(price))
                         .setPayer(new Payer().setOpenid(openId));
        logger.info("订单创建参数:"+ JSONUtil.toJsonStr(unifiedOrderModel));

        IJPayHttpResponse response = WxPayApi.v3(
                RequestMethod.POST,
                WxDomain.CHINA.toString(),
                WxApiType.JS_API_PAY.toString(),
                wxPay.getMchId(),
                getSerialNumber(),
                null,
                wxPay.getKeyPath(),
                JSONUtil.toJsonStr(unifiedOrderModel)
        );
        logger.info("统一下单响应:{}", response.toString());

        if ( response.getStatus()==200 ) {
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPay.getPlatformCertPath());
            logger.info("verifySignature: {}", verifySignature);
            if (verifySignature) {
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = WxPayKit.jsApiCreateSign(wxPay.getAppId(), prepayId, wxPay.getKeyPath());
                logger.info("唤起支付参数:{}", map);
                return map;
            }
        }
        return null;
    }



	@Override
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        logger.info("收到微信支付回调");
        Map<String, String> map = new HashMap<>(12);

        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            logger.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            logger.info("支付通知密文: {}", result);

            // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                                   wxPay.getApiKey3(), wxPay.getPlatformCertPath());
            logger.info("支付通知明文 {}", plainText);

            //具体的业务情况
            savePayPlainText(plainText);

            //回复微信
            if (StrUtil.isNotEmpty(plainText)) {
                response.setStatus(200);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }




	/**
     * 获取证书序列号
     */
    private String getSerialNumber() throws IOException {
        //这个是证书文件,后续要调整成读取证书文件的服务器存放地址
        String certPath = wxPay.getCertPath();
        logger.info("path:{}", certPath);
        // 获取证书序列号
        X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(certPath));
        String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
        logger.info("获取证书序列号:{},", serialNo);
        return serialNo;
    }




	/**
     * 保存订单的支付通知明文
     */
    private void savePayPlainText(String plainText) {
        JSONObject jsonObject = JSONUtil.parseObj(plainText);
        //这个就是发起订单时的那个订单号
        String outTradeNo = jsonObject.getStr("out_trade_no");
        //todo 把微信支付回调的明文消息存进数据库,方便后续校验查看
        
        //todo 把微信支付后需要处理的具体业务处理了
        
    }



退款接口的控制层代码

	/**
     * 微信退款
     * 2个参数只传1个即可
     * transactionId: 原支付交易对应的微信订单号
     * outTradeNo: 原支付交易对应的商户订单号
     */
    @GetMapping(value = "/payRefund")
    public String payRefund(String transactionId, String outTradeNo) {
        return wxPayService.payRefund(transactionId, outTradeNo);
    }

退款接口的服务层代码

	@Override
    public String payRefund(String transactionId, String outTradeNo) {
        logger.info("进入退款接口------>" );
        logger.info("执行操作的 原支付交易对应的微信订单号:{}", transactionId);
        logger.info("执行操作的 原支付交易对应的商户订单号:{}", outTradeNo );

        try {
            String outRefundNo = PayKit.generateStr();
            logger.info("商户退款单号: {}", outRefundNo);
            List<RefundGoodsDetail> list = new ArrayList<>();
            RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail()
                    .setMerchant_goods_id( outRefundNo )
                    .setGoods_name( "取消订单" )
                    .setUnit_price( 1 )     //金额,单位为分
                    .setRefund_amount( 1 )
                    .setRefund_quantity(1);
            list.add(refundGoodsDetail);

            RefundModel refundModel = new RefundModel()
                    .setOut_refund_no(outRefundNo)
                    .setReason("取消订单")
                    //.setNotify_url(wechatPayV3Bean.getDomain().concat("/wechat/operation/pay/refundNotify")) //退款异步回调通知
                    .setAmount(new RefundAmount().setRefund(1).setTotal(1).setCurrency("CNY"))
                    .setGoods_detail(list);

            if (transactionId!=null && !transactionId.equals("")) {
                refundModel.setTransaction_id(transactionId);
            }
            if (outTradeNo!=null && !outTradeNo.equals("")) {
                refundModel.setOut_trade_no( outTradeNo );
            }
            logger.info("退款参数: {}", JSONUtil.toJsonStr(refundModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.DOMESTIC_REFUNDS.toString(),
                    wxPay.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPay.getKeyPath(),
                    JSONUtil.toJsonStr(refundModel)
            );
            logger.info("退款响应 {}", response);

            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPay.getPlatformCertPath());
            logger.info("verifySignature: {}", verifySignature);

            String body = response.getBody();
            JSONObject jsonObject = JSONUtil.parseObj(body); //转换为JSON

            if(response.getStatus()==200){ //退款成功,处理业务逻辑
                
            }
            if (verifySignature) {
                return response.getBody();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return null;
    }
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐