Google Play 内购实现

本文写于 2024-1-26

接上一次苹果内购记录-CSDN博客,我又接了一把 Google Play 内购。

Google Play 内购有啥好说的?一般人都不用。

只能说时代变了,没想到啊没想到,Google Play 这个浓眉大眼的的家伙也叛变了。

付款 - Play 管理中心帮助 (google.com)

你看,真叛变了吧?

1. 准备事项

  1. Google Play 账号
  2. Google Cloud 账号
  3. 付款信息
  4. 提供一个账号给开发
  5. 配置内购产品信息:名称和价格这些属性

嘿嘿,以上这些事情统统交给产品。如果没有产品,那就交给老板吧。

如果他们不愿意,和他们说的时候带上这个

3. 开搞前缕缕思路

思路和苹果内购应该是差不多的,毕竟苹果内购"珠玉在前"嘛。

和苹果内购一样,这里最关键的一步是:【完成交易】。如果这一步出问题,那就是用户给钱了,但没给用户发服务。和苹果一脉相承的掉单问题。老规矩:

第一步:穿上西装站直 第二步:弯腰90度 第三步:虔诚地说出:su mi ma sen 第四步:请不要着急,如果没收到权益,3天后自动给您退款

Google Play 这波是被掉单搞怕了,哈哈。

言归正传:

第一步发现问题:

  • 用户写邮件来对线了
  • 每天查一下 Google Play 后台交易成功的数量和后台数据库交易成功的数量能不能对上
  • 客户端和服务端加上日志

第二步排查问题:

  • 用户找你的时候要他提供订单号,然后可以通过Google Play 后台查询到这笔订单的信息
  • Google Play 后台数据和服务后台数据对不上的时候赶紧查日志去

第三步解决问题:

  • 客户端做另外的掉单处理,比如说轮训查还有没有交易是没完成的,然后重新提交服务端
  • 手动给用户补发
  • 退款(Google Play 后台可以退,终于不用被别人掐住脖子了)

4. 差不多了,开搞

官方整合文档:Google Play 结算系统 | Google Play's billing system | Android Developers

4.1 准备

  • 创建一个 Google Cloud Project,并启用相关API和服务
  • Google Play 应用关联 Google Cloud Project
  • Google Cloud 创建ServiceAccount服务账号
  • Google Play 添加服务账号,并分配权限
  • Google Cloud Pub/Sub创建主题以及订阅
  • Google Play 启用实时通知,并配置Google Cloud 中创建的主题
  1. 创建Google Cloud Project,并启用相关API和服务
  1. Google Play 中关联 Google Cloud Project
  1. Google Cloud 创建ServiceAccount服务账号

创建完账号,把密钥也创建好,下载到本地

  1. Google Play 添加服务账号,并分配权限

把创建好的账号填进来

这一步完成,其实就可以使用服务账号的密钥信息调用 Google Play API了。下面只是为了接收Google Play 通知的时候用的,不用的可以不看,当然我建议你看看,不然用户退款的时候,我估计老板会找你。

Google Play 把产品购买分成两种类型:

  • 一次性购买
  • 订阅

对于一次性购买来说,实时通知只是为了接收退款的回调,购买成功是没有回调的,喵的,文档里面写了有事件,实际又没发。这个破问题了困扰了我一天,最后社区问到的答案。

RTDN messages are not sent to Topic when using Test licences - Google Play Developer Community(*...+#@>此处省略1800个字)

对于订阅来说,不说了。我没做订阅,你自己去看吧。

订阅生命周期 | Google Play's billing system | Android Developers

  1. Google Cloud Pub/Sub创建主题以及订阅

给Google Play 固定的服务账号加权限,注意这个账号是一个固定的,不是刚刚创建的哪个服务账号 google-play-developer-notifications@system.gserviceaccount.com

  1. Google Play 启用实时通知,并配置Google Cloud 中创建的主题

点击发送测试通知,测试一下。然后在 Pub/Sub 中看看能不能拉取到消息

没问题之后,把订阅的类型改成推送,这样就能通过http发送到我们的服务了。

重试策略看需要调整,感觉退避算法合理点。这个自己选择,也就是出问题你自己背锅的意思。

4.2 交易凭证验证

准备就绪,正片上代码。

  1. 依赖导入
xml 复制代码
<dependency>  
    <groupId>com.google.auth</groupId>  
    <artifactId>google-auth-library-oauth2-http</artifactId>  
    <version>1.19.0</version>  
</dependency>  
<dependency>  
    <groupId>com.google.apis</groupId>  
    <artifactId>google-api-services-androidpublisher</artifactId>  
    <version>v3-rev20231115-2.0.0</version>  
</dependency>
  1. 配置和配置类
properties 复制代码
google-play.packageName=com.xxx.xxx
# 这个json就是刚刚创建服务账号密钥的时候下载的
google-play.serviceAccountJson=xxxxx.json
java 复制代码
@Data  
@Configuration  
@ConfigurationProperties(prefix = "google-play")  
public class GooglePlayConfig {  
    private String packageName;  
    private String serviceAccountJson;  
  
    @Bean  
    public GoogleCredentials googleCredentials() throws IOException {  
	    // 懒得搞配置文件了,直接丢resources读进来
        return GoogleCredentials.fromStream(new ClassPathResource(serviceAccountJson).getInputStream())  
                .createScoped(AndroidPublisherScopes.ANDROIDPUBLISHER);  
    }  
  
    @Bean  
    public AndroidPublisher androidPublisher(GoogleCredentials credentials) throws IOException, GeneralSecurityException {  
        return new AndroidPublisher.Builder(  
                GoogleNetHttpTransport.newTrustedTransport(),  
                GsonFactory.getDefaultInstance(),  
                new HttpCredentialsAdapter(credentials)  
        ).setApplicationName(packageName).build();  
    }  
}
  1. 验签逻辑,其实就是根据凭证查下这笔单在不在
java 复制代码
@Component  
@Slf4j  
public class GooglePlayComponent {  
    @Resource  
    private GooglePlayConfig googlePlayConfig;
    @Resource  
    private AndroidPublisher androidPublisher;// 注入进来咔咔用  
  
    public ProductPurchase productPurchase(String sku, String purchaseToken) {  
        try {  
            return androidPublisher  
                    .purchases().products()  
                    .get(googlePlayConfig.getPackageName(), sku, purchaseToken)  
                    .execute();  
        } catch (IOException e) {  
            log.error("failed to query product purchase. {}", purchaseToken, e);  
            ServiceException.throwInternalServerEx("failed to query product purchase:" + purchaseToken);  
            return null;  
        }  
    }  
}

4.3 接收回调

java 复制代码
@PostMapping("/google_play_webhook")
public Object googlePlayWebhook(@RequestBody String body) {
    log.info("Google play subscription webhook: {}", body);

    if (StringUtils.isBlank(body)) {
        log.warn("Google play subscription webhook body is EMPTY");
        return ResponseEntity.status(400).body("Empty body");
    }

    DeveloperNotification developerNotification;
    try {
        developerNotification = JacksonUtils.parseJson(body, DeveloperNotification.class);
    } catch (Exception e) {
        log.error("failed to parse body. {}", body, e);
        return ResponseEntity.status(400).body(e.getMessage());
    }
	// TODO 处理过程自己写去

    return ResponseEntity.ok().body("OK");
}

Ref

官方集成文档:做好准备 | Google Play's billing system | Android Developers

回调的一些Bean代码:blog.csdn.net/jack2350536...

一次性购买无法收到回调问题: support.google.com/googleplay/...

相关推荐
艾伦~耶格尔1 小时前
Spring Boot 三层架构开发模式入门
java·spring boot·后端·架构·三层架构
man20172 小时前
基于spring boot的篮球论坛系统
java·spring boot·后端
攸攸太上2 小时前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway
罗曼蒂克在消亡3 小时前
graphql--快速了解graphql特点
后端·graphql
潘多编程3 小时前
Spring Boot与GraphQL:现代化API设计
spring boot·后端·graphql
大神薯条老师3 小时前
Python从入门到高手4.3节-掌握跳转控制语句
后端·爬虫·python·深度学习·机器学习·数据分析
2401_857622664 小时前
Spring Boot新闻推荐系统:性能优化策略
java·spring boot·后端
知否技术4 小时前
为什么nodejs成为后端开发者的新宠?
前端·后端·node.js
AskHarries4 小时前
如何优雅的处理NPE问题?
java·spring boot·后端
计算机学姐5 小时前
基于SpringBoot+Vue的高校运动会管理系统
java·vue.js·spring boot·后端·mysql·intellij-idea·mybatis