java对接支付宝小程序登录

支付宝小程序登录和微信小程序登录有点不太一样
具体可以看这一篇微信小程序登录的文章
✅ 支付宝小程序没有 phoneCode 机制
✅ 必须通过服务端解密 encryptedData 获取手机号

前端代码

通过my.getAuthCode()方法拿到authCode(也是一个入参)

通过my.getPhoneNumber()方法拿到下面的JSON出参用户手机号密文(只用到了encryptedData)

{
  "encryptedData": "5SR8/SmdDDwaXjJSncBG4FPZ1HtBvpnx/RMYXFBFm2tRNiWBGhur21nbT5CR7Mes3595QXoI9cCJ4GctsAnxMw==",
  "errMsg": "getPhoneNumber:ok",
  "sign": "e7rhAA2LE/qREXcOXz8BV3ChKRpvUk8nze2LatvJl1BIRGg0ugJXGrDu6vTePABmRcOgx6an0aP/OZOj8c9GGEEZzDL7eVzoRhVLwse23+DJS2vUtFKLdh/WvEFCcii+VGiB7vabzMfOKvx9nRJ9b67DxeZmGg93RwmYs4XTm+zF8wJ4z/fZw056YXksyGRS8DGIvSHJobDmrTweALxcM3g/FE84PxWmrw+Paxd5qA2D5rGjKk+T0U/3xZWq0g7/KIZA8SXocI5RLm6lAkw43CIL/SzDO7dKduLtTKYo0WFxrYdQIGMRLgaRUuHlnIcbW0iMdPEHaVjnNaM97LiAzg=="
}

引入支付宝的SDK

  <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.40.523.ALL</version>
        </dependency>

java后端代码

支付宝配置文件

package com.boboboom.pro.module.system.config;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@Getter
public class AlipayMiniConfig {

    @Value("${alipay.app-id}")
    private String alipayAppId;       // 你的应用ID

    @Value("${alipay.private-key}")
    private String alipayPrivateKey;  //你的应用私钥

    @Value("${alipay.server-url:https://openapi.alipay.com/gateway.do}")
    private String alipayServerUrl;

    @Value("${alipay.public-key}") // 你的支付宝公钥(验签用)
    private String alipayPublicKey;

    @Value("${alipay.aes-key}") // 加密敏感数据的AES密钥
    private String alipayAesKey;


    @Bean
    public AlipayClient alipayClient() {
        return new DefaultAlipayClient(
                alipayServerUrl,
                alipayAppId,
                alipayPrivateKey,
                "json",
                "UTF-8",
                alipayPublicKey,
                "RSA2"
        );
    }
}

支付宝开放平台小程序配置
在这里插入图片描述

支付宝小程序登录入参

public class LoginMobileParam {
    private String code;           // authCode
    private String encryptedData;  // 手机号加密数据
}





支付宝登录响应出参

package com.boboboom.pro.api.system.user.result;

import lombok.AllArgsConstructor;
import lombok.Data;

// 支付宝登录响应 DTO
@Data
@AllArgsConstructor
public class AlipayOpenDTO {
    private String openId;
    private String accessToken;
}
 
   public LoginMobileResult loginAlipay(LoginMobileParam param) {
        String authCode = param.getCode(); // 支付宝登录凭证authCode
        String encryptedData = param.getEncryptedData();// 加密后的数据
        //String sign = param.getSign(); // 支付宝签名
        AssertUtil.notEmpty(authCode, "authCode不能为空");
        AssertUtil.notEmpty(encryptedData, "encryptedData不能为空");
        return sysUserApi.loginAlipay(param);
    }

在这里插入图片描述


    @Autowired
    private AlipayClient alipayClient;
    @Autowired
    private AlipayMiniConfig alipayMiniConfig;
    
 /**
     * 支付宝小程序登录
     *
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginMobileResult loginAlipay(LoginMobileParam param) {
        log.info("【支付宝】小程序登录入参: {}", JSONUtil.toJsonStr(param));
        // === Step 1: 用 code 换取 alipayOpenid + alipayUnionid + accessToken ===
        AlipayOpenDTO alipayDTO = getAlipayOpenIdByCode(param.getCode());
        String alipayOpenid = alipayDTO.getOpenId();
        String accessToken = alipayDTO.getAccessToken();

        log.info("【支付宝】获取到 alipayDTO: {}", JSONUtil.toJsonStr(alipayDTO));

        param.setOpenid(alipayOpenid);

        // === Step 2: 获取手机号(若用户已授权)===
        String phone = getAlipayPhoneNumber(param.getEncryptedData());
        param.setPhone(phone);
        log.info("【支付宝】获取到手机号: {}", phone);

        // === Step 3: 查询用户是否存在
        LoginMobileResult existingResult = getLoginResultByAlipayOpenid(param.getPhone(), alipayOpenid);
        if (existingResult != null) {
            existingResult.setOpenId(alipayOpenid);
            return existingResult;
        }

        // === Step 4: 新用户注册(写入 alipayOpenid/alipayUnionid)===
        Long userId = handleAlipayUserLogin(param, alipayOpenid);
        LoginMobileResult result = generateLoginResult(userId, param.getPhone(), param.getNickName());
        result.setOpenId(alipayOpenid);

        return result;
    }

```java
  /**
     * 支付宝:用小程序 code 换取 openId / unionId / accessToken
     */
    private AlipayOpenDTO getAlipayOpenIdByCode(String code) {
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setGrantType("authorization_code");
        request.setCode(code);

        try {
            AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
            log.info("【支付宝】换取 openId 结果: {}", JSONUtil.toJsonStr(response));
            if (!response.isSuccess()) {
                log.warn("支付宝登录失败: code={}, msg={}, sub_code={}, sub_msg={}",
                        response.getCode(), response.getMsg(),
                        response.getSubCode(), response.getSubMsg());
                throw new ServerException("支付宝登录失败: " + response.getSubMsg());
            }

            return new AlipayOpenDTO(
                    response.getOpenId(),
                    response.getAccessToken()
            );

        } catch (Exception e) {
            log.error("【支付宝】换取 openId 异常", e);
            throw new ServerException("系统异常,请稍后重试");
        }
    }
    /**
     * 支付宝:使用SDK内置方法解密手机号
     */
    private String getAlipayPhoneNumber(String encryptedData) {
        try {
            //  1. 直接使用SDK内置方法解密
            String decryptedPhoneInfo = AlipayEncrypt.decryptContent(
                    encryptedData,
                    "AES",
                    alipayMiniConfig.getAlipayAesKey(),
                    "UTF-8"
            );

            // 2. 解析手机号
            JSONObject jsonObject = JSONObject.parseObject(decryptedPhoneInfo);
            log.info("【支付宝】解密手机号结果: {}", JSONUtil.toJsonStr(jsonObject));
            String phone = jsonObject.getString("mobile");

            if (StringUtils.isBlank(phone)) {
                phone = jsonObject.getString("phoneNumber");
                if (StringUtils.isBlank(phone)) {
                    throw new ServerException("解密数据中未找到手机号: " + decryptedPhoneInfo);
                }
            }

            log.info("【支付宝】解密手机号成功: {}", phone);
            return phone;

        } catch (Exception e) {
            log.error("【支付宝】解密手机号异常", e);
            throw new ServerException("解密手机号失败,请重试");
        }
    }
<h1>返回的实体类参考</h1>

```java
package com.boboboom.pro.api.system.user.result;

import lombok.Data;

import java.io.Serializable;

@Data
public class LoginMobileResult implements Serializable {
    private Long userId; //用户ID
    private String token; //用户登录token默认30天有效期
    private String openId; //用户登录openId
}

JWT方法参考

    /**
     * 生成登录结果(含JWT令牌生成和Redis存储)
     */
    private LoginMobileResult generateLoginResult(Long userId, String userName, String nickName) {
        // 生成JWT
        long expireTime = System.currentTimeMillis() + MobileConsent.TOKEN_EXPIRETIME_VERIFIED;
        JwtUserInfo jwtUserInfo = new JwtUserInfo();
        jwtUserInfo.setUserId(userId);
        jwtUserInfo.setUserName(userName);
        jwtUserInfo.setNickName(nickName);
        jwtUserInfo.setSource(UserSourceEnum.MOBILE.getCode());
        String token = jwtTokenUtil.generateToken(jwtUserInfo, expireTime);

        // 以token为key存入Redis
        RedisUtil.setEx(RedisKeyUtil.getMobileTokenKey(token), jwtUserInfo,
                MobileConsent.TOKEN_EXPIRETIME_VERIFIED, TimeUnit.MILLISECONDS);

        // 以jwtUserInfo为key存入Redis
        RedisUtil.setEx(RedisKeyUtil.getMobileUserIdKey(userId), token,
                MobileConsent.TOKEN_EXPIRETIME_VERIFIED, TimeUnit.MILLISECONDS);

        // 构建结果
        LoginMobileResult result = new LoginMobileResult();
        result.setUserId(userId);
        result.setToken(token);
        return result;
    }
posted @ 2025-11-20 17:33  难忘是想起  阅读(3)  评论(0)    收藏  举报  来源