Golang 实现 Apple 内购服务器验证

内购验证很简单就一个网络请求,区分生产和沙箱环境。 


先验证生产环境,再验证沙箱环境(这个没有关系,沙箱环境只有你自己添加了沙箱账号才有效,没有啥安全问题。为了避免客服审核时候使用沙箱账号,导致客服支付后数据没变化被拒绝)


生产环境地址 https://buy.itunes.apple.com/verifyReceipt

沙箱环境地址 https://sandbox.itunes.apple.com/verifyReceipt


代码

func appleVerify(url, transactionId, transactionReceipt string) (err error, rawProductId, rawTransactionId, rawEnvironment, raw string) {
   // 请求数据
   var param struct{
      ReceiptData string `json:"receipt-data"`
   }
   // 返回数据
   var result struct {
      Environment string
      Receipt struct {
         Bundle_id string
         In_app [] struct{
            Product_id string
            Transaction_id string
         }
      }
   }

   // 苹果验证
   param.ReceiptData = transactionReceipt
   client := &http.Client{Timeout: 15 * time.Second}
   jsonStr, _ := json.Marshal(param)
   resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonStr))
   //·21000 App Store 无法读取你提供的JSON数据
   //·21002 收据数据不符合格式
   //·21003 收据无法被验证
   //·21004 你提供的共享密钥和账户的共享密钥不一致
   //·21005 收据服务器当前不可用
   //·21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
   //·21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
   //·21008 收据信息是产品环境中使用,但却被发送到测试环境中验证
   if err != nil {
      return errors.New( "校验超时, 请稍后再试"), "", "", "", ""
   }
   defer resp.Body.Close()
   body, _ := ioutil.ReadAll(resp.Body)
   err = json.Unmarshal(body, &result)
   if err != nil {
      return errors.New( "校验超时, 请稍后再试"), "", "","", string(body)
   }
   if len(result.Receipt.In_app) <= 0 {
      return errors.New( "空凭证"), "", "", "", string(body)
   }
   if result.Receipt.Bundle_id != function.Config.AppleClientId {
      return errors.New( "bundleID 无效"), "", "", "", string(body)
   }

   // 取出对应凭证
   for i:=0; i<len(result.Receipt.In_app); i++ {
      val := result.Receipt.In_app[i]
      if transactionId == val.Transaction_id {
         rawProductId = val.Product_id
         rawTransactionId = val.Transaction_id
         break
      }
   }

   if len(rawTransactionId) <= 0 {
      return errors.New( "raw内无对应凭证"), "", "", "", string(body)
   }

   return nil,
      rawProductId,
      rawTransactionId,
      result.Environment,
      string(body)
}


使用方法

// 正式环境
err, rawProductId, rawTransactionId, environment, raw := appleVerify(
   "https://buy.itunes.apple.com/verifyReceipt", transactionId, transactionReceipt)
if err != nil || len(productId) < 0 {
   // 沙箱环境
   err, rawProductId, rawTransactionId, environment, raw = appleVerify(
      "https://sandbox.itunes.apple.com/verifyReceipt", transactionId, transactionReceipt)
   if err != nil || len(productId) < 0 {
      res.Err(c, err.Error())
      return
   }
}


3285
1
2年前