配置: config.go

package payimport "time"const (SandboxUrl        = "https://sandbox.itunes.apple.com/verifyReceipt" // (沙盒)链接ProductionUrl     = "https://buy.itunes.apple.com/verifyReceipt"     //  (正式)链接RequestUrlTimeout = 15 * time.Second                                 // 请求接口超时时间
)var ResponseStatusMsg = map[int64]string{21000: "App Store 的请求不是使用 HTTP POST 请求方法发出的。", //·21000 对 App Store 的请求不是使用 HTTP POST 请求方法发出的。21001: "App Store 不再发送此状态代码。",                 //·21001 App Store 不再发送此状态代码。21002: "属性中的数据receipt-data格式不正确或服务遇到临时问题。",    //·21002 属性中的数据receipt-data格式不正确或服务遇到临时问题。再试一次。21003: "无法验证收据。",                              //·21003 无法验证收据。21004: "您提供的共享密钥与您帐户的文件共享密钥不匹配。",              //·21004 您提供的共享密钥与您帐户的文件共享密钥不匹配。21005: "收据服务器暂时无法提供收据。",                       //·21005 收据服务器暂时无法提供收据。再试一次21006: "此收据有效,但订阅已过期。",                        //·21006 此收据有效,但订阅已过期。当此状态代码返回到您的服务器时,收据数据也会被解码并作为响应的一部分返回。仅针对自动续订订阅的 iOS 6 样式交易收据返回。21007: "这条回执是来自测试环境,但它是发送到生产环境进行验证的。",         //·21007 这条回执是来自测试环境,但它是发送到生产环境进行验证的。21008: "这条回执来自生产环境,但它被发送到测试环境进行验证。",           //·21008 这条回执来自生产环境,但它被发送到测试环境进行验证。21009: "内部数据访问错误。",                            //·21009 内部数据访问错误。稍后再试。21010: "用户帐户找不到或已被删除。",                        //·21010 用户帐户找不到或已被删除。
}

逻辑处理部分

package payimport ("bytes""encoding/json""errors""fmt""io/ioutil""net/http"
)// 官方文档 https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
type Pay struct {Params   ParamsPay   `json:"params"`   //请求参数Response ResponsePay `json:"response"` // 返回参数Error    error       `json:"error"`    //错误信息
}func NewPay() *Pay {return &Pay{Params:   ParamsPay{},Response: ResponsePay{},Error:    nil,}
}// 请求参数
type ParamsPay struct {AppleClientId string `json:"appleClientId"` // 收据所属应用的捆绑包标识符(app应用的bundle_id)Password      string `json:"password"`      // 您的应用程序的共享密钥,它是一个十六进制字符串Url           string `json:"url"`           // 请求链接//TransactionId      string `json:"transactionId"`      // 交易的唯一标识符TransactionReceipt string `json:"transactionReceipt"` //Base64 编码的收据数据
}// 内购支付返回的结构体 https://developer.apple.com/documentation/appstorereceipts/responsebody
type ResponsePay struct {Status      *int64 `json:"status"`      //具体注释看 config.ResponseStatusMsg 0:正确,其他失败Environment string `json:"environment"` //(生成收据的环境) 值:Sandbox(沙盒), Production(正式环境)Receipt     struct {ReceiptType                string `json:"receipt_type"`                // 生成的收据类型。该值对应于进行应用程序或 VPP 购买的环境。可能的值:Production, ProductionVPP, ProductionSandbox, ProductionVPPSandboxAdamId                     int64  `json:"adam_id"`                     //见。app_item_idAppItemId                  int64  `json:"app_item_id"`                 // 由 App Store Connect 生成并由 App Store 用于唯一标识所购买的应用程序。仅在生产中为应用分配此标识符。将此值视为 64 位长整数。ApplicationVersion         string `json:"application_version"`         // 应用程序的版本号。应用程序的版本号对应于. 在生产中,此值是设备上基于. 在沙盒中,该值始终为。CFBundleVersionCFBundleShortVersionStringInfo.plistreceipt_creation_date_ms"1.0"BundleId                   string `json:"bundle_id"`                   //收据所属应用的捆绑包标识符DownloadId                 int64  `json:"download_id"`                 //应用下载交易的唯一标识符。VersionExternalIdentifier  int64  `json:"version_external_identifier"` // 一个任意数字,用于标识您的应用程序的修订版。在沙箱中,此键的值为“0”。ReceiptCreationDate        string `json:"receipt_creation_date"`       // App Store 生成收据的时间,采用类似于 ISO 8601 的日期时间格式。ReceiptCreationDateMs      string `json:"receipt_creation_date_ms"`    // App Store 生成收据的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。这个值不会改变。ReceiptCreationDatePst     string `json:"receipt_creation_date_pst"`   // App Store 生成收据的时间,在太平洋时区。RequestDate                string `json:"request_date"`                // 处理对端点的请求并生成响应的时间,采用类似于 ISO 8601 的日期时间格式。verifyReceiptRequestDateMs              string `json:"request_date_ms"`             // 处理对端点的请求并生成响应的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。verifyReceiptRequestDatePst             string `json:"request_date_pst"`            // 在太平洋时区处理对端点的请求并生成响应的时间。verifyReceiptOriginalPurchaseDate       string `json:"original_purchase_date"`      // 原始应用购买的时间,采用类似于 ISO 8601 的日期时间格式。OriginalPurchaseDateMs     string `json:"original_purchase_date_ms"`   // 原始应用购买的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。OriginalPurchaseDatePst    string `json:"original_purchase_date_pst"`  // 原始应用购买时间,太平洋时区。OriginalApplicationVersion string `json:"original_application_version"`PreorderDate               string `json:"preorder_date"`       // 用户订购可预购应用的时间,采用类似于 ISO 8601 的日期时间格式。PreorderDateMs             string `json:"preorder_date_ms"`    // 用户订购可用于预订的应用程序的时间,采用 UNIX 纪元时间格式,以毫秒为单位。此字段仅在用户预购应用程序时出现。使用此时间格式处理日期。PreorderDatePst            string `json:"preorder_date_pst"`   // 用户在太平洋时区订购可供预订的应用程序的时间。ExpirationDate             string `json:"expiration_date"`     // 通过批量购买计划购买的应用程序ray1234据到期时间,采用类似于 ISO 8601 的日期时间格式。ExpirationDateMs           string `json:"expiration_date_ms"`  // 通过批量购买计划购买的应用程序的收据到期时间,采用 UNIX 纪元时间格式,以毫秒为单位。如果通过批量购买计划购买的应用程序没有此密钥,则收据不会过期。使用此时间格式处理日期。ExpirationDatePst          string `json:"expiration_date_pst"` // 通过批量购买计划购买的应用程序在太平洋时区的收据到期时间。InApp                      []struct {CancellationDate      string `json:"cancellation_date"`       // App Store 退还交易或从家庭共享中撤销交易的时间,采用类似于 ISO 8601 的日期时间格式。此字段仅适用于已退款或撤销的交易。CancellationDateMs    string `json:"cancellation_date_ms"`    // App Store 退还交易或从家庭共享中撤销交易的时间,采用 UNIX 纪元时间格式,以毫秒为单位。此字段仅适用于退款或撤销的交易。使用此时间格式处理CancellationDatePst   string `json:"cancellation_date_pst"`   // App Store 退还交易或从家庭共享中撤销交易的时间,在太平洋时区。此字段仅适用于退款或撤销的交易。CancellationReason    string `json:"cancellation_reason"`     // 退款或撤销交易的原因。值“1”表示客户由于您的应用程序中的实际或感知问题而取消了他们的交易。值“0”表示交易因其他原因被取消;例如,如果客户意外购买。  可能的值:1, 0ExpiresDate           string `json:"expires_date"`            // 订阅到期时间或续订时间,采用类似于 ISO 8601 的日期时间格式。ExpiresDateMs         string `json:"expires_date_ms"`         // 订阅到期或续订的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期ExpiresDatePst        string `json:"expires_date_pst"`        // 太平洋时区的订阅到期时间或续订时间。OriginalTransactionId string `json:"original_transaction_id"` // 原始购买的交易标识符。ProductId             string `json:"product_id"`              // 购买的产品的唯一标识符。您在 App Store Connect 中创建产品时提供此值,它对应于存储在交易的支付属性中的对象的属性PromotionalOfferId    string `json:"promotional_offer_id"`    // 用户兑换的订阅优惠的标识符Quantity              string `json:"quantity"`                // 购买的消耗品数量。此值对应于SKPayment存储在交易的支付属性中的对象的数量属性。“1”除非使用可变付款进行修改,否则该值通常是不变的。最大值为 10。TransactionId         string `json:"transaction_id"`          // 交易的唯一标识符,例如购买、恢复或续订WebOrderLineItemId    string `json:"web_order_line_item_id"`  // 跨设备购买事件的唯一标识符,包括订阅续订事件。该值是识别订阅购买的主键。} `json:"in_app"` //包含所有应用内购买交易的应用内购买收据字段的数组} `json:"receipt"` //发送以供验证的收据的 JSON 表示形式。
}// 请求苹果内购校验收据接口
func (this *Pay) RequestReceiptUrl() *Pay {if this.Error != nil {return this}resData := ResponsePay{}// 请求数据var param struct {ReceiptData string `json:"receipt-data"` //(必需的)Base64 编码的收据数据Password    string `json:"password"`     //您的应用程序的共享密钥,它是一个十六进制字符串。//ExcludeOldTransactions bool   `json:"exclude-old-transactions"` // 将此值设置true为以使响应仅包含任何订阅的最新续订交易。此字段仅用于包含自动续订订阅的应用收据。}param.Password = this.Params.Passwordif this.Params.TransactionReceipt == "" {this.Error = errors.New("缺少凭证数据")return this}// 苹果验证param.ReceiptData = this.Params.TransactionReceiptclient := &http.Client{Timeout: RequestUrlTimeout}jsonStr, _ := json.Marshal(param)resp, err := client.Post(this.Params.Url, "application/json", bytes.NewBuffer(jsonStr))if err != nil {this.Error = errors.New(fmt.Sprintf("校验超时, 请稍后再试.%v", err))return this}defer resp.Body.Close()body, _ := ioutil.ReadAll(resp.Body)err = json.Unmarshal(body, &resData)this.Response = resDataif err != nil {this.Error = errors.New(fmt.Sprintf("校验超时, 请稍后再试:%v", err))return this}return this
}// 自定义校验
func (this *Pay) AppleVerify() *Pay {if this.Error != nil {return this}result := this.Responseif result.Status == nil {this.Error = errors.New("url request fail")return this}if *result.Status != 0 {if statusMsg, ok := ResponseStatusMsg[*result.Status]; ok {this.Error = errors.New(statusMsg)return this}this.Error = errors.New(fmt.Sprintf("返回的状态异常:%d", result.Status))return this}// result.Receipt 发送以供验证的收据的 JSON 表示形式。if len(result.Receipt.InApp) <= 0 {this.Error = errors.New("空凭证")return this}if result.Receipt.BundleId != this.Params.AppleClientId {this.Error = errors.New("bundleID 无效")return this}// --- > 业务处理//var (//    rawTransactionId = ""//  rawProductId     = ""//)取出对应凭证//for i := 0; i < len(result.Receipt.InApp); i++ {// val := result.Receipt.InApp[i]//   if this.Params.TransactionId == val.TransactionId {//     rawProductId = val.ProductId//     rawTransactionId = val.TransactionId//     break// }//}////if len(rawTransactionId) <= 0 {//   this.Error = errors.New("raw内无对应凭证")//   return this//}//if len(rawProductId) <= 0 {//   this.Error = errors.New("raw内无对应产品")//   return this//}return this
}// 正式环境-参数
func (this *Pay) AppleProductionParams(params ParamsPay) *Pay {this.Params = paramsthis.Params.Url = ProductionUrlreturn this
}// 沙盒环境-参数
func (this *Pay) AppleSandboxParams(params ParamsPay) *Pay {this.Params = paramsthis.Params.Url = SandboxUrlreturn this
}
func (this *Pay) AppleParams(params ParamsPay) *Pay {this.Params = paramsreturn this
}// 公共校验(优先校验正式环境,再校验沙盒环境)
func AppleCommonVerify(params ParamsPay) *Pay {response := NewPay().AppleProductionParams(params).RequestReceiptUrl().AppleVerify()if response.Error != nil {return NewPay().AppleSandboxParams(params).RequestReceiptUrl().AppleVerify()}return response
}// 自定义环境
func AppleVerify(params ParamsPay) *Pay {return NewPay().AppleParams(params).RequestReceiptUrl().AppleVerify()
}// 正式环境检验
func AppleProductionVerify(params ParamsPay) *Pay {return NewPay().AppleProductionParams(params).RequestReceiptUrl().AppleVerify()
}// 测试环境校验
func AppleSandboxVerify(params ParamsPay) *Pay {return NewPay().AppleSandboxParams(params).RequestReceiptUrl().AppleVerify()
}

go test

package payimport ("fmt""testing"
)var (appleClientId      = ""password           = ""transactionReceipt = "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"
)// 先请求正式模式,然后在请求沙盒模式
func TestAppleCommonVerify(t *testing.T) {params := ParamsPay{AppleClientId:      appleClientId, // 固定配置 --> app应用的bundle_idPassword:           password,      // 固定配置--> 应用程序的共享密钥TransactionReceipt: transactionReceipt,}response := AppleCommonVerify(params)if response.Error != nil {t.Log("err --> ", fmt.Sprintf("err:%v", response.Error))} t.Log("response ---> ", fmt.Sprintf("response:%+v", response.Response))}func TestAppleVerify(t *testing.T) {params := ParamsPay{AppleClientId:      appleClientId, // 固定配置 --> app应用的bundle_idPassword:           password,      // 固定配置--> 应用程序的共享密钥TransactionReceipt: transactionReceipt,Url:                SandboxUrl,}response := AppleVerify(params)if response.Error != nil {t.Log("err --> ", fmt.Sprintf("err:%v", response.Error))} t.Log("response ---> ", fmt.Sprintf("response:%+v", response.Response))
}

golang 实现苹果内购服务端验证相关推荐

  1. postman关闭ssl验证_【第5期】springboot:苹果内购服务端验证

    ​苹果内购: 只要你在苹果系统购买APP中虚拟物品(虚拟货币,VIP充值等),必须通过内购方式进行支付,苹果和商家进行三七开 验证模式有两种: Validating Receipts With the ...

  2. Python Google内购服务端验证

    Google内购完成后,服务端需要校验订单的状态是否正确(是否已经成功付款). 一.申请认证 参考https://developers.google.cn/android-publisher/gett ...

  3. ios内购php验证码,PHP (Laravel) 实现 iOS 内购服务端验证

    /** * @Author woann * @param Request $request * @return \Illuminate\Http\JsonResponse * @des ios内购支付 ...

  4. iOS内购 - 服务端票据验证及漏单引发的思考

    因业务需要实现了APP内购处理,但在过程中出现了部分不可控的因素,导致部分用户反映有充值不成并漏单的情况. 仔细考虑了几个付费安全上的问题,凡是涉及到付费的问题都很敏感,任何一方出现损失都是不能接受的 ...

  5. golang 苹果登录,服务端验证identityToken(真实有效)

    介绍 2019年之后,对于Apple App来说,如果要支持第三方登录,则必须同时支持苹果的第三方登录,即Sign in With Apple, 本文主要介绍如何使用Go语言实现Sign in Wit ...

  6. php 苹果支付验证,IOS苹果内购 PHP后端验证票据

    大体流程: 1.IOS端需要在iTunes Connect上面添加配置一些内购商品,并审核通过,每个内购商品有自己的唯一标识product_id. 2.PHP后端要有一套与之对应的内购商品.IOS应用 ...

  7. IOS苹果内购 PHP后端验证票据

    大体流程: 1.IOS端需要在iTunes Connect上面添加配置一些内购商品,并审核通过,每个内购商品有自己的唯一标识product_id. 2.PHP后端要有一套与之对应的内购商品.IOS应用 ...

  8. 苹果授权登陆 服务端验证(java)

    需要的jar包: jose4j-0.6.4.jar:jwks-rsa-0.9.0.jar:jjwt-0.9.1.jar: jar包下载地址:https://download.csdn.net/down ...

  9. 苹果内购-后端注意事项

    老大要收保护费,我等小弟那也没办法呀...是的,我说的就是苹果内购! 1.先上php验证函数: /*** 验证AppStore内付* @param string $receipt_data 付款后凭证 ...

  10. 为什么苹果内购总是失败_苹果官网送货流程将改革,最快次日达丨iPhone6s 等钉子户终于要换手机了...

    文字编辑:XC丨插图来自于网络 苹果送货程序将改革 据外媒表示,苹果将利用零售店的库存来进行就近发货方式,主要目的就是为了减少用户收到货的时间. 在此之前,苹果官网下单的用户,无论是那个位置都将是总部 ...

最新文章

  1. thinkphp5框架一小时搭建一个php后端(1)
  2. 网络营销十技之六:联署计划营销
  3. 关于hadoop与jstl冲突的jar包
  4. golang hmac的sha1加密例子
  5. 【控制】《多智能体机器人系统信息融合与协调》范波老师-第6章-基于分布式强化学习的多 Agent 协调方法
  6. 字节流和字符流哪个不刷新_不喜欢节流吗?
  7. (194)FPGA上电后IO的默认状态(ISE软件默认为0)
  8. 如何使用 Spring 对数据库进行 CURD?
  9. 2013编程之美资格赛【传话游戏】
  10. hash function/ hash table 背后的数学基础(二)
  11. 百度地图电子围栏功能的实现
  12. 易优CMS:arcpagelist 瀑布流分页列表
  13. 烤仔TVのCCW | 智能合约间的四种调用(下)
  14. 一对一或一对多音视频通话会议系统可以通过哪些方式实现?
  15. 荣耀 MagicBook 系列新品发布会汇总:多款锐龙本、平板电脑登场~~~
  16. 解决pdd接口老大难的问题,稳定可靠的新接口
  17. vs程序出错运行上次的成功的exe
  18. Device Orientation——设备方向
  19. Spring Kafka实战(3)—message listener创建方式探讨
  20. Android OpenGLES2.0(十七)——球形天空盒VR效果实现

热门文章

  1. 解决windows虚机系统时间与北京时间相差8小时
  2. linux关机电源不断电,[转发]黑苹果修改DSDT彻底解决关机不断电和睡眠问题
  3. 如何把一个app导入另一个app?教你快速解决!
  4. MySQL - Failed to open the referenced table XXX
  5. 微信小程序_页面加载不出来/页面跳转不成功的若干可能原因
  6. 7个向上管理技巧,让你的职场一路开挂
  7. PHP中explode()和implode()函数讲解和实战用法
  8. 【JZOJ 5421】【NOIP2017提高A组集训10.25】嘟嘟噜
  9. Kai Ge - Q绑查询V1.3
  10. 读《哈佛大学》纪录片 后感