Compare commits
42 Commits
v1.0.0
...
purchasefl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96a12064d7 | ||
|
|
d8b160b5cb | ||
|
|
e811b02798 | ||
|
|
5b94718573 | ||
|
|
db9e8f1637 | ||
|
|
2b7d6c1820 | ||
|
|
d74d2cfd2c | ||
|
|
d5b4694461 | ||
|
|
b0a4ee19ee | ||
|
|
202310d2c3 | ||
|
|
45aa311b47 | ||
|
|
51a2c89640 | ||
|
|
36d117cd16 | ||
|
|
e28611afcb | ||
|
|
1fe1bd0f92 | ||
|
|
f0ee201e43 | ||
|
|
312aa41817 | ||
|
|
eb89375f2e | ||
|
|
d9ae3a1d88 | ||
|
|
b739b19032 | ||
|
|
be7b768650 | ||
|
|
ac84b97cd8 | ||
|
|
c4c303e812 | ||
|
|
b5222c00cf | ||
|
|
c8962b67cb | ||
|
|
627fa5e7d1 | ||
|
|
b31646baf4 | ||
|
|
1bd99243d7 | ||
|
|
7d624ad068 | ||
|
|
31a625b71e | ||
|
|
4209b06a64 | ||
|
|
3d3bd2b6cf | ||
|
|
934d4ffbbd | ||
|
|
9c0e16f820 | ||
|
|
e895c80eb0 | ||
|
|
062102b0f3 | ||
|
|
f0014d5a79 | ||
|
|
c446edf3c4 | ||
|
|
c6347f9585 | ||
|
|
c8899d8d2d | ||
|
|
ebccfd0170 | ||
|
|
b4e4bec42f |
@@ -1,6 +1,9 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.2
|
||||
- 1.12.5
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
before_install:
|
||||
- sudo pip install codecov
|
||||
install:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
go-iap
|
||||
======
|
||||
|
||||

|
||||

|
||||
[](https://travis-ci.org/awa/go-iap)
|
||||
[](https://codecov.io/github/awa/go-iap?branch=master)
|
||||
|
||||
@@ -14,13 +14,6 @@ Current API Documents:
|
||||
* Amazon AppStore: [](https://godoc.org/github.com/awa/go-iap/amazon)
|
||||
|
||||
|
||||
# Dependencies
|
||||
```
|
||||
go get golang.org/x/oauth2
|
||||
go get golang.org/x/oauth2/google
|
||||
go get google.golang.org/api/androidpublisher/v2
|
||||
```
|
||||
|
||||
# Installation
|
||||
```
|
||||
go get github.com/awa/go-iap/appstore
|
||||
|
||||
@@ -76,16 +76,27 @@ type (
|
||||
CancellationDatePST string `json:"cancellation_date_pst,omitempty"`
|
||||
}
|
||||
|
||||
// The GracePeriodDate type indicates the grace period date for the subscription
|
||||
GracePeriodDate struct {
|
||||
GracePeriodDate string `json:"grace_period_expires_date,omitempty"`
|
||||
GracePeriodDateMS string `json:"grace_period_expires_date_ms,omitempty"`
|
||||
GracePeriodDatePST string `json:"grace_period_expires_date_pst,omitempty"`
|
||||
}
|
||||
|
||||
// The InApp type has the receipt attributes
|
||||
InApp struct {
|
||||
Quantity string `json:"quantity"`
|
||||
ProductID string `json:"product_id"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
OriginalTransactionID string `json:"original_transaction_id"`
|
||||
WebOrderLineItemID string `json:"web_order_line_item_id,omitempty"`
|
||||
Quantity string `json:"quantity"`
|
||||
ProductID string `json:"product_id"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
OriginalTransactionID string `json:"original_transaction_id"`
|
||||
WebOrderLineItemID string `json:"web_order_line_item_id,omitempty"`
|
||||
PromotionalOfferID string `json:"promotional_offer_id"`
|
||||
SubscriptionGroupIdentifier string `json:"subscription_group_identifier"`
|
||||
|
||||
IsTrialPeriod string `json:"is_trial_period"`
|
||||
IsInIntroOfferPeriod string `json:"is_in_intro_offer_period,omitempty"`
|
||||
IsUpgraded string `json:"is_upgraded,omitempty"`
|
||||
|
||||
ExpiresDate
|
||||
|
||||
PurchaseDate
|
||||
@@ -120,6 +131,8 @@ type (
|
||||
SubscriptionPriceConsentStatus string `json:"price_consent_status"`
|
||||
ProductID string `json:"product_id"`
|
||||
OriginalTransactionID string `json:"original_transaction_id"`
|
||||
|
||||
GracePeriodDate
|
||||
}
|
||||
|
||||
// The IAPResponse type has the response properties
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestNumericString_UnmarshalJSON(t *testing.T) {
|
||||
{
|
||||
name: "object case",
|
||||
in: []byte("{\"ID\":{\"Num\": 8080}}"),
|
||||
err: errors.New("json: cannot unmarshal object into Go value of type json.Number"),
|
||||
err: errors.New("json: cannot unmarshal object into Go struct field foo.ID of type json.Number"),
|
||||
out: foo{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ const (
|
||||
// Subscription was canceled by Apple customer support.
|
||||
NotificationTypeCancel NotificationType = "CANCEL"
|
||||
// Automatic renewal was successful for an expired subscription.
|
||||
// Deprecated: DID_RECOVER should be used instead of RENEWAL
|
||||
NotificationTypeRenewal NotificationType = "RENEWAL"
|
||||
// Expired subscription recovered through a billing retry.
|
||||
NotificationTypeDidRecover NotificationType = "DID_RECOVER"
|
||||
// Customer renewed a subscription interactively after it lapsed.
|
||||
NotificationTypeInteractiveRenewal NotificationType = "INTERACTIVE_RENEWAL"
|
||||
// Customer changed the plan that takes affect at the next subscription renewal. Current active plan is not affected.
|
||||
@@ -53,6 +56,14 @@ type NotificationReceipt struct {
|
||||
CancellationDate
|
||||
}
|
||||
|
||||
type NotificationUnifiedReceipt struct {
|
||||
Status int `json:"status"`
|
||||
Environment Environment `json:"environment"`
|
||||
LatestReceipt string `json:"latest_receipt"`
|
||||
LatestReceiptInfo []InApp `json:"latest_receipt_info"`
|
||||
PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
|
||||
}
|
||||
|
||||
type SubscriptionNotification struct {
|
||||
Environment NotificationEnvironment `json:"environment"`
|
||||
NotificationType NotificationType `json:"notification_type"`
|
||||
@@ -71,17 +82,29 @@ type SubscriptionNotification struct {
|
||||
ExpirationIntent string `json:"expiration_intent"`
|
||||
|
||||
// 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"`
|
||||
|
||||
// HACK (msyrus): Separate Subscriptiton Notification from Notification verification response
|
||||
Status int `json:"status,omitempty"`
|
||||
Receipt NotificationReceipt `json:"recipt"`
|
||||
SubscriptionRetryFlag string `json:"is_in_billing_retry_period,omitempty"`
|
||||
|
||||
// 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.
|
||||
// Not posted for notification_type CANCEL.
|
||||
// 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"`
|
||||
|
||||
// In the new notifications above properties latest_receipt, latest_receipt_info are moved under this one
|
||||
UnifiedReceipt NotificationUnifiedReceipt `json:"unified_receipt"`
|
||||
|
||||
// Posted only if the notification_type is RENEWAL or CANCEL or if renewal failed and subscription expired.
|
||||
// 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"`
|
||||
|
||||
// Posted only if the notification_type is CANCEL.
|
||||
|
||||
@@ -96,64 +96,64 @@ func NewWithClient(client *http.Client) *Client {
|
||||
}
|
||||
|
||||
// Verify sends receipts and gets validation result
|
||||
func (c *Client) Verify(ctx context.Context, reqBody IAPRequest, result interface{}) error {
|
||||
func (c *Client) Verify(ctx context.Context, reqBody IAPRequest, result interface{}) (Environment, error) {
|
||||
b := new(bytes.Buffer)
|
||||
if err := json.NewEncoder(b).Encode(reqBody); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", c.ProductionURL, b)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", ContentType)
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := c.httpCli.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return c.parseResponse(resp, result, ctx, reqBody)
|
||||
}
|
||||
|
||||
func (c *Client) parseResponse(resp *http.Response, result interface{}, ctx context.Context, reqBody IAPRequest) error {
|
||||
func (c *Client) parseResponse(resp *http.Response, result interface{}, ctx context.Context, reqBody IAPRequest) (Environment, error) {
|
||||
// Read the body now so that we can unmarshal it twice
|
||||
buf, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPTURL
|
||||
var r StatusResponse
|
||||
err = json.Unmarshal(buf, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if r.Status == 21007 {
|
||||
b := new(bytes.Buffer)
|
||||
if err := json.NewEncoder(b).Encode(reqBody); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", c.SandboxURL, b)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", ContentType)
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := c.httpCli.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(result)
|
||||
// 21007 is found when the receipt is from the test environment
|
||||
return Sandbox, json.NewDecoder(resp.Body).Decode(result)
|
||||
}
|
||||
|
||||
return nil
|
||||
return Production, nil
|
||||
}
|
||||
|
||||
15
go.mod
Normal file
15
go.mod
Normal file
@@ -0,0 +1,15 @@
|
||||
module github.com/awa/go-iap
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.39.0 // indirect
|
||||
go.opencensus.io v0.22.0 // indirect
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0
|
||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 // indirect
|
||||
google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f
|
||||
google.golang.org/appengine v1.6.0
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 // indirect
|
||||
google.golang.org/grpc v1.21.0 // indirect
|
||||
)
|
||||
93
go.sum
Normal file
93
go.sum
Normal file
@@ -0,0 +1,93 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
|
||||
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/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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
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.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
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/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/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/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/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/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
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/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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-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/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-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-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
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-20190523182746-aaccbc9213b0 h1:xFEXbcD0oa/xhqQmMXztdZ0bWvexAWds+8c1gRN8nu0=
|
||||
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/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/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.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/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/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-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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
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.1-0.20190526001144-9f3a303b451f h1:tAgkkP6ovjCY8HraRtpXwh0CVqHwGqEAVLHwXyfFjIM=
|
||||
google.golang.org/api v0.5.1-0.20190526001144-9f3a303b451f/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
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/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
|
||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
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/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-20190530194941-fb225487d101 h1:wuGevabY6r+ivPNagjUXGGxF+GqgMd+dBhjsxW4q9u4=
|
||||
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/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
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=
|
||||
@@ -13,13 +13,18 @@ import (
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
androidpublisher "google.golang.org/api/androidpublisher/v2"
|
||||
androidpublisher "google.golang.org/api/androidpublisher/v3"
|
||||
)
|
||||
|
||||
// The IABClient type is an interface to verify purchase token
|
||||
type IABClient interface {
|
||||
VerifySubscription(context.Context, string, string, string) (*androidpublisher.SubscriptionPurchase, error)
|
||||
// The IABProduct type is an interface for product service
|
||||
type IABProduct interface {
|
||||
VerifyProduct(context.Context, string, string, string) (*androidpublisher.ProductPurchase, error)
|
||||
}
|
||||
|
||||
// The IABSubscription type is an interface for subscription service
|
||||
type IABSubscription interface {
|
||||
AcknowledgeSubscription(context.Context, string, string, string, *androidpublisher.SubscriptionPurchasesAcknowledgeRequest) error
|
||||
VerifySubscription(context.Context, string, string, string) (*androidpublisher.SubscriptionPurchase, error)
|
||||
CancelSubscription(context.Context, string, string, string) error
|
||||
RefundSubscription(context.Context, string, string, string) error
|
||||
RevokeSubscription(context.Context, string, string, string) error
|
||||
@@ -53,6 +58,25 @@ func NewWithClient(jsonKey []byte, cli *http.Client) (*Client, error) {
|
||||
return &Client{conf.Client(ctx)}, err
|
||||
}
|
||||
|
||||
// AcknowledgeSubscription acknowledges a subscription purchase.
|
||||
func (c *Client) AcknowledgeSubscription(
|
||||
ctx context.Context,
|
||||
packageName string,
|
||||
subscriptionID string,
|
||||
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()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// VerifySubscription verifies subscription status
|
||||
func (c *Client) VerifySubscription(
|
||||
ctx context.Context,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
androidpublisher "google.golang.org/api/androidpublisher/v3"
|
||||
"google.golang.org/appengine/urlfetch"
|
||||
)
|
||||
|
||||
@@ -70,10 +71,29 @@ func TestNewWithClient(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcknowledgeSubscription(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Exception scenario
|
||||
expected := "googleapi: Error 400: Invalid Value, invalid"
|
||||
|
||||
client, _ := New(jsonKey)
|
||||
ctx := context.Background()
|
||||
req := &androidpublisher.SubscriptionPurchasesAcknowledgeRequest{
|
||||
DeveloperPayload: "user001",
|
||||
}
|
||||
err := client.AcknowledgeSubscription(ctx, "package", "subscriptionID", "purchaseToken", req)
|
||||
|
||||
if err.Error() != expected {
|
||||
t.Errorf("got %v\nwant %v", err, expected)
|
||||
}
|
||||
|
||||
// TODO Normal scenario
|
||||
}
|
||||
|
||||
func TestVerifySubscription(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Exception scenario
|
||||
expected := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
|
||||
expected := "googleapi: Error 400: Invalid Value, invalid"
|
||||
|
||||
client, _ := New(jsonKey)
|
||||
ctx := context.Background()
|
||||
@@ -101,7 +121,7 @@ func TestVerifySubscriptionAndroidPublisherError(t *testing.T) {
|
||||
func TestVerifyProduct(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Exception scenario
|
||||
expected := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
|
||||
expected := "googleapi: Error 400: Invalid Value, invalid"
|
||||
|
||||
client, _ := New(jsonKey)
|
||||
ctx := context.Background()
|
||||
@@ -139,7 +159,7 @@ func TestCancelSubscription(t *testing.T) {
|
||||
}
|
||||
|
||||
client, _ = New(jsonKey)
|
||||
expectedStr := "googleapi: Error 404: No application was found for the given package name., applicationNotFound"
|
||||
expectedStr := "googleapi: Error 400: Invalid Value, invalid"
|
||||
actual = client.CancelSubscription(ctx, "package", "productID", "purchaseToken")
|
||||
|
||||
if actual.Error() != expectedStr {
|
||||
|
||||
Reference in New Issue
Block a user