Merge pull request #65 from jun06t/amazon

Support custom client for Amazon
This commit is contained in:
Junpei Tsuji
2018-05-18 12:30:47 +09:00
committed by GitHub
5 changed files with 45 additions and 75 deletions

View File

@@ -1,6 +1,6 @@
language: go language: go
go: go:
- 1.9 - 1.10.2
before_install: before_install:
- sudo pip install codecov - sudo pip install codecov
install: install:

View File

@@ -12,10 +12,4 @@ test:
go test -v ./... go test -v ./...
cover: cover:
go test -v -coverprofile=coverage.txt -covermode=count ./appstore go test -coverprofile=coverage.txt ./...
go test -v -coverprofile=playstore.txt -covermode=count ./playstore
cat playstore.txt | grep -v "mode: count" >> coverage.txt
rm playstore.txt
go test -v -coverprofile=amazon.txt -covermode=count ./amazon
cat amazon.txt | grep -v "mode: count" >> coverage.txt
rm amazon.txt

View File

@@ -1,7 +1,7 @@
go-iap go-iap
====== ======
![](https://img.shields.io/badge/golang-1.9-blue.svg?style=flat) ![](https://img.shields.io/badge/golang-1.10-blue.svg?style=flat)
[![Build Status](https://travis-ci.org/awa/go-iap.svg?branch=master)](https://travis-ci.org/awa/go-iap) [![Build Status](https://travis-ci.org/awa/go-iap.svg?branch=master)](https://travis-ci.org/awa/go-iap)
[![codecov.io](https://codecov.io/github/awa/go-iap/coverage.svg?branch=master)](https://codecov.io/github/awa/go-iap?branch=master) [![codecov.io](https://codecov.io/github/awa/go-iap/coverage.svg?branch=master)](https://codecov.io/github/awa/go-iap?branch=master)

View File

@@ -1,12 +1,12 @@
package amazon package amazon
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"time"
) )
const ( const (
@@ -24,13 +24,6 @@ func getSandboxURL() string {
return url return url
} }
// Config is a configuration to initialize client
type Config struct {
IsProduction bool
Secret string
TimeOut time.Duration
}
// The IAPResponse type has the response properties // The IAPResponse type has the response properties
type IAPResponse struct { type IAPResponse struct {
ReceiptID string `json:"receiptId"` ReceiptID string `json:"receiptId"`
@@ -49,41 +42,38 @@ type IAPResponseError struct {
// IAPClient is an interface to call validation API in Amazon App Store // IAPClient is an interface to call validation API in Amazon App Store
type IAPClient interface { type IAPClient interface {
Verify(string, string) (IAPResponse, error) Verify(context.Context, string, string) (IAPResponse, error)
} }
// Client implements IAPClient // Client implements IAPClient
type Client struct { type Client struct {
URL string URL string
Secret string Secret string
TimeOut time.Duration httpCli *http.Client
} }
// New creates a client object // New creates a client object
func New(secret string) IAPClient { func New(secret string) *Client {
client := Client{ client := &Client{
URL: getSandboxURL(), URL: getSandboxURL(),
Secret: secret, Secret: secret,
TimeOut: time.Second * 5, httpCli: http.DefaultClient,
} }
if os.Getenv("IAP_ENVIRONMENT") == "production" { if os.Getenv("IAP_ENVIRONMENT") == "production" {
client.URL = ProductionURL client.URL = ProductionURL
} }
return client return client
} }
// NewWithConfig creates a client with configuration // NewWithClient creates a client with a custom client.
func NewWithConfig(config Config) Client { func NewWithClient(secret string, cli *http.Client) *Client {
if config.TimeOut == 0 { client := &Client{
config.TimeOut = time.Second * 5
}
client := Client{
URL: getSandboxURL(), URL: getSandboxURL(),
Secret: config.Secret, Secret: secret,
TimeOut: config.TimeOut, httpCli: cli,
} }
if config.IsProduction { if os.Getenv("IAP_ENVIRONMENT") == "production" {
client.URL = ProductionURL client.URL = ProductionURL
} }
@@ -91,13 +81,16 @@ func NewWithConfig(config Config) Client {
} }
// Verify sends receipts and gets validation result // Verify sends receipts and gets validation result
func (c Client) Verify(userID string, receiptID string) (IAPResponse, error) { func (c *Client) Verify(ctx context.Context, userID string, receiptID string) (IAPResponse, error) {
result := IAPResponse{} result := IAPResponse{}
url := fmt.Sprintf("%v/version/1.0/verifyReceiptId/developer/%v/user/%v/receiptId/%v", c.URL, c.Secret, userID, receiptID) url := fmt.Sprintf("%v/version/1.0/verifyReceiptId/developer/%v/user/%v/receiptId/%v", c.URL, c.Secret, userID, receiptID)
client := http.Client{ req, err := http.NewRequest("GET", url, nil)
Timeout: c.TimeOut, if err != nil {
return result, err
} }
resp, err := client.Get(url) req = req.WithContext(ctx)
resp, err := c.httpCli.Do(req)
if err != nil { if err != nil {
return result, fmt.Errorf("%v", err) return result, fmt.Errorf("%v", err)
} }

View File

@@ -1,6 +1,7 @@
package amazon package amazon
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@@ -25,6 +26,7 @@ func TestHandle497Error(t *testing.T) {
// status 400 // status 400
expected = errors.New("Purchase token/app user mismatch") expected = errors.New("Purchase token/app user mismatch")
_, actual = client.Verify( _, actual = client.Verify(
context.Background(),
"99FD_DL23EMhrOGDnur9-ulvqomrSg6qyLPSD3CFE=", "99FD_DL23EMhrOGDnur9-ulvqomrSg6qyLPSD3CFE=",
"q1YqVrJSSs7P1UvMTazKz9PLTCwoTswtyEktM9JLrShIzCvOzM-LL04tiTdW0lFKASo2NDEwMjCwMDM2MTC0AIqVAsUsLd1c4l18jIxdfTOK_N1d8kqLLHVLc8oK83OLgtPNCit9AoJdjJ3dXG2BGkqUrAxrAQ", "q1YqVrJSSs7P1UvMTazKz9PLTCwoTswtyEktM9JLrShIzCvOzM-LL04tiTdW0lFKASo2NDEwMjCwMDM2MTC0AIqVAsUsLd1c4l18jIxdfTOK_N1d8kqLLHVLc8oK83OLgtPNCit9AoJdjJ3dXG2BGkqUrAxrAQ",
) )
@@ -47,6 +49,7 @@ func TestHandle400Error(t *testing.T) {
// status 400 // status 400
expected = errors.New("Failed to parse receipt Id") expected = errors.New("Failed to parse receipt Id")
_, actual = client.Verify( _, actual = client.Verify(
context.Background(),
"99FD_DL23EMhrOGDnur9-ulvqomrSg6qyLPSD3CFE=", "99FD_DL23EMhrOGDnur9-ulvqomrSg6qyLPSD3CFE=",
"q1YqVrJSSs7P1UvMTazKz9PLTCwoTswtyEktM9JLrShIzCvOzM-LL04tiTdW0lFKASo2NDEwMjCwMDM2MTC0AIqVAsUsLd1c4l18jIxdfTOK_N1d8kqLLHVLc8oK83OLgtPNCit9AoJdjJ3dXG2BGkqUrAxrAQ", "q1YqVrJSSs7P1UvMTazKz9PLTCwoTswtyEktM9JLrShIzCvOzM-LL04tiTdW0lFKASo2NDEwMjCwMDM2MTC0AIqVAsUsLd1c4l18jIxdfTOK_N1d8kqLLHVLc8oK83OLgtPNCit9AoJdjJ3dXG2BGkqUrAxrAQ",
) )
@@ -56,11 +59,10 @@ func TestHandle400Error(t *testing.T) {
} }
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
t.Parallel() expected := &Client{
expected := Client{
URL: SandboxURL, URL: SandboxURL,
TimeOut: time.Second * 5,
Secret: "developerSecret", Secret: "developerSecret",
httpCli: http.DefaultClient,
} }
actual := New("developerSecret") actual := New("developerSecret")
@@ -70,11 +72,10 @@ func TestNew(t *testing.T) {
} }
func TestNewWithEnvironment(t *testing.T) { func TestNewWithEnvironment(t *testing.T) {
t.Parallel() expected := &Client{
expected := Client{
URL: ProductionURL, URL: ProductionURL,
TimeOut: time.Second * 5,
Secret: "developerSecret", Secret: "developerSecret",
httpCli: http.DefaultClient,
} }
os.Setenv("IAP_ENVIRONMENT", "production") os.Setenv("IAP_ENVIRONMENT", "production")
@@ -86,40 +87,20 @@ func TestNewWithEnvironment(t *testing.T) {
} }
} }
func TestNewWithConfig(t *testing.T) { func TestNewWithClient(t *testing.T) {
t.Parallel() expected := &Client{
config := Config{ URL: ProductionURL,
IsProduction: true, Secret: "developerSecret",
Secret: "developerSecret", httpCli: &http.Client{
TimeOut: time.Second * 2, Timeout: time.Second * 2,
},
} }
os.Setenv("IAP_ENVIRONMENT", "production")
expected := Client{ cli := &http.Client{
URL: ProductionURL, Timeout: time.Second * 2,
TimeOut: time.Second * 2,
Secret: "developerSecret",
} }
actual := NewWithClient("developerSecret", cli)
actual := NewWithConfig(config)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected)
}
}
func TestNewWithConfigTimeout(t *testing.T) {
t.Parallel()
config := Config{
IsProduction: true,
Secret: "developerSecret",
}
expected := Client{
URL: ProductionURL,
TimeOut: time.Second * 5,
Secret: "developerSecret",
}
actual := NewWithConfig(config)
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {
t.Errorf("got %v\nwant %v", actual, expected) t.Errorf("got %v\nwant %v", actual, expected)
} }
@@ -143,6 +124,7 @@ func TestVerify(t *testing.T) {
} }
actual, _ := client.Verify( actual, _ := client.Verify(
context.Background(),
"99FD_DL23EMhrOGDnur9-ulvqomrSg6qyLPSD3CFE=", "99FD_DL23EMhrOGDnur9-ulvqomrSg6qyLPSD3CFE=",
"q1YqVrJSSs7P1UvMTazKz9PLTCwoTswtyEktM9JLrShIzCvOzM-LL04tiTdW0lFKASo2NDEwMjCwMDM2MTC0AIqVAsUsLd1c4l18jIxdfTOK_N1d8kqLLHVLc8oK83OLgtPNCit9AoJdjJ3dXG2BGkqUrAxrAQ", "q1YqVrJSSs7P1UvMTazKz9PLTCwoTswtyEktM9JLrShIzCvOzM-LL04tiTdW0lFKASo2NDEwMjCwMDM2MTC0AIqVAsUsLd1c4l18jIxdfTOK_N1d8kqLLHVLc8oK83OLgtPNCit9AoJdjJ3dXG2BGkqUrAxrAQ",
) )
@@ -158,7 +140,8 @@ func TestVerifyTimeout(t *testing.T) {
defer server.Close() defer server.Close()
expected := errors.New("") expected := errors.New("")
_, actual := client.Verify("timeout", "timeout") ctx := context.Background()
_, actual := client.Verify(ctx, "timeout", "timeout")
if !reflect.DeepEqual(reflect.TypeOf(actual), reflect.TypeOf(expected)) { if !reflect.DeepEqual(reflect.TypeOf(actual), reflect.TypeOf(expected)) {
t.Errorf("got %v\nwant %v", actual, expected) t.Errorf("got %v\nwant %v", actual, expected)
} }
@@ -172,6 +155,6 @@ func testTools(code int, body string) (*httptest.Server, *Client) {
fmt.Fprintln(w, body) fmt.Fprintln(w, body)
})) }))
client := &Client{URL: server.URL, TimeOut: time.Second * 2, Secret: "developerSecret"} client := &Client{URL: server.URL, Secret: "developerSecret", httpCli: &http.Client{Timeout: 2 * time.Second}}
return server, client return server, client
} }