背景
最近在做谷歌退款订单监控,在开始做之前一直都有一个包在正常的跑着,退款信息都可以正常的获取,但是在同一个google play(以下简称GP)上上架的第二个app就出现了403错误,就开始了踩坑
实现退款监控的方式
我这里是通过服务端定时任务扫描,发现有新的订单然后触发钉钉报警。我简单的说下这个流程要如何做,首先需要在gcp上面创建一个服务账号,生成秘钥,我使用的是P12,创建之后在gp 的api权限里面能看到,如果你看不到我的理解也无所谓,因为我其中的一个测试服务账号就一直不在api 权限列表里面,(可以在gcp的IAM管理里面添加此服务账号为主账号,这样就可以看到了)。接下来需要在gp用户和权限里面授权账号权限,授权完就可以了(stackoverflow上面有说需要等待24小时让配置下发,如果遇到莫名其妙的问题你就等吧)。
代码实现
public AndroidPublisher getAndroidPublisher() throws GeneralSecurityException, IOException {
HttpTransport transport = GoogleNetHttpTransport
.newTrustedTransport();
PrivateKey privateKey = SecurityUtils.loadPrivateKeyFromKeyStore(
SecurityUtils.getPkcs12KeyStore(),
this.getClass().getResourceAsStream(P12_KEY), STORE_PASS,
ALIAS, KEY_PASS);
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(transport)
.setJsonFactory(JacksonFactory.getDefaultInstance())
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections
.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER))
.setServiceAccountPrivateKey(privateKey).build();
return new AndroidPublisher.Builder(transport,
JacksonFactory.getDefaultInstance(), credential).setApplicationName("com.XXXXX").build();
}
private List<VoidedPurchase> getPurchaseToken(AppTypeEnum appTypeEnum) throws GeneralSecurityException, IOException {
boolean isExistData = true;
AndroidPublisher publisher = this.getAndroidPublisher(appTypeEnum.getPackageName());
AndroidPublisher.Purchases.Voidedpurchases voidedpurchases = publisher.purchases().voidedpurchases();
//后面还可以跟.setMaxResults 设置返回结果最大值默认1000 .setToken查询更多结果时设置(下一页nextPageToken)
AndroidPublisher.Purchases.Voidedpurchases.List refundInfo = voidedpurchases.list(appTypeEnum.getPackageName());
refundInfo.setStartTime(startTime);
refundInfo.setEndTime(System.currentTimeMillis());
refundInfo.setPackageName(appTypeEnum.getPackageName());
//执行调用动作,返回结果
VoidedPurchasesListResponse refundList = refundInfo.execute();
List<VoidedPurchase> voidedPurchases = refundList.getVoidedPurchases();
//获取分页token,若退款订单当前页没有传完,则返回参数中会有NextPageToken,用来循环请求下一页的退款订单
TokenPagination tokenPagination = refundList.getTokenPagination();
while (isExistData) {
if (tokenPagination != null) {
AndroidPublisher.Purchases.Voidedpurchases.List nextRefundInfo = voidedpurchases.list(appTypeEnum.getPackageName())
.setToken(tokenPagination.getNextPageToken());
VoidedPurchasesListResponse nextRefundLists = nextRefundInfo.execute();
List<VoidedPurchase> nextVoidedPurchasess = nextRefundLists.getVoidedPurchases();
tokenPagination = nextRefundLists.getTokenPagination();
voidedPurchases.addAll(nextVoidedPurchasess);
} else {
isExistData = false;
}
}
return voidedPurchases;
}
问题
就这样一套流程,第一个上架的包都正常的在跑,但是最近上架的就出现了403错误,详细报错如下:com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden GET www.googleapis.com/androidpubl… { "code" : 403, "errors" : [ { "domain" : "androidpublisher", "message" : "The current user has insufficient permissions to perform the requested operation.", "reason" : "forbidden" } ], "message" : "The current user has insufficient permissions to perform the requested operation." }看到这个报错的第一反应就是服务账号的权限有问题,开始从gcp的服务账号查询,p12换成json、升级google-api-services-androidpublisher 、对gp账号添加app权限还是不行。后来提交谷歌工单,给的回复是”内部团队建议您先将帐号的 App permissions 先移除,并仅保留 Account permissions 即可。完成此步骤后,请再尝试呼叫 API 看看相同的问题是否存在。“看到了曙光,移除了app权限,然并卵,问题还在! 我感觉我这一辈子都找不到问题在哪了,这个时候不知道为什么去修改了startTime的参数,改成2天以内,然后成功了,然后改回之前的60天,又是同样的错误,终于找到问题了。
反思
期初startTime传入60天是因为第一次查询需要把上线依赖所有的退款订单全部查询出来,第二次就会覆盖第一次的startTime,就是代码中的这个time传入的坑用了我2天的时间。 谷歌的报错给人的第一反应就是权限的问题,需要再权限分配上面找问题,当初以为新上架的包就需要使用最近的api授权,这个报错误导人,按照国内的研发的习惯这样的报错难道不是类似这样的”参数错误“、”超过可查询范围“等等之类的,为什么报个”当前用户没有足够的权限来执行请求的操作“,谷歌做个人吧。 我去看了文档里面这个参数的描述,并没有对这个参数有约束的描述。
谷歌,做个人吧!