使用 UDP 包实现应用内购
要针对 UDP 在游戏中实现应用内购,请执行以下步骤:
完成这些步骤后,即可继续构建游戏。
查询合作伙伴商店的库存
初始化成功后,请调用 QueryInventory
方法。此方法可以查询合作伙伴商店的库存。
此方法可用于:
- 检查是否有未消耗的 IAP 商品
- 检查是否有未交付的已购商品
- 查询商品详细信息
此方法返回非消耗品和尚未消耗的消耗品的商品信息(商品名称、ID、价格、描述)。
例如,这样可以在应用程序崩溃后恢复未消耗的购买商品。
从游戏向 UDP 库存发送查询
如果指定商品 ID,则会获得指定 IAP 商品的商品信息。
StoreService.QueryInventory(List<string> productIds, IPurchaseListener listener);
如果不指定商品 ID,则会获得所有 IAP 商品的信息。
StoreService.QueryInventory(IPurchaseListener listener);
为与购买服务相关的事件实现监听器。
下面是一个示例:
public class PurchaseListener : IPurchaseListener
{
public void OnPurchase(PurchaseInfo purchaseInfo)
{
// 购买已成功。
// 如果购买的商品是消耗品,则应在此处消耗。
// 否则,请交付商品。
}
public void OnPurchaseFailed(string message, PurchaseInfo purchaseInfo)
{
Debug.Log("Purchase Failed: " + message);
}
public void OnPurchaseRepeated(string productCode)
{
// 一些商店不支持 queryInventory。
}
public void OnPurchaseConsume(PurchaseInfo purchaseInfo)
{
// 消耗成功。
// 应在此处交付商品。
}
public void OnPurchaseConsumeFailed(string message, PurchaseInfo purchaseInfo)
{
// 消耗失败。
}
public void OnQueryInventory(Inventory inventory)
{
// 查询库存成功。
}
public void OnQueryInventoryFailed(string message)
{
// 查询库存失败。
}
}
购买 IAP 商品
要从游戏发出购买请求,请在用户购买商品时调用 Purchase 方法。UDP 会自动检查购买收据以检查购买是否有效。
从游戏向 UDP 发送购买请求
调用 Purchase 方法时,请提供:
productId
- 玩家想要购买的 IAP 商品的唯一标识符。developerPayload
- 你要发送到 UDP SDK 的信息。IPurchaseListener
- 这是一个监听器,可告知你所有与购买有关的事件的结果。
例如:
StoreService.Purchase(string productId, string developerPayload, IPurchaseListener listener);
注意:使用 IAP 的游戏必须包含 Purchase
方法。
购买完成后,UDP 将信息返回给游戏。
某些合作伙伴商店的付款网关无法实时获取付款回调。因此可能会阻碍 UDP 快速接收付款 SUCCESS 或 FAILED 回调。在这种情况下,UDP 认为回调是 FAILED。要解决此问题,Unity 建议在服务器上查询订单以获取最新状态。
对于在线游戏,可以通过回调通知功能在游戏服务器上验证购买。UDP 将回调通知发送到 UDP 设置中指定的 URL。
消耗商品
对于可消耗商品,游戏需要向 UDP SDK 发送一个 Consume
请求。在商品被消耗后,游戏应交付该商品。这样可以防止重复交付商品。
注意:OnPurchase 会返回 PurchaseInfo。
在 PurchaseListener
类的 OnPurchase
事件中,检查商品是否为消耗品。如果该商品为消耗品,请消耗该商品并在 PurchaseListener
类的 OnPurchaseConsume
事件中实现所购买商品的游戏逻辑。例如:
StoreService.ConsumePurchase(PurchaseInfo, IPurchaseListener);
// 实现购买游戏逻辑
从游戏向 UDP 发送消耗请求
验证客户端集成
UDP 自动执行客户端验证。用户购买 IAP 商品时,合作伙伴商店将返回有效负载和签名。然后,UDP SDK 验证签名。如果验证失败,则购买将相应失败。
在服务器上验证购买状态
可通过以下方式之一在服务器端验证购买状态:
注意:华为 AppGallery 商店目前不支持回调通知。
可在 UDP 沙盒环境中测试服务器端实现情况。
查询订单
游戏可通过调用 HTTP GET 请求来查询 UDP 中的订单信息。
查询 UDP 中的订单信息
GET https://distribute.dashboard.unity.com/udp/developer/api/order?orderQueryToken=<orderQueryToken>&orderId=<orderId>&clientId=<clientId>&sign=<sign>
API 可能由于以下原因返回“未确认”(unconfirmed) 状态:
- 商店目前无法获取订单状态
- 商店不支持实时订单状态查询
在这种情况下,请间隔一定时间重试 QueryOrder API。商店将向 UDP 发送一个回调(近乎实时),而 UDP 可以将状态返回给游戏。
获取有关订单查询参数的更多信息。
接收回调通知
购买成功后,如果指定了回调 URL,则 UDP 服务器会向游戏服务器通知付款结果。请实现一个 HTTP POST 请求并接受以下 JSON 格式的请求主体:
属性名称 | 格式 | 必需/可选 | 描述 |
---|---|---|---|
payload | JSON 字符串 | 必需 | 购买订单的内容。获取有关 JSON 有效负载的更多信息。 |
signature | 字符串 | 必需 | 有效负载的 PKCS1 v1.5 签名 |
使用证书
要验证证书,请使用 Unity 客户端 RSA 公钥 (Client RSA Public Key)。如果证书通过验证,请从证书中提取 RSA 公钥,然后使用此密钥来验证签名。要生成签名,请使用 RSA-SHA1 算法对有效负载进行加密。
下面是一个示例:
公钥:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qxbtUqsrvwk2FZ+F2J0EkUDKLdZSVE3qPgxzKxOrgScGrCZULLav9CPzRP91HN9GccvmShH2bsegP3RVtMdwU1eV7C2JdOW1sylCyKIgylCT8tLdQeUMRaIlt7fOfl+k3bkUouWJx8WnrQYM6a7oDeCGklIlekvpQ2NcS1eg7Jp646vBzyu8FMBiuj5LZOhCJg/XXs0kRpvSOBAPndUu/HgqD9aFaXNZBxMN++efxq6PnAVRzRdTtRur+OZSBGjXxgaBKrdbXCkEM3fkMgXP9egq6vnzCiQhZ7UDFXtXQ3DPqviqrTY5WsR9t4X6JxCXo6yGlQAEK/ft9MWN13nrQIDAQAB
请求主体:
{
"signature": "swWWZpg0/Y26XBohvqqC/for4nyhS5zwzru5s8AJI7YYC+ECHOk7KQjOyFw7cWxM3QNpd7N7E7Umy3vYwDXjV2Y4BLnuJy5gGIpO5jKU4xBNQf793FmI0Fk93YrU31QyiIjXymg1O/H1nKSJXqMz6bycBugiStqsuGp1/CctTHE0Dpv4hC6fZoNWIHYpPJQuKh4DyP1lgE32omcuKUh7IAQduRPDa+qiYJRCA8bV17xK6T8ajS3RlhKue9hjE2a21t8p017ViaOS5OWdzptUwgnWaFi6gs1k0cjdn7o/0QJEgk5j6a8WYE/S8F7YfsYcAwUQV4KY3ex0ULsH3GQEGA==",
"payload": "{\"ClientId\":\"Q_sX9CXfn-rTcWmpP9VEfw\",\"CpOrderId\":\"0bckmoqhel5yd13f\",\"ProductId\":\"com.mystudio.mygame.productid1\",\"ChannelType\":\"APTOIDE\",\"Currency\":\"APPC\",\"Amount\":\"1.01\",\"Country\":\"CHINA\",\"Quantity\":1,\"Rev\":\"0\",\"Status\":\"SUCCESS\",\"PaidTime\":\"2018-09-28T06:43:20Z\",\"Extension\":\"{\\\"key\\\":\\\"value\\\"}\"}"
}
以下代码示例显示了如何在 Go 中验证证书:
func verify(data []byte, publicKey string, sign string) bool {
decodePublic, err := base64.StdEncoding.DecodeString(publicKey)
if err != nil {
panic(err)
}
pubInterface, err := x509.ParsePKIXPublicKey(decodePublic)
if err != nil {
panic(err)
}
pub := pubInterface.(*rsa.PublicKey)
decodeSign, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
return false
}
sh1 := sha1.New()
sh1.Write(data)
hashData := sh1.Sum(nil)
err = rsa.VerifyPKCS1v15(pub, crypto.SHA1, hashData, decodeSign)
if err != nil {
return false
}
return true
}
填写 IAP 目录
Unity 建议在 UDP 控制台中添加 IAP 商品,因为这样做可以提供更多定义 IAP 的选项。 但是,Unity 建议仍然在编辑器中至少创建一个 IAP,以便测试 Unity 编辑器和 UDP 控制台是否正确同步。
注意:如果未在游戏客户端中使用 IAP 目录(例如,如果仅在游戏服务器上维护 IAP 商品),仍必须在 UDP 控制台上创建 IAP 目录。
- 对于 2019.4 及更低版本的 Unity 编辑器,请打开 UDP Settings Inspector 窗口。
对于 2020.1 及更高版本的 Unity 编辑器,请打开 IAP Catalog 窗口。 - 在 IAP Catalog 部分中,输入每个 IAP 商品的商品信息。
- 遵循商品 ID 的要求,以确保它们对应用商店而言有效。
- 确保在游戏中定义的 IAP 商品使用的是 IAP 目录中设置的产品 ID (Product ID)。
- 要将单个 IAP 商品保存到 UDP 控制台,请在商品旁边的下拉选单中选择 Push。
- 要将所有 IAP 商品保存到 UDP 控制台,请选择顶部的 Push 按钮。
- 要将所有 IAP 商品保存到 UDP 控制台,请选择顶部的 Push 按钮。
- 要添加更多商品,请选择 Add new IAP。
为了确保正确保存了 IAP 目录,请检查 UDP 控制台中是否显示了所添加的商品。
在实现并配置 UDP 后,请按步骤构建游戏。