forked from Mirrors/go-iap
Use new struct to check status & Remove IAP_ENVIRONMENT
Use a new struct called `HttpStatusResponse` to check the status code returned in the receipt. This is ios-type independent and will redirect the receipt to sandbox if needed. Following Apple's recommendation, always hit production and then sandbox. There is no more need to specify which environment to hit.
This commit is contained in:
@@ -102,7 +102,7 @@ type (
|
|||||||
// We defined each field by the current IAP response, but some fields are not mentioned
|
// We defined each field by the current IAP response, but some fields are not mentioned
|
||||||
// in the following Apple's document;
|
// in the following Apple's document;
|
||||||
// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
|
// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
|
||||||
// If you get other types or fileds from the IAP response, you should use the struct you defined.
|
// If you get other types or fields from the IAP response, you should use the struct you defined.
|
||||||
IAPResponse struct {
|
IAPResponse struct {
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
Environment string `json:"environment"`
|
Environment string `json:"environment"`
|
||||||
@@ -112,4 +112,10 @@ type (
|
|||||||
PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info"`
|
PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info"`
|
||||||
IsRetryable bool `json:"is-retryable"`
|
IsRetryable bool `json:"is-retryable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The HttpStatusResponse struct contains the status code returned by the store
|
||||||
|
// Used as a workaround to detect when to hit the production appstore or sandbox appstore regardless of receipt type
|
||||||
|
HttpStatusResponse struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,7 +17,6 @@ const (
|
|||||||
|
|
||||||
// Config is a configuration to initialize client
|
// Config is a configuration to initialize client
|
||||||
type Config struct {
|
type Config struct {
|
||||||
IsProduction bool
|
|
||||||
TimeOut time.Duration
|
TimeOut time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,9 +27,9 @@ type IAPClient interface {
|
|||||||
|
|
||||||
// Client implements IAPClient
|
// Client implements IAPClient
|
||||||
type Client struct {
|
type Client struct {
|
||||||
URL string
|
ProductionURL string
|
||||||
TimeOut time.Duration
|
|
||||||
SandboxURL string
|
SandboxURL string
|
||||||
|
TimeOut time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleError returns error message by status code
|
// HandleError returns error message by status code
|
||||||
@@ -80,12 +78,9 @@ func HandleError(status int) error {
|
|||||||
// New creates a client object
|
// New creates a client object
|
||||||
func New() Client {
|
func New() Client {
|
||||||
client := Client{
|
client := Client{
|
||||||
URL: SandboxURL,
|
ProductionURL: ProductionURL,
|
||||||
TimeOut: time.Second * 5,
|
|
||||||
SandboxURL: SandboxURL,
|
SandboxURL: SandboxURL,
|
||||||
}
|
TimeOut: time.Second * 5,
|
||||||
if os.Getenv("IAP_ENVIRONMENT") == "production" {
|
|
||||||
client.URL = ProductionURL
|
|
||||||
}
|
}
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
@@ -97,12 +92,10 @@ func NewWithConfig(config Config) Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := Client{
|
client := Client{
|
||||||
URL: SandboxURL,
|
ProductionURL: ProductionURL,
|
||||||
|
SandboxURL: SandboxURL,
|
||||||
TimeOut: config.TimeOut,
|
TimeOut: config.TimeOut,
|
||||||
}
|
}
|
||||||
if config.IsProduction {
|
|
||||||
client.URL = ProductionURL
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
@@ -116,7 +109,7 @@ func (c *Client) Verify(req IAPRequest, result interface{}) error {
|
|||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
json.NewEncoder(b).Encode(req)
|
json.NewEncoder(b).Encode(req)
|
||||||
|
|
||||||
resp, err := client.Post(c.URL, "application/json; charset=utf-8", b)
|
resp, err := client.Post(c.ProductionURL, "application/json; charset=utf-8", b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -127,11 +120,8 @@ func (c *Client) Verify(req IAPRequest, result interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always verify your receipt first with the production URL; proceed to verify with the sandbox URL if you receive
|
|
||||||
// a 21007 status code
|
|
||||||
//
|
|
||||||
// https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPTURL
|
// https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPTURL
|
||||||
r, ok := result.(*IAPResponse)
|
r, ok := result.(*HttpStatusResponse)
|
||||||
if ok && r.Status == 21007 {
|
if ok && r.Status == 21007 {
|
||||||
b = new(bytes.Buffer)
|
b = new(bytes.Buffer)
|
||||||
json.NewEncoder(b).Encode(req)
|
json.NewEncoder(b).Encode(req)
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package appstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
"net/http/httptest"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandleError(t *testing.T) {
|
func TestHandleError(t *testing.T) {
|
||||||
@@ -93,9 +93,9 @@ func TestHandleError(t *testing.T) {
|
|||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
expected := Client{
|
expected := Client{
|
||||||
URL: SandboxURL,
|
ProductionURL: SandboxURL,
|
||||||
TimeOut: time.Second * 5,
|
TimeOut: time.Second * 5,
|
||||||
SandboxURL:SandboxURL,
|
SandboxURL: SandboxURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := New()
|
actual := New()
|
||||||
@@ -106,9 +106,9 @@ func TestNew(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewWithEnvironment(t *testing.T) {
|
func TestNewWithEnvironment(t *testing.T) {
|
||||||
expected := Client{
|
expected := Client{
|
||||||
URL: ProductionURL,
|
ProductionURL: ProductionURL,
|
||||||
TimeOut: time.Second * 5,
|
TimeOut: time.Second * 5,
|
||||||
SandboxURL:SandboxURL,
|
SandboxURL: SandboxURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Setenv("IAP_ENVIRONMENT", "production")
|
os.Setenv("IAP_ENVIRONMENT", "production")
|
||||||
@@ -122,12 +122,11 @@ func TestNewWithEnvironment(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewWithConfig(t *testing.T) {
|
func TestNewWithConfig(t *testing.T) {
|
||||||
config := Config{
|
config := Config{
|
||||||
IsProduction: true,
|
|
||||||
TimeOut: time.Second * 2,
|
TimeOut: time.Second * 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := Client{
|
expected := Client{
|
||||||
URL: ProductionURL,
|
ProductionURL: ProductionURL,
|
||||||
TimeOut: time.Second * 2,
|
TimeOut: time.Second * 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +137,10 @@ func TestNewWithConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewWithConfigTimeout(t *testing.T) {
|
func TestNewWithConfigTimeout(t *testing.T) {
|
||||||
config := Config{
|
config := Config{}
|
||||||
IsProduction: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := Client{
|
expected := Client{
|
||||||
URL: ProductionURL,
|
ProductionURL: ProductionURL,
|
||||||
TimeOut: time.Second * 5,
|
TimeOut: time.Second * 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +166,7 @@ func TestVerifyTimeout(t *testing.T) {
|
|||||||
|
|
||||||
func TestVerifyBadURL(t *testing.T) {
|
func TestVerifyBadURL(t *testing.T) {
|
||||||
client := New()
|
client := New()
|
||||||
client.URL = "127.0.0.1"
|
client.ProductionURL = "127.0.0.1"
|
||||||
|
|
||||||
req := IAPRequest{
|
req := IAPRequest{
|
||||||
ReceiptData: "dummy data",
|
ReceiptData: "dummy data",
|
||||||
@@ -186,7 +183,7 @@ func TestVerifyBadPayload(t *testing.T) {
|
|||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
client := New()
|
client := New()
|
||||||
client.URL = s.URL
|
client.ProductionURL = s.URL
|
||||||
expected := &IAPResponse{
|
expected := &IAPResponse{
|
||||||
Status: 21002,
|
Status: 21002,
|
||||||
}
|
}
|
||||||
@@ -209,7 +206,7 @@ func TestVerifyBadResponse(t *testing.T) {
|
|||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
client := New()
|
client := New()
|
||||||
client.URL = s.URL
|
client.ProductionURL = s.URL
|
||||||
req := IAPRequest{
|
req := IAPRequest{
|
||||||
ReceiptData: "dummy data",
|
ReceiptData: "dummy data",
|
||||||
}
|
}
|
||||||
@@ -229,7 +226,7 @@ func TestVerifySandboxReceipt(t *testing.T) {
|
|||||||
defer sandboxServ.Close()
|
defer sandboxServ.Close()
|
||||||
|
|
||||||
client := New()
|
client := New()
|
||||||
client.URL = s.URL
|
client.ProductionURL = s.URL
|
||||||
client.TimeOut = time.Second * 100
|
client.TimeOut = time.Second * 100
|
||||||
client.SandboxURL = sandboxServ.URL
|
client.SandboxURL = sandboxServ.URL
|
||||||
|
|
||||||
@@ -255,7 +252,7 @@ func TestVerifySandboxReceiptFailure(t *testing.T) {
|
|||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
client := New()
|
client := New()
|
||||||
client.URL = s.URL
|
client.ProductionURL = s.URL
|
||||||
client.TimeOut = time.Second * 100
|
client.TimeOut = time.Second * 100
|
||||||
client.SandboxURL = "localhost"
|
client.SandboxURL = "localhost"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user