微信SDK实现小程序授权登录、支付、退款、企业打款(提现)前端+后端

一、小程序授权登录

前置条件

已开通微信应用(小程序),授权登录主要用到AppID(小程序ID)+AppSecret(小程序密钥)

点击跳转微信公众平台
在这里插入图片描述

1.添加微信SDK

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-miniapp</artifactId>
            <version>4.0.0</version>
        </dependency>

2.在Properties配置文件中配置微信参数

#微信配置
wx:
  pay:
    #微信公众号或者小程序等的appid
    app-id: xxxxxxxxxxxxxxxxxxxxxxxxx
    #应用密钥
    app-secret: xxxxxxxxxxxxxxxxxxxxxxxxx

3.增加WeiXinProperties类,用来获取配置文件的微信参数

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "wx.pay")
@Primary //覆盖 当有多个这个的WxPayProperties实现类 注入这个
public class WeiXinProperties {

    /** appId : 微信公众号或者小程序等的appid */
    private String appId;
    /** appSecret : 应用密钥 */
    private String appSecret;
 }

4.创建微信配置类WxConfiguration

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.jiuren.common.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 支付配置
 * @author LWL
 * @date 2022-03-17
 */
@Slf4j
@Configuration
@ConditionalOnClass(WxPayService.class) //引入WxPayService这个类 下面两个才会实例化
@EnableConfigurationProperties(WeiXinProperties.class) //注入WxPayProperties类
@RequiredArgsConstructor
public class WxConfiguration {

    private final WeiXinProperties properties;

    /**
     *  获取小程序WxMaService
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public WxMaService wxMaService() {
        //实例这个 WxMaConfig
        WxMaConfig wxMaConfig = new WxMaDefaultConfigImpl();
        ((WxMaDefaultConfigImpl) wxMaConfig).setAppid(StringUtils.trimToNull(this.properties.getAppId()));
        ((WxMaDefaultConfigImpl) wxMaConfig).setSecret(StringUtils.trimToNull(this.properties.getAppSecret()));
        WxMaService wxMaService = new WxMaServiceImpl();
        //设置配置文件
        wxMaService.setWxMaConfig(wxMaConfig);
        return wxMaService;
    }

}

5.授权登录的实现方法

微信授权入参对象

code、encryptedData、iv、nickName、avatar、gender这几个参数由前端调取wx.login和wx.getUserinfo获取
(现在微信获取用户信息不再返回性别和地址信息)

在这里插入图片描述

@Data
@ApiModel(value = "微信授权参数", description = "")
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class WxLoginEntity implements Serializable {

    @ApiModelProperty(value = "用户登录凭证")
    private String code;

    @ApiModelProperty(value = "加密用户数据")
    private String encryptedData;

    @ApiModelProperty(value = "加密算法的初始向量")
    private String iv;

    @ApiModelProperty(value = "昵称")
    private String nickName;

    @ApiModelProperty(value = "头像")
    private String avatar;

    @ApiModelProperty(value = "openid")
    private String openId;

    @ApiModelProperty(value = "性别: 0:未知、1:男、2:女")
    private Integer gender;

    @ApiModelProperty(value = "手机号")
    private String phoneNumber;
}

实现方法

/**
     *  微信授权获取用户信息
     * @param entity
     * @return
     * @throws WxErrorException
     */
    public WxLoginEntity wxAuth(WxLoginEntity entity) throws WxErrorException {

        // 获取微信用户session
        WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(entity.getCode());
        //ValidationUtill是我自己封装的参数验证判空的方法
        ValidationUtil.isNull(session,"获取微信Session失败");
        ValidationUtil.isNull(session.getOpenid(),"获取openid失败");


        // 解密手机号码信息
        WxMaPhoneNumberInfo wxMaPhoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(session.getSessionKey(),
                entity.getEncryptedData(), entity.getIv());
        if (wxMaPhoneNumberInfo == null || StringUtils.isEmpty(wxMaPhoneNumberInfo.getPhoneNumber())){
            throw new BadRequestException("获取用户手机号失败");
        }
        log.info(String.format("============用户登录注册获取微信用户信息===========> openId=%s, nickName=%s,phone=%s", session.getOpenid(), entity.getNickName(),wxMaPhoneNumberInfo.getPhoneNumber()));
        //封装返回用户信息
        WxLoginEntity wxLoginEntity = WxLoginEntity.builder()
                .avatar(entity.getAvatar())
                .nickName(entity.getNickName())
                .openId(session.getOpenid())
                .gender(Number.ZERO)
                .phoneNumber(wxMaPhoneNumberInfo.getPhoneNumber())
                .build();

        return wxLoginEntity;
    }

拿到微信授权信息和openid就可以实现自己的注册登录逻辑(用openid在数据库是否可以查询到用户:有-登录;没有-注册)

二、小程序支付

前置条件

已开通微信支付,主要用到商户号+商户秘钥+商户证书

1.添加微信支付SDK

weixin-java-pay 包含支付,退款,商家打款等操作

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
            <version>4.0.0</version>
        </dependency>

2.在Properties配置文件中加上微信支付参数

#微信配置
wx:
  pay:
    #微信公众号或者小程序等的appid
    app-id: xxxxxxxxxxxxxxxxxx
    #应用密钥
    app-secret: xxxxxxxxxxxxxxxxxx
    #微信支付商户号
    mch-id: xxxxxxxxxxxxxxxxxx
    #微信支付商户密钥
    mch-key: xxxxxxxxxxxxxxxxxx

3.WeiXinProperties类增加微信支付参数

import com.jiuren.common.utils.FileUtil;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "wx.pay")
@Primary //覆盖 当有多个这个的WxPayProperties实现类 注入这个
public class WeiXinProperties {

    /** appId : 微信公众号或者小程序等的appid */
    private String appId;
    /** appSecret : 应用密钥 */
    private String appSecret;
    /** mchId : 微信支付商户号 */
    private String mchId;
    /** mchKey : 微信支付商户密钥 */
    private String mchKey;
	/** mchKey : 商户证书目录 */
    private String keyPath = FileUtil.getResourcesPath()+ "refund_certificate/apiclient_cert.p12";
}

4.在微信配置类WxConfiguration增加微信支付的bean

    @Bean
    @ConditionalOnMissingBean
    public WxPayService wxPayService() {

        //实例payConfig 设置固定参数
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
        payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
//        payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
//        payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
        payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));

        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);

        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

5.微信支付的实现方法

	private final WxPayService wxPayService;

    public Object wxPay(WxPayEntity entity) {
        /**
         * 处理内部业务,校验订单等
         */
        final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                //调起支付的人的 openId
                .openid("这里写openid")
                //订单编号
                .outTradeNo("自己生成的订单号")
                //小程序支付
                .tradeType(WxPayConstants.TradeType.JSAPI)
                //订单金额(单位是分)BaseWxPayRequest.yuanToFen()这个方法是将元转分
                .totalFee(BaseWxPayRequest.yuanToFen("这里写支付金额"))
                //商品描述
                .body("描述")
                //获取本地IP
                .spbillCreateIp("客户端ip")
                //回调的 URL 地址
                .notifyUrl("这里写支付成功后微信的微信接口")
                //过期时间 格式yyyyMMddHHmmss (一般设置个10分钟后)
                .timeExpire("支付过期时间"))
                //当前时间 格式yyyyMMddHHmmss
                .timeStart("当前时间")
                .build();
        wxPayUnifiedOrderRequest.setSignType(WxPayConstants.SignType.MD5);

        Object order = null;
        try {
            order = wxPayService.createOrder(wxPayUnifiedOrderRequest);
        } catch (WxPayException e) {
            throw new BadRequestException(e.getErrCodeDes());
        }
        return order;
    }

然后将微信返回的参数order返回给前端,由前端调起支付

6.小程序支付回调接口

用户支付成功后微信会回调我们写的回调接口,需要在回调接口写上处理订单的业务逻辑

    private final WxPayService wxPayService;
    
	@ApiOperation("微信支付回调接口")
    @RequestMapping(value = "/payNotify")
    public String payNotify(HttpServletRequest request) {
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlResult);
            //getOutTradeNo 自己生成的订单号
            String orderId = notifyResult.getOutTradeNo();
            //支付成功
            if("SUCCESS".equals(notifyResult.getResultCode())) {
                log.info("================>微信支付回调:订单号<{}>",orderId);
                //自己处理订单的业务逻辑,需要判断订单是否已经支付过,否则可能会重复调用
                
            }
            //必须要给微信返回回调成功状态,否则微信会一直回调
            return WxPayNotifyResponse.success("成功");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因{}", e.getMessage());
            return WxPayNotifyResponse.success("code:"+9999+"微信回调结果异常,异常原因:"+e.getMessage());
        }
    }

三、小程序退款

依赖和配置参数在上面小程序支付。下面直接写实现方法

1.小程序退款实现方法

    private final WxPayService wxPayService;

	/**
     * 退款
     * @throws WxPayException
     */
    public void refundOrder(WxRefundEntity entity) {

        WxPayRefundRequest wxPayRefundRequest = WxPayRefundRequest.newBuilder()
                //订单总金额(分)
                .totalFee(BaseWxPayRequest.yuanToFen("支付的总金额"))
                //订单编号
                .outTradeNo("这个订单号是小程序支付的时候填写的订单号")
                //退款编号
                .outRefundNo("这个是退款的订单号")
                //退款金额(分)
                .refundFee(BaseWxPayRequest.yuanToFen("退款金额"))
                //回调接口
                .notifyUrl("微信退款成功后的回调接口")
                .build();

        try {
            wxPayService.refund(wxPayRefundRequest);
        } catch (WxPayException e) {
            throw new BadRequestException(e.getErrCodeDes());
        }
    }

退款不需要前端操作,到此退款已经申请完成。等待微信处理(一般也就是不到一分钟吧)

2.小程序退款回调接口


    private final WxPayService wxPayService;
    
    @ApiOperation("微信退款回调接口")
    @RequestMapping(value = "/refundNotify")
    public String refundNotify(HttpServletRequest request) {
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayRefundNotifyResult wxPayRefundNotifyResult = wxPayService.parseRefundNotifyResult(xmlResult);
            //小程序支付时的订单号(不是退款订单号)
            String outTradeNo = wxPayRefundNotifyResult.getReqInfo().getOutTradeNo();
            //退款金额
            Integer refundFee = wxPayRefundNotifyResult.getReqInfo().getRefundFee();
            //还有其他返回的参数从getReqInfo方法返回的ReqInfo这个对象中取,有退款订单号等等。

            //退款成功
            if("SUCCESS".equals(wxPayRefundNotifyResult.getReqInfo().getRefundStatus())) {
			//自己处理订单退款成功后的业务逻辑,需要判断该退款订单是否已经退款过,否则可能会重复调用
                
            }
			//必须要给微信返回回调成功状态,否则微信会一直回调
            return WxPayNotifyResponse.success("成功");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因{}", e.getMessage());
            return WxPayNotifyResponse.success("code:"+9999+"微信回调结果异常,异常原因:"+e.getMessage());
        }
    }

四、商家打款(提现)

前置条件

已开通微信商户,并且开通企业付款到零钱,主要用到商户号+商户秘钥+商户证书

依赖和配置参数在上面小程序支付。下面直接写实现方法

1.商家打款到微信实现方法


    private final WxPayService wxPayService;
    
	/**
     * 企业打款
     */
    public EntPayResult entPay(WxEntPayEntity entity) {

        EntPayRequest entPayRequest = EntPayRequest.newBuilder()
                .openid("openid")
                //商户订单号,需保持唯一性(只能是字母或者数字,不能包含有其它字符)
                .partnerTradeNo("自己生成一个提现订单号")
                //是否检验真实姓名  NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
                .checkName("FORCE_CHECK")
                //收款用户真实姓名。如果check_name设置为FORCE_CHECK,则必填用户真实姓名 如需电子回单,需要传入收款用户姓名
                .reUserName("微信实名认证的真实姓名")
                //转账金额,单位为分
                .amount(BaseWxPayRequest.yuanToFen("提现金额"))
                //付款备注,必填。注意:备注中的敏感词会被转成字符*
                .description("备注")
                //该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP。
                .spbillCreateIp("用户客户端ip"))
                .build();

        try {
            return wxPayService.getEntPayService().entPay(entPayRequest);
        } catch (WxPayException e) {
            throw new BadRequestException(e.getErrCodeDes());
        }
    }

五.小程序端代码

1.前端调用wx.login(),获取微信临时登录凭证code

//示例代码
wx.login({
  success (res) {
    if (res.code) {
      //发起网络请求
      wx.request({
        url: 'https://example.com/onLogin',
        data: {
          code: res.code
        }
      })
    } else {
      console.log('登录失败!' + res.errMsg)
    }
  }
})

注:临时登录凭证 code 只能使用一次,code生成以后5分钟失效

2.用户同意授权之后,可以通过wx. getUserProfile()拿到用户信息

<button class="btn" wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile"> 微信登录 </button>
//在JS中
 getUserProfile(e) {
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '用于登录', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })

        // 4 授权成功后,发送用户信息(昵称+头像和code)给后端,返回完整的用户信息
        let session_id = wx.getStorageSync('sessionid');
        
        console.log("发起授权网络请求")
        wx.request({
          url: '后端登录接口', 
          method: 'POST',
          data: {
            nickname: res.userInfo.nickName,
            avatar_url: res.userInfo.avatarUrl,
          },
          header: { 'content-type': 'application/x-www-form-urlencoded', 'Cookie': session_id ,
          'token': app.globalData.token},
          success (res) {
            if(res.data.code == 200) {
              wx.setStorageSync('isLogin', true)
              //把数据存在全局变量
              app.globalData.user = res.data.data
              //打印出来看看,确认已经头像和昵称已经有了
              console.log("app.globalData.user"+app.globalData.user)
              //跳转进入首页              
              wx.switchTab({
                url:'/pages/index/index'
              })             
            }
            else{
              console.log("res.data.code="+res.data.code)
            }
          },
          fail:function(err){
            console.log("跳转")
            console.log(err)
          },
          complete:function(){
            console.log("complete")
          }
        })
      }
    })
  },

3.小程序调起支付

<view class="tx-18" bindtap="postMoney">确认支付</view>
postMoney() { // 点击确认支付
    wx.showLoading({
      title: '加载中',
    })
    let that = this
    app.http.getData({ //第一步请求后台接口,获取发起支付所需要的数据
      amount: that.data.orderId // 我这里发起请求传过去的是订单编号
    }).then((res1) => {
            if (res1.code == 200) {
                wx.requestPayment({ // 这一步是调起微信支付
                  "appId": res1.appId,
                  "timeStamp": res1.timeStamp,
                  "nonceStr": res1.nonceStr,
                  "package": res1.package,
                  "signType": res1.signType,
                  "paySign": res1.paySign,
                  "success": function (res) {
                    wx.hideLoading({})
                    app.ShowToast('支付成功')
                  },
                  "fail": function (res) {
                    app.ShowToast('支付失败');
                  },
                  "complete": function (res) {
                    app.ShowToast('取消支付');
                  }
                })
            } else {
              wx.hideLoading({})
              app.ShowToast('支付失败')
            }
    })
  },

六、源码地址

后端源码地址:https://gitee.com/ssoocc1995/wx_demo.git

  • 14
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
微信小程序SDK架构是指在开发微信小程序时所使用的软件开发工具包,它包含了一系列的API和工具,能够帮助开发者更轻松地构建和发布微信小程序微信小程序SDK架构基本上可以分为两个核心部分:前端开发和后端开发。前端开发主要是用来实现小程序界面和功能的展示,后端开发则是负责处理与服务器之间的数据交互和业务逻辑。 在前端开发中,微信小程序SDK提供了一些基本的API,例如界面渲染、用户交互、网络请求和存储等功能的调用接口。开发者可以通过调用这些API来实现小程序的各种功能,例如页面跳转、数据展示和用户登录等。此外,微信小程序SDK还提供了一套丰富的组件库,开发者可以使用这些组件来快速构建小程序界面,提高开发效率。 在后端开发中,微信小程序SDK也提供了一些API和工具,用于实现与服务器之间的数据交互和业务逻辑的处理。开发者可以通过这些API来发送网络请求、存储数据、调用服务器接口等。同时,微信小程序SDK还提供了一些开发工具,如调试工具、性能监控和错误上报等,帮助开发者更好地进行项目管理和问题排查。 总的来说,微信小程序SDK架构是一个包含前端开发和后端开发两个部分的开发工具包,通过提供丰富的API和工具,帮助开发者更轻松地构建和发布微信小程序实现小程序的界面展示、功能实现和数据交互等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值