awa / go-iap Goto Github PK
View Code? Open in Web Editor NEWgo-iap verifies the purchase receipt via AppStore, GooglePlayStore, AmazonAppStore and Huawei HMS.
License: MIT License
go-iap verifies the purchase receipt via AppStore, GooglePlayStore, AmazonAppStore and Huawei HMS.
License: MIT License
Current New()
method doesn't support timeout control with context.Context.
It should stop a procedure if assigned context passed through args notified signals.
I send from my app like this:
{"receipt-data":"MIISZAYJKoZIhvcNAQcCoIISVTCCElECAQExCzAJBgUrDgMCGgUAMIICBQYJ etc..."}
Is it right?
My code:
var IAPTest = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
err := r.ParseForm()
if err != nil {
panic(err)
}
v := r.Form
var re string
for i := range v {
re = i
}
fmt.Println("This is re: ", re)
client := appstore.New()
req := appstore.IAPRequest{
ReceiptData: re,
}
resp := &appstore.IAPResponse{}
ctx := context.Background()
err2 := client.Verify(ctx, req, resp)
fmt.Println("client", client)
fmt.Println("req", req)
fmt.Println("resp", resp)
fmt.Println("ctx", ctx)
fmt.Println("err", err2)
And my response:
client &{https://buy.itunes.apple.com/verifyReceipt https://sandbox.itunes.apple.com/verifyReceipt 0x1a12f680}
req {{"receipt-data":"MIIWdgYJKoZIhvcNAQcCoIIWZzCCFmMCAQExCzAJBgUrDgMCGgUAMIIGFwYJK
resp &{21002 { 0 0 [] { } { } { }} [] [] false}
Hey,
Thank you for this awesome package, we are using go-iap to verify apple receipts, and we have some failing requests with EOF errors in production, after checking the failed receipts I can verify that they are valid, and after a retry the verify request is passing with no issue, I think it might be related to empty response from apple side, any idea how to handle it?
We are using v1.3.8 here is a code snippet -
iapReq := appstore.IAPRequest{
ReceiptData: receiptData,
Password: password,
}
iapResp := &appstore.IAPResponse{}
err := s.asc.Verify(ctx, iapReq, iapResp)
Thanks!
we need this interface,3Q
I have the following code...
var t appstore.SubscriptionNotification
err := json.Unmarshal(body, &t)
It gets an error when receiving a S2S from Apple.
json: cannot unmarshal number into Go struct field NotificationUnifiedReceipt.unified_receipt.status of type string
The status field coming from Apple is 0
- an int. Not a string!
https://developer.apple.com/documentation/appstoreservernotifications/unified_receipt
Defines it as an Integer
Can I verify receipt before send to Apple server? If yes how to do?
I got the following error message
go/src/github.com/awa/go-iap/playstore/validator.go:26:68: undefined: androidpublisher.SubscriptionPurchasesAcknowledgeRequest go/src/github.com/awa/go-iap/playstore/validator.go:67:7: undefined: androidpublisher.SubscriptionPurchasesAcknowledgeRequest go/src/github.com/awa/go-iap/playstore/validator.go:75:10: ps.Acknowledge undefined (type *androidpublisher.PurchasesSubscriptionsService has no field or method Acknowledge)
As per the official documentation, since 2021 3/10 the "latest_receipt" field is now an old field and requires "unified_reciept" as well NotificationUnifiedReciept.
ref:
https://developer.apple.com/documentation/appstoreservernotifications/responsebody
latest_receipt
byte
Removed. As of March 10, 2021 this object is no longer provided in production and sandbox environments. Use the latest_receipt field found in the unified_receipt object instead.
We have started seeing client timeouts while verifying apple receipts, Does this means that we need to increase the default timeout to prevent this?
From android developers blog:
Starting December 1, 2019, versions 1 and 2 of the Google Play Developer API will no longer be available so you need to update to version 3 ahead of this date.
Can you make a release?
According to this man
https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/server-data-model-0000001050986133-V5#EN-US_TOPIC_0000001050986133__section264617465219
field consumptionState in struct InAppPurchaseData must be int, not string
Update it please
as title
always get v1.0.0 now
thanks
From apple developer reference there is a is_in_intro_offer_period
and below is a sample response struct from my project
"in_app": [
{
"quantity": "1",
"product_id": "com.ghostcall.testsubscription.1monthpass",
"transaction_id": "1000000463911073",
"original_transaction_id": "1000000463911073",
"purchase_date": "2018-10-27 18:02:50 Etc/GMT",
"purchase_date_ms": "1540663370000",
"purchase_date_pst": "2018-10-27 11:02:50 America/Los_Angeles",
"original_purchase_date": "2018-10-27 18:02:50 Etc/GMT",
"original_purchase_date_ms": "1540663370000",
"original_purchase_date_pst": "2018-10-27 11:02:50 America/Los_Angeles",
"expires_date": "2018-10-27 18:07:50 Etc/GMT",
"expires_date_ms": "1540663670000",
"expires_date_pst": "2018-10-27 11:07:50 America/Los_Angeles",
"web_order_line_item_id": "1000000041021420",
"is_trial_period": "false",
"is_in_intro_offer_period": "false"
}
]
But the is_in_intro_offer_period
is missing at InApp model in appstore package
Per this doc
`latest_receipt
Only returned for receipts containing auto-renewable subscriptions. For iOS 6 style transaction receipts, this is the base-64 encoded receipt for the most recent renewal. For iOS 7 style app receipts, this is the latest base-64 encoded app receipt.
latest_receipt_info
Only returned for receipts containing auto-renewable subscriptions. For iOS 6 style transaction receipts, this is the JSON representation of the receipt for the most recent renewal. For iOS 7 style app receipts, the value of this key is an array containing all in-app purchase transactions. This excludes transactions for a consumable product that have been marked as finished by your app.`
However, I cannot find those two fields defined in IAPResponseForIOS6
?
Is there any plans to provide library as docker image ?
according to the doc: there should be bid
and bvrs
in struct SubscriptionNotification.
When I purchased the first item, the validation that the appstore returned to me did not include the transactionID that the client sent me.
environment:SandBox
So some tests fail now.
--- FAIL: TestCancelSubscription (0.09s)
validator_test.go:166: got googleapi: Error 400: Invalid Value, invalid
want googleapi: Error 404: No application was found for the given package name., applicationNotFound
--- FAIL: TestVerifyProduct (0.09s)
validator_test.go:131: got googleapi: Error 400: Invalid Value, invalid
--- FAIL: TestVerifySubscription (0.09s)
validator_test.go:103: got googleapi: Error 400: Invalid Value, invalid
want googleapi: Error 404: No application was found for the given package name., applicationNotFound
--- FAIL: TestAcknowledgeSubscription (0.10s)
validator_test.go:87: got googleapi: Error 400: Invalid Value, invalid
want googleapi: Error 404: No application was found for the given package name., applicationNotFound
FAIL
The appstore appears to be returning HTML from an API call resulting in the following error propagating from the client.Verify
call (I believe its actually coming from the parseResponse method when attempting to decode JSON that is actually in HTML format. I have logged the same error from other API's that have returned HTML when JSON is expected in code I have written.
invalid character '<' looking for beginning of value
library version: 1.2.0/1.3.0
go version: 1.13.12
Is there any plan to support server notification responseBodyV2?
https://developer.apple.com/documentation/appstoreservernotifications/responsebodyv2
Calling appstore.Verify
is giving me EOF errors. On closer inspection, these are being thrown by https://github.com/dogenzaka/go-iap/blob/e9d5da1f8f6bff09e3f0ff777a44fe617e45ae52/appstore/validator.go#L112 as the body from the previous request is empty.
Inspecting the gorequest with AsCurlCommand()
and running that as a curl command, the request succeeds with a valid (albeit large) response body every time.
Ultimately, what appears to be happening is when the receipt response is large (this can happen very quickly with iOS especially with autorenewing subscriptions) something isn't reading it properly. I don't know if this is in the std lib or gorequest, all I know is the string being passed to the json decoder is empty, and it's never empty when the same request is ran with curl.
I think this would be a useful feature to add to the playstore
package, by invoking https://godoc.org/google.golang.org/api/androidpublisher/v2#PurchasesSubscriptionsService.Defer. I'd be happy to open a PR.
When validating ios receipt data, some of the receipts are failing the client.Verify call with the following error
invalid character '<' looking for beginning of value
The data is the raw base64 encoded string that the app receives. Having passed the data through base64 decode using the following site https://www.base64decode.org/ it would appear that changing the charset from UTF-8 to ASCII means the receipt then decodes as expected, but given this is only happening for some receipts and not others I believe the issue is better fixed in this library.
Any thoughts on what might be causing this?
unified_receipt.latet_recipet_info
and responseBody.latest_receipt_info
need filed app_account_token
json: cannot unmarshal number into Go struct field SubscriptionNotification.original_transaction_id of type string
Seems this is set to string in the struct when apple are sending a long. https://developer.apple.com/documentation/appstoreservernotifications/responsebodyv1
Based on this doc: https://developer.apple.com/documentation/appstoreservernotifications/responsebody
The appstore.SubscriptionNotification
struct missed these fields
auto_renew_status_change_date
auto_renew_status_change_date_ms
auto_renew_status_change_date_pst
The appstore.NotificationEnv
constants are currently "SANDBOX"
and "PROD"
. I just noticed that Apple sends "Sandbox"
- i.e. different letter case.
Apple's documentation contradicts itself on the same page: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html
First, Table 6-3 says the values are "Sandbox"
and "PROD"
.
Later it says "To determine if a status update notifications for a subscription event is in the test environment, check if the value of the environment key in the statusUpdateNotification JSON object equals SANDBOX."
Based on observation, I'm guessing the second paragraph has a bug and got the case wrong.
I think the appstore.NotificationEnvSandbox
constant needs to be changed to "Sandbox"
Thanks for open sourcing the library!
I have a problem that if the oauth compulsory when I only want to call client.VerifyProduct
.
Google App Engine is a sandboxed environment. We can't make calls to the outside world without using the net/http
's Request
to create a http.Client
by:
import (
"net/http"
"google.golang.org/appengine"
"google.golang.org/appengine/urlfetch"
)
ctx := appengine.NewContext(r)
client := urlfetch.Client(ctx)
Without doing that, the library doesn't work.
The way Stripe-Go-Library handles it is like this: https://github.com/stripe/stripe-go#google-appengine
Basically Stripe allows Google App Engine users the ability to feed in their own http Client
and the library will then use that.
ctx := appengine.NewContext(r)
httpClient := urlfetch.Client(ctx)
sc := client.New(key, stripe.NewBackends(httpClient))
The way I handle it is like this:
https://github.com/pjebs/optimus-go/blob/master/gae.go
https://github.com/pjebs/optimus-go/blob/master/standalone.go
My way may be a lot easier to implement.
In your code, everytime you refer to a http Client, you instead call: client()
from the files above.
There will be no affect to the rest of your code.
https://cloud.google.com/appengine/docs/go/issue-requests
https://cloud.google.com/appengine/docs/go/urlfetch/reference
I haven't dig deep into the code. Is it thread safe?
Hi.
I'm trying to validate product purchase but it returns "Error 400: Invalid Value, invalid".
Could you explain what I'm doing in wrong way?
IAPResponse.latest_receipt_info from VerifyReceipt API may be a appstore.InApp
instead of a []appstore.InApp
My response struct is :
IAPResponse struct {
Receipt struct {
AppItemID string json:"app_item_id"
Bid string json:"bid"
Bvrs string json:"bvrs"
ItemID string json:"item_id"
OriginalPurchaseDate string json:"original_purchase_date"
OriginalPurchaseDateMs string json:"original_purchase_date_ms"
OriginalPurchaseDatePst string json:"original_purchase_date_pst"
OriginalTransactionID string json:"original_transaction_id"
ProductID string json:"product_id"
PurchaseDate string json:"purchase_date"
PurchaseDateMs string json:"purchase_date_ms"
PurchaseDatePst string json:"purchase_date_pst"
Quantity string json:"quantity"
TransactionID string json:"transaction_id"
UniqueIdentifier string json:"unique_identifier"
UniqueVendorIdentifier string json:"unique_vendor_identifier"
VersionExternalIdentifier string json:"version_external_identifier"
} json:"receipt"
Status int json:"status"
}
Thank you for this nice & compact library.
We're considering using this in our company.
I wanted to check if you have considered adding Roku support and if so, will you be willing to accept a PR if I send you one?
Is there any plan to support new App Store server APIs
i'd like to fork and add new feature
Some fields of appstore.Receipt
are not mentioned in the Apple IAP Doc, such as ReceiptType
, AdamID
and AppItemID
. Although they are appear in the response, using them can be a risk. Because they are not promised by Apple, things would be changed in the future.
So that I suggest that they should be removed to avoid misuse.
Per doc, the ios receipt should be encoded by base64
req := appstore.IAPRequest{
ReceiptData: "your receipt data encoded by base64",
}
Then I try it through
base64.StdEncoding.EncodeToString([]byte(receipt))
and the value of receipt
like
"{\n\t\"signature\" = \"ZwXG56AezlHRTBhL8cTqA==\";\n\t\"purchase-info\" = \"RXRjL0dNVCI7Cn0=\";\n\t\"environment\" = \"Sandbox\";\n\t\"pod\" = \"100\";\n\t\"signing-status\" = \"0\";\n}"
However, I got the status of response is 21002
, which is The data in the receipt-data property was malformed or missing
How to encode ios receipt by base64 or is there anything I am missing?
I want to do method in API to purchase products. How to do it? How to pass receipt from API? What does the recipe consist of?
package main
import (
"github.com/awa/go-iap/appstore"
)
func main() {
r := mux.NewRouter()
r.Handle("/iaptest", IAPTest).Methods("GET")
fmt.Println("Server is runned on port 3000")
if err := http.ListenAndServe(":3000", handlers.LoggingHandler(os.Stdout, r)); err != nil {
log.Fatal(err)
}
}
var IAPTest = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
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)
})
There is new notification status that Apple started sending, which is not in their official documentation (https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html), but is mentioned in this article: https://developer.apple.com/documentation/storekit/in-app_purchase/enabling_status_update_notifications
I also started receiving this notification type recently so it's legit.
Due to this: golang/oauth2#201
I run the test for a lot of receipts in prod env and see that the field last_receipt_info is is both array and map. Some receipt apple return array, some else return object. I tested with the receipt the same app and the same subscription. I check on the documentation here https://developer.apple.com/documentation/appstorereceipts/responsebody and see they said that last_receipt_info is an array, but it is not sometimes.
How to cover this case.
On an error, iTunes API returns a 200 because it's terrible.
This means that this line is redundant:
https://github.com/dogenzaka/go-iap/blob/master/appstore/validator.go#L113
How should we progress forward with errors? Parse the status inside the library?
When I try to verify subscription test receipt on iOS, I got the following error,
cannot unmarshal object into Go struct field IAPResponse.latest_receipt_info of type []appstore.InApp
Then I define another testResponse
IAPTestResponse struct {
Status int `json:"status"`
Environment Environment `json:"environment"`
Receipt Receipt `json:"receipt"`
LatestReceiptInfo InApp `json:"latest_receipt_info"`
LatestReceipt string `json:"latest_receipt"`
PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info"`
IsRetryable bool `json:"is-retryable"`
}
which is used to json.Unmarshal
, it seems work well now.
Anything I am missing?
go get ./...
# golang.org/x/net/context/ctxhttp
../../../golang.org/x/net/context/ctxhttp/ctxhttp.go:35: req.Cancel undefined (type *http.Request has no field or method Cancel)
make: *** [setup] Error 2
I see Client has both Production URL and Sandbox URL.
But there is no mentioned way to verify to sandbox URL?
This works:
key, err := ioutil.ReadFile("key.json")
c(err, true) // check error
conf, err := google.JWTConfigFromJSON(key, androidpublisher.AndroidpublisherScope)
c(err, true) // check error...
cli := conf.Client(context.Background())
service, err := androidpublisher.New(cli)
c(err, true)
inapps := androidpublisher.NewInappproductsService(service)
iplc := inapps.List(packageName)
resp, err := iplc.Context(context.Background()).Do()
c(err, true)
return resp.Inappproduct
The same API library (publisher v2 or v3), checking purchase status not works:
key, err := ioutil.ReadFile("key.json")
c(err, true) //check error
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Timeout: 10 * time.Second})
conf, err := google.JWTConfigFromJSON(key, androidpublisher.AndroidpublisherScope)
c(err, true)
client := conf.Client(ctx)
sr, err := androidpublisher.New(client)
c(err, true)
pr := androidpublisher.NewPurchasesProductsService(sr)
pe, err := pr.Get(packageName, "sku", "purchase token (android sended to my server)").Context(ctx).Do()
if err != nil {
fmt.Println("Error:", err)
}
Output:
Error: googleapi: Error 401: The current user has insufficient permissions to perform the requested operation., permissionDenied
As stated here Apple Receipt Fields "app_item_id" field should be a string but in the library it is mapped to int64. This causes an error during json decoding.
EDIT:
Apparently Apple is giving out different responses some has app_item_id as string and some has it as a number. In this case a custom type that implements Unmarshaler interface would be better suited for this. Something like the following:
Receipt struct {
ReceiptType string `json:"receipt_type"`
AdamID int64 `json:"adam_id"`
AppItemID NumericString `json:"app_item_id"`
BundleID string `json:"bundle_id"`
ApplicationVersion string `json:"application_version"`
DownloadID int64 `json:"download_id"`
OriginalApplicationVersion string `json:"original_application_version"`
InApp []InApp `json:"in_app"`
RequestDate
OriginalPurchaseDate
}
func (n NumericString) UnmarshalJSON(data []byte) error {
n = NumericString(string(data))
return nil
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.