Skip to content

Commit f149686

Browse files
committed
feat(api): add error
1 parent 2f4f795 commit f149686

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

error.go

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package appstore
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"strconv"
8+
)
9+
10+
type Error struct {
11+
errorCode int
12+
errorMessage string
13+
14+
// retryAfter is the number of seconds after which the client can retry the request.
15+
// This field is only set to the `Retry-After` header if you receive the HTTP 429 error, that informs you when you can next send a request.
16+
retryAfter int64
17+
}
18+
19+
func newError(errorCode int, errorMessage string) *Error {
20+
return &Error{
21+
errorCode: errorCode,
22+
errorMessage: errorMessage,
23+
}
24+
}
25+
26+
type appStoreAPIErrorResp struct {
27+
ErrorCode int `json:"errorCode"`
28+
ErrorMessage string `json:"errorMessage"`
29+
}
30+
31+
func newAppStoreAPIError(b []byte, hd http.Header) (*Error, bool) {
32+
if len(b) == 0 {
33+
return nil, false
34+
}
35+
var rErr appStoreAPIErrorResp
36+
if err := json.Unmarshal(b, &rErr); err != nil {
37+
return nil, false
38+
}
39+
if rErr.ErrorCode == 0 {
40+
return nil, false
41+
}
42+
if rErr.ErrorCode == 4290000 {
43+
retryAfter, err := strconv.ParseInt(hd.Get("Retry-After"), 10, 64)
44+
if err == nil {
45+
return &Error{errorCode: rErr.ErrorCode, errorMessage: rErr.ErrorMessage, retryAfter: retryAfter}, true
46+
}
47+
}
48+
return &Error{errorCode: rErr.ErrorCode, errorMessage: rErr.ErrorMessage}, true
49+
}
50+
51+
func (e *Error) Error() string {
52+
return fmt.Sprintf("errorCode: %d, errorMessage: %s", e.errorCode, e.errorMessage)
53+
}
54+
55+
func (e *Error) As(target interface{}) bool {
56+
if targetErr, ok := target.(*Error); ok {
57+
*targetErr = *e
58+
return true
59+
}
60+
return false
61+
}
62+
63+
func (e *Error) Is(target error) bool {
64+
if other, ok := target.(*Error); ok && other.errorCode == e.errorCode {
65+
return true
66+
}
67+
return false
68+
}
69+
70+
func (e *Error) ErrorCode() int {
71+
return e.errorCode
72+
}
73+
74+
func (e *Error) ErrorMessage() string {
75+
return e.errorMessage
76+
}
77+
78+
func (e *Error) RetryAfter() int64 {
79+
return e.retryAfter
80+
}
81+
82+
var (
83+
// Retryable errors
84+
AccountNotFoundRetryableError = newError(4040002, "Account not found. Please try again.")
85+
AppNotFoundRetryableError = newError(4040004, "App not found. Please try again.")
86+
GeneralInternalRetryableError = newError(5000001, "An unknown error occurred. Please try again.")
87+
OriginalTransactionIdNotFoundRetryableError = newError(4040006, "Original transaction id not found. Please try again.")
88+
// Errors
89+
AccountNotFoundError = newError(4040001, "Account not found.")
90+
AppNotFoundError = newError(4040003, "App not found.")
91+
FamilySharedSubscriptionExtensionIneligibleError = newError(4030007, "Subscriptions that users obtain through Family Sharing can't get a renewal date extension directly.")
92+
GeneralInternalError = newError(5000000, "An unknown error occurred.")
93+
GeneralBadRequestError = newError(4000000, "Bad request.")
94+
InvalidAppIdentifierError = newError(4000002, "Invalid request app identifier.")
95+
InvalidEmptyStorefrontCountryCodeListError = newError(4000027, "Invalid request. If provided, the list of storefront country codes must not be empty.")
96+
InvalidExtendByDaysError = newError(4000009, "Invalid extend by days value.")
97+
InvalidExtendReasonCodeError = newError(4000010, "Invalid extend reason code.")
98+
InvalidOriginalTransactionIdError = newError(4000008, "Invalid original transaction id.")
99+
InvalidRequestIdentifierError = newError(4000011, "Invalid request identifier.")
100+
InvalidRequestRevisionError = newError(4000005, "Invalid request revision.")
101+
InvalidRevokedError = newError(4000030, "Invalid request. The revoked parameter is invalid.")
102+
InvalidStatusError = newError(4000031, "Invalid request. The status parameter is invalid.")
103+
InvalidStorefrontCountryCodeError = newError(4000028, "Invalid request. A storefront country code was invalid.")
104+
InvalidTransactionIdError = newError(4000006, "Invalid transaction id.")
105+
OriginalTransactionIdNotFoundError = newError(4040005, "Original transaction id not found.")
106+
RateLimitExceededError = newError(4290000, "Rate limit exceeded.")
107+
StatusRequestNotFoundError = newError(4040009, "The server didn't find a subscription-renewal-date extension request for this requestIdentifier and productId combination.")
108+
SubscriptionExtensionIneligibleError = newError(4030004, "Forbidden - subscription state ineligible for extension.")
109+
SubscriptionMaxExtensionError = newError(4030005, "Forbidden - subscription has reached maximum extension count.")
110+
TransactionIdNotFoundError = newError(4040010, "Transaction id not found.")
111+
// Notification test and history errors
112+
InvalidEndDateError = newError(4000016, "Invalid request. The end date is not a timestamp value represented in milliseconds.")
113+
InvalidNotificationTypeError = newError(4000018, "Invalid request. The notification type or subtype is invalid.")
114+
InvalidPaginationTokenError = newError(4000014, "Invalid request. The pagination token is invalid.")
115+
InvalidStartDateError = newError(4000015, "Invalid request. The start date is not a timestamp value represented in milliseconds.")
116+
InvalidTestNotificationTokenError = newError(4000020, "Invalid request. The test notification token is invalid.")
117+
InvalidInAppOwnershipTypeError = newError(4000026, "Invalid request. The in-app ownership type parameter is invalid.")
118+
InvalidProductIdError = newError(4000023, "Invalid request. The product id parameter is invalid.")
119+
InvalidProductTypeError = newError(4000022, "Invalid request. The product type parameter is invalid.")
120+
InvalidSortError = newError(4000021, "Invalid request. The sort parameter is invalid.")
121+
InvalidSubscriptionGroupIdentifierError = newError(4000024, "Invalid request. The subscription group identifier parameter is invalid.")
122+
MultipleFiltersSuppliedError = newError(4000019, "Invalid request. Supply either a transaction id or a notification type, but not both.")
123+
PaginationTokenExpiredError = newError(4000017, "Invalid request. The pagination token is expired.")
124+
ServerNotificationURLNotFoundError = newError(4040007, "No App Store Server Notification URL found for provided app. Check that a URL is configured in App Store Connect for this environment.")
125+
StartDateAfterEndDateError = newError(4000013, "Invalid request. The end date precedes the start date or the dates are the same.")
126+
StartDateTooFarInPastError = newError(4000012, "Invalid request. The start date is earlier than the allowed start date.")
127+
TestNotificationNotFoundError = newError(4040008, "Either the test notification token is expired or the notification and status are not yet available.")
128+
)

store.go

+6
Original file line numberDiff line numberDiff line change
@@ -467,5 +467,11 @@ func (c *StoreClient) Do(ctx context.Context, method string, url string, body io
467467
return resp.StatusCode, nil, fmt.Errorf("appstore read http body err %w", err)
468468
}
469469

470+
if resp.StatusCode != http.StatusOK {
471+
if rErr, ok := newAppStoreAPIError(byteData, resp.Header); ok {
472+
return resp.StatusCode, byteData, rErr
473+
}
474+
}
475+
470476
return resp.StatusCode, byteData, err
471477
}

0 commit comments

Comments
 (0)