| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- package apple
- import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "bet24.com/log"
- )
- // Receipt is information returned by Apple
- //
- // Documentation: https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW10
- type Receipt struct {
- OriginalPurchaseDatePst string `json:"original_purchase_date_pst"`
- UniqueIdentifier string `json:"unique_identifier"`
- OriginalTransactionId string `json:"original_transaction_id"`
- Bvrs string `json:"bvrs"`
- AppItemId string `json:"app_item_id"`
- TransactionId string `json:"transaction_id"`
- Quantity string `json:"quantity"`
- UniqueVendorIdentifier string `json:"unique_vendor_identifier"`
- ProductId string `json:"product_id"`
- ItemId string `json:"item_id"`
- VersionExternalIdentifier string `json:"version_external_identifier"`
- Bid string `json:"bid"`
- IsInIntroOfferPeriod string `json:"is_in_intro_offer_period"`
- PurchaseDateMs string `json:"purchase_date_ms"`
- PurchaseDate string `json:"purchase_date"`
- IsTrialPeriod string `json:"is_trial_period"`
- PurchaseDatePst string `json:"purchase_date_pst"`
- OriginalPurchaseDate string `json:"original_purchase_date"`
- OriginalPurchaseDateMs string `json:"original_purchase_date_ms"`
- }
- type receiptRequestData struct {
- Receiptdata string `json:"receipt-data"`
- }
- const (
- appleSandboxURL string = "https://sandbox.itunes.apple.com/verifyReceipt"
- appleProductionURL string = "https://buy.itunes.apple.com/verifyReceipt"
- )
- // Simple interface to get the original error code from the error object
- type ErrorWithCode interface {
- Code() float64
- }
- type Error struct {
- error
- errCode float64
- }
- // Simple method to get the original error code from the error object
- func (e *Error) Code() float64 {
- return e.errCode
- }
- // Given receiptData (base64 encoded) it tries to connect to either the sandbox (useSandbox true) or
- // apples ordinary service (useSandbox false) to validate the receipt. Returns either a receipt struct or an error.
- func VerifyReceipt(receiptData string, useSandbox bool) (*Receipt, error, string) {
- return sendReceiptToApple(receiptData, verificationURL(useSandbox))
- }
- // Selects the proper url to use when talking to apple based on if we should use the sandbox environment or not
- func verificationURL(useSandbox bool) string {
- if useSandbox {
- return appleSandboxURL
- }
- return appleProductionURL
- }
- // Sends the receipt to apple, returns the receipt or an error upon completion
- func sendReceiptToApple(receiptData, url string) (*Receipt, error, string) {
- requestData, err := json.Marshal(receiptRequestData{receiptData})
- if err != nil {
- return nil, err, ""
- }
- toSend := bytes.NewBuffer(requestData)
- resp, err := http.Post(url, "application/json", toSend)
- if err != nil {
- log.Error("apple.sendReceiptToApple post fail %v ==> receiptData=%s", err, receiptData)
- return nil, err, ""
- }
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- //log.Debug("sendReceiptToApple body ==> %s", body)
- var responseData struct {
- Status float64 `json:"status"`
- ReceiptContent *Receipt `json:"receipt"`
- }
- responseData.ReceiptContent = new(Receipt)
- err = json.Unmarshal(body, &responseData)
- if err != nil {
- return nil, err, ""
- }
- if responseData.Status != 0 {
- return nil, verificationError(responseData.Status), string(body)
- }
- return responseData.ReceiptContent, nil, string(body)
- }
- // Error codes as they returned by the App Store
- const (
- UnreadableJSON = 21000 //App Store无法读取您提供的JSON对象。
- MalformedData = 21002 //该receipt-data属性中的数据格式错误或丢失。
- AuthenticationError = 21003 //收据无法认证。
- UnmatchedSecret = 21004 //您提供的共享密码与您帐户的文件共享密码不匹配。
- ServerUnavailable = 21005 //收据服务器当前不可用。
- SubscriptionExpired = 21006 //该收据有效,但订阅已过期。当此状态代码返回到您的服务器时,收据数据也会被解码并作为响应的一部分返回。仅针对自动续订的iOS 6样式交易收据返回。
- SandboxReceiptOnProd = 21007 //该收据来自测试环境,但已发送到生产环境以进行验证。而是将其发送到测试环境。
- ProdReceiptOnSandbox = 21008 //该收据来自生产环境,但是已发送到测试环境以进行验证。而是将其发送到生产环境。
- ErrorReceiptNoAuth = 21010 //此收据无法授权。就像从未进行过购买一样对待。
- ErrorInternalMin = 21100 //21100~21199 内部数据访问错误。
- ErrorInternalMax = 21199 //21100~21199 内部数据访问错误。
- )
- // Generates the correct error based on a status error code
- func verificationError(errCode float64) error {
- var errorMessage string
- switch errCode {
- case UnreadableJSON:
- errorMessage = "The App Store could not read the JSON object you provided."
- case MalformedData:
- errorMessage = "The data in the receipt-data property was malformed."
- case AuthenticationError:
- errorMessage = "The receipt could not be authenticated."
- case UnmatchedSecret:
- errorMessage = "The shared secret you provided does not match the shared secret on file for your account."
- case ServerUnavailable:
- errorMessage = "The receipt server is not currently available."
- case SubscriptionExpired:
- errorMessage = "This receipt is valid but the subscription has expired. When this status code is returned to your server, " +
- "the receipt data is also decoded and returned as part of the response."
- case SandboxReceiptOnProd:
- errorMessage = "This receipt is a sandbox receipt, but it was sent to the production service for verification."
- case ProdReceiptOnSandbox:
- errorMessage = "This receipt is a production receipt, but it was sent to the sandbox service for verification."
- default:
- errorMessage = "An unknown error ocurred"
- }
- errorMessage = fmt.Sprintf("Code:%.f %s", errCode, errorMessage)
- return &Error{errors.New(errorMessage), errCode}
- }
|