修改路径

This commit is contained in:
ayflying
2025-02-28 17:45:44 +08:00
parent 9f337df9de
commit 74a746bc47
29 changed files with 7 additions and 7 deletions

View File

@@ -0,0 +1,40 @@
package playstore
// GetStatus 获取产品的状态,例如产品是否处于活跃状态。
//
// 返回值 EProductStatus 代表产品状态。
// 可能的状态包括:
//
// ProductStatus_Unspecified // 未指定状态。
// ProductStatus_active // 产品已发布且在商店中处于活跃状态。
// ProductStatus_inactive // 产品未发布,因此在商店中处于非活跃状态。
func (iap InAppProduct) GetStatus() EProductStatus {
return EProductStatus(iap.AndroidPublisherInAppProduct.Status)
}
// GetSubscriptionPeriod 获取订阅的周期。
//
// 返回值 ESubscriptionPeriod 代表订阅周期。
// 可能的周期包括:
//
// SubscriptionPeriod_Invalid : 无效的订阅(可能是消耗品)。
// SubscriptionPeriod_OneWeek (一周)。
// SubscriptionPeriod_OneMonth (一个月)。
// SubscriptionPeriod_ThreeMonths (三个月)。
// SubscriptionPeriod_SixMonths (六个月)。
// SubscriptionPeriod_OneYear (一年)。
func (iap InAppProduct) GetSubscriptionPeriod() ESubscriptionPeriod {
return ESubscriptionPeriod(iap.AndroidPublisherInAppProduct.SubscriptionPeriod)
}
// GetPurchaseType 获取产品的购买类型。
//
// 返回值 EPurchaseType 代表产品的购买类型。
// 可能的类型包括:
//
// EPurchaseType_Unspecified (未指定购买类型)。
// EPurchaseType_ManagedUser 可以被单次或多次购买(消耗品、非消耗品)。
// EPurchaseType_Subscription (应用内产品,具有周期性消费)。
func (iap InAppProduct) GetPurchaseType() EPurchaseType {
return EPurchaseType(iap.AndroidPublisherInAppProduct.PurchaseType)
}

View File

@@ -0,0 +1,34 @@
package playstore
// EProductStatus 定义了产品的状态,例如产品是否处于活跃状态。
type EProductStatus string
// 定义了产品可能的状态常量。
const (
ProductStatus_Unspecified EProductStatus = "statusUnspecified" // 未指定状态。
ProductStatus_active EProductStatus = "active" // 产品已发布且在商店中处于活跃状态。
ProductStatus_inactive EProductStatus = "inactive" // 产品未发布,因此在商店中处于非活跃状态。
)
// ESubscriptionPeriod 定义了订阅的周期。
type ESubscriptionPeriod string
// 定义了订阅可能的周期常量。
const (
SubscriptionPeriod_Invalid ESubscriptionPeriod = "" // 无效的订阅(可能是消耗品)。
SubscriptionPeriod_OneWeek ESubscriptionPeriod = "P1W" // 一周。
SubscriptionPeriod_OneMonth ESubscriptionPeriod = "P1M" // 一个月。
SubscriptionPeriod_ThreeMonths ESubscriptionPeriod = "P3M" // 三个月。
SubscriptionPeriod_SixMonths ESubscriptionPeriod = "P6M" // 六个月。
SubscriptionPeriod_OneYear ESubscriptionPeriod = "P1Y" // 一年。
)
// EPurchaseType 定义了产品的购买类型,例如周期性订阅。
type EPurchaseType string
// 定义了产品可能的购买类型常量。
const (
EPurchaseType_Unspecified EPurchaseType = "purchaseTypeUnspecified" // 未指定购买类型。
EPurchaseType_ManagedUser EPurchaseType = "managedUser" // 默认的产品类型 - 可以单次或多次购买(消耗品、非消耗品)。
EPurchaseType_Subscription EPurchaseType = "subscription" // 应用内具有周期性的产品。
)

View File

@@ -0,0 +1,80 @@
package playstore
import (
"context"
"google.golang.org/api/androidpublisher/v3"
)
// IABProduct 接口定义了商品服务的基本操作。
type IABProduct interface {
// VerifyProduct 验证指定的内购产品购买信息。
// ctx: 上下文,用于控制请求的取消、超时等。
// packageName: 应用包名。
// productId: 内购商品ID。
// purchaseToken: 购买凭证。
// 返回经过验证的购买信息和可能的错误。
VerifyProduct(context.Context, string, string, string) (*androidpublisher.ProductPurchase, error)
// AcknowledgeProduct 确认指定的内购产品的购买。
// ctx: 上下文。
// packageName: 应用包名。
// productId: 内购商品ID。
// purchaseToken: 购买凭证。
// orderId: 订单ID。
// 返回可能发生的错误。
AcknowledgeProduct(context.Context, string, string, string, string) error
}
// IABSubscription 接口定义了订阅服务的基本操作。
type IABSubscription interface {
// AcknowledgeSubscription 确认指定订阅的购买。
// ctx: 上下文。
// packageName: 应用包名。
// subscriptionId: 订阅ID。
// purchaseToken: 购买凭证。
// acknowledgeRequest: 确认请求参数。
// 返回可能发生的错误。
AcknowledgeSubscription(context.Context, string, string, string, *androidpublisher.SubscriptionPurchasesAcknowledgeRequest) error
// VerifySubscription 验证指定订阅的购买信息。
// ctx: 上下文。
// packageName: 应用包名。
// subscriptionId: 订阅ID。
// purchaseToken: 购买凭证。
// 返回经过验证的订阅购买信息和可能的错误。
VerifySubscription(context.Context, string, string, string) (*androidpublisher.SubscriptionPurchase, error)
// CancelSubscription 取消指定的订阅。
// ctx: 上下文。
// packageName: 应用包名。
// subscriptionId: 订阅ID。
// purchaseToken: 购买凭证。
// 返回可能发生的错误。
CancelSubscription(context.Context, string, string, string) error
// RefundSubscription 为指定的订阅办理退款。
// ctx: 上下文。
// packageName: 应用包名。
// subscriptionId: 订阅ID。
// purchaseToken: 购买凭证。
// 返回可能发生的错误。
RefundSubscription(context.Context, string, string, string) error
// RevokeSubscription 撤销指定的订阅。
// ctx: 上下文。
// packageName: 应用包名。
// subscriptionId: 订阅ID。
// purchaseToken: 购买凭证。
// 返回可能发生的错误。
RevokeSubscription(context.Context, string, string, string) error
}
// Client 结构体实现了 IABSubscription 接口,提供了具体的操作实现。
type Client struct {
service *androidpublisher.Service
}
// InAppProduct 结构体封装了 androidpublisher.InAppProduct并提供了一些辅助方法。
type InAppProduct struct {
AndroidPublisherInAppProduct *androidpublisher.InAppProduct
}

View File

@@ -0,0 +1,62 @@
package playstore
// SubscriptionNotificationType 定义了订阅通知的类型。
type SubscriptionNotificationType int
// 预定义的订阅通知类型。
const (
SubscriptionNotificationTypeRecovered SubscriptionNotificationType = iota + 1 // 订阅已恢复
SubscriptionNotificationTypeRenewed // 订阅已续订
SubscriptionNotificationTypeCanceled // 订阅已取消
SubscriptionNotificationTypePurchased // 订阅已购买
SubscriptionNotificationTypeAccountHold // 订阅账户暂停
SubscriptionNotificationTypeGracePeriod // 宽限期通知
SubscriptionNotificationTypeRestarted // 订阅已重新开始
SubscriptionNotificationTypePriceChangeConfirmed // 订阅价格变更已确认
SubscriptionNotificationTypeDeferred // 订阅延迟
SubscriptionNotificationTypePaused // 订阅已暂停
SubscriptionNotificationTypePauseScheduleChanged // 暂停计划已更改
SubscriptionNotificationTypeRevoked // 订阅已撤销
SubscriptionNotificationTypeExpired // 订阅已过期
)
// OneTimeProductNotificationType 定义了一次性产品通知的类型。
type OneTimeProductNotificationType int
// 预定义的一次性产品通知类型。
const (
OneTimeProductNotificationTypePurchased OneTimeProductNotificationType = iota + 1 // 一次性产品已购买
OneTimeProductNotificationTypeCanceled // 一次性产品已取消
)
// DeveloperNotification 是通过 Pub/Sub 主题发送给开发者的通知。
// 详细描述请参见https://developer.android.com/google/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 包含订阅状态通知类型、token 和订阅ID用于通过Google Android Publisher API确认状态。
type SubscriptionNotification struct {
Version string `json:"version"` // 版本号
NotificationType SubscriptionNotificationType `json:"notificationType,omitempty"` // 通知类型
PurchaseToken string `json:"purchaseToken,omitempty"` // 购买token
SubscriptionID string `json:"subscriptionId,omitempty"` // 订阅ID
}
// OneTimeProductNotification 包含一次性产品状态通知类型、token 和产品IDSKU用于通过Google Android Publisher API确认状态。
type OneTimeProductNotification struct {
Version string `json:"version"` // 版本号
NotificationType OneTimeProductNotificationType `json:"notificationType,omitempty"` // 通知类型
PurchaseToken string `json:"purchaseToken,omitempty"` // 购买token
SKU string `json:"sku,omitempty"` // 产品IDSKU
}
// TestNotification 是仅通过Google Play开发者控制台发送的测试发布通知。
type TestNotification struct {
Version string `json:"version"` // 版本号
}

View File

@@ -0,0 +1,118 @@
package playstore
import (
"context"
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"fmt"
"net/http"
"time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/androidpublisher/v3"
"google.golang.org/api/option"
)
// New 创建并返回一个包含访问androidpublisher API所需凭证的http客户端。
//
// @Description: 通过提供的JSON密钥创建一个配置好的Client实例可用于与Google Play Store API交互。
// @param jsonKey 用于构建JWT配置的JSON密钥字节切片。
// @return *Client 返回初始化好的Client实例。
// @return error 如果在创建过程中遇到任何错误则返回非nil的error。
func New(jsonKey []byte) (*Client, error) {
// 设置http客户端超时时间为10秒
c := &http.Client{Timeout: 10 * time.Second}
// 为context设置HTTP客户端以便在OAuth2流程中使用
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, c)
// 使用JSON密钥和所需范围配置JWT
conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
if err != nil {
return nil, err
}
// 验证JWT配置是否正确并获取访问令牌
val := conf.Client(ctx).Transport.(*oauth2.Transport)
_, err = val.Source.Token()
if err != nil {
return nil, err
}
// 使用配置的HTTP客户端初始化androidpublisher服务
service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
if err != nil {
return nil, err
}
// 返回初始化好的Client实例
return &Client{service}, err
}
// NewWithClient returns http client which includes the custom http client.
// 使用自定义的http客户端创建并返回一个包含访问androidpublisher API所需凭证的http客户端。
func NewWithClient(jsonKey []byte, cli *http.Client) (*Client, error) {
if cli == nil {
return nil, fmt.Errorf("client is nil")
}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cli)
conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
if err != nil {
return nil, err
}
service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
if err != nil {
return nil, err
}
return &Client{service}, err
}
// VerifySignature 验证应用内购买的签名。
// 您需要为您的 Android 应用的内购准备公钥,可在 https://play.google.com/apps/publish/ 上完成。
// 参数:
//
// base64EncodedPublicKey string - 经过 Base64 编码的公钥字符串。
// receipt []byte - 购买收据的字节数据。
// signature string - 购买收据的签名字符串。
//
// 返回值:
//
// isValid bool - 标识签名是否验证成功。
// err error - 验证过程中遇到的错误。
func VerifySignature(base64EncodedPublicKey string, receipt []byte, signature string) (isValid bool, err error) {
// 准备公钥
decodedPublicKey, err := base64.StdEncoding.DecodeString(base64EncodedPublicKey)
if err != nil {
return false, fmt.Errorf("failed to decode public key")
}
publicKeyInterface, err := x509.ParsePKIXPublicKey(decodedPublicKey)
if err != nil {
return false, fmt.Errorf("failed to parse public key")
}
publicKey, _ := publicKeyInterface.(*rsa.PublicKey)
// 从收据生成哈希值
hasher := sha1.New()
hasher.Write(receipt)
hashedReceipt := hasher.Sum(nil)
// 解码签名
decodedSignature, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return false, fmt.Errorf("failed to decode signature")
}
// 验证签名
if err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA1, hashedReceipt, decodedSignature); err != nil {
return false, nil
}
return true, nil
}

View File

@@ -0,0 +1,64 @@
package playstore
import (
"context"
"google.golang.org/api/androidpublisher/v3"
)
// VerifyProduct 验证产品状态
//
// 参数:
// - ctx: 上下文,用于控制请求的生命周期。
// - packageName: 应用的包名(例如,'com.some.thing')。
// - productID: 内购产品的SKU例如'com.some.thing.inapp1')。
// - token: 用户购买内购产品时设备上提供的令牌。
//
// 返回值:
// - *androidpublisher.ProductPurchase: 验证购买后的详细信息。
// - error: 执行过程中出现的错误。
func (c *Client) VerifyProduct(ctx context.Context, packageName string, productID string, token string) (*androidpublisher.ProductPurchase, error) {
ps := androidpublisher.NewPurchasesProductsService(c.service)
result, err := ps.Get(packageName, productID, token).Context(ctx).Do()
return result, err
}
// AcknowledgeProduct 确认内购商品购买
//
// 注意此函数必须在购买后的约24小时内对所有购买调用否则购买将被自动撤销。
//
// 参数:
// - ctx: 上下文,用于控制请求的生命周期。
// - packageName: 应用的包名(例如,'com.some.thing')。
// - productId: 内购产品的SKU例如'com.some.thing.inapp1')。
// - token: 用户购买内购产品时设备上提供的令牌。
// - developerPayload: 开发者自定义信息。
//
// 返回值:
// - error: 执行过程中出现的错误。
func (c *Client) AcknowledgeProduct(ctx context.Context, packageName, productID, token, developerPayload string) error {
ps := androidpublisher.NewPurchasesProductsService(c.service)
acknowledgeRequest := &androidpublisher.ProductPurchasesAcknowledgeRequest{DeveloperPayload: developerPayload}
err := ps.Acknowledge(packageName, productID, token, acknowledgeRequest).Context(ctx).Do()
return err
}
// ConsumeProduct 消费购买应用内商品。
func (c *Client) ConsumeProduct(ctx context.Context, packageName, productID, token string) error {
ps := androidpublisher.NewPurchasesProductsService(c.service)
//acknowledgeRequest := &androidpublisher.PurchasesProductsConsumeCall{DeveloperPayload: developerPayload}
//err := ps.Consume(packageName, productID, token).Context(ctx).Do()
_, err := ps.Get(packageName, productID, token).Context(ctx).Do()
return err
}
// Voidedpurchases 获取已撤销的购买列表
//
// 参数:
// - packageName: 应用的包名(例如,'com.some.thing')。
//
// 返回值:
// - *androidpublisher.VoidedPurchasesListResponse: 已撤销购买的列表响应。
// - error: 执行过程中出现的错误。
func (c *Client) Voidedpurchases(packageName string) (*androidpublisher.VoidedPurchasesListResponse, error) {
return androidpublisher.NewPurchasesVoidedpurchasesService(c.service).List(packageName).Do()
}

View File

@@ -0,0 +1,63 @@
package playstore
import (
"context"
"google.golang.org/api/androidpublisher/v3"
)
// AcknowledgeSubscription acknowledges a subscription purchase.
// 功能:确认订阅购买。
// 参数packageName应用包名subscriptionID订阅IDtoken购买令牌req确认请求对象
// 实现使用PurchasesSubscriptionsService服务的Acknowledge方法来确认指定订阅。
func (c *Client) AcknowledgeSubscription(ctx context.Context, packageName string, subscriptionID string, token string,
req *androidpublisher.SubscriptionPurchasesAcknowledgeRequest) error {
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
err := ps.Acknowledge(packageName, subscriptionID, token, req).Context(ctx).Do()
return err
}
// VerifySubscription verifies subscription status
// 功能:验证订阅状态。
// 参数packageName应用包名subscriptionID订阅IDtoken购买令牌
// 实现使用PurchasesSubscriptionsService的Get方法来获取订阅的当前状态。
// 返回值SubscriptionPurchase对象包含订阅详情。
func (c *Client) VerifySubscription(ctx context.Context, packageName string, subscriptionID string, token string) (*androidpublisher.SubscriptionPurchase, error) {
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
result, err := ps.Get(packageName, subscriptionID, token).Context(ctx).Do()
return result, err
}
// CancelSubscription cancels a user's subscription purchase.
// 功能:取消用户的订阅购买。
// 参数packageName应用包名subscriptionID订阅IDtoken购买令牌
// 实现使用PurchasesSubscriptionsService的Cancel方法来取消订阅。
func (c *Client) CancelSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
err := ps.Cancel(packageName, subscriptionID, token).Context(ctx).Do()
return err
}
// RefundSubscription refunds a user's subscription purchase, but the subscription remains valid
// until its expiration time and it will continue to recur.
// 功能:退款用户的订阅购买,但订阅在到期前仍有效,并且会继续递延。
// 参数packageName应用包名subscriptionID订阅IDtoken购买令牌
// 实现使用PurchasesSubscriptionsService的Refund方法来退款但不取消订阅。
func (c *Client) RefundSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
err := ps.Refund(packageName, subscriptionID, token).Context(ctx).Do()
return err
}
// RevokeSubscription refunds and immediately revokes a user's subscription purchase.
// Access to the subscription will be terminated immediately and it will stop recurring.
// 功能:退款并立即撤销用户的订阅购买。订阅将立即终止,并停止递延。
// 参数packageName应用包名subscriptionID订阅IDtoken购买令牌
// 实现使用PurchasesSubscriptionsService的Revoke方法来退款并撤销订阅。
func (c *Client) RevokeSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
err := ps.Revoke(packageName, subscriptionID, token).Context(ctx).Do()
return err
}

View File

@@ -0,0 +1,33 @@
package playstore
import "context"
// GetProduct 获取应用内商品信息,该商品可以是管理型商品或订阅。
//
// - packageName: 应用的包名。
// - productID: 应用内商品的唯一标识符SKU
//
// 返回值为InAppProduct类型的商品信息和可能出现的错误。
func (c *Client) GetProduct(ctx context.Context, packageName string, productID string) (*InAppProduct, error) {
// 通过Google Play 商店API获取指定商品的信息
var iap, err = c.service.Inappproducts.Get(packageName, productID).Context(ctx).Do()
return &InAppProduct{iap}, err
}
// ConvertRegionPrices 将商品的价格区域配置转换为指定货币单位。
//
// - ctx: 上下文,用于控制请求的取消、超时等。
// - packageName: 应用的包名。
// - productID: 应用内商品的唯一标识符。
// - inAppProduct: 需要转换价格区域的InAppProduct对象。
//
// 返回转换后的InAppProduct对象和可能出现的错误。
//
// 注:此函数暂未实现。
//func (c *Client) ConvertRegionPrices(ctx context.Context, packageName string, productID string, inAppProduct InAppProduct) (*InAppProduct, error) {
// // TODO: 实现商品价格区域转换逻辑
// // c.service.
//
// // 返回未实现的错误
// return &InAppProduct{iap}, err
//}