当前位置: 首页 > 工具软件 > pay-SDK > 使用案例 >

springboot微信sdk方式进行微信支付

穆正祥
2023-12-01

微信支付:
官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
第三方SDK:https://github.com/Pay-Group/best-pay-sdk

首先说明一下,微信支付功能接口权限只有微信服务号才拥有。

首先来看一下公众号支付的业务流程图。

支付开发中,一般在第10步异步通知成功了,就基本上可以百分百说明支付成功了,可以修改支付状态为已支付。

开发步骤:

1.pom.xml中添加Maven依赖

<!-- 微信公众号支付依赖 -->

<dependency>
    <groupId>cn.springboot</groupId>
    <artifactId>best-pay-sdk</artifactId>
    <version>1.1.0</version>

</dependency>

2.微信服务号信息配置

package com.wechat.order.config;
 
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
/*微信公众号账号设置*/
@Data//使用了lombok依赖,相当于在代码中getter和setter的作用
@Component
@ConfigurationProperties(prefix = "wechat")//账号信息写在了application.yml中。
public class WeChatAccountConfig {
 
    private String mpAppId;
 
    private String mpAppSecret;
 
    /**
     * 商户号
     */
    private String mchId;
 
    /**
     * 商户密钥
     */
    private String mchKey;
 
    /**
     * 商户证书路径
     */
    private String keyPath;
 
    /**
     * 异步通知路径
     */
    private String notifyUrl;
}
package com.wechat.order.config;
 
import com.lly835.bestpay.config.WxPayH5Config;
import com.lly835.bestpay.service.impl.BestPayServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
 
/*微信公众账号支付配置*/
@Component
public class WeChatPayConfig {
 
    @Autowired
    private WeChatAccountConfig weChatAccountConfig;
 
    //把你要实例化的对象转化成一个Bean,放在IoC容器中
    @Bean
    public BestPayServiceImpl bestPayService() {
 
        //支付类, 所有方法都在这个类里
        BestPayServiceImpl bestPayService = new BestPayServiceImpl();
        bestPayService.setWxPayH5Config(wxPayH5Config());
        return bestPayService;
    }
 
    @Bean
    private WxPayH5Config wxPayH5Config() {
        WxPayH5Config wxPayH5Config = new WxPayH5Config();
        wxPayH5Config.setAppId(weChatAccountConfig.getMpAppId());//设置微信公众号的appid
        wxPayH5Config.setAppSecret(weChatAccountConfig.getMpAppSecret());// 设置微信公众号的app corpSecret
        wxPayH5Config.setMchId(weChatAccountConfig.getMchId());// 设置商户号
        wxPayH5Config.setMchKey(weChatAccountConfig.getMchKey());// 设置商户密钥
        wxPayH5Config.setKeyPath(weChatAccountConfig.getKeyPath());// 设置商户证书路径
        wxPayH5Config.setNotifyUrl(weChatAccountConfig.getNotifyUrl());// 设置支付后异步通知url
        return wxPayH5Config;
    }
}
3.调用方法发起支付

controller

    /**
     * 微信支付发起
     * @param orderId
     * @param returnUrl
     * @return
     */
    @RequestMapping("/create")
    public ModelAndView create(@RequestParam("orderId")String orderId,
                               @RequestParam("returnUrl")String returnUrl,
                               HashMap<String, Object> map){
 
        //根据订单id查询订单,数据库查询
        OrderDTO orderDTO = orderService.findOne(orderId);
        if(orderDTO == null){//订单不存在抛出错误
            throw new SellException(ResultEnum.ORDER_NOT_EXIST);
        }
 
        //微信支付
        PayResponse payResponse = wechatPayService.create(orderDTO);
 
        //携带参数传入网页发起支付的前端模板
        map.put("orderId", orderId);
        map.put("payResponse", payResponse);
 
        return new ModelAndView("pay/create",map);
    }
service:

    //微信支付发起
    public PayResponse create(OrderDTO orderDTO) {
        PayRequest payRequest = new PayRequest();
        payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);//支付方式,微信公众账号支付
        payRequest.setOrderId(orderDTO.getOrderId());//订单号.
        payRequest.setOrderName(payName);//订单名字.
        payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());//订单金额.
        payRequest.setOpenid(orderDTO.getBuyerOpenid());//微信openid, 仅微信支付时需要
        log.info("【微信支付】request={}", JsonUtil.toJson(payRequest));//将payRequest格式化一下,再显示在日志上,便于观看数据
        PayResponse payResponse = bestPayService.pay(payRequest);
        log.info("【微信支付】response={}", JsonUtil.toJson(payResponse));//将payResponse格式化一下,再显示在日志上,便于观看响应后返回的数据);
        return payResponse;
    }
JsonUtil格式转换工具类:

/**
 * 把对象转换成json格式,便于观看
 */
public class JsonUtil {
 
    public static String toJson(Object object) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();
        Gson gson = gsonBuilder.create();
        return gson.toJson(object);
    }
}
4.在static文件夹下编写微信内H5调起支付的前端代码pay.html(可以从微信支付官方文档中拷贝代码),检测是否可以支付, 此步骤可省略。
ps:为什么把支付的前端代码写在后台中?
如果放在前端的话,加入几个地方或几个模块都可以发起微信支付,每次发起都要调用这个前端代码,
这个前端代码就要写好几次。如果放在后端的话,只要给个请求,就可以直接调用这段代码。

5.对支付的前端代码动态注入参数以发起支付。
这里我们用到模板技术。

(1)在pom.xml中引入freemarker依赖

        <!-- freemarker模板技术 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

(2)把controller中发起支付的方法返回类型改为ModelAndView,return new ModelAndView("path",model);//path为模板存放的路径,不包括模板后缀名。这里model我们采用Map类型,把payResponse和returnUrl携带过去。
(3)在相应的地方,创建模板文件xx.ftl。如我上述步骤的path为pay/create,则我在resources/templates目录下创建文件夹pay,在pay目录下再创建create.ftl文件。
(4)修改pay.html成模板,参数动态注入。${xxxx}

(5)模板中,支付后进行跳转到returnUrl。

create.ftl模板:

<script>
    function onBridgeReady(){
        WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId":"${payResponse.appId}",     //公众号名称,由商户传入
                    "timeStamp":"${payResponse.timeStamp}",         //时间戳,自1970年以来的秒数
                    "nonceStr":"${payResponse.nonceStr}", //随机串
                    "package":"${payResponse.packAge}",
                    "signType":"MD5",         //微信签名方式:
                    "paySign":"${payResponse.paySign}" //微信签名
    },
        function(res){
            if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
        }
        local.href="${returnUrl}";
    );
    }
    if (typeof WeixinJSBridge == "undefined"){
        if( document.addEventListener ){
            document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
        }else if (document.attachEvent){
            document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
            document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
        }
    }else{
        onBridgeReady();
    }
</script>
6.填写微信支付的支付授权目录。
比如我的前端支付代码访问路径是http://xxxxx(网站根路径)/sell/pay.html。
支付授权目录就写http://xxxxx(网站根路径)/sell/

7.微信异步通知,并在相应的controller中进行支付状态修改。
依靠前端返回的支付成功来判断成功并不安全,容易被篡改。必须依靠后端异步通知来判断是否已支付成功
(1)验证签名,支付状态
(2)校验支付金额与订单金额是否一致,2个金额相减小于0.01即认为相等。
(3)修改订单支付状态

service:

   /*微信支付异步通知*/
    @Override
    public PayResponse notify(String notifyData) {
        //1. 验证签名
        //2. 支付的状态
        //3. 支付金额
        //4. 支付人(下单人 == 支付人)
 
        PayResponse payResponse = bestPayService.asyncNotify(notifyData);
        log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));
 
        //查询订单
        OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());
 
        //判断订单是否存在
        if (orderDTO == null) {
            log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
            throw new SellException(ResultEnum.ORDER_NOT_EXIST);
        }
 
        //判断金额是否一致(0.10   0.1)
        if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
            log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
                    payResponse.getOrderId(),
                    payResponse.getOrderAmount(),
                    orderDTO.getOrderAmount());
            throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
        }
 
        //修改订单的支付状态
        orderService.paid(orderDTO);
 
        return payResponse;
    }
controller:

    /**
     * 微信异步通知
     * @param notifyData
     */
    @PostMapping("/notify")
    public ModelAndView notify(@RequestBody String notifyData) {
        wechatPayService.notify(notifyData);
 
        //返回给微信处理结果
        return new ModelAndView("pay/success");
    }
}
success.ftl异步通知成功模板:

<xml>
    <return_code><![CDATA[SUCCESS]]></return_code>
    <return_msg><![CDATA[OK]]></return_msg>
</xml>

8.微信退款
(1)设置退款人,退款金额,退款方式
(2)退款

service:

   /**
     * 退款
     * @param orderDTO
     */
    @Override
    public RefundResponse refund(OrderDTO orderDTO) {
        RefundRequest refundRequest = new RefundRequest();
        refundRequest.setOrderId(orderDTO.getOrderId());
        refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
        refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
        log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
 
        RefundResponse refundResponse = bestPayService.refund(refundRequest);
        log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));
 
        return refundResponse;
    }
}


————————————————
版权声明:本文为CSDN博主「xsg6509」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xsg6509/article/details/80342744

 类似资料: