Skip to content

Commit

Permalink
add acknowledge product, refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Sabrina committed Jan 27, 2020
1 parent ac84b97 commit 31f386e
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 118 deletions.
93 changes: 49 additions & 44 deletions playstore/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto/x509"
"encoding/base64"
"fmt"
"google.golang.org/api/option"
"net/http"
"time"

Expand All @@ -19,6 +20,7 @@ import (
// The IABProduct type is an interface for product service
type IABProduct interface {
VerifyProduct(context.Context, string, string, string) (*androidpublisher.ProductPurchase, error)
AcknowledgeProduct(context.Context, string, string, string, string) error
}

// The IABSubscription type is an interface for subscription service
Expand All @@ -32,30 +34,55 @@ type IABSubscription interface {

// The Client type implements VerifySubscription method
type Client struct {
httpCli *http.Client
service *androidpublisher.Service
}

// New returns http client which includes the credentials to access androidpublisher API.
// You should create a service account for your project at
// https://console.developers.google.com and download a JSON key file to set this argument.
func New(jsonKey []byte) (*Client, error) {
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Timeout: 10 * time.Second})
c := &http.Client{Timeout: 10 * time.Second}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, c)


conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
if err != nil {
return nil, err
}

return &Client{conf.Client(ctx)}, err
val := conf.Client(ctx).Transport.(*oauth2.Transport)
_, err = val.Source.Token()
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
}

// NewWithClient returns http client which includes the custom http client.
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
}

return &Client{conf.Client(ctx)}, err
service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
if err != nil {
return nil, err
}

return &Client{service}, err
}

// AcknowledgeSubscription acknowledges a subscription purchase.
Expand All @@ -66,13 +93,8 @@ func (c *Client) AcknowledgeSubscription(
token string,
req *androidpublisher.SubscriptionPurchasesAcknowledgeRequest,
) error {
service, err := androidpublisher.New(c.httpCli)
if err != nil {
return err
}

ps := androidpublisher.NewPurchasesSubscriptionsService(service)
err = ps.Acknowledge(packageName, subscriptionID, token, req).Context(ctx).Do()
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
err := ps.Acknowledge(packageName, subscriptionID, token, req).Context(ctx).Do()

return err
}
Expand All @@ -84,12 +106,7 @@ func (c *Client) VerifySubscription(
subscriptionID string,
token string,
) (*androidpublisher.SubscriptionPurchase, error) {
service, err := androidpublisher.New(c.httpCli)
if err != nil {
return nil, err
}

ps := androidpublisher.NewPurchasesSubscriptionsService(service)
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
result, err := ps.Get(packageName, subscriptionID, token).Context(ctx).Do()

return result, err
Expand All @@ -102,54 +119,42 @@ func (c *Client) VerifyProduct(
productID string,
token string,
) (*androidpublisher.ProductPurchase, error) {
service, err := androidpublisher.New(c.httpCli)
if err != nil {
return nil, err
}

ps := androidpublisher.NewPurchasesProductsService(service)
ps := androidpublisher.NewPurchasesProductsService(c.service)
result, err := ps.Get(packageName, productID, token).Context(ctx).Do()

return result, err
}

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
}

// CancelSubscription cancels a user's subscription purchase.
func (c *Client) CancelSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
service, err := androidpublisher.New(c.httpCli)
if err != nil {
return err
}

ps := androidpublisher.NewPurchasesSubscriptionsService(service)
err = ps.Cancel(packageName, subscriptionID, token).Context(ctx).Do()
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.
func (c *Client) RefundSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
service, err := androidpublisher.New(c.httpCli)
if err != nil {
return err
}

ps := androidpublisher.NewPurchasesSubscriptionsService(service)
err = ps.Refund(packageName, subscriptionID, token).Context(ctx).Do()
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.
func (c *Client) RevokeSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
service, err := androidpublisher.New(c.httpCli)
if err != nil {
return err
}

ps := androidpublisher.NewPurchasesSubscriptionsService(service)
err = ps.Revoke(packageName, subscriptionID, token).Context(ctx).Do()
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
err := ps.Revoke(packageName, subscriptionID, token).Context(ctx).Do()

return err
}
Expand Down
115 changes: 41 additions & 74 deletions playstore/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
"reflect"
"testing"

"golang.org/x/oauth2"
androidpublisher "google.golang.org/api/androidpublisher/v3"
"google.golang.org/api/androidpublisher/v3"
"google.golang.org/appengine/urlfetch"
)

Expand Down Expand Up @@ -38,20 +37,12 @@ func TestNew(t *testing.T) {
// Exception scenario
expected := "oauth2: cannot fetch token: 400 Bad Request\nResponse: {\n \"error\": \"invalid_grant\",\n \"error_description\": \"Invalid issuer: Not a service account.\"\n}"

actual, _ := New(dummyKey)
val := actual.httpCli.Transport.(*oauth2.Transport)
token, err := val.Source.Token()
if token != nil {
t.Errorf("got %#v", token)
}
if err.Error() != expected {
_, err := New(dummyKey)
if err == nil || err.Error() != expected {
t.Errorf("got %v\nwant %v", err, expected)
}

// TODO Normal scenario
actual, _ = New(jsonKey)
val = actual.httpCli.Transport.(*oauth2.Transport)
token, err = val.Source.Token()
_, err = New(jsonKey)
if err != nil {
t.Errorf("got %#v", err)
}
Expand All @@ -63,14 +54,23 @@ func TestNewWithClient(t *testing.T) {
ctx := context.Background()
httpClient := urlfetch.Client(ctx)

cli, _ := NewWithClient(dummyKey, httpClient)
tr, _ := cli.httpCli.Transport.(*oauth2.Transport)

if !reflect.DeepEqual(tr.Base, httpClient.Transport) {
_, err := NewWithClient(dummyKey, httpClient)
if err != nil {
t.Errorf("transport should be urlfetch's one")
}
}

func TestNewWithNoClient(t *testing.T) {
t.Parallel()
expected := errors.New("client is nil")

_, actual := NewWithClient(dummyKey, nil)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
}

}

func TestAcknowledgeSubscription(t *testing.T) {
t.Parallel()
// Exception scenario
Expand All @@ -83,7 +83,7 @@ func TestAcknowledgeSubscription(t *testing.T) {
}
err := client.AcknowledgeSubscription(ctx, "package", "subscriptionID", "purchaseToken", req)

if err.Error() != expected {
if err == nil || err.Error() != expected {
t.Errorf("got %v\nwant %v", err, expected)
}

Expand All @@ -99,25 +99,13 @@ func TestVerifySubscription(t *testing.T) {
ctx := context.Background()
_, err := client.VerifySubscription(ctx, "package", "subscriptionID", "purchaseToken")

if err.Error() != expected {
if err == nil || err.Error() != expected {
t.Errorf("got %v\nwant %v", err, expected)
}

// TODO Normal scenario
}

func TestVerifySubscriptionAndroidPublisherError(t *testing.T) {
t.Parallel()
client := Client{nil}
expected := errors.New("client is nil")
ctx := context.Background()
_, actual := client.VerifySubscription(ctx, "package", "subscriptionID", "purchaseToken")

if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
}
}

func TestVerifyProduct(t *testing.T) {
t.Parallel()
// Exception scenario
Expand All @@ -127,42 +115,37 @@ func TestVerifyProduct(t *testing.T) {
ctx := context.Background()
_, err := client.VerifyProduct(ctx, "package", "productID", "purchaseToken")

if err.Error() != expected {
if err == nil || err.Error() != expected {
t.Errorf("got %v", err)
}

// TODO Normal scenario
}

func TestVerifyProductAndroidPublisherError(t *testing.T) {
func TestAcknowledgeProduct(t *testing.T) {
t.Parallel()
client := Client{nil}
expected := errors.New("client is nil")
// Exception scenario
expected := "googleapi: Error 400: Invalid Value, invalid"

client, _ := New(jsonKey)
ctx := context.Background()
_, actual := client.VerifyProduct(ctx, "package", "productID", "purchaseToken")
err := client.AcknowledgeProduct(ctx, "package", "productID", "purchaseToken", "")

if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
if err == nil || err.Error() != expected {
t.Errorf("got %v", err)
}

// TODO Normal scenario
}

func TestCancelSubscription(t *testing.T) {
t.Parallel()
// Exception scenario
client := &Client{nil}
expected := errors.New("client is nil")
ctx := context.Background()
actual := client.CancelSubscription(ctx, "package", "productID", "purchaseToken")

if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
}

client, _ = New(jsonKey)
client, _ := New(jsonKey)
expectedStr := "googleapi: Error 400: Invalid Value, invalid"
actual = client.CancelSubscription(ctx, "package", "productID", "purchaseToken")
actual := client.CancelSubscription(ctx, "package", "productID", "purchaseToken")

if actual.Error() != expectedStr {
if actual == nil || actual.Error() != expectedStr {
t.Errorf("got %v\nwant %v", actual, expectedStr)
}

Expand All @@ -171,21 +154,13 @@ func TestCancelSubscription(t *testing.T) {

func TestRefundSubscription(t *testing.T) {
t.Parallel()
// Exception scenario
client := &Client{nil}
expected := errors.New("client is nil")
ctx := context.Background()
actual := client.RefundSubscription(ctx, "package", "productID", "purchaseToken")

if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
}

client, _ = New(jsonKey)
ctx := context.Background()
client, _ := New(jsonKey)
expectedStr := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
actual = client.RefundSubscription(ctx, "package", "productID", "purchaseToken")
actual := client.RefundSubscription(ctx, "package", "productID", "purchaseToken")

if actual.Error() != expectedStr {
if actual == nil || actual.Error() != expectedStr {
t.Errorf("got %v\nwant %v", actual, expectedStr)
}

Expand All @@ -194,21 +169,13 @@ func TestRefundSubscription(t *testing.T) {

func TestRevokeSubscription(t *testing.T) {
t.Parallel()
// Exception scenario
client := &Client{nil}
expected := errors.New("client is nil")
ctx := context.Background()
actual := client.RevokeSubscription(ctx, "package", "productID", "purchaseToken")

if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
}

client, _ = New(jsonKey)
ctx := context.Background()
client, _ := New(jsonKey)
expectedStr := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
actual = client.RevokeSubscription(ctx, "package", "productID", "purchaseToken")
actual := client.RevokeSubscription(ctx, "package", "productID", "purchaseToken")

if actual.Error() != expectedStr {
if actual == nil || actual.Error() != expectedStr {
t.Errorf("got %v\nwant %v", actual, expectedStr)
}

Expand Down

0 comments on commit 31f386e

Please sign in to comment.