最近公司在做一个app购买的功能,主要思路就是客户在app上购买套餐以后,Google自动推送消息到Java后端,然后Java后端通过订单的token获取订单信息,保存到数据库。

Java后端要获取订单信息,除了一个订单token还不够,还需要通过goole的oauth验证拿到一个accessToken才行,但是怎么才能获取到accessToken呢,

在这个过程中,我采坑无数,终于在伟大的同性交友网站GitHub上面找到了答案。

首先,网上的教程一般都是这样的:

  一. 在Google Developer Console中创建一个Oauth客户端ID,选择Web Application账户,得到client_id,client_secret 和 redirect_uri,这3个参数后边步骤常用到(此为前提)

二. 使用上一步获取到的参数获取Authorization code

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher
&response_type=code
&access_type=offline
&redirect_uri={REDIRECT_URIS}
&client_id={CLIENT_ID}

我们需要将这个URL以浏览器的形式打开,这时会跳出提示你Sign in with your Google Account,然后在用有project授权的谷歌账户登录,地址栏会出现我们所需的code。例如:https://www.example.com/oauth2callback?code=4/CpVOd8CljO_gxTRE1M5jtwEFwf8gRD44vrmKNDi4GSS.kr-GHuseD-oZEnp6UADFXm0E0MD3FlAI

三. 利用code 获取access_token,refresh_token

https://accounts.google.com/o/oauth2/token?
code={CODE}
&client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&redirect_uri={REDIRECT}
&grant_type=authorization_code
 

我们这一步的目的是获取refresh_token,只要有了这个长效token,access_token是随时可以获取的,第一次发起请求得到的JSON字符串如下所示,以后再请求将不再出现refresh_token,要保存好。expires_in是指access_token的时效,为3600秒。

{
    "access_token": "ya29.3gC2jw5vm77YPkylq0H5sPJeJJDHX93Kq8qZHRJaMlknwJ85595eMogL300XKDOEI7zIsdeFEPY6zg",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1/FbQD448CdDPfDEDpCy4gj_m3WDr_M0U5WupquXL_o"
}

四. 进一步可利用refresh_token获取新的access_token

https://accounts.google.com/o/oauth2/token?
grant_type=refresh_token
&client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&refresh_token={REFRESH_TOKEN}

五. 使用access_token 调用Google API 达到最终目的(如果access_token过时,回到第四步)

  然后使用accessToken即可调用谷歌的API了,例如要查看订单的购买信息

  https://www.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionName}/tokens/ {purchaseToken}?access_token={accessToken}

然后看起来是不是很简单,很方便?

可是我要在Java后端实现这些操作,却没有想象中这么简单了,首先第一步,获取Authorization code这块就犯了难,后端发起一个请求倒是不难,可是用有project授权的谷歌账户登录授权,这块真的是想大喊一声:臣妾做不到啊!

那还能怎么办?当然不能就此罢休,在这里还是感谢万能的度娘,终于让我发现了plan B:

创建一个服务账户,通过账户的秘钥来获取token,简要步骤如下:

1.获取服务账户 Service Account

2.创建访问程序,加载Service Account文件,获取token并访问请求API

是不是很简单?很nice?来来我们上图说话:

1.首先选中我们创造的Oauth客户端,然后点击创建服务账户秘钥

2.选择app Engine,Json格式

3.ok了,然后我们再使用如下的代码加载服务账户,获取accessToken

import java.io.FileInputStream;
import java.util.Arrays;
import java.util.List; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; public class GoogleOAuth2ServiceAccountSample { /** OAuth 2.0 scopes. 重要,规范访问者的查看范围*/
private static final List<String> SCOPES = Arrays.asList(
"https://www.googleapis.com/auth/androidpublisher"); public static void main(String[] args) {
try {
// 根据Service Account文件构造认证实例 GoogleCredential
GoogleCredential credential = GoogleCredential
.fromStream(new FileInputStream(
"Google_Wallet-94e38f1f23f7.json"))// 加载服务帐户认证文件
.createScoped(SCOPES); // 刷新token
credential.refreshToken(); // 获取token
System.out.println(credential.getAccessToken()); } catch (Exception e) {
e.printStackTrace();
}
}
}

4.这样我们就能获取到accessToken了吗?是的,我拿到了心心念念的accessToken,可是在我使用token获取订单详情的时候,却报错了。

  "errors": [
{
"domain": "androidpublisher",
"reason": "permissionDenied",
"message": "The current user has insufficient permissions to perform the requested operation."
}
],
"code": 401,
"message": "The current user has insufficient permissions to perform the requested operation."
}
}

说实话到这一步,我已经有些气馁了,谷歌的官方文档看了N遍,也没有头绪,到底该怎么办呢?

没办法,只能继续google查找资料,毕竟国外的资料可能多一些,直到我看到这一篇文章:

5.好吧 看到这块我是如获至宝,赶紧操作起来,打开https://play.google.com/apps/publish,邀请我注册的服务账户的邮箱,并给予管理订单和查看财务数据的权限,ok!

6.然后就ok了吗?我兴致勃勃的又试了一下请求获取订单详情的api,oh no!又是该死的报错,和上面的一模一样:

  "errors": [
{
"domain": "androidpublisher",
"reason": "permissionDenied",
"message": "The current user has insufficient permissions to perform the requested operation."
}
],
"code": 401,
"message": "The current user has insufficient permissions to perform the requested operation."
}
}
7.我彻底无奈了,短短的三天已经过去了,对于这么简单的小功能我却拿不下,实在有点羞愧难当,难道就这样放弃了吗?不!我永不言败!
然后就继续常规操作,google用英文关键字查询,虽然本人英文很渣,但是靠着强大的谷歌翻译,还是能看懂七八分,哈哈!功夫不负有心人,靠着我顽强的毅力
终于让我看到了一个答案:
what?
谷歌,你是我哥,真的!还有这种操作,账户更改需要24小时才能生效,word妈!
好吧 ,既然故事到了这里,我就只能等吧,不就是24小时么,就当是我程序员的长安十二时辰了,我等!
一夜无眠。
翌日,我熟悉的启动IDEA,启动tomcat,心要跳到了嗓子眼,这一刻时间仿佛凝固了,我发起请求,一串期待已久的json字符串出现在我眼前:
{
"kind": "androidpublisher#subscriptionPurchase",
"startTimeMillis": "1564021170426",
"expiryTimeMillis": "1564023267679",
"autoRenewing": false,
"priceCurrencyCode": "HKD",
"priceAmountMicros": "949000000",
"countryCode": "HK",
"developerPayload": "",
"cancelReason": 1,
"orderId": "GPA.3309-8698-7096-01554..5",
"purchaseType": 0,
"acknowledgementState": 1
}
真的是激动的心,颤抖的手,就问兄弟你有没有!哈哈!我可以仰天大笑出门去,我辈岂是蓬蒿人!
快哉!!!!
好了,这个问题就告一段落了,在这里在标注一下这个过程常见的一些问题,如果有道友也遇到,希望可以解忧!

问题1: projectNotLinked

{
    "error": {
        "errors": [
            {
                "domain": "androidpublisher",
                "reason": "projectNotLinked",
                "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
            }
        ],
        "code": 403,
        "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
    }
}

在这个页设置关联:https://play.google.com/apps/publish/

ps:注意登陆账户必须是app所有者

Google Play Developer Console

1.  "Google Play Developer Console" > "Settings" > subcategory "API access".

2.  Make a link to your "Linked Project".

3.  "Service Account" place maybe already showing ur "Service account" CLIENT ID which made "google developer console".

 

程序员的长安十二时辰:Java实现从Google oauth2.0认证调用谷歌内部api的更多相关文章

  1. DOS程序员手册(十二)

    DOS可安全使用 610页 在DOS控制台I/O操作进行轮询循环时,有规律地调用中断,以便允许终止 并驻留(TSR)程序(如适用于DOS的实用程序PRINT.COM),知道它可安全 地使用文件操作和其 ...

  2. 20155301第十二周java课程程序

    20155301第十二周java课程程序 内容一:在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常,错误和边界情况 String类 charAt split Ar ...

  3. 程序员带你十天快速入门Python,玩转电脑软件开发(二)

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  4. 程序员带你十天快速入门Python,玩转电脑软件开发(一)

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  5. JAVA程序员必看的15本书-JAVA自学书籍推荐

    作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...

  6. 程序员带你十天快速入门Python,玩转电脑软件开发(四)

    本系列文章立志于从一个已经习得一门编程语言的基础之上,全面介绍Python的相关开发过程和相关经验总结.本篇文章主要是基于上一篇的程序员带你十天快速入门Python,玩转电脑软件开发(三)的基础之上, ...

  7. 程序员带你十天快速入门Python,玩转电脑软件开发(三)

    声明:本次教程主要适用于已经习得一门编程语言的程序员.想要学习第二门语言.有梦想,立志做全栈攻城狮的你 . 如果是小白,也可以学习本教程.不过可能有些困难.如有问题在文章下方进行讨论.或者添加QQ群5 ...

  8. 三十二、Java图形化界面设计——布局管理器之CardLayout(卡片布局)

    摘自 http://blog.csdn.net/liujun13579/article/details/7773945 三十二.Java图形化界面设计--布局管理器之CardLayout(卡片布局) ...

  9. 学Java的前景与就业,资深程序员教你怎么开始学Java!

    IT行业一直是就业的热门岗位,程序员这个职业稳定性和收入比都有着不错的前景,那么学Java的前景和就业是什么样的呢?随着入行Java的准程序员越来越多,各种学习Java的流派也层出不穷!其实在编程的世 ...

随机推荐

  1. Hadoop集群(第3期)机器信息分布表

    1.分布式环境搭建 采用4台安装Linux环境的机器来构建一个小规模的分布式集群. 图1 集群的架构 其中有一台机器是Master节点,即名称节点,另外三台是Slaver节点,即数据节点.这四台机器彼 ...

  2. SYN1610型B码时统设备

       SYN1610型B码时统设备 产品概述 SYN1610型B码时统设备是由西安同步电子科技有限公司精心设计.自行研发生产的一款模块化配置的通用性时统终端,可接收北斗/ GPS信号/ IRIG-B码 ...

  3. Spring特点

    1.非侵入式所谓非侵入式是指,Spring框架的API不会在业务逻辑上出现,即业务逻辑是POJO(Plain Old Java Objects).由于业务逻辑中没有Spring的API,所以业务逻辑可 ...

  4. HTML连载12-体验CSS

    一.通过标签来修改标签有哪些缺点: (1)需要记忆那些标签有哪些属性 (2)若该标签没有这个属性,则修改失败 (3)需求变更,需要修改大量的代码 (4)HTML标签及用于添加语义,与我们的定义不相符 ...

  5. Python基础(七) 闭包与装饰器

    闭包的定义 闭包是嵌套在函数中的函数. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用. 闭包格式: def func(): lst=[] def inner(a): lst.append(a ...

  6. JAVA8之lambda表达式详解

    原文:http://blog.csdn.net/jinzhencs/article/details/50748202 lambda表达式详解 一.问题 1.什么是lambda表达式? 2.lambda ...

  7. Flume —— 安装部署

    一.前置条件 Flume需要依赖JDK 1.8+,JDK安装方式见本仓库: Linux环境下JDK安装 二 .安装步骤 2.1 下载并解压 下载所需版本的Flume,这里我下载的是CDH版本的Flum ...

  8. 如何用 Flutter 实现混合开发?闲鱼公开源代码实例

    Flutter: 必火,转两篇软文预热哈哈~ 中文网: https://flutterchina.club/get-started/test-drive/ 如何用 Flutter 实现混合开发?闲鱼公 ...

  9. CSS3背景与渐变

    一.CSS3 背景图像区域 background-clip(指定背景绘制区域) ackground-clip: border-box / padding-box / content-box; /*没有 ...

  10. [apue] popen/pclose 疑点解惑

    问题请看这里: [apue] 使用 popen/pclose 的一点疑问 当时怀疑是pclose关闭了使用完成的管道,因此在pclose之前加一个足够长的sleep,再次观察进程文件列表: 哈哈,这下 ...