说明下场景: 玩家在游戏内购买一个订阅的商品, 每月定时从google平台扣款并通知到设定URL
大概流程应该是这样: 玩家->游戏前端购买订阅商品->谷歌->通知设定的URL->发货给玩家,后续的每月谷歌会定时通知
我仅负责服务端接收google通知并通知游戏发货, 所以跟google的N多服务要打交道
首先, 这个文档要仔细看
https://developer.android.com/google/play/billing/realtime_developer_notifications
一定要按照上面的步骤来, 如果你用的是子账号, 那么一定会遇到权限的各种问题, 比如要为子账号开通GCP权限
我们使用的谷歌的推送方式 https://cloud.google.com/pubsub/docs/push
意思是我提供接口,让google将信息传给我, 我在去调用google的接口去验证并获取详情
大致步骤如下:
1 选择你的项目
2 在此页面增加你的订阅项目
3 增加服务账号, 大概步骤(这一步本人无法100%确认, 因为我用的子账号, 这一步看不到)
- 绑定项目(项目不能绑定错,项目不能绑定错,项目不能绑定错)
- Play Console > 设置 > API 权限 > 检查“关联的项目”是否有开启
- "APIs & auth" subcategory "Credentials" > service account ID (客户端ID)
设置 server accounts 时 会提示下载 json 或 p12文件,选择json就好
当前JSON文件非常重要,这是免登入直接获得google play授权的凭证
尤于我不是通过OAuth2去验证,是通过的免登录获得授权,所以其他博客要创建的一些东西,我是可以不创建的(当然创建了也没关系)
权限要加好, 否则后面会报403错误
4 增加专题
此处要按照上面发的文档步骤来, 其中在传送类型里选择推送,
然后填入要通知的URL, 如果提示参数错误, 可能又两个原因, 1 是权限, 2是填入的URL没有验证
5 增加API服务
然后创建凭证
建立之后查看其信息
下载json文件, 客户端id, 客户端密匙, 保存好, 程序里要用到
大概步骤如上, 由于我不是管理员账号, 所以怎么关联的那块, 我看不到具体的页面长什么样...
接下来就是服务端接收与验证了
首先要现在google-client sdk
github: https://github.com/googleapis/google-api-php-client
建议使用composer将其下载下来
composer require google/apiclient:"^2.0"
主逻辑大致如下
$json = file_get_contents('php://input');
$message = json_decode($json, true);
$data = json_decode(base64_decode($message['message']['data']), true);
if (empty($data)) {
logs("parse message->data error");
return;
}
$purchase_token = $data['subscriptionNotification']['purchaseToken'];
$product_id = $data['subscriptionNotification']['subscriptionId'];
$package_name = $data['packageName'];
require_once APPPATH . 'third_party/google-client/autoload.php';
try {
$client = new Google_Client();
$client->setAuthConfig("json文件");
$client->setApplicationName($package_name);
$client->addScope(Google_Service_AndroidPublisher::ANDROIDPUBLISHER);
$client->setClientId("客户端id");
$client->setClientSecret("客户端密匙");
$publisher = new Google_Service_AndroidPublisher($client);
$subs = $publisher->purchases_subscriptions->get($package_name, $product_id, $purchase_token);
$subs_array = (array) $subs;
$this->_logMessage($subs_array);
//支付状态: 0 付款等待中, 1 已收到付款, 2 免费试用, 3 待延期升级/降级
$pay_status = $subs_array['paymentState'];
//取消原因: null 续订中, 0 用户取消了订阅, 1 订阅已被系统取消,例如由于计费问题, 2 订阅已被新订阅所取代, 3 订阅已被开发者取消
$cancel_reason = $subs_array['cancelReason'];
//确认状态: 0 还未确认, 1 已确认
$ack_status = $subs_array['acknowledgementState'];
if ($cancel_reason !== null) {
$this->json(Constants::ORDER_SUBSCRIBE_CANCEL, "subscribe cancel, status: {$cancel_reason}");
}
if ($pay_status != 1) {
$this->json(Constants::ORDER_SUBSCRIBE_UNPAID, "subscribe unpaid, status: {$pay_status}");
}
if ($ack_status != 1) {
$this->json(Constants::ORDER_SUBSCRIBE_UNCONFIRMED, "subscribe unconfirmed, status: {$ack_status}");
}
//透传字段样例:{"game_id":N, "app_id": N ,"server_id":"1","role_id":"135","uid":"700000000027","goods_id":"test.subs.monthlycard3","order_status":4,"cp_order_id":"测试订单1560942046","server_name":"测试服","src":3}
$payload = json_decode($subs_array['developerPayload'], true);
if (empty($payload)) {
$this->json(Constants::ORDER_SUBSCRIBE_DEVELOPER_PAYLOAD_ERROR, "developerPayload parse fail");
}
//验证谷歌的订单号是否存在
$google_order_id = $subs_array['orderId'];
//下面就是业务逻辑了
} catch (Exception $exception) {
$this->_logMessage("android subscribe exception: " . $exception->getMessage());
$this->json(Constants::FAILURE);
}
返回的字段信息说明
https://developers.google.com/android-publisher/api-ref/purchases/subscriptions#resource
遇到过这个问题
{ "error": { "errors": [{ "domain": "androidpublisher", "reason": "projectNotLinked", "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console." }], "code": 403, "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console." } }
后来通过管理员将创建的服务账号与api正确关联之后就正确了