Google授权登录及Android支付(uniapp-app)

uniapp项目进行 “app端Google授权登录及拉起支付” 流程, 以及google后台相关配置

谷歌授权登录

uniapp项目谷歌授权登录参考:
官方链接

app端授权登录配置注意:

manifest.json => APP 模块配置 => OAuth(登录鉴权) => Google登录 => IOS平台客户端ID

此处填入ios端 客户端ID

对于android端配置,无须在manifest.json中配置,但是在Google Cloud后台,也同样需要创建 android端客户端ID

android端,请为项目创建keystore文件,根据keystore文件获取对应的SHA-1证书指纹,对应生成命令在Google Cloud后台配置客户端ID下方有

google-uniapp

针对 SHA-1证书指纹,调试时,请将此 SHA-1指纹替换为 本地keystore文件的对应SHA-1值,若app需要上线或针对已上传到google后台的aab包进行测试,请将此处SHA-1值替换为Google生成的【应用签名密钥证书】(可在google play console–设置–应用签名 页面查看),否则google登录无法回调

google-uniapp

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
// #ifdef APP-PLUS
const _that = this;
uni.login({
provider: 'google',
success: function(loginRes) {
console.log(loginRes)
if (loginRes.errMsg === 'login:ok') {
// 登录成功
uni.getUserInfo({
provider: 'google',
success: function(info) {
console.log('google 登录===>:', info);
// openid: info.userInfo.openid,
// unionid: info.userInfo.unionid,
// email: info.userInfo.email,
// todo your own business
},
});
} else {
uni.showToast({
icon: 'none',
title: 'Login Fail: ' + JSON.stringify(loginRes),
});
}
},
fail: function(err) {
console.log(err);
uni.showToast({
icon: 'none',
title: 'Connect Fail: ' + JSON.stringify(err),
});
}
});
// #endif

google支付app端拉起

代码层面:uniapp插件市场购买插件,按说明使用

业务逻辑层面:

1、点击支付,检查历史订单(查询成功,若有遗留则服务端验证并消耗),是否已被消费,处理完成,查询Google商品列表,查询成功,创建订单,创建订单成功,发起Google支付,将订单传递到google支付方法内
2、针对google支付方法,支付成功,服务器验证是否购买成功,针对结果进行提示
3、对于第二步,支付结果非成功,若errorCode===7,代表此商品未消费,则查询历史订单,服务端验证并消耗
4、对于消费,这里是服务端验证并消费订单,前端并没有进行消费,注意用户购买成功后,必须先服务端验证订单有效性,再去消费订单,接着再去给用户发送相关服务
5、用户进入app时,可查询购买记录,针对需要处理的订单进行再次消费(及后续操作)处理

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// #ifdef APP
const googlePay = uni.requireNativePlugin('GT-GooglePay-V5__PayModule');
// #endif

export default {
data() {
return {
// #ifdef APP
prdList: [],
payList: [],
isGoogleConnect: false,
isPaying: false,
google_sku_id: 'app_appname_companyname_coin10000'
// #endif
}
},
onUnload() {
uni.setStorageSync('pli', [])
uni.hideLoading()
},
methods: {
doInit() {
googlePay.doInit({}, e => {
if (e.code == 200) {
// 初始化成功
console.log('连接谷歌成功');
this.isGoogleConnect = true
// 查询历史订单
this.doQueryPurchases((res) => {
// console.log(res)
this.doQuerySku()
})
} else {
// 初始化失败
console.log('连接谷歌失败');
uni.hideLoading()
that.showToast('连接谷歌失败');
this.isPaying = false
}
});
},
doQueryPurchases(callback) {
let that = this;
// console.log('do doQueryPurchases function.');
uni.setStorageSync('pli', []) // 针对重复请求
googlePay.doQueryPurchases(
'inapp',
e => {
if (e.code == 200) {
// 查询成功
// console.log('queryPurchases成功');
if (e.purchasesList == null || e.purchasesList.length == 0) {
// 无遗留待处理订单
callback()
} else {
that.payList = e.purchasesList
// 服务端验证并消耗
that.verifyOrder(callback)
}
} else {
// 查询失败
console.log('google queryPurchases失败');
}
}
);
},
// 服务器确认订单
verifyOrder(callback) {
const promises = []
this.payList.map((item, index) => {
promises.push(this.verifyOrderRequest(JSON.parse(item.originalJson)))
})
Promise.allSettled(promises).then(res => {
if (promises.length === res.length) {
callback && callback(res)
}
})
},
// 服务器确认订单 接口请求 验证订单并消耗
verifyOrderRequest(data) {
return new Promise((resolve, reject) => {
// 判断是否已存在 已存在则不请求
const storageList = uni.getStorageSync('pli') || [];
const curToken = data.purchaseToken
if (storageList.includes(curToken)) {
uni.hideLoading()
return false
}
uni.setStorageSync('pli', [...storageList, curToken])
// 发起请求
uni.request({
url: '/api/paygoogle',
data, // google 回调参数
method: 'POST',
success: res => {
// console.log('服务器验证verifyOrder: ===>', res)
// 成功 服务器消耗
if (res.data.code === 1) {
resolve(data)
} else {
// 请求出错 删除缓存 可重新请求
const newStorageList = uni.getStorageSync('pli') || []
const index = newStorageList.findIndex(item => item === curToken)
if (index) {
newStorageList.splice(index, 1)
uni.setStorageSync('pli', newStorageList)
}
// 错误结果
if (res.data.code === 0) {
reject(res.data.msg)
} else {
reject('服务器验证出错,请联系管理员')
}
}
},
fail: (data, code) => {
uni.showToast({
icon: "none",
title: '服务器验证出错,请联系管理员'
})
}
});
})
},
/**
* 查询sku
*/
doQuerySku() {
let that = this;
googlePay.doQuerySku({
inapp: [this.google_sku_id], // GOOGLE后台配置的商品ID
},
e => {
if (e.code == 200) {
// 查询成功
// e.list; // 查询结果, array
// console.log('查询成功: ', e.list);
that.prdList = e.list;
that.createOrder()
} else {
uni.hideLoading()
// 查询失败
that.showToast('未查询到应用内商品');
console.log('未查询到应用内商品');
this.isPaying = false
}
}
);
},
// 创建订单
createOrder() {
uni.request({
url: '/api/payGoogle/sumbitOrder',
data: {
google_sku_id: this.google_sku_id,
},
method: 'POST',
success: res => {
uni.hideLoading()
if (res.data.code === 1) {
const orderSn = res.data.data.order_id
this.doPayAll(orderSn)
} else {
uni.showToast({
icon: "none",
title: '创建订单失败'
})
this.isPaying = false
}
},
fail: (data, code) => {
uni.hideLoading()
this.isPaying = false
uni.showToast({
icon: "none",
title: '网络出错'
})
}
});
},
/**
* 发起支付2
* 支付参数为查询sku结果的某一项
*/
doPayAll(orderSn) {
const that = this
if (!orderSn) {
that.showToast('未创建订单,请重试');
return
}
console.log('do pay function.');
if (that.prdList == null || that.prdList.length == 0) {
that.showToast('产品列表为空,不可支付');
return;
}
googlePay.doPayAll({
productId: that.prdList[0].productId,
offerToken: '', //可选,折扣token
accountId: orderSn, // 可选,用户Id
profileId: "", // 可选,个人资料Id
}, e => {
// console.log('支付结果:', e);
if (e.code == 200) {
// 支付成功
if (e && e.data) {
that.payList = e.data;
// 服务器验证是否购买成功
that.verifyOrder((res) => {
const hasCurrentSuccessProList = res.find(item => item.status ===
'fulfilled' && item.value.productId === that.curSkuSelected)
// 服务端已成功处理并消费订单
if (hasCurrentSuccessProList) {
// console.log('服务端已成功处理并消费订单:',hasCurrentSuccessProList, res)
uni.navigateBack()
setTimeout(() => {
that.showToast('购买成功') // 正式购买成功
}, 2000)
} else {
const fail = res.find(item => item.status === 'rejected')
// console.log('fail:', fail)
fail && that.showToast(fail.reason)
}
this.isPaying = false
})
}
} else {
if (e.errorCode === 7) {
// 根据商品id,查询该未消耗商品的信息
// 服务器验证是否购买成功
that.doQueryPurchases()
} else if (e.errorCode !== 1) {
console.log('支付失败: ', e);
that.showToast("支付失败: " + e.data);
}
this.isPaying = false
}
});
},
}
}

GOOGLE支付 H5端

针对h5端,文档内有完整代码demo,可查看此处

h5端支付需要第三方网关进行处理google返回的回调结果,一般个人公司是无法进行处理支付的,因此直接集成方式一般都不适用。

查看google支持的第三方网关列表

对于页面样式,谷歌审核也有要求,需要按照 集成核对清单 进行操作

开发测试完成,需要申请对Google Pay API的正式访问权。
google-uniapp

h5端测试

请查看申请测试卡套件, 加入“用户群组”

google-uniapp

app端调试

Google Play Console 页面,设置—许可测试页面,添加测试人员的电子邮件,许可相应请设置为 “LICENSED”

服务端权限校验

创建服务号并与项目关联,通过服务号密钥进行权限校验

参考此文章进行配置
参考此文档

请注意 OAuth同意屏幕中,在范围(scope)选项,需要添加对应的API权限

google-uniapp

google play 内部应用分享设置

google-uniapp

其他相关

修改目标 API 级别

manifest.json源码/“app-plus”/“distribute”/“android”/
“targetSdkVersion”: 33

uniapp 云打包,需要勾选 Google Play(AAB)选项,打包生成.aab格式文件,将此文件上传到 Google play console后台中

------ 本文结束------
坚持原创技术分享,您的支持将鼓励我继续创作!