Skip to content

Commit

Permalink
Merge pull request #172 from richzw/master
Browse files Browse the repository at this point in the history
feat(appstore): upgrade refund history api to v2 and add send consumption information api
  • Loading branch information
takecy authored Oct 19, 2022
2 parents 32ff8d7 + 7ce9d3c commit efe3f16
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 15 deletions.
31 changes: 22 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,30 @@ import(
"github.com/awa/go-iap/appstore/api"
)

// For generate key file and download it, please refer to https://developer.apple.com/documentation/appstoreserverapi/creating_api_keys_to_use_with_the_app_store_server_api
const ACCOUNTPRIVATEKEY = `
-----BEGIN PRIVATE KEY-----
FAKEACCOUNTKEYBASE64FORMAT
-----END PRIVATE KEY-----
`
func main() {
// For generate key file and download it, please refer to https://developer.apple.com/documentation/appstoreserverapi/creating_api_keys_to_use_with_the_app_store_server_api
token := &api.Token{
KeyContent : "key content", // Loads a .p8 certificate
KeyID : "2X9R4HXF34", // Your private key ID from App Store Connect (Ex: 2X9R4HXF34)
BundleID : "com.example.testbundleid2021", // Your app’s bundle ID
Issuer : "57246542-96fe-1a63-e053-0824d011072a", // Your issuer ID from the Keys page in App Store Connect (Ex: "57246542-96fe-1a63-e053-0824d011072a")
Sandbox : true, // default is Production
c := &api.StoreConfig{
KeyContent: []byte(ACCOUNTPRIVATEKEY), // Loads a .p8 certificate
KeyID: "FAKEKEYID", // Your private key ID from App Store Connect (Ex: 2X9R4HXF34)
BundleID: "fake.bundle.id", // Your app’s bundle ID
Issuer: "xxxxx-xx-xx-xx-xxxxxxxxxx",// Your issuer ID from the Keys page in App Store Connect (Ex: "57246542-96fe-1a63-e053-0824d011072a")
Sandbox: false, // default is Production
}
originalTransactionId := "FAKETRANSACTIONID"
a := api.NewStoreClient(c)
query := &url.Values{}
query.Set("productType", "AUTO_RENEWABLE")
query.Set("productType", "NON_CONSUMABLE")
responses, err := a.GetTransactionHistory(originalTransactionId, query)

for _, response := range responses {
transantions, err := a.ParseSignedTransactions(response.SignedTransactions)
}
client := api.NewStoreClient(token)
resp, err := client.GetTransactionHistory("transactionID")
}
```

Expand Down
18 changes: 18 additions & 0 deletions appstore/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ type LastTransactionsItem struct {
SignedTransactionInfo string `json:"signedTransactionInfo"`
}

// ConsumptionRequestBody https://developer.apple.com/documentation/appstoreserverapi/consumptionrequest
type ConsumptionRequestBody struct {
AccountTenure int `json:"accountTenure"`
AppAccountToken string `json:"appAccountToken"`
ConsumptionStatus int `json:"consumptionStatus"`
CustomerConsented bool `json:"customerConsented"`
DeliveryStatus int `json:"deliveryStatus"`
LifetimeDollarsPurchased int `json:"lifetimeDollarsPurchased"`
LifetimeDollarsRefunded int `json:"lifetimeDollarsRefunded"`
Platform int `json:"platform"`
PlayTime int `json:"playTime"`
SampleContentProvided bool `json:"sampleContentProvided"`
UserStatus int `json:"userStatus"`
}

type JWSRenewalInfoDecodedPayload struct {
}

Expand Down Expand Up @@ -71,6 +86,9 @@ type JWSTransaction struct {
SignedDate int64 `json:"signedDate,omitempty"`
OfferType int64 `json:"offerType,omitempty"`
OfferIdentifier string `json:"offerIdentifier,omitempty"`
RevocationDate int64 `json:"revocationDate,omitempty"`
RevocationReason int `json:"revocationReason,omitempty"`
IsUpgraded bool `json:"isUpgraded,omitempty"`
}

func (J JWSTransaction) Valid() error {
Expand Down
50 changes: 44 additions & 6 deletions appstore/api/store.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"github.com/golang-jwt/jwt/v4"
Expand All @@ -19,15 +20,27 @@ const (
PathTransactionHistory = "/inApps/v1/history/{originalTransactionId}"
PathRefundHistory = "/inApps/v2/refund/lookup/{originalTransactionId}"
PathGetALLSubscriptionStatus = "/inApps/v1/subscriptions/{originalTransactionId}"
PathConsumptionInfo = "/inApps/v1/transactions/consumption/{originalTransactionId}"
)

type StoreConfig struct {
KeyContent []byte // Loads a .p8 certificate
KeyID string // Your private key ID from App Store Connect (Ex: 2X9R4HXF34)
BundleID string // Your app’s bundle ID
Issuer string // Your issuer ID from the Keys page in App Store Connect (Ex: "57246542-96fe-1a63-e053-0824d011072a")
Sandbox bool // default is Production
}

type StoreClient struct {
Token *Token
cert *Cert
}

// NewStoreClient create appstore server api client
func NewStoreClient(token *Token) *StoreClient {
func NewStoreClient(config *StoreConfig) *StoreClient {
token := &Token{}
token.WithConfig(config)

client := &StoreClient{
Token: token,
cert: &Cert{},
Expand All @@ -41,7 +54,7 @@ func (a *StoreClient) GetALLSubscriptionStatuses(originalTransactionId string) (
if a.Token.Sandbox {
URL = HostSandBox + PathGetALLSubscriptionStatus
}
URL = strings.Replace(URL, "{orderId}", originalTransactionId, -1)
URL = strings.Replace(URL, "{originalTransactionId}", originalTransactionId, -1)
statusCode, body, err := a.Do(http.MethodGet, URL, nil)
if err != nil {
return
Expand Down Expand Up @@ -86,7 +99,7 @@ func (a *StoreClient) LookupOrderID(invoiceOrderId string) (rsp *OrderLookupResp
}

// GetTransactionHistory https://developer.apple.com/documentation/appstoreserverapi/get_transaction_history
func (a *StoreClient) GetTransactionHistory(originalTransactionId string) (responses []*HistoryResponse, err error) {
func (a *StoreClient) GetTransactionHistory(originalTransactionId string, query *url.Values) (responses []*HistoryResponse, err error) {
URL := HostProduction + PathTransactionHistory
if a.Token.Sandbox {
URL = HostSandBox + PathTransactionHistory
Expand All @@ -95,12 +108,14 @@ func (a *StoreClient) GetTransactionHistory(originalTransactionId string) (respo
rsp := HistoryResponse{}

for {
data := url.Values{}
if query == nil {
query = &url.Values{}
}
if rsp.HasMore && rsp.Revision != "" {
data.Set("revision", rsp.Revision)
query.Set("revision", rsp.Revision)
}

statusCode, body, errOmit := a.Do(http.MethodGet, URL+"?"+data.Encode(), nil)
statusCode, body, errOmit := a.Do(http.MethodGet, URL+"?"+query.Encode(), nil)
if errOmit != nil {
return nil, errOmit
}
Expand Down Expand Up @@ -164,6 +179,29 @@ func (a *StoreClient) GetRefundHistory(originalTransactionId string) (responses
return
}

// SendConsumptionInfo https://developer.apple.com/documentation/appstoreserverapi/send_consumption_information
func (a *StoreClient) SendConsumptionInfo(originalTransactionId string, body ConsumptionRequestBody) (statusCode int, err error) {
URL := HostProduction + PathConsumptionInfo
if a.Token.Sandbox {
URL = HostSandBox + PathConsumptionInfo
}
URL = strings.Replace(URL, "{originalTransactionId}", originalTransactionId, -1)

bodyBuf := new(bytes.Buffer)
err = json.NewEncoder(bodyBuf).Encode(body)
if err != nil {
return 0, err
}

statusCode, _, err = a.Do(http.MethodPut, URL, bodyBuf)
if err != nil {
return statusCode, err
}
return statusCode, nil
}

// ParseSignedTransactions parse the jws singed transactions
// Per doc: https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6
func (a *StoreClient) ParseSignedTransactions(transactions []string) ([]*JWSTransaction, error) {
result := make([]*JWSTransaction, 0)
for _, v := range transactions {
Expand Down
8 changes: 8 additions & 0 deletions appstore/api/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ type Token struct {
Bearer string // Authorized bearer token
}

func (t *Token) WithConfig(c *StoreConfig) {
t.KeyContent = append(t.KeyContent[:0:0], c.KeyContent...)
t.KeyID = c.KeyID
t.BundleID = c.BundleID
t.Issuer = c.Issuer
t.Sandbox = c.Sandbox
}

// GenerateIfExpired checks to see if the token is about to expire and generates a new token.
func (t *Token) GenerateIfExpired() (string, error) {
t.Lock()
Expand Down

0 comments on commit efe3f16

Please sign in to comment.