uni-app小程序开发-支付宝小程序一键登录

前言

一键登录的思路是

获取支付宝账号的手机号,如果手机号已存在就自动登录,如果不存在就生成该手机号的账号信息,当然也可以让用户绑定自己的账号信息。

所以我们要先获取手机号:

https://opendocs.alipay.com/mini/api/getphonenumber?pathHash=a67c2790

image-20240520180829776

注意

获取会员手机号仅对企业支付宝账户开放,个人账号不支持,详见获取会员手机号

获取会员手机号仅对企业支付宝账户开放,个人账号不支持,详见获取会员手机号

获取会员手机号仅对企业支付宝账户开放,个人账号不支持,详见获取会员手机号

常用地址

开放平台

https://open.alipay.com/develop/manage

创建小程序

https://open.alipay.com/develop/mini/create?bundleId=com.alipay.alipaywallet&from=createMenuPage

小程序API

https://opendocs.alipay.com/mini/api/

获取手机号

https://opendocs.alipay.com/mini/api/getphonenumber

获取用户信息

方式1

my.getAuthCode 引导用户授权其信息给当前小程序,取得的代表用户授权的授权码(authCode),

后续需由小程序服务端使用,向支付宝换取用户信息(如 user_id(open_id)、头像、昵称、手机号、地区、性别、出生日期等)。

这种方式较通用,但也相对复杂。

获取不到用户的ID,用户ID需要小程序获取authCode发给后台,后台获取。

https://opendocs.alipay.com/mini/api/openapi-authorize?pathHash=22642781

后台

https://opendocs.alipay.com/open/84bc7352_alipay.system.oauth.token?scene=common&pathHash=fe1502d5

方式2

对于简单需求

获取用户昵称和头像,请使用 my.getOpenUserInfo

获取用户手机号,请使用 my.getPhoneNumber

方式1 后台获取

小程序获取授权码,后台获取用户openid、昵称、头像、手机号。

https://opendocs.alipay.com/mini/api/openapi-authorize?pathHash=22642781

对应权限的申请位置

img

在线调试

https://open.alipay.com/api/apiDebug?apiNames=alipay.system.oauth.token&frontProdCode=I1080300001000042699&backProdCode=I1220100001000025884

步骤

  1. 小程序获取授权码

页面

1
<button open-type="getAuthorize" scope="userInfo" @getAuthorize="getOpenUserInfo"> 会员基础信息授权 </button>

JS

1
2
3
4
5
6
7
8
9
10
11
getOpenUserInfo: function () {
my.getAuthCode({
scopes: 'auth_user',
success: (authCodeRes) => {
const authCode = authCodeRes.authCode;
},
fail: (error) => {
console.error('获取授权码失败', error);
}
});
},
  1. 服务端通过授权码获取用户信息

服务端使用 authCode,调用 alipay.system.oauth.token 取得 user_id(open_id) 和 token(授权令牌)。

服务端继续使用所取得的 token 调用 alipay.user.info.share 最终获得用户信息。

方式2

这种方式

小程序获取授权码,后台获取用户openid、昵称、头像

小程序获取手机号

小程序获取用户的基本信息及加密的手机号,后台解密手机号。

只能获取用户的昵称、头像、手机号,用手机号作为唯一标识。

获取用户基本信息

获取用户基本信息不用在小程序的后台申请权限/

1
2
3
4
5
6
7
8
9
10
// 继续获取用户信息
my.getOpenUserInfo({
success: (userInfoRes) => {
const userInfo = JSON.parse(userInfoRes.response).response;
console.log('User Info:', userInfo);
},
fail: (error) => {
console.error('获取用户信息失败', error);
}
});

获取用户信息不用解密,用户信息仅包含用户的昵称和头像。

用户信息中能获取到的是avatarnickName

小程序获取手机号

申请权限

img

小程序

页面中

1
2
3
4
<!-- #ifdef  MP-ALIPAY  -->
<button class="login" type="default" open-type="getAuthorize" @getAuthorize="onGetAuthorize"
@error="onAuthError" scope='phoneNumber'>授权手机号一键登录</button>
<!-- #endif -->

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
onGetAuthorize: function() {
return new Promise((resolve, reject) => {
my.getPhoneNumber({
scopes: "auth_user",
success: res => {
console.info("response", res.response);
this.zfbLogin(res.response); // 调取这个自定义方法
resolve(res);
},
fail: res => {
reject(res);
}
});
});
},

response 是一个JSON对象转的字符串,包含两个属性:

responsesign,我们可以前端取传给后端,也可以传整个字符串让后端解析。

服务端解密

添加引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.39.74.ALL</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.50</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipayEncrypt;
import com.alipay.api.internal.util.AlipaySignature;

import java.util.Map;

public class MyTest {
public static void main(String[] args) throws Exception {
String response = "{\"response\":\"xxxxxxx\",\"sign\":\"xxx\"}";

getResult(response);
}

public static String getResult(String response) throws Exception {
//1. 获取验签和解密所需要的参数
Map<String, String> openapiResult = JSON.parseObject(response, new TypeReference<Map<String, String>>() {
}, Feature.OrderedField);

String sign = openapiResult.get("sign");
String content = openapiResult.get("response");
//判断是否为加密内容
boolean isDataEncrypted = !content.startsWith("{");
boolean signCheckPass = false;
//2. 验签
String signType = "RSA2";
String charset = "UTF-8";
String encryptType = "AES";
String signContent = content;
String signVeriKey = "你的小程序对应的支付宝公钥(为扩展考虑建议用appId+signType做密钥存储隔离)";
String decryptKey = "你的小程序对应的加解密密钥(为扩展考虑建议用appId+encryptType做密钥存储隔离)";//如果是加密的报文则需要在密文的前后添加双引号
if (isDataEncrypted) {
signContent = "\"" + signContent + "\"";
}
try {
signCheckPass = AlipaySignature.rsaCheck(signContent, sign, signVeriKey, charset, signType);
} catch (AlipayApiException e) {
// 验签异常, 日志
}
if (!signCheckPass) {
//验签不通过(异常或者报文被篡改),终止流程(不需要做解密)
throw new Exception("验签失败");
}
//3. 解密
String plainData = null;
if (isDataEncrypted) {
try {
plainData = AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
} catch (AlipayApiException e) {
//解密异常, 记录日志
throw new Exception("解密异常");
}
} else {
plainData = content;
}

System.out.println("plainData:" + plainData);
return plainData;
}
}

新的要求

https://open.alipay.com/portal/forum/post/173201040

只获取用户openid可以静默获取

静默获取用户的openid

1
2
3
4
5
6
7
8
9
10
11
getOpenUserInfo: function () {
my.getAuthCode({
scopes: 'auth_base',
success: (authCodeRes) => {
const authCode = authCodeRes.authCode;
},
fail: (error) => {
console.error('获取授权码失败', error);
}
});
},