IAP应用的收据验证
使用Receipt Verification Service (RVS),可以验证应用用户进行的购买。
RVS概述
下图显示了带有收据验证的购买工作流程。RVS在IAP API完成购买并将购买收据返回给应用后启动。

下表中的步骤与上图中的标注匹配:
步骤 | 组件 | 任务 |
---|---|---|
1 | IAP API | IAP API与用户交互以完成购买。IAP API向应用返回购买收据。 |
2 | 应用 | 应用将购买收据转发给应用服务器。 |
3 | 应用服务器 | 应用服务器向RVS服务器发送验证收据请求。 |
4 | RVS服务器 | RVS服务器确认收据是否有效。 |
5 | 应用服务器 | 应用服务器向用户提供内容。 |
只要是通过亚马逊购买,您还可以使用RVS访问在其他平台(如您的网站)上购买的订阅。以下情景描述了此工作流程:
- 应用用户从您的公司网站上通过亚马逊购买订阅。
- 应用收到所购买订阅的收据
- 为允许访问,应用会向您的服务器发送收据中的信息。最后,服务器通过查询RVS来验证此交易。
设置RVS
RVS提供了两个环境选项,具体取决于应用是处于开发/测试阶段,还是已发布到亚马逊应用商店:
- RVS Cloud Sandbox: 在开发和测试应用时,请使用RVS沙盒环境验证App Tester测试工具生成的收据。要设置RVS Cloud Sandbox,请参阅使用RVS Cloud Sandbox。
- RVS生产服务器: 将应用发布到亚马逊应用商店后,您可以使用Amazon RVS生产服务器。请参阅RVS生产环境的说明。
RVS请求语法
使用RVS来验证PurchaseResponse
对象或PurchaseUpdatesResponse
对象。可以从这些响应对象中提取一个UserId
,它表示用户的唯一标识符。PurchaseResponse
对象包含ReceiptId
,它与UserId
联合用于对购买执行带外服务器端验证。出于安全原因,来自您的服务器的请求需要传递共享密钥以确认您的身份。
这些请求使用以下格式:
<Protocol>//<Server>[/<sandbox>]/version/<Operation_version_number>/verifyReceiptId/developer/<Shared_Secret>/user/<UserId>/receiptId/<ReceiptId>
尖括号中的术语是请求参数,请将它们替换为待验证交易的以下值:
- Protocol: 用于与服务器或沙盒通信的协议,如https:。
- Server: 您要与之通信的RVS服务器URL。
- RVS Cloud Sandbox服务器和RVS生产服务器都使用URL "appstore-sdk.amazon.com"。
- sandbox: 如果使用RVS Cloud Sandbox服务器,请使用值"sandbox"。如果使用RVS生产服务器,请省略此参数。
- Operation_version_number:
verifyReceiptId
操作版本号。此版本号独立于IAP版本号。当前verifyReceiptId
版本号为“1.0”。 - Shared_secret: 用于标识发出请求的开发者的共享密钥。可在亚马逊应用商店中您的开发者账户“Shared Key”(共享密钥)页面上找到共享密钥:https://developer.amazon.com/sdk/shared-key.html。对于RVSSandbox,共享密钥可以是任何非空字符串。
- UserId: 代表亚马逊应用商店应用中不同亚马逊客户的ID:
PurchaseResponse.getUserData().getUserId()
。 - ReceiptId: 购买的唯一ID:
PurchaseResponse.getReceipt().getReceiptId()
或PurchaseUpdatesResponse.getReceipts()
→Receipt.getReceiptId()
。
RVS响应语法
RVS提供了RESTful JSON API接口。作为最佳实践,使用JSON解析器类从RVS服务器读取JSON响应。
发出验证交易的请求后,RVS服务器或沙盒会返回响应代码来指示请求是否成功。如果成功,则返回的JSON响应包括有关交易的信息。
下例展示了一个成功响应:
{
"autoRenewing":false,
"betaProduct":false,
"cancelDate":null,
"cancelReason":null,
"freeTrialEndDate":null,
"fulfillmentDate":null,
"fulfillmentResult":null,
"gracePeriodEndDate":null,
"parentProductId":null,
"productId":"com.amazon.iapsamplev2.gold_medal",
"productType":"CONSUMABLE",
"promotions":null,
"purchaseDate":1399070221749,
"purchaseMetadataMap":null,
"quantity":1,
"receiptId":"wE1EG1gsEZI9q9UnI5YoZ2OxeoVKPdR5bvPMqyKQq5Y=:1:11",
"renewalDate":null,
"term":null,
"termSku":null,
"testTransaction":true
}
RVS响应代码
Receipt Verification Service会使用以下代码之一进行响应,这些代码可以指示验证检查的结果:
响应代码 | 描述 |
---|---|
HTTP 200 | 成功: 收据ID、用户ID和共享密钥都是有效的。产品类型是以下项之一: “ENTITLED”、“CONSUMABLE”或“SUBSCRIPTION” |
HTTP 400 | 此receiptId 表示的交易无效,或没有找到此receiptId 的交易。 |
HTTP 410 | 此receiptId 表示的交易不再有效。请将其作为已取消的收据。 |
HTTP 429 | 该请求已被限制节流。请降低您的调用速率并稍后重试。 |
HTTP 496 | sharedSecret 无效 |
HTTP 497 | 用户ID无效 |
HTTP 500 | 出现内部服务器错误 |
成功交易的RVS响应字段
下表列出并描述了成功交易的RVS响应中包含的字段:
字段 | 数据类型 | 描述 |
---|---|---|
autoRenewing |
布尔值 | 表示客户的订阅是否会自动续订。 |
betaProduct |
布尔值 | 指示所购买的产品是否是动态应用测试产品。 |
cancelDate |
长整数 | 取消购买的日期,或订阅到期的日期。如果购买未取消,则此字段为null。时间以毫秒为单位。 |
cancelReason |
整数 | 指示取消产品的原因。可能的值为null、0、1或2,其中每个整数分别代表一项取消原因: null - 购买未取消。0 - 当前无法提供取消原因,将在之后提供。1 - 客户取消了订单。2 - 亚马逊系统取消了购买。例如,客户用于购买订阅的付款无效,并且无法在宽限期内完成购买。如果亚马逊客户支持部门应客户要求取消订单,也会返回此代码。 |
freeTrialEndDate |
长整数 | 表示订阅处于免费试用期内。提供订阅免费试用的结束日期,以epoch(毫秒)为单位。如果订阅不在免费试用期内,则此字段为null。 |
fulfillmentDate |
长整数 | 在订阅购买中,为确认履行的日期。以纪元以后的毫秒数存储。如果未确认订阅履行,则为null。消费品和权利始终为null。 |
fulfillmentResult |
字符串 | 在订阅购买中,履行的状态。有效值:
|
gracePeriodEndDate |
长整数 | 表示订阅处于宽限期内。提供订阅宽限期的结束日期,以epoch(毫秒)为单位。如果订阅不在宽限期内,则此字段为null。 |
parentProductId |
字符串 | Null。预留以供将来使用。 |
productId |
字符串 | 您在应用中为此商品定义的SKU。 |
productType |
字符串 | 所购买产品的类型。有效产品类型为CONSUMABLE 、SUBSCRIPTION 和ENTITLED 。 |
promotions |
List<Promotion> | 订阅购买的促销定价详情。如果没有促销,则为Null。请参阅RVS中的促销定价。 |
purchaseDate |
长整数 | 购买日期,以纪元以后的毫秒数存储。对于订阅商品,purchaseDate 代表首次购买日期,而不是后续续订的购买日期。 |
purchaseMetadataMap |
映射<字符串,布尔值> | 如果购买是通过快速订阅发起的,则值为{"QuickSubscribe":true} 。否则为null。 |
quantity |
整数 | 购买的数量。始终为null或1。 |
receiptId |
字符串 | 购买的唯一标识符。 |
renewalDate |
长整数 | 需要续订订阅购买的日期。日期以纪元以后的毫秒数存储。 |
term |
字符串 | 订阅IAP将保持有效的持续时间(期限从购买之日开始)。期限由数字和时间段(天、周、月、年)构成,如1周或2个月。 |
termSku |
字符串 | 对应于订阅期的唯一SKU。 |
testTransaction |
布尔值 | 表明此购买是否作为亚马逊发布和测试过程的一部分进行。 |
RVS中的促销定价
有关如何设置促销定价优惠的详细信息,请参阅设置促销定价。如果客户以促销定价购买了订阅,则通过RVS返回的收据包括促销详情。上一节所述的JSON响应包含一个promotions
(促销)字段。本节将更详细地介绍promotions
字段。促销详情仅显示在与客户以促销定价购买的订阅相对应的收据上。
如果没有与收据相关联的促销,则promotions
字段为空。否则,该字段将包含Promotion
(促销)对象列表,其中包含以下字段。
字段 | 数据类型 | 描述 |
---|---|---|
promotionType |
字符串 | 促销类型。有效值: 试销价 、促销价 。请参阅有效值描述。 |
promotionStatus |
字符串 | 该客户的促销状态。有效值: 已加入队列 、进行中 、已完成 。请参阅有效值描述。 |
示例:
"promotions": [
{
"promotionType":"试销价 - 所有客户",
"promotionStatus":"已完成"
}
]
有效值描述
promotionType
字段有效的值为:
试销价 - 所有客户
— 向所有客户(包括新客户和到期客户)提供的优惠。促销价 - 到期客户
— 仅对到期客户提供的优惠。.
promotionStatus
字段有效的值为:
已加入队列
— 客户使用促销优惠购买了订阅。目前,他们正在进行免费试用,尚未开始促销期。进行中
— 客户目前正在享受促销优惠。已完成
— 客户已完成促销期。
促销定价常见问题解答
- 问: 是否所有客户订阅收据都会提供促销详情?
- 不是,促销详情仅显示在以促销定价完成的订阅购买所对应的收据上。
- 问: 取消收据后是否显示促销详情?
- 是的,如果客户的订阅被取消,则促销详细信息显示为
已完成
。 - 问: 如果客户在免费试用期间、续订为促销计划之前取消订阅,则促销状态是什么?
- 由于客户没有开始促销期,因此收据上不会显示任何与之相关联的促销详情。
取消日期和续订日期
renewalDate
字段包含自动续订订阅购买下一次需要续订的日期。此字段仅适用于订阅购买。如果客户按月订阅,则订阅会每月在与客户首次订阅相同的日期续订。如果下个月不包括该确切日期,则预订日期为距其最近的前一个日期。例如:
- 如果客户在1月2日订阅,则接下来的三个续订日期为2月2日、3月2日和4月2日。
- 如果客户在1月31日订阅,接下来的三个续订日期为2月28日(如果是闰年,则为2月29日)、3月31日和4月30日。
cancelDate
字段包含订阅购买到期的日期或亚马逊客户服务取消购买的日期。取消日期表示客户失去相应内容访问权限的日期。如果客户通过关闭自动续订来取消其订阅,则取消日期是之前原定的续订日期。
renewalDate
和cancelDate
字段以毫秒为单位存储。可以使用java.util.Date(timeInMillis)
将值转换为日期对象。
消费品或权利购买
在有效的收据中,取消日期和续订日期都包含null值。如果取消日期字段不为null,则会包含亚马逊客户服务取消购买的日期。
订阅购买
在有效的订阅收据中,取消日期为null。如果cancelDate
字段不为null,则会包含订阅到期的日期或亚马逊客户服务取消购买的日期。
renewalDate
字段包含自动续订订阅购买下一次需要续订的日期。如果订阅未设置为自动续订,则字段值为null。
在以下示例中,用户拥有已取消的订阅:
- 订阅的有效期为2016年1月1日 – 2016年3月1日。在此收据中,此订阅的purchaseDate设置为2016/01/01,cancelDate设置为2016/03/01。
- 如果此订阅后来于2016年4月1日重新激活,则订阅会具有第二个收据。第二个收据会显示purchaseDate为2016年4月1日,cancelDate为null。
RVS沙盒和生产示例
请参阅RVS示例。
Last updated: 2023年12月4日