首页 > 其他分享 >springboot 使用微信支付

springboot 使用微信支付

时间:2023-02-16 13:58:01浏览次数:56  
标签:return String 微信 支付 new 退款 springboot

参考:Java实现微信支付_被编程征服的秃发女子的博客-CSDN博客

一.微信支付流程

支付流程:

用户点击支付按钮调用接口 [/deposit] =>

返回给小程序payInfo和订单编号orderNum =>

小程序端拿着payInfo和orderNum弹出支付 =>

等待用户支付完成调用支付完成接口 [/deposit/success],支付完成接口中验证是否存在订单编号,存在则订单充值成功,插入流水和增加余额。

提现流程:

用户点击提现调用接口 [/withdraw] 等待后台管理审核 =>

后台管理调用接口 [/agree] 同意提现、 [/reject] 拒绝提现,同时操作流水。

需要注意的是,在这个项目中,用户提现使用的是微信的 转账到账户 ,而不是网上大部分教程中的 企业付款到个人。

参考:Java整合微信商家转账到个人&开通流程 -CSDN博客_微信商户转账到个人

二.项目导入微信支付

1.引入依赖 并 更新 Maven

		<!-- 微信支付 -->
		<dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>wx-java-pay-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-miniapp</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2.Yaml中添加以下

project:
  weixin:
    #appid
    app-id: 'xxxx'
    #密钥
    app-secret: 'xxxx'
    # 商户号
    mch-id: 111111
    # 商户密钥
    mch-key: xxxxx
    # 用户付款回调地址 保证外网能访问
    pay-notify-url: https://test.cn/recycle/wx/pay/notify
    # 用户退款回调地址
    refund-notify-url: https://test.cn/recycle/wx/pay/refund/notify
    # p12证书的位置,可以绝对路径,可以指定类路径 以classpath:开头(classpath为项目resource文件夹)
    cert-path: classpath:apiclient_cert.pem
    key-path: classpath:apiclient_key.pem

3. 新建一个属性类 WeChatProperties

@Configuration
@ConfigurationProperties(prefix = "project.weixin")
@Data
public class WeChatProperties {
    private String appId;
    private String appSecret;
    private String mchId;
    private String mchKey;
    private String payNotifyUrl;
    private String refundNotifyUrl;
    private String keyPath;
    private String certPath;
}

4.新建配置类 WeChatConfig

@Configuration
public class WeChatConfig {

    @Autowired
    private WeChatProperties properties;

    @Bean
    public WxPayConfig wxPayConfig() {
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(properties.getAppId());
        payConfig.setMchId(properties.getMchId());
        payConfig.setMchKey(properties.getMchKey());
        payConfig.setNotifyUrl(properties.getPayNotifyUrl());
        payConfig.setApiV3Key(properties.getMchKey());
        payConfig.setPrivateKeyPath(properties.getKeyPath());
        payConfig.setPrivateCertPath(properties.getCertPath());
        payConfig.setTradeType("JSAPI");
        payConfig.setSignType("MD5");
        return payConfig;
    }
    @Bean
    public WxPayService wxPayService(WxPayConfig payConfig) {
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

    @Bean
    public WxMaService wxMaService(){
        WxMaServiceImpl wxMaService = new WxMaServiceImpl();
        wxMaService.setWxMaConfig(wxMaConfigMemory());
        return wxMaService;
    }

    @Bean
    public WxMaConfig wxMaConfigMemory(){
        WxMaDefaultConfigImpl wxMaConfig = new WxMaDefaultConfigImpl();
        wxMaConfig.setAppid(properties.getAppId());
        wxMaConfig.setSecret(properties.getAppSecret());
        return wxMaConfig;
    }
}

至此,微信支付相关的配置完了。

5.添加 WeChatPayService和实现类

service:

public interface WeChatPayService {
    //用户支付
    WxPayReturnInfoVO weChatPay(String openId, String orderNum, Integer totalFee, String desc);
    //用户退款(退款支付的订单)
    Boolean refund(String orderNum, String refundNum, Integer totalFee, String desc);
	//转账到用户
    void entPay(String openId, String tradeNo, Integer amount, String ip);
}

serviceImpl:

@Slf4j
@Service
public class WeChatPayServiceImpl implements WeChatPayService {

    @Autowired
    WxPayService wxPayService;

    @Autowired
    WeChatProperties weChatProperties;

    private static final String REFUND_SUCCESS = "SUCCESS";
    @Override
    public WxPayReturnInfoVO weChatPay(String openId, String orderNum, Integer totalFee, String desc) {
        /**
         * 系统内部业务逻辑
         */
        // 构建支付参数
        final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                //调起支付的人的 openId
                .openid(openId)
                //用户生成的唯一订单编号
                .outTradeNo(orderNum)
                //订单金额 单位分
                .totalFee(totalFee)
                //商品描述
                .body(desc)
                //获取本地IP
                .spbillCreateIp(InetAddress.getLoopbackAddress().getHostAddress())
                //回调的 URL 地址
                .notifyUrl(weChatProperties.getPayNotifyUrl())
                .build();
        WxPayUnifiedOrderResult wxPayUnifiedOrderResult = null;
        try {
            wxPayUnifiedOrderResult = wxPayService.unifiedOrder(wxPayUnifiedOrderRequest);
        } catch (WxPayException e) {
            e.printStackTrace();
            log.error("微信支付调起失败!");
        }
        //组合参数构建支付
        Map<String, String> paySignInfo = new HashMap<>(5);
        String timeStamp = createTimestamp();
        String nonceStr = String.valueOf(System.currentTimeMillis());
        paySignInfo.put("appId", weChatProperties.getAppId());
        paySignInfo.put("nonceStr", nonceStr);
        paySignInfo.put("timeStamp", timeStamp);
        paySignInfo.put("signType", "MD5");
        paySignInfo.put("package", "prepay_id=" + wxPayUnifiedOrderResult.getPrepayId());
        String[] signInfo = new String[0];
        String paySign = SignUtils.createSign(paySignInfo, "MD5", weChatProperties.getMchKey(), signInfo);

        //组合支付参数
        WxPayReturnInfoVO returnPayInfoVO = new WxPayReturnInfoVO();
        returnPayInfoVO.setAppId(weChatProperties.getAppId());
        returnPayInfoVO.setNonceStr(nonceStr);
        returnPayInfoVO.setPaySign(paySign);
        returnPayInfoVO.setSignType("MD5");
        returnPayInfoVO.setPrepayId("prepay_id=" + wxPayUnifiedOrderResult.getPrepayId());
        returnPayInfoVO.setTimeStamp(timeStamp);
        return returnPayInfoVO;
    }

    /**
     * 商城订单退款
     * @param orderNum
     * @param refundNum
     * @param totalFee
     * @param desc
     * @return
     */
    @Override
    public Boolean refund(String orderNum,String refundNum,Integer totalFee,String desc) {
        //申请退款
        WxPayRefundRequest refundInfo = WxPayRefundRequest.newBuilder()

                // 支付订单号
                .outTradeNo(orderNum)
                // 退款订单号
                .outRefundNo(refundNum)
                // 支付金额 单位分
                .totalFee(totalFee)
                // 退款金额 单位分
                .refundFee(totalFee)
                // 微信退款回调地址
                .notifyUrl(weChatProperties.getRefundNotifyUrl())
                .refundAccount("REFUND_SOURCE_RECHARGE_FUNDS")
                .build();
        WxPayRefundResult wxPayRefundResult;
        try {
            wxPayRefundResult = wxPayService.refund(refundInfo);
            //判断退款信息是否正确
            if (REFUND_SUCCESS.equals(wxPayRefundResult.getReturnCode())
                    && REFUND_SUCCESS.equals(wxPayRefundResult.getResultCode())) {
                /**
                 * 系统内部业务逻辑
                 */
                return true;
            }
        } catch (WxPayException e) {
            log.error("微信退款接口错误信息= {}", e);
            System.err.println(e.getMessage());
            return false;
        }
        return false;
    }

    /**
     * 转账到个人(提现用)
     * @param openId
     * @param tradeNo
     * @param amount
     * @param ip
     */
    @Override
    public void entPay(String openId, String tradeNo, Integer amount, String ip) {
        TransferService transferService = wxPayService.getTransferService();
        //获取appId
        String appId = wxPayService.getConfig().getAppId();
        //创建批次对象
        TransferBatchesRequest transferBatchesRequest=new TransferBatchesRequest();
        transferBatchesRequest.setAppid(appId);
        //设置批次名称 可不写
        transferBatchesRequest.setBatchName("测试批次");
        //设置批次备注 可不写
        transferBatchesRequest.setBatchRemark("测试");
        //设置该批次编号
        transferBatchesRequest.setOutBatchNo(DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomNumbers(3));
        //设置该批次总个数
        transferBatchesRequest.setTotalNum(1);
        //设置该批次总金额
        transferBatchesRequest.setTotalAmount(amount);
        //创建收款人请求对象
        ArrayList<TransferBatchesRequest.TransferDetail> transferDetails = new ArrayList<>();
        TransferBatchesRequest.TransferDetail transferDetail=new TransferBatchesRequest.TransferDetail();
        //转账的编号
        transferDetail.setOutDetailNo(tradeNo);
        //转账的金额
        transferDetail.setTransferAmount(amount);
        //转账的注释
        transferDetail.setTransferRemark("用户提现");
        //以实际微信公众号那边的openid为准
        transferDetail.setOpenid(openId);
        //把收款人对象放到批次里面
        transferDetails.add(transferDetail);
        transferBatchesRequest.setTransferDetailList(transferDetails);
        TransferBatchesResult transferBatchesResult=null;
        try {
            transferBatchesResult = transferService.transferBatches(transferBatchesRequest);
            log.info("企业支付完成:[msg:{}]",transferBatchesResult);
        } catch (WxPayException e) {
            e.printStackTrace();
            throw new CaptchaException("企业支付失败:"+e.getMessage());
        }
    }
    
    /**
     * 时间
     * @return 时间戳
     */
    private String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}

6.回调接口(第二点yaml文件要配置的那两个接口)

在这个项目里实际上只用到了支付回调接口,用于判断用户是否支付成功

	@ApiOperation(value = "微信的付款回调:前端不需要使用", hidden = true)
    @PostMapping("/pay/notify")
    public String parseOrderNotifyResult(@RequestBody String xmlData) {
        try {
            final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
            if (SUCCESS.equals(notifyResult.getResultCode())) {

                PayCallback payCallback = new PayCallback();
                payCallback.setTotalFee(notifyResult.getTotalFee());
                payCallback.setTradeNo(notifyResult.getOutTradeNo());
                payCallback.setOpenId(notifyResult.getOpenid());
                payCallback.setTime(LocalDateTime.now());
                payCallback.setStatus(0);
                payCallbackService.save(payCallback);
                log.error("订单:" + notifyResult.getOutTradeNo() + "回调成功");
            } else {
                log.error("订单:" + notifyResult.getOutTradeNo() + "回调成功但支付失败");
            }
            return WxPayNotifyResponse.success("回调成功!");
        } catch (WxPayException e) {
            e.printStackTrace();
            return WxPayNotifyResponse.fail("回调有误!");
        }
    }


    /**
     * 仅支持一次性退款,多次退款需要修改逻辑
     */
    @ApiOperation(value = "微信的退款回调:前端不需要使用", hidden = true)
    @PostMapping("/refund/notify")
    public String refundNotify(@RequestBody String xmlData) {
        WxPayRefundNotifyResult wxPayRefundNotifyResult;
        try {
            wxPayRefundNotifyResult = wxPayService.parseRefundNotifyResult(xmlData);
        } catch (WxPayException e) {
            log.error("退款失败,失败信息:{}", e);
            return WxPayNotifyResponse.fail("退款失败");
        }
        //判断你返回状态信息是否正确
        if (SUCCESS.equals(wxPayRefundNotifyResult.getReturnCode())) {
            WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();
            //判断退款状态
            if (SUCCESS.equals(reqInfo.getRefundStatus())) {
                try {
                    /**
                     * 一、可能会重复回调,需要做防重判断
                     * 二、处理我们系统内部业务,做修改订单状态,释放资源等!
                     */
                    reqInfo.getOutTradeNo();
                    return WxPayNotifyResponse.success("退款成功!");
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println(e.getMessage());
                }
            }
        }
        return WxPayNotifyResponse.fail("退款失败!");
    }

标签:return,String,微信,支付,new,退款,springboot
From: https://www.cnblogs.com/j0kerlu/p/17126389.html

相关文章

  • 微信小程序
     短信打开小程序开发者可通过以下3种方式实现短信打开小程序:通过URLScheme实现通过服务端接口或在小程序管理后台生成URLScheme后,自行开发中转H5页面。将带有中转H......
  • 一封传话一行代码实现微信消息推送
    一行代码实现微信消息推送介绍通过调用一个简单的接口,将要推送的消息提交后,您将在微信收到推送的消息,简单快捷可以应用的场景设备上下线,量化交易,网站公告,服务器异常信息......
  • 微信小程序通过npm使用 Vant Weapp
    微信小程序通过npm使用VantWeapp安装步骤一通过npm安装使用npm构建前,请先阅读微信官方的npm支持#首先进入小程序的目录#初始化生成package.jsonnpminit......
  • 前后端分离项目(vue+springboot)集成pageoffice实现在线编辑office文件
    前后端分离项目下使用PageOffice原理图集成步骤前端vue项目在您Vue项目的根目录下index.html中引用后端项目根目录下pageoffice.js文件。例如:<scripttype="text/......
  • SpringBoot集成MybatisPlus
    SpringBoot集成MybatisPlus一、依赖<properties><mybatis.plus.version>3.4.0</mybatis.plus.version></properties> <dependencies> ......
  • 火爆全网的ChatGPT智能AI机器人微信小程序源码 (附带部署教程)
    最近ChatGPT智能AI聊天突然爆火了ChatGPT是OpenAI开发的一款专门从事对话的人工智能聊天机器人原型。聊天机器人是一种大型语言模型,采用监督学习和强化学习技术。ChatGP......
  • 简单-SpringBoot整合RabbitMQ
    目录1.windows下安装erlang环境和rabbitMq服务1.1客户端页面2.准备工作2.1pom依赖2.1启动类注解开启:@EnableRabbit2.2application配置文件3.队列的简单使用3.1配置交换器......
  • SpringBoot容器化的多环境配置
    SpringBoot容器化的多环境配置部署通常会有多个环境,如"dev"/"test"/"prod"等环境容器化部署通常使用环境变量,而非手动修改配置例子来自《SpringBoot实战派》中,并进行......
  • springboot--多环境启动
    法一:   法二:       ......
  • springboot starter 原理解析及实践
    什么是springbootstarterstarter是springBoot的一个重要部分。通过starter,我们能够快速的引入一个功能,而无需额外的配置。同时starter一般还会给我提供预留的自定配置选......