go-iap verifies the purchase receipt via AppStore, GooglePlayStore, AmazonAppStore and Huawei HMS.
go-iap verifies the purchase receipt via AppStore, GooglePlayStore, AmazonAppStore and Huawei HMS.
go-iap verifies the purchase receipt via AppStore, GooglePlayStore, Amazon AppStore, HMS or MicrosoftStore.
Current API Documents:
go get github.com/awa/go-iap/appstore
go get github.com/awa/go-iap/playstore
go get github.com/awa/go-iap/amazon
go get github.com/awa/go-iap/hms
go get github.com/awa/go-iap/microsoftstoreimport(
"github.com/awa/go-iap/appstore"
)
func main() {
client := appstore.New()
req := appstore.IAPRequest{
ReceiptData: "your receipt data encoded by base64",
}
resp := &appstore.IAPResponse{}
ctx := context.Background()
err := client.Verify(ctx, req, resp)
}Note: The verifyReceipt API has been deprecated as of 5 Jun 2023. Please use App Store Server API instead.
import(
"github.com/awa/go-iap/playstore"
)
func main() {
// You need to prepare a public key for your Android app's in app billing
// at https://console.developers.google.com.
jsonKey, err := ioutil.ReadFile("jsonKey.json")
if err != nil {
log.Fatal(err)
}
client := playstore.New(jsonKey)
ctx := context.Background()
resp, err := client.VerifySubscription(ctx, "package", "subscriptionID", "purchaseToken")
}import(
"github.com/awa/go-iap/amazon"
)
func main() {
client := amazon.New("developerSecret")
ctx := context.Background()
resp, err := client.Verify(ctx, "userID", "receiptID")
}import(
"github.com/awa/go-iap/hms"
)
func main() {
// If "orderSiteURL" and/or "subscriptionSiteURL" are empty,
// they will be default to AppTouch German.
// Please refer to https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/api-common-statement-0000001050986127-V5 for details.
client := hms.New("clientID", "clientSecret", "orderSiteURL", "subscriptionSiteURL")
ctx := context.Background()
resp, err := client.VerifySubscription(ctx, "purchaseToken", "subscriptionID", 1)
}Note
The App Store Server API differentiates between a sandbox and a production environment based on the base URL:
If you're unsure about the environment, follow these steps:
4040010, indicating a TransactionIdNotFoundError, make a call to the endpoint using the sandbox URL.api.NewStoreClientWithSandboxFallback does this for you. It returns an *api.StoreClientWithSandboxFallback, which satisfies api.StoreAPIClient, so it is a drop-in replacement for a client built with api.NewStoreClient.
Sandbox fallback
An app distributed through App Review or TestFlight is a production build whose purchases are made in Sandbox, so the production host cannot find those transactions. This client queries production first and retries against Sandbox on TransactionIdNotFoundError.
import(
"github.com/awa/go-iap/appstore/api"
)
func main() {
c := &api.StoreConfig{
KeyContent: []byte(ACCOUNTPRIVATEKEY),
KeyID: "FAKEKEYID",
BundleID: "fake.bundle.id",
Issuer: "xxxxx-xx-xx-xx-xxxxxxxxxx",
// Sandbox is ignored: both hosts are built.
}
a := api.NewStoreClientWithSandboxFallback(c)
ctx := context.Background()
// Falls back to Sandbox when production reports the transaction missing.
response, err := a.GetALLSubscriptionStatuses(ctx, "FAKETRANSACTIONID", nil)
}The retry applies to the lookups Apple documents as returning TransactionIdNotFoundError: GetTransactionInfo, GetALLSubscriptionStatuses, GetTransactionHistory, GetRefundHistory and GetAppTransactionInfo. Every other method behaves exactly as it does on StoreClient.
Sandbox is also asked when production answers 401: that is how it refuses an app that has never shipped there, as an App Store Commerce Engineer confirmed. Such an app fails before the lookup, so TransactionIdNotFoundError never arrives — Apple's documented procedure covers only that error.
When the App Store answers without an error code, the status reaches you through the error:
var withStatus interface{ StatusCode() int }
if errors.As(err, &withStatus) && withStatus.StatusCode() == http.StatusTooManyRequests {
// back off
}Nothing else is retried. 401 is overloaded, so reach for this client only where a Sandbox answer is acceptable. NewStoreClient never falls back.
……import (
"github.com/awa/go-iap/appstore"
"github.com/golang-jwt/jwt/v5"
)
func main() {
tokenStr := "SignedRenewalInfo Encode String" // or SignedTransactionInfo string
token := jwt.Token{}
client := appstore.New()
err := client.ParseNotificationV2(tokenStr, &token)
claims, ok := token.Claims.(jwt.MapClaims)
for key, val := range claims {
fmt.Printf("Key: %v, value: %v\n", key, val) // key value of SignedRenewalInfo
}
}This validator supports the receipt type for iOS7 or above.
This validator uses Version 3 API.
This validator uses RVS for IAP v2.0.
This validator uses Version 2 API.
This validator uses Version 1.0+
This validator uses Version 1.0
go-iap is licensed under the MIT.
No open issues yet, or sync has not completed.