Golang服务端对接Google Play结算系统订阅
Google订阅
公司产品需要需对Google订阅,查了很多资料和相关文档,最终总结出以下内容。如果本文中存在任何不准确的地方,请不吝指出,我会尽快改正。
Google相关文档:
1. 配置
- 在应用配置页,点击创收设置→商品→订阅,给对应的应用新增订阅内容及基础方案
- 前往API和服务,点击凭据,点击创建凭据,选择服务账号,填充相关信息创建一个服务账号
- 在Pub/Sub配置页,创建Pub/Sub主题和订阅
- 在对应主题中,给谷歌-play-developer-notifications@system.gserviceaccount.com添加Pub/Sub Publisher权限,一定是这个账号,这个是Google官方的服务账号,别搞错了,只有给它授权了才能发送商品订阅的消息到对应主题。同时在订阅处,给之前的服务账号授权Pub/Sub Subscriber。
- 在IAM处,给之前的服务账号授予Viewer的角色。
- 前往Play管理中心,点击 设置→API权限→服务账号,在之前创建的服务账号旁点击查看Play管理中心权限,在应用权限上配置对应应用的权限,并在财务数据→查看财务数据打勾
- 前往Play管理中心,点击设置→API权限→API,启用Google Play Android Developer API。前往Google Play Android Developer API配置页,点击凭据,在下方的服务账号,找到之前创建的服务账号,点击修改进入到修改页面,点击密钥,再点击添加密钥→创建新密钥,选择JSON,点击创建,保管好这份JSON文件,服务端调用API时需要使用这份文件中的配置来初始化Client。
- 回到应用配置页,找到对应的应用,点击创收设置,配置发送通知的主题名称,可以点击发送测试通知。如果是推送订阅,我们配置的端点地址将会收到通知;如果是拉取订阅,我们可以在Pub/Sub配置页找到对应的订阅进行消息拉取。
2. 相关枚举介绍
2.1 谷歌回调一次性购买通知类型
参考文档:https://developer.android.com/谷歌/play/billing/rtdn-reference#one-time
通知类型 | 枚举值 | 说明 |
---|---|---|
ONE_TIME_PRODUCT_PURCHASED | 1 | 用户成功购买了一次性商品。 |
ONE_TIME_PRODUCT_CANCELED | 2 | 用户已取消待处理的一次性商品购买交易。 |
// OneTimeProductNotificationType 谷歌回调一次性购买通知类型
// 参考文档:https://developer.android.com/谷歌/play/billing/rtdn-reference#one-time
type OneTimeProductNotificationType int
const (
OneTimeProductNotificationTypePurchased OneTimeProductNotificationType = iota 1
OneTimeProductNotificationTypeCanceled
)
2.2 谷歌回调订阅通知类型
参考文档:https://developer.android.com/谷歌/play/billing/rtdn-reference#sub
通知类型 | 枚举值 | 说明 |
---|---|---|
SUBSCRIPTION_RECOVERED | 1 | 恢复订阅。从账号保留状态恢复订阅。 |
SUBSCRIPTION_RENEWED | 2 | 续订。 |
SUBSCRIPTION_CANCELED | 3 | 订阅取消。指的是用户手动进行的订阅取消操作。 |
SUBSCRIPTION_PURCHASED | 4 | 新订阅 |
SUBSCRIPTION_ON_HOLD | 5 | 订阅已进入帐号保留状态。一般是用户的付款信息存在问题且已经任何关联的宽限期都结束时发生的。 |
SUBSCRIPTION_IN_GRACE_PERIOD | 6 | 订阅已进入宽限期。宽限期指的是订阅周期结束之后的一段时间内提供的额外时间,可选是否启用。 |
SUBSCRIPTION_RESTARTED | 7 | 到期之前恢复订阅。 |
SUBSCRIPTION_PRICE_CHANGE_CONFIRMED | 8 | 用户已成功确认订阅价格变动。表示业务方对订阅价格进行了更改,并且用户已经确认接受新价格。 |
SUBSCRIPTION_DEFERRED | 9 | 续订时间延期。指的是订阅到期前,由于付款方式问题等原因导致续订付款失败。 |
SUBSCRIPTION_PAUSED | 10 | 订阅已暂停。表示用户已经暂停了订阅,可选是否启用。 |
SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED | 11 | 订阅暂停计划已更改。表示用户已经更改了暂停订阅的计划,依赖暂停功能启用。 |
SUBSCRIPTION_REVOKED | 12 | 订阅撤销。系统出于各种原因撤消用户的订阅,包括服务的主动调接口或购买交易被退款等。 |
SUBSCRIPTION_EXPIRED | 13 | 订阅过期。 |
// SubscriptionNotificationType 谷歌回调订阅通知类型
// 参考文档:https://developer.android.com/谷歌/play/billing/rtdn-reference#sub
type SubscriptionNotificationType int
func (s SubscriptionNotificationType) ToInt() int {
return int(s)
}
const (
SubscriptionNotificationTypeRecovered SubscriptionNotificationType = iota 1 // 从账号保留状态恢复订阅
SubscriptionNotificationTypeRenewed // 续订
SubscriptionNotificationTypeCanceled // 订阅取消 指的是用户手动进行的订阅取消操作
SubscriptionNotificationTypePurchased // 新订阅
SubscriptionNotificationTypeAccountHold // 订阅已进入帐号保留状态,一般是用户的付款信息存在问题且已经任何关联的宽限期都结束时发生的。
SubscriptionNotificationTypeGracePeriod // 订阅已进入宽限期。宽限期指的是订阅周期结束之后的一段时间内提供的额外时间。(可选是否启用)
SubscriptionNotificationTypeRestarted // 处理到期之前恢复订阅
SubscriptionNotificationTypePriceChangeConfirmed // 用户已成功确认订阅价格变动,表示业务方对订阅价格进行了更改,并且用户已经确认接受新价格。
SubscriptionNotificationTypeDeferred // 续订时间延期 指的是订阅到期前,由于付款方式问题等原因导致续订付款失败
SubscriptionNotificationTypePaused // 订阅已暂停,表示用户已经暂停了订阅。(可选是否启用)
SubscriptionNotificationTypePauseScheduleChanged // 订阅暂停计划已更改,表示用户已经更改了暂停订阅的计划。(依赖暂停功能启用)
SubscriptionNotificationTypeRevoked // 订阅撤销 系统出于各种原因撤消用户的订阅,包括服务的主动调接口或购买交易被退款等
SubscriptionNotificationTypeExpired // 订阅过期。
)
2.3 订阅确认状态
通知类型 | 枚举值 | 说明 |
---|---|---|
ACKNOWLEDGEMENT_STATE_UNSPECIFIED | ACKNOWLEDGEMENT_STATE_UNSPECIFIED | 未指定的确认状态 |
ACKNOWLEDGEMENT_STATE_PENDING | ACKNOWLEDGEMENT_STATE_PENDING | 订阅尚未确认 |
ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED | ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED | 订阅已确认 |
// AcknowledgementState 订阅的确认状态
// 参考文档:https://developers.谷歌.com/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2?hl=zh-cn#AcknowledgementState
type AcknowledgementState string
func (g AcknowledgementState) String() string {
return string(g)
}
const (
AcknowledgementStateUnspecified AcknowledgementState = "ACKNOWLEDGEMENT_STATE_UNSPECIFIED" // 未指定的确认状态
AcknowledgementStatePending AcknowledgementState = "ACKNOWLEDGEMENT_STATE_PENDING" // 订阅尚未确认
AcknowledgementStateAcknowledged AcknowledgementState = "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED" // 订阅已确认
)
2.4 订阅状态
通知类型 | 枚举值 | 说明 |
---|---|---|
SUBSCRIPTION_STATE_UNSPECIFIED | SUBSCRIPTION_STATE_UNSPECIFIED | 未指定订阅状态。 |
SUBSCRIPTION_STATE_PENDING | SUBSCRIPTION_STATE_PENDING | 订阅已创建,但在注册期间正在等待付款。在此状态下,所有商品都正在等待付款。 |
SUBSCRIPTION_STATE_ACTIVE | SUBSCRIPTION_STATE_ACTIVE | 订阅处于有效状态。- (1) 如果订阅是自动续订方案,则至少有一个项目已自动续订且未过期。- (2) 如果订阅是预付费方案,至少有一项不会过期。 |
SUBSCRIPTION_STATE_PAUSED | SUBSCRIPTION_STATE_PAUSED | 订阅已暂停。仅当订阅是自动续订方案时,这个状态才可用。在此状态下,所有内容都会处于暂停状态。 |
SUBSCRIPTION_STATE_IN_GRACE_PERIOD | SUBSCRIPTION_STATE_IN_GRACE_PERIOD | 订阅处于宽限期。仅当订阅是自动续订方案时,这个状态才可用。在此状态下,所有内容都处于宽限期。 |
SUBSCRIPTION_STATE_ON_HOLD | SUBSCRIPTION_STATE_ON_HOLD | 订阅处于暂停状态(已暂停)。仅当订阅是自动续订方案时,这个状态才可用。在此状态下,所有内容都会处于保全状态。 |
SUBSCRIPTION_STATE_CANCELED | SUBSCRIPTION_STATE_CANCELED | 订阅已取消,但尚未到期。仅当订阅是自动续订方案时,这个状态才可用。所有内容的 autoRenewEnabled 都设为 false。 |
SUBSCRIPTION_STATE_EXPIRED | SUBSCRIPTION_STATE_EXPIRED | 订阅已过期。所有项的过期时间均为过去时间。 |
// GoogleSubscriptionState 谷歌订阅状态
// 参考文档:https://developers.谷歌.cn/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2?hl=zh-cn#SubscriptionState
type GoogleSubscriptionState string
func (g GoogleSubscriptionState) String() string {
return string(g)
}
const (
GoogleSubscriptionStateUnspecified GoogleSubscriptionState = "SUBSCRIPTION_STATE_UNSPECIFIED" // 未指定订阅状态。
GoogleSubscriptionStatePending GoogleSubscriptionState = "SUBSCRIPTION_STATE_PENDING" // 订阅已创建,但在注册期间正在等待付款。在此状态下,所有商品都正在等待付款。
GoogleSubscriptionStateActive GoogleSubscriptionState = "SUBSCRIPTION_STATE_ACTIVE" // 订阅处于有效状态。- (1) 如果订阅是自动续订方案,则至少有一个项目已自动续订且未过期。- (2) 如果订阅是预付费方案,至少有一项不会过期。
GoogleSubscriptionStatePaused GoogleSubscriptionState = "SUBSCRIPTION_STATE_PAUSED" // 订阅已暂停。仅当订阅是自动续订方案时,这个状态才可用。在此状态下,所有内容都会处于暂停状态。
GoogleSubscriptionStateInGracePeriod GoogleSubscriptionState = "SUBSCRIPTION_STATE_IN_GRACE_PERIOD" // 订阅处于宽限期。仅当订阅是自动续订方案时,这个状态才可用。在此状态下,所有内容都处于宽限期。
GoogleSubscriptionStateOnHold GoogleSubscriptionState = "SUBSCRIPTION_STATE_ON_HOLD" // 订阅处于暂停状态(已暂停)。仅当订阅是自动续订方案时,这个状态才可用。在此状态下,所有内容都会处于保全状态。
GoogleSubscriptionStateCanceled GoogleSubscriptionState = "SUBSCRIPTION_STATE_CANCELED" // 订阅已取消,但尚未到期。仅当订阅是自动续订方案时,这个状态才可用。所有内容的 autoRenewEnabled 都设为 false。
GoogleSubscriptionStateExpired GoogleSubscriptionState = "SUBSCRIPTION_STATE_EXPIRED" // 订阅已过期。所有项的过期时间均为过去时间。
)
3. 相关库及其使用
3.1 获取官方库
go get 谷歌.golang.org/api/androidpublisher/v3
3.2 初始化service
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cli)
conf, err := 谷歌.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
if err != nil {
return
}
service, err = androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
if err != nil {
return
}
3.3 查询订阅信息v1
参考文档:https://developers.谷歌.cn/android-publisher/api-ref/rest/v3/purchases.subscriptions/get?hl=zh-cn
subscriptionInfoV1, err = androidpublisher.NewPurchasesSubscriptionsService(handler.androidPublisherService).Get(packageName, subscriptionId, token).Context(handler.ctx).Do()
if err != nil {
return
}
3.4 查询订阅信息v2
参考文档:https://developers.谷歌.cn/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2/get?hl=zh-cn
subscriptionInfoV2, err = androidpublisher.NewPurchasesSubscriptionsv2Service(handler.androidPublisherService).Get(packageName, token).Context(handler.ctx).Do()
if err != nil {
return
}
3.5 确认订阅
err = handler.androidPublisherService.Purchases.Subscriptions.Acknowledge(packageName,subscriptionId,purchaseToken,&androidpublisher.SubscriptionPurchasesAcknowledgeRequest{},).Do()
if err != nil {
return
}
3.6 校验回调身份
参考文档:https://cloud.谷歌.com/pubsub/docs/push?hl=zh-cn#validate_tokens
authHeader := r.Header.Get("Authorization")
if authHeader == "" || len(strings.Split(authHeader, " ")) != 2 {
err = errors.New("missing Authorization header")
return
}
token := strings.Split(authHeader, " ")[1]
v, err := idtoken.NewValidator(handler.ctx, option.WithHTTPClient(&http.Client{
Timeout: time.Second * time.Duration(10),
}))
if err != nil {
return
}
payload, err := v.Validate(handler.ctx, token, "my_endpoint")
if err != nil {
return
}
if payload.Issuer != "accounts.谷歌.com" && payload.Issuer != "https://accounts.谷歌.com" {
err = errors.New("wrong issuer")
return
}
if payload.Claims["email"] != "ClientEmail" || payload.Claims["email_verified"] != true {
err = errors.New("unexpected email identity")
return
}
3.7 回调信息结构
参考文档1:https://developer.android.com/谷歌/play/billing/rtdn-reference?hl=zh-cn#encoding
参考文档2:https://developer.android.com/谷歌/play/billing/rtdn-reference#json_specification
// GoogleNotification 谷歌回调信息结构体
// 参考文档:https://developer.android.com/谷歌/play/billing/rtdn-reference?hl=zh-cn#encoding
type GoogleNotification struct {
Message GoogleNotificationMessage `json:"message"`
Subscription string `json:"subscription"`
}
type GoogleNotificationMessage struct {
Data string `json:"data"`
MessageID string `json:"messageId"`
PublishTime time.Time `json:"publishTime"`
}
// DeveloperNotification 谷歌回调信息荷载
// 参考文档:https://developer.android.com/谷歌/play/billing/rtdn-reference#json_specification
type DeveloperNotification struct {
Version string `json:"version"`
PackageName string `json:"packageName"`
EventTimeMillis string `json:"eventTimeMillis"`
SubscriptionNotification *SubscriptionNotification `json:"subscriptionNotification,omitempty"`
OneTimeProductNotification *OneTimeProductNotification `json:"oneTimeProductNotification,omitempty"`
TestNotification *TestNotification `json:"testNotification,omitempty"`
}
// SubscriptionNotification 谷歌回调订阅通知信息结构体
type SubscriptionNotification struct {
Version string `json:"version"`
NotificationType enum.SubscriptionNotificationType `json:"notificationType,omitempty"`
PurchaseToken string `json:"purchaseToken,omitempty"`
SubscriptionID string `json:"subscriptionId,omitempty"`
}
// OneTimeProductNotification 谷歌回调一次性购买通知信息结构体
type OneTimeProductNotification struct {
Version string `json:"version"`
NotificationType enum.OneTimeProductNotificationType `json:"notificationType,omitempty"`
PurchaseToken string `json:"purchaseToken,omitempty"`
SKU string `json:"sku,omitempty"`
}
// TestNotification 通过Google Play开发者控制台发送的通知信息结构体
type TestNotification struct {
Version string `json:"version"`
}
4. 如何测试
-
测试的谷歌账号必须绑定过银行卡或者信用卡,否则无法使用
-
将对应的谷歌账号设置为许可测试人员,官方文档:使用应用许可来测试应用内购结算功能
-
测试订阅在续订速度上比实际订阅快,最多可续订六次。相关文档:测试订阅专用功能
基于时间的订阅功能
功能 测试期 购买交易确认 5 分钟 免费试用 3 分钟 初次体验价周期 与订阅测试周期相同 宽限期(3 天和 7 天) 5 分钟 帐号保留功能 10 分钟 暂停(1 个月) 5 分钟 暂停(2 个月) 10 分钟 暂停(3 个月) 15 分钟 续订期
生产订阅期 测试订阅续订 1 周 5 分钟 1 个月 5 分钟 3 个月 10 分钟 6 个月 15 分钟 1 年 30 分钟
5. 注意事项
- 订阅暂停功能如不需要可以在应用配置页,找到对应的应用,点击创收设置,在上方订阅设置处将其关闭。
- 在应用配置页,点击创收设置→商品→订阅,可进行宽限期的设置,如不需要可直接设置成无宽限期。
- 在重新订阅时,是区分到期之前恢复和到期之后重新订阅的,到期之后重新订阅会要求用户打开应用,而处理此类购买交易的方式与处理其他应用外购买相同,也就是说,客户端需要监听并通知到后端发生了重新订阅这个事件。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfaihi
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01