forked from Mirrors/go-iap
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd5a78348a | |||
|
|
dad27d5283 | ||
|
|
777e3c50ef | ||
|
|
4f7b16717e | ||
|
|
a564d013c9 | ||
|
|
134bcb9b89 | ||
|
|
5cc0a53c63 | ||
|
|
22c0b76a09 | ||
|
|
0ae05687d4 | ||
|
|
b04c08d22b | ||
|
|
9b772f3398 | ||
|
|
073874b26d | ||
|
|
a14bd11b0c | ||
|
|
7f77f5e33e | ||
|
|
eacfb2d096 | ||
|
|
91cd5b791c | ||
|
|
2700fc2ec0 | ||
|
|
e4338cd607 | ||
|
|
8ddfbdb30a | ||
|
|
dd8af1ccd2 | ||
|
|
9ba6e70200 | ||
|
|
052ce72134 | ||
|
|
a3f651b46e | ||
|
|
588d81166b | ||
|
|
a61c519ac8 | ||
|
|
3c88ce1648 | ||
|
|
0aa274084a | ||
|
|
c61599bf8e | ||
|
|
e54455a635 | ||
|
|
d9ae3a1d88 | ||
|
|
b739b19032 | ||
|
|
be7b768650 | ||
|
|
98cf7b036f | ||
|
|
8359bd764f | ||
|
|
3fcc899200 | ||
|
|
31f386e220 | ||
|
|
ac84b97cd8 | ||
|
|
c4c303e812 | ||
|
|
b5222c00cf | ||
|
|
c8962b67cb | ||
|
|
627fa5e7d1 |
@@ -1,6 +1,6 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.12.5
|
- 1.13.x
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
|
|||||||
23
Makefile
23
Makefile
@@ -1,15 +1,22 @@
|
|||||||
|
.PHONEY: all
|
||||||
.PHONEY: all setup test cover
|
|
||||||
|
|
||||||
all: setup cover
|
all: setup cover
|
||||||
|
|
||||||
|
.PHONEY: setup
|
||||||
setup:
|
setup:
|
||||||
go get golang.org/x/tools/cmd/cover
|
go get golang.org/x/tools/cmd/cover
|
||||||
go get google.golang.org/appengine/urlfetch
|
go get google.golang.org/appengine/urlfetch
|
||||||
go get ./...
|
go get ./...
|
||||||
|
|
||||||
|
.PHONEY: test
|
||||||
test:
|
test:
|
||||||
go test -v ./...
|
go test -v ./...
|
||||||
|
|
||||||
|
.PHONEY: cover
|
||||||
cover:
|
cover:
|
||||||
go test -coverprofile=coverage.txt ./...
|
go test -coverprofile=coverage.txt ./...
|
||||||
|
|
||||||
|
.PHONEY: generate
|
||||||
|
generate:
|
||||||
|
rm -rf ./appstore/mocks/*
|
||||||
|
rm -rf ./playstore/mocks/*
|
||||||
|
go generate ./...
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
go-iap
|
go-iap
|
||||||
======
|
======
|
||||||
|
|
||||||

|

|
||||||
[](https://travis-ci.org/awa/go-iap)
|
[](https://travis-ci.org/awa/go-iap)
|
||||||
[](https://codecov.io/github/awa/go-iap?branch=master)
|
[](https://codecov.io/github/awa/go-iap?branch=master)
|
||||||
|
|
||||||
|
|||||||
49
appstore/mocks/appstore.go
Normal file
49
appstore/mocks/appstore.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/awa/go-iap/appstore (interfaces: IAPClient)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
appstore "github.com/awa/go-iap/appstore"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockIAPClient is a mock of IAPClient interface
|
||||||
|
type MockIAPClient struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockIAPClientMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockIAPClientMockRecorder is the mock recorder for MockIAPClient
|
||||||
|
type MockIAPClientMockRecorder struct {
|
||||||
|
mock *MockIAPClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockIAPClient creates a new mock instance
|
||||||
|
func NewMockIAPClient(ctrl *gomock.Controller) *MockIAPClient {
|
||||||
|
mock := &MockIAPClient{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockIAPClientMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockIAPClient) EXPECT() *MockIAPClientMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify mocks base method
|
||||||
|
func (m *MockIAPClient) Verify(arg0 context.Context, arg1 appstore.IAPRequest, arg2 interface{}) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Verify", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify indicates an expected call of Verify
|
||||||
|
func (mr *MockIAPClientMockRecorder) Verify(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockIAPClient)(nil).Verify), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
@@ -149,6 +149,7 @@ type (
|
|||||||
PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
|
PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
|
||||||
IsRetryable bool `json:"is-retryable,omitempty"`
|
IsRetryable bool `json:"is-retryable,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// The IAPLatestResponse type has the response properties
|
// The IAPLatestResponse type has the response properties
|
||||||
// If you use latest_receipt as token to verify, response should be like following struct
|
// If you use latest_receipt as token to verify, response should be like following struct
|
||||||
IAPLatestResponse struct {
|
IAPLatestResponse struct {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ const (
|
|||||||
NotificationTypeDidChangeRenewalPreference NotificationType = "DID_CHANGE_RENEWAL_PREF"
|
NotificationTypeDidChangeRenewalPreference NotificationType = "DID_CHANGE_RENEWAL_PREF"
|
||||||
// Customer changed the subscription renewal status. Current active plan is not affected.
|
// Customer changed the subscription renewal status. Current active plan is not affected.
|
||||||
NotificationTypeDidChangeRenewalStatus NotificationType = "DID_CHANGE_RENEWAL_STATUS"
|
NotificationTypeDidChangeRenewalStatus NotificationType = "DID_CHANGE_RENEWAL_STATUS"
|
||||||
|
// Subscription failed to renew due to a billing issue.
|
||||||
|
NotificationTypeDidFailToRenew NotificationType = "DID_FAIL_TO_RENEW"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotificationEnvironment string
|
type NotificationEnvironment string
|
||||||
@@ -54,10 +56,11 @@ type NotificationReceipt struct {
|
|||||||
OriginalPurchaseDate
|
OriginalPurchaseDate
|
||||||
NotificationExpiresDate
|
NotificationExpiresDate
|
||||||
CancellationDate
|
CancellationDate
|
||||||
|
CancellationReason string `json:"cancellation_reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotificationUnifiedReceipt struct {
|
type NotificationUnifiedReceipt struct {
|
||||||
Status string `json:"status"`
|
Status int `json:"status"`
|
||||||
Environment Environment `json:"environment"`
|
Environment Environment `json:"environment"`
|
||||||
LatestReceipt string `json:"latest_receipt"`
|
LatestReceipt string `json:"latest_receipt"`
|
||||||
LatestReceiptInfo []InApp `json:"latest_receipt_info"`
|
LatestReceiptInfo []InApp `json:"latest_receipt_info"`
|
||||||
@@ -82,7 +85,7 @@ type SubscriptionNotification struct {
|
|||||||
ExpirationIntent string `json:"expiration_intent"`
|
ExpirationIntent string `json:"expiration_intent"`
|
||||||
|
|
||||||
// Auto renew info
|
// Auto renew info
|
||||||
AutoRenewStatus string `json:"auto_renew_status"` // false or true
|
AutoRenewStatus string `json:"auto_renew_status"` // false or true
|
||||||
AutoRenewProductID string `json:"auto_renew_product_id"`
|
AutoRenewProductID string `json:"auto_renew_product_id"`
|
||||||
|
|
||||||
// HACK (msyrus): Separate Subscriptiton Notification from Notification verification response
|
// HACK (msyrus): Separate Subscriptiton Notification from Notification verification response
|
||||||
@@ -93,16 +96,21 @@ type SubscriptionNotification struct {
|
|||||||
// Posted if the notification_type is RENEWAL or INTERACTIVE_RENEWAL, and only if the renewal is successful.
|
// Posted if the notification_type is RENEWAL or INTERACTIVE_RENEWAL, and only if the renewal is successful.
|
||||||
// Posted also if the notification_type is INITIAL_BUY.
|
// Posted also if the notification_type is INITIAL_BUY.
|
||||||
// Not posted for notification_type CANCEL.
|
// Not posted for notification_type CANCEL.
|
||||||
LatestReceipt string `json:"latest_receipt"`
|
// Deprecated: use UnifiedReceipt.LatestReceipt instead. See details: https://developer.apple.com/documentation/appstoreservernotifications/ .
|
||||||
|
LatestReceipt string `json:"latest_receipt"`
|
||||||
|
// Deprecated: use UnifiedReceipt.LatestReceiptInfo instead. See details: https://developer.apple.com/documentation/appstoreservernotifications/ .
|
||||||
LatestReceiptInfo NotificationReceipt `json:"latest_receipt_info"`
|
LatestReceiptInfo NotificationReceipt `json:"latest_receipt_info"`
|
||||||
|
|
||||||
// In the new notifications above properties latest_receipt, latest_receipt_info are moved under this one
|
// In the new notifications above properties latest_receipt, latest_receipt_info are moved under this one
|
||||||
UnifiedReceipt NotificationUnifiedReceipt `json:"unified_receipt"`
|
UnifiedReceipt NotificationUnifiedReceipt `json:"unified_receipt"`
|
||||||
|
|
||||||
// Posted only if the notification_type is RENEWAL or CANCEL or if renewal failed and subscription expired.
|
// Posted only if the notification_type is RENEWAL or CANCEL or if renewal failed and subscription expired.
|
||||||
LatestExpiredReceipt string `json:"latest_expired_receipt"`
|
// Deprecated: see details: https://developer.apple.com/documentation/appstoreservernotifications/ .
|
||||||
|
LatestExpiredReceipt string `json:"latest_expired_receipt"`
|
||||||
|
// Deprecated: see details: https://developer.apple.com/documentation/appstoreservernotifications/ .
|
||||||
LatestExpiredReceiptInfo NotificationReceipt `json:"latest_expired_receipt_info"`
|
LatestExpiredReceiptInfo NotificationReceipt `json:"latest_expired_receipt_info"`
|
||||||
|
|
||||||
// Posted only if the notification_type is CANCEL.
|
// Posted only if the notification_type is CANCEL.
|
||||||
CancellationDate
|
CancellationDate
|
||||||
|
CancellationReason string `json:"cancellation_reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/appstore.go -package=mocks github.com/awa/go-iap/appstore IAPClient
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// SandboxURL is the endpoint for sandbox environment.
|
// SandboxURL is the endpoint for sandbox environment.
|
||||||
SandboxURL string = "https://boron2.ngrok.io/verifyReceipt"
|
SandboxURL string = "https://sandbox.itunes.apple.com/verifyReceipt"
|
||||||
// ProductionURL is the endpoint for production environment.
|
// ProductionURL is the endpoint for production environment.
|
||||||
ProductionURL string = "https://buy.itunes.apple.com/verifyReceipt"
|
ProductionURL string = "https://buy.itunes.apple.com/verifyReceipt"
|
||||||
// ContentType is the request content-type for apple store.
|
// ContentType is the request content-type for apple store.
|
||||||
@@ -31,47 +34,53 @@ type Client struct {
|
|||||||
httpCli *http.Client
|
httpCli *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAppStoreServer = errors.New("AppStore server error")
|
||||||
|
|
||||||
|
ErrInvalidJSON = errors.New("The App Store could not read the JSON object you provided.")
|
||||||
|
ErrInvalidReceiptData = errors.New("The data in the receipt-data property was malformed or missing.")
|
||||||
|
ErrReceiptUnauthenticated = errors.New("The receipt could not be authenticated.")
|
||||||
|
ErrInvalidSharedSecret = errors.New("The shared secret you provided does not match the shared secret on file for your account.")
|
||||||
|
ErrServerUnavailable = errors.New("The receipt server is not currently available.")
|
||||||
|
ErrReceiptIsForTest = errors.New("This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.")
|
||||||
|
ErrReceiptIsForProduction = errors.New("This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.")
|
||||||
|
ErrReceiptUnauthorized = errors.New("This receipt could not be authorized. Treat this the same as if a purchase was never made.")
|
||||||
|
|
||||||
|
ErrInternalDataAccessError = errors.New("Internal data access error.")
|
||||||
|
ErrUnknown = errors.New("An unknown error occurred")
|
||||||
|
)
|
||||||
|
|
||||||
// HandleError returns error message by status code
|
// HandleError returns error message by status code
|
||||||
func HandleError(status int) error {
|
func HandleError(status int) error {
|
||||||
var message string
|
var e error
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case 0:
|
case 0:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case 21000:
|
case 21000:
|
||||||
message = "The App Store could not read the JSON object you provided."
|
e = ErrInvalidJSON
|
||||||
|
|
||||||
case 21002:
|
case 21002:
|
||||||
message = "The data in the receipt-data property was malformed or missing."
|
e = ErrInvalidReceiptData
|
||||||
|
|
||||||
case 21003:
|
case 21003:
|
||||||
message = "The receipt could not be authenticated."
|
e = ErrReceiptUnauthenticated
|
||||||
|
|
||||||
case 21004:
|
case 21004:
|
||||||
message = "The shared secret you provided does not match the shared secret on file for your account."
|
e = ErrInvalidSharedSecret
|
||||||
|
|
||||||
case 21005:
|
case 21005:
|
||||||
message = "The receipt server is not currently available."
|
e = ErrServerUnavailable
|
||||||
|
|
||||||
case 21007:
|
case 21007:
|
||||||
message = "This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead."
|
e = ErrReceiptIsForTest
|
||||||
|
|
||||||
case 21008:
|
case 21008:
|
||||||
message = "This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead."
|
e = ErrReceiptIsForProduction
|
||||||
|
|
||||||
case 21010:
|
case 21010:
|
||||||
message = "This receipt could not be authorized. Treat this the same as if a purchase was never made."
|
e = ErrReceiptUnauthorized
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if status >= 21100 && status <= 21199 {
|
if status >= 21100 && status <= 21199 {
|
||||||
message = "Internal data access error."
|
e = ErrInternalDataAccessError
|
||||||
} else {
|
} else {
|
||||||
message = "An unknown error occurred"
|
e = ErrUnknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New(message)
|
return fmt.Errorf("status %d: %w", status, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a client object
|
// New creates a client object
|
||||||
@@ -113,6 +122,9 @@ func (c *Client) Verify(ctx context.Context, reqBody IAPRequest, result interfac
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode >= 500 {
|
||||||
|
return "", fmt.Errorf("Received http status code %d from the App Store: %w", resp.StatusCode, ErrAppStoreServer)
|
||||||
|
}
|
||||||
return c.parseResponse(resp, result, ctx, reqBody)
|
return c.parseResponse(resp, result, ctx, reqBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +163,10 @@ func (c *Client) parseResponse(resp *http.Response, result interface{}, ctx cont
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode >= 500 {
|
||||||
|
return Sandbox, fmt.Errorf("Received http status code %d from the App Store Sandbox: %w", resp.StatusCode, ErrAppStoreServer)
|
||||||
|
}
|
||||||
|
|
||||||
// 21007 is found when the receipt is from the test environment
|
// 21007 is found when the receipt is from the test environment
|
||||||
return Sandbox, json.NewDecoder(resp.Body).Decode(result)
|
return Sandbox, json.NewDecoder(resp.Body).Decode(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,52 +26,52 @@ func TestHandleError(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "status 21000",
|
name: "status 21000",
|
||||||
in: 21000,
|
in: 21000,
|
||||||
out: errors.New("The App Store could not read the JSON object you provided."),
|
out: ErrInvalidJSON,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21002",
|
name: "status 21002",
|
||||||
in: 21002,
|
in: 21002,
|
||||||
out: errors.New("The data in the receipt-data property was malformed or missing."),
|
out: ErrInvalidReceiptData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21003",
|
name: "status 21003",
|
||||||
in: 21003,
|
in: 21003,
|
||||||
out: errors.New("The receipt could not be authenticated."),
|
out: ErrReceiptUnauthenticated,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21004",
|
name: "status 21004",
|
||||||
in: 21004,
|
in: 21004,
|
||||||
out: errors.New("The shared secret you provided does not match the shared secret on file for your account."),
|
out: ErrInvalidSharedSecret,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21005",
|
name: "status 21005",
|
||||||
in: 21005,
|
in: 21005,
|
||||||
out: errors.New("The receipt server is not currently available."),
|
out: ErrServerUnavailable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21007",
|
name: "status 21007",
|
||||||
in: 21007,
|
in: 21007,
|
||||||
out: errors.New("This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead."),
|
out: ErrReceiptIsForTest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21008",
|
name: "status 21008",
|
||||||
in: 21008,
|
in: 21008,
|
||||||
out: errors.New("This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead."),
|
out: ErrReceiptIsForProduction,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21010",
|
name: "status 21010",
|
||||||
in: 21010,
|
in: 21010,
|
||||||
out: errors.New("This receipt could not be authorized. Treat this the same as if a purchase was never made."),
|
out: ErrReceiptUnauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status 21100 ~ 21199",
|
name: "status 21100 ~ 21199",
|
||||||
in: 21100,
|
in: 21100,
|
||||||
out: errors.New("Internal data access error."),
|
out: ErrInternalDataAccessError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "status unknown",
|
name: "status unknown",
|
||||||
in: 100,
|
in: 100,
|
||||||
out: errors.New("An unknown error occurred"),
|
out: ErrUnknown,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ func TestHandleError(t *testing.T) {
|
|||||||
t.Run(v.name, func(t *testing.T) {
|
t.Run(v.name, func(t *testing.T) {
|
||||||
out := HandleError(v.in)
|
out := HandleError(v.in)
|
||||||
|
|
||||||
if !reflect.DeepEqual(out, v.out) {
|
if !errors.Is(out, v.out) {
|
||||||
t.Errorf("input: %d\ngot: %v\nwant: %v\n", v.in, out, v.out)
|
t.Errorf("input: %d\ngot: %v\nwant: %v\n", v.in, out, v.out)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -180,29 +180,30 @@ func TestResponses(t *testing.T) {
|
|||||||
result := &IAPResponse{}
|
result := &IAPResponse{}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
|
name string
|
||||||
testServer *httptest.Server
|
testServer *httptest.Server
|
||||||
sandboxServ *httptest.Server
|
sandboxServ *httptest.Server
|
||||||
expected *IAPResponse
|
expected *IAPResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []testCase{
|
testCases := []testCase{
|
||||||
// VerifySandboxReceipt
|
|
||||||
{
|
{
|
||||||
|
name: "VerifySandboxReceipt",
|
||||||
testServer: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 21007}`)),
|
testServer: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 21007}`)),
|
||||||
sandboxServ: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 0}`)),
|
sandboxServ: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 0}`)),
|
||||||
expected: &IAPResponse{
|
expected: &IAPResponse{
|
||||||
Status: 0,
|
Status: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// VerifyBadPayload
|
|
||||||
{
|
{
|
||||||
|
name: "VerifyBadPayload",
|
||||||
testServer: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 21002}`)),
|
testServer: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 21002}`)),
|
||||||
expected: &IAPResponse{
|
expected: &IAPResponse{
|
||||||
Status: 21002,
|
Status: 21002,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// SuccessPayload
|
|
||||||
{
|
{
|
||||||
|
name: "SuccessPayload",
|
||||||
testServer: httptest.NewServer(serverWithResponse(http.StatusBadRequest, `{"status": 0}`)),
|
testServer: httptest.NewServer(serverWithResponse(http.StatusBadRequest, `{"status": 0}`)),
|
||||||
expected: &IAPResponse{
|
expected: &IAPResponse{
|
||||||
Status: 0,
|
Status: 0,
|
||||||
@@ -213,57 +214,65 @@ func TestResponses(t *testing.T) {
|
|||||||
client := New()
|
client := New()
|
||||||
client.SandboxURL = "localhost"
|
client.SandboxURL = "localhost"
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
defer tc.testServer.Close()
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client.ProductionURL = tc.testServer.URL
|
defer tc.testServer.Close()
|
||||||
if tc.sandboxServ != nil {
|
client.ProductionURL = tc.testServer.URL
|
||||||
client.SandboxURL = tc.sandboxServ.URL
|
if tc.sandboxServ != nil {
|
||||||
}
|
client.SandboxURL = tc.sandboxServ.URL
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
err := client.Verify(ctx, req, result)
|
err := client.Verify(ctx, req, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Test case %d - %s", i, err.Error())
|
t.Errorf("%s", err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(result, tc.expected) {
|
if !reflect.DeepEqual(result, tc.expected) {
|
||||||
t.Errorf("Test case %d - got %v\nwant %v", i, result, tc.expected)
|
t.Errorf("got %v\nwant %v", result, tc.expected)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrors(t *testing.T) {
|
func TestHttpStatusErrors(t *testing.T) {
|
||||||
req := IAPRequest{
|
req := IAPRequest{
|
||||||
ReceiptData: "dummy data",
|
ReceiptData: "dummy data",
|
||||||
}
|
}
|
||||||
result := &IAPResponse{}
|
result := &IAPResponse{}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
|
name string
|
||||||
testServer *httptest.Server
|
testServer *httptest.Server
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []testCase{
|
testCases := []testCase{
|
||||||
// VerifySandboxReceiptFailure
|
|
||||||
{
|
{
|
||||||
testServer: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 21007}`)),
|
name: "status 200",
|
||||||
|
testServer: httptest.NewServer(serverWithResponse(http.StatusOK, `{"status": 21000}`)),
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
// VerifyBadResponse
|
|
||||||
{
|
{
|
||||||
|
name: "status 500",
|
||||||
testServer: httptest.NewServer(serverWithResponse(http.StatusInternalServerError, `qwerty!@#$%^`)),
|
testServer: httptest.NewServer(serverWithResponse(http.StatusInternalServerError, `qwerty!@#$%^`)),
|
||||||
|
err: ErrAppStoreServer,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
client := New()
|
client := New()
|
||||||
client.SandboxURL = "localhost"
|
client.SandboxURL = "localhost"
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
defer tc.testServer.Close()
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client.ProductionURL = tc.testServer.URL
|
defer tc.testServer.Close()
|
||||||
|
client.ProductionURL = tc.testServer.URL
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
err := client.Verify(ctx, req, result)
|
err := client.Verify(ctx, req, result)
|
||||||
if err == nil {
|
if !errors.Is(err, tc.err) {
|
||||||
t.Errorf("Test case %d - expected error to be not nil since the sandbox is not responding", i)
|
t.Errorf("expected error to be not nil since the sandbox is not responding")
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,12 +306,10 @@ func serverWithResponse(statusCode int, response string) http.Handler {
|
|||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if "POST" == r.Method {
|
if "POST" == r.Method {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(statusCode)
|
||||||
w.Write([]byte(response))
|
w.Write([]byte(response))
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
w.Write([]byte(`unsupported request`))
|
w.Write([]byte(`unsupported request`))
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(statusCode)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
21
go.mod
21
go.mod
@@ -1,27 +1,16 @@
|
|||||||
module github.com/awa/go-iap
|
module git.deineagentur.com/DeineAgenturUG/go-iap
|
||||||
|
|
||||||
go 1.12
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.39.0 // indirect
|
cloud.google.com/go v0.39.0 // indirect
|
||||||
github.com/golang/mock v1.3.1 // indirect
|
github.com/golang/mock v1.4.0
|
||||||
github.com/google/btree v1.0.0 // indirect
|
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect
|
|
||||||
github.com/kr/pty v1.1.4 // indirect
|
|
||||||
go.opencensus.io v0.22.0 // indirect
|
go.opencensus.io v0.22.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect
|
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff // indirect
|
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect
|
|
||||||
golang.org/x/mobile v0.0.0-20190509164839-32b2708ab171 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0
|
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0
|
||||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 // indirect
|
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 // indirect
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
|
||||||
golang.org/x/tools v0.0.0-20190530215528-75312fb06703 // indirect
|
|
||||||
google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f
|
google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f
|
||||||
google.golang.org/appengine v1.6.0
|
google.golang.org/appengine v1.6.5
|
||||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 // indirect
|
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 // indirect
|
||||||
google.golang.org/grpc v1.21.0 // indirect
|
google.golang.org/grpc v1.21.0 // indirect
|
||||||
honnef.co/go/tools v0.0.0-20190530170028-a1efa522b896 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
56
go.sum
56
go.sum
@@ -4,71 +4,52 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||||||
cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
|
cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
|
||||||
cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
|
cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
|
||||||
golang.org/x/mobile v0.0.0-20190509164839-32b2708ab171/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190517181255-950ef44c6e07 h1:XC1K3wNjuz44KaI+cj85C9TW85w/46RH7J+DTXNH5Wk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190517181255-950ef44c6e07/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0 h1:xFEXbcD0oa/xhqQmMXztdZ0bWvexAWds+8c1gRN8nu0=
|
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0 h1:xFEXbcD0oa/xhqQmMXztdZ0bWvexAWds+8c1gRN8nu0=
|
||||||
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -78,27 +59,22 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 h1:R4dVlxdmKenVdMRS/tTspEpSTRWINYrHD8ySIU9yCIU=
|
||||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190530215528-75312fb06703/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM=
|
google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM=
|
||||||
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f h1:tAgkkP6ovjCY8HraRtpXwh0CVqHwGqEAVLHwXyfFjIM=
|
google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f h1:tAgkkP6ovjCY8HraRtpXwh0CVqHwGqEAVLHwXyfFjIM=
|
||||||
@@ -106,15 +82,13 @@ google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f/go.mod h1:8k5glujaEP+
|
|||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 h1:4rNOqY4ULrKzS6twXa619uQgI7h9PaVd4ZhjFQ7C5zs=
|
|
||||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
|
||||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 h1:wuGevabY6r+ivPNagjUXGGxF+GqgMd+dBhjsxW4q9u4=
|
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 h1:wuGevabY6r+ivPNagjUXGGxF+GqgMd+dBhjsxW4q9u4=
|
||||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||||
@@ -122,9 +96,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
|||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
honnef.co/go/tools v0.0.0-20190530170028-a1efa522b896/go.mod h1:wtc9q0E9zm8PjdRMh29DPlTlCCHVzKDwnkT4GskQVzg=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|||||||
158
playstore/mocks/playstore.go
Normal file
158
playstore/mocks/playstore.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/awa/go-iap/playstore (interfaces: IABProduct,IABSubscription)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
v3 "google.golang.org/api/androidpublisher/v3"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockIABProduct is a mock of IABProduct interface
|
||||||
|
type MockIABProduct struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockIABProductMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockIABProductMockRecorder is the mock recorder for MockIABProduct
|
||||||
|
type MockIABProductMockRecorder struct {
|
||||||
|
mock *MockIABProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockIABProduct creates a new mock instance
|
||||||
|
func NewMockIABProduct(ctrl *gomock.Controller) *MockIABProduct {
|
||||||
|
mock := &MockIABProduct{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockIABProductMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockIABProduct) EXPECT() *MockIABProductMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcknowledgeProduct mocks base method
|
||||||
|
func (m *MockIABProduct) AcknowledgeProduct(arg0 context.Context, arg1, arg2, arg3, arg4 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AcknowledgeProduct", arg0, arg1, arg2, arg3, arg4)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcknowledgeProduct indicates an expected call of AcknowledgeProduct
|
||||||
|
func (mr *MockIABProductMockRecorder) AcknowledgeProduct(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcknowledgeProduct", reflect.TypeOf((*MockIABProduct)(nil).AcknowledgeProduct), arg0, arg1, arg2, arg3, arg4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyProduct mocks base method
|
||||||
|
func (m *MockIABProduct) VerifyProduct(arg0 context.Context, arg1, arg2, arg3 string) (*v3.ProductPurchase, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "VerifyProduct", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].(*v3.ProductPurchase)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyProduct indicates an expected call of VerifyProduct
|
||||||
|
func (mr *MockIABProductMockRecorder) VerifyProduct(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyProduct", reflect.TypeOf((*MockIABProduct)(nil).VerifyProduct), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockIABSubscription is a mock of IABSubscription interface
|
||||||
|
type MockIABSubscription struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockIABSubscriptionMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockIABSubscriptionMockRecorder is the mock recorder for MockIABSubscription
|
||||||
|
type MockIABSubscriptionMockRecorder struct {
|
||||||
|
mock *MockIABSubscription
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockIABSubscription creates a new mock instance
|
||||||
|
func NewMockIABSubscription(ctrl *gomock.Controller) *MockIABSubscription {
|
||||||
|
mock := &MockIABSubscription{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockIABSubscriptionMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockIABSubscription) EXPECT() *MockIABSubscriptionMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcknowledgeSubscription mocks base method
|
||||||
|
func (m *MockIABSubscription) AcknowledgeSubscription(arg0 context.Context, arg1, arg2, arg3 string, arg4 *v3.SubscriptionPurchasesAcknowledgeRequest) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AcknowledgeSubscription", arg0, arg1, arg2, arg3, arg4)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcknowledgeSubscription indicates an expected call of AcknowledgeSubscription
|
||||||
|
func (mr *MockIABSubscriptionMockRecorder) AcknowledgeSubscription(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcknowledgeSubscription", reflect.TypeOf((*MockIABSubscription)(nil).AcknowledgeSubscription), arg0, arg1, arg2, arg3, arg4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelSubscription mocks base method
|
||||||
|
func (m *MockIABSubscription) CancelSubscription(arg0 context.Context, arg1, arg2, arg3 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CancelSubscription", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelSubscription indicates an expected call of CancelSubscription
|
||||||
|
func (mr *MockIABSubscriptionMockRecorder) CancelSubscription(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelSubscription", reflect.TypeOf((*MockIABSubscription)(nil).CancelSubscription), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefundSubscription mocks base method
|
||||||
|
func (m *MockIABSubscription) RefundSubscription(arg0 context.Context, arg1, arg2, arg3 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "RefundSubscription", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefundSubscription indicates an expected call of RefundSubscription
|
||||||
|
func (mr *MockIABSubscriptionMockRecorder) RefundSubscription(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefundSubscription", reflect.TypeOf((*MockIABSubscription)(nil).RefundSubscription), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeSubscription mocks base method
|
||||||
|
func (m *MockIABSubscription) RevokeSubscription(arg0 context.Context, arg1, arg2, arg3 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "RevokeSubscription", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeSubscription indicates an expected call of RevokeSubscription
|
||||||
|
func (mr *MockIABSubscriptionMockRecorder) RevokeSubscription(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeSubscription", reflect.TypeOf((*MockIABSubscription)(nil).RevokeSubscription), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifySubscription mocks base method
|
||||||
|
func (m *MockIABSubscription) VerifySubscription(arg0 context.Context, arg1, arg2, arg3 string) (*v3.SubscriptionPurchase, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "VerifySubscription", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].(*v3.SubscriptionPurchase)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifySubscription indicates an expected call of VerifySubscription
|
||||||
|
func (mr *MockIABSubscriptionMockRecorder) VerifySubscription(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifySubscription", reflect.TypeOf((*MockIABSubscription)(nil).VerifySubscription), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
@@ -11,14 +11,19 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/api/option"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
androidpublisher "google.golang.org/api/androidpublisher/v3"
|
androidpublisher "google.golang.org/api/androidpublisher/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/playstore.go -package=mocks github.com/awa/go-iap/playstore IABProduct,IABSubscription
|
||||||
|
|
||||||
// The IABProduct type is an interface for product service
|
// The IABProduct type is an interface for product service
|
||||||
type IABProduct interface {
|
type IABProduct interface {
|
||||||
VerifyProduct(context.Context, string, string, string) (*androidpublisher.ProductPurchase, error)
|
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
|
// The IABSubscription type is an interface for subscription service
|
||||||
@@ -32,22 +37,41 @@ type IABSubscription interface {
|
|||||||
|
|
||||||
// The Client type implements VerifySubscription method
|
// The Client type implements VerifySubscription method
|
||||||
type Client struct {
|
type Client struct {
|
||||||
httpCli *http.Client
|
service *androidpublisher.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns http client which includes the credentials to access androidpublisher API.
|
// New returns http client which includes the credentials to access androidpublisher API.
|
||||||
// You should create a service account for your project at
|
// 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.
|
// https://console.developers.google.com and download a JSON key file to set this argument.
|
||||||
func New(jsonKey []byte) (*Client, error) {
|
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)
|
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.
|
// NewWithClient returns http client which includes the custom http client.
|
||||||
func NewWithClient(jsonKey []byte, cli *http.Client) (*Client, error) {
|
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)
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cli)
|
||||||
|
|
||||||
conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
|
conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
|
||||||
@@ -55,7 +79,12 @@ func NewWithClient(jsonKey []byte, cli *http.Client) (*Client, error) {
|
|||||||
return nil, err
|
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.
|
// AcknowledgeSubscription acknowledges a subscription purchase.
|
||||||
@@ -66,13 +95,8 @@ func (c *Client) AcknowledgeSubscription(
|
|||||||
token string,
|
token string,
|
||||||
req *androidpublisher.SubscriptionPurchasesAcknowledgeRequest,
|
req *androidpublisher.SubscriptionPurchasesAcknowledgeRequest,
|
||||||
) error {
|
) error {
|
||||||
service, err := androidpublisher.New(c.httpCli)
|
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
|
||||||
if err != nil {
|
err := ps.Acknowledge(packageName, subscriptionID, token, req).Context(ctx).Do()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := androidpublisher.NewPurchasesSubscriptionsService(service)
|
|
||||||
err = ps.Acknowledge(packageName, subscriptionID, token, req).Context(ctx).Do()
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -84,12 +108,7 @@ func (c *Client) VerifySubscription(
|
|||||||
subscriptionID string,
|
subscriptionID string,
|
||||||
token string,
|
token string,
|
||||||
) (*androidpublisher.SubscriptionPurchase, error) {
|
) (*androidpublisher.SubscriptionPurchase, error) {
|
||||||
service, err := androidpublisher.New(c.httpCli)
|
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := androidpublisher.NewPurchasesSubscriptionsService(service)
|
|
||||||
result, err := ps.Get(packageName, subscriptionID, token).Context(ctx).Do()
|
result, err := ps.Get(packageName, subscriptionID, token).Context(ctx).Do()
|
||||||
|
|
||||||
return result, err
|
return result, err
|
||||||
@@ -102,26 +121,24 @@ func (c *Client) VerifyProduct(
|
|||||||
productID string,
|
productID string,
|
||||||
token string,
|
token string,
|
||||||
) (*androidpublisher.ProductPurchase, error) {
|
) (*androidpublisher.ProductPurchase, error) {
|
||||||
service, err := androidpublisher.New(c.httpCli)
|
ps := androidpublisher.NewPurchasesProductsService(c.service)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := androidpublisher.NewPurchasesProductsService(service)
|
|
||||||
result, err := ps.Get(packageName, productID, token).Context(ctx).Do()
|
result, err := ps.Get(packageName, productID, token).Context(ctx).Do()
|
||||||
|
|
||||||
return result, err
|
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.
|
// CancelSubscription cancels a user's subscription purchase.
|
||||||
func (c *Client) CancelSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
|
func (c *Client) CancelSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
|
||||||
service, err := androidpublisher.New(c.httpCli)
|
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
|
||||||
if err != nil {
|
err := ps.Cancel(packageName, subscriptionID, token).Context(ctx).Do()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := androidpublisher.NewPurchasesSubscriptionsService(service)
|
|
||||||
err = ps.Cancel(packageName, subscriptionID, token).Context(ctx).Do()
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -129,13 +146,8 @@ func (c *Client) CancelSubscription(ctx context.Context, packageName string, sub
|
|||||||
// RefundSubscription refunds a user's subscription purchase, but the subscription remains valid
|
// RefundSubscription refunds a user's subscription purchase, but the subscription remains valid
|
||||||
// until its expiration time and it will continue to recur.
|
// until its expiration time and it will continue to recur.
|
||||||
func (c *Client) RefundSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
|
func (c *Client) RefundSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
|
||||||
service, err := androidpublisher.New(c.httpCli)
|
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
|
||||||
if err != nil {
|
err := ps.Refund(packageName, subscriptionID, token).Context(ctx).Do()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := androidpublisher.NewPurchasesSubscriptionsService(service)
|
|
||||||
err = ps.Refund(packageName, subscriptionID, token).Context(ctx).Do()
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -143,13 +155,8 @@ func (c *Client) RefundSubscription(ctx context.Context, packageName string, sub
|
|||||||
// RevokeSubscription refunds and immediately revokes a user's subscription purchase.
|
// RevokeSubscription refunds and immediately revokes a user's subscription purchase.
|
||||||
// Access to the subscription will be terminated immediately and it will stop recurring.
|
// 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 {
|
func (c *Client) RevokeSubscription(ctx context.Context, packageName string, subscriptionID string, token string) error {
|
||||||
service, err := androidpublisher.New(c.httpCli)
|
ps := androidpublisher.NewPurchasesSubscriptionsService(c.service)
|
||||||
if err != nil {
|
err := ps.Revoke(packageName, subscriptionID, token).Context(ctx).Do()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := androidpublisher.NewPurchasesSubscriptionsService(service)
|
|
||||||
err = ps.Revoke(packageName, subscriptionID, token).Context(ctx).Do()
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"google.golang.org/api/androidpublisher/v3"
|
||||||
androidpublisher "google.golang.org/api/androidpublisher/v3"
|
|
||||||
"google.golang.org/appengine/urlfetch"
|
"google.golang.org/appengine/urlfetch"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,22 +35,19 @@ func TestNew(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Exception scenario
|
// 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}"
|
expected := "oauth2: cannot fetch token: 400 Bad Request\nResponse: {\"error\":\"invalid_grant\",\"error_description\":\"Invalid JWT: iss field missing.\"}"
|
||||||
|
|
||||||
actual, _ := New(dummyKey)
|
_, err := New(dummyKey)
|
||||||
val := actual.httpCli.Transport.(*oauth2.Transport)
|
if err == nil || err.Error() != expected {
|
||||||
token, err := val.Source.Token()
|
|
||||||
if token != nil {
|
|
||||||
t.Errorf("got %#v", token)
|
|
||||||
}
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Errorf("got %v\nwant %v", err, expected)
|
t.Errorf("got %v\nwant %v", err, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Normal scenario
|
_, actual := New(nil)
|
||||||
actual, _ = New(jsonKey)
|
if actual == nil || actual.Error() != "unexpected end of JSON input" {
|
||||||
val = actual.httpCli.Transport.(*oauth2.Transport)
|
t.Errorf("got %v\nwant %v", actual, expected)
|
||||||
token, err = val.Source.Token()
|
}
|
||||||
|
|
||||||
|
_, err = New(jsonKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("got %#v", err)
|
t.Errorf("got %#v", err)
|
||||||
}
|
}
|
||||||
@@ -63,14 +59,31 @@ func TestNewWithClient(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
httpClient := urlfetch.Client(ctx)
|
httpClient := urlfetch.Client(ctx)
|
||||||
|
|
||||||
cli, _ := NewWithClient(dummyKey, httpClient)
|
_, err := NewWithClient(dummyKey, httpClient)
|
||||||
tr, _ := cli.httpCli.Transport.(*oauth2.Transport)
|
if err != nil {
|
||||||
|
|
||||||
if !reflect.DeepEqual(tr.Base, httpClient.Transport) {
|
|
||||||
t.Errorf("transport should be urlfetch's one")
|
t.Errorf("transport should be urlfetch's one")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewWithClientErrors(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
httpClient := urlfetch.Client(ctx)
|
||||||
|
|
||||||
|
_, actual = NewWithClient(nil, httpClient)
|
||||||
|
if actual == nil || actual.Error() != "unexpected end of JSON input" {
|
||||||
|
t.Errorf("got %v\nwant %v", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestAcknowledgeSubscription(t *testing.T) {
|
func TestAcknowledgeSubscription(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Exception scenario
|
// Exception scenario
|
||||||
@@ -83,7 +96,7 @@ func TestAcknowledgeSubscription(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.AcknowledgeSubscription(ctx, "package", "subscriptionID", "purchaseToken", req)
|
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)
|
t.Errorf("got %v\nwant %v", err, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,25 +112,13 @@ func TestVerifySubscription(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_, err := client.VerifySubscription(ctx, "package", "subscriptionID", "purchaseToken")
|
_, 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)
|
t.Errorf("got %v\nwant %v", err, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Normal scenario
|
// 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) {
|
func TestVerifyProduct(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Exception scenario
|
// Exception scenario
|
||||||
@@ -127,42 +128,37 @@ func TestVerifyProduct(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_, err := client.VerifyProduct(ctx, "package", "productID", "purchaseToken")
|
_, err := client.VerifyProduct(ctx, "package", "productID", "purchaseToken")
|
||||||
|
|
||||||
if err.Error() != expected {
|
if err == nil || err.Error() != expected {
|
||||||
t.Errorf("got %v", err)
|
t.Errorf("got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Normal scenario
|
// TODO Normal scenario
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifyProductAndroidPublisherError(t *testing.T) {
|
func TestAcknowledgeProduct(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := Client{nil}
|
// Exception scenario
|
||||||
expected := errors.New("client is nil")
|
expected := "googleapi: Error 400: Invalid Value, invalid"
|
||||||
ctx := context.Background()
|
|
||||||
_, actual := client.VerifyProduct(ctx, "package", "productID", "purchaseToken")
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
client, _ := New(jsonKey)
|
||||||
t.Errorf("got %v\nwant %v", actual, expected)
|
ctx := context.Background()
|
||||||
|
err := client.AcknowledgeProduct(ctx, "package", "productID", "purchaseToken", "")
|
||||||
|
|
||||||
|
if err == nil || err.Error() != expected {
|
||||||
|
t.Errorf("got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Normal scenario
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCancelSubscription(t *testing.T) {
|
func TestCancelSubscription(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Exception scenario
|
|
||||||
client := &Client{nil}
|
|
||||||
expected := errors.New("client is nil")
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
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 !reflect.DeepEqual(actual, expected) {
|
if actual == nil || actual.Error() != expectedStr {
|
||||||
t.Errorf("got %v\nwant %v", actual, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, _ = New(jsonKey)
|
|
||||||
expectedStr := "googleapi: Error 400: Invalid Value, invalid"
|
|
||||||
actual = client.CancelSubscription(ctx, "package", "productID", "purchaseToken")
|
|
||||||
|
|
||||||
if actual.Error() != expectedStr {
|
|
||||||
t.Errorf("got %v\nwant %v", actual, expectedStr)
|
t.Errorf("got %v\nwant %v", actual, expectedStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,21 +167,13 @@ func TestCancelSubscription(t *testing.T) {
|
|||||||
|
|
||||||
func TestRefundSubscription(t *testing.T) {
|
func TestRefundSubscription(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Exception scenario
|
|
||||||
client := &Client{nil}
|
|
||||||
expected := errors.New("client is nil")
|
|
||||||
ctx := context.Background()
|
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 !reflect.DeepEqual(actual, expected) {
|
if actual == nil || actual.Error() != expectedStr {
|
||||||
t.Errorf("got %v\nwant %v", actual, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, _ = New(jsonKey)
|
|
||||||
expectedStr := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
|
|
||||||
actual = client.RefundSubscription(ctx, "package", "productID", "purchaseToken")
|
|
||||||
|
|
||||||
if actual.Error() != expectedStr {
|
|
||||||
t.Errorf("got %v\nwant %v", actual, expectedStr)
|
t.Errorf("got %v\nwant %v", actual, expectedStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,21 +182,13 @@ func TestRefundSubscription(t *testing.T) {
|
|||||||
|
|
||||||
func TestRevokeSubscription(t *testing.T) {
|
func TestRevokeSubscription(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Exception scenario
|
|
||||||
client := &Client{nil}
|
|
||||||
expected := errors.New("client is nil")
|
|
||||||
ctx := context.Background()
|
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 !reflect.DeepEqual(actual, expected) {
|
if actual == nil || actual.Error() != expectedStr {
|
||||||
t.Errorf("got %v\nwant %v", actual, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, _ = New(jsonKey)
|
|
||||||
expectedStr := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
|
|
||||||
actual = client.RevokeSubscription(ctx, "package", "productID", "purchaseToken")
|
|
||||||
|
|
||||||
if actual.Error() != expectedStr {
|
|
||||||
t.Errorf("got %v\nwant %v", actual, expectedStr)
|
t.Errorf("got %v\nwant %v", actual, expectedStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user