facebook twitter hatena line email

Php/アプリストア連携/返金API/GooglePlayStore/RTDN

提供: 初心者エンジニアの簡易メモ
2025年6月18日 (水) 16:39時点におけるAdmin (トーク | 投稿記録)による版 (ページの作成:「==Google Play Consoleでの設定== #Google Play Consoleにログイン #「Monetization」 > 「Monetization setup」に移動 #「Real-time developer notifications」...」)

(差分) ←前の版 | 最新版 (差分) | 次の版→ (差分)
移動: 案内検索

Google Play Consoleでの設定

  1. Google Play Consoleにログイン
  2. 「Monetization」 > 「Monetization setup」に移動
  3. 「Real-time developer notifications」セクションで通知を受け取るエンドポイントURLを設定

phpサンプル

// RTDNエンドポイント用のスクリプト

// Google Playの公開鍵を取得(キャッシュ推奨)
function getGooglePublicKey($keyId) {
    // 実際にはここでGoogleの公開鍵を取得するロジックを実装
    // 例: https://www.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}
    // またはキャッシュから取得
    
    // 簡易実装(実際には適切な鍵取得処理が必要)
    $publicKeys = [
        'your-key-id' => '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----'
    ];
    
    return isset($publicKeys[$keyId]) ? $publicKeys[$keyId] : null;
}

// JWTの検証
function verifyJwt($jwt) {
    list($header, $payload, $signature) = explode('.', $jwt);
    
    $header = json_decode(base64_decode($header), true);
    $payload = json_decode(base64_decode($payload), true);
    
    // 公開鍵を取得
    $publicKey = getGooglePublicKey($header['kid']);
    if (!$publicKey) {
        return false;
    }
    
    // 署名検証
    $data = "$header.$payload";
    $signature = base64_decode(str_replace(['-', '_'], ['+', '/'], $signature));
    
    $result = openssl_verify($data, $signature, $publicKey, 'SHA256');
    
    return $result === 1 ? $payload : false;
}

// メイン処理
function handleRtdnNotification() {
    // リクエストボディを取得
    $input = file_get_contents('php://input');
    $data = json_decode($input, true);
    
    if (!$data || !isset($data['message']['data'])) {
        http_response_code(400);
        exit;
    }
    
    // JWTをデコード
    $jwt = base64_decode($data['message']['data']);
    $payload = verifyJwt($jwt);
    
    if (!$payload) {
        http_response_code(401);
        exit;
    }
    
    // 通知タイプに応じて処理
    $notificationType = $payload['eventType'];
    $subscriptionId = $payload['subscriptionNotification']['subscriptionId'];
    $purchaseToken = $payload['subscriptionNotification']['purchaseToken'];
    
    switch ($notificationType) {
        case 'SUBSCRIPTION_CANCELED':
            // 定期購入がキャンセルされた
            handleSubscriptionCanceled($subscriptionId, $purchaseToken);
            break;
            
        case 'SUBSCRIPTION_PURCHASED':
            // 新しい定期購入
            handleSubscriptionPurchased($subscriptionId, $purchaseToken);
            break;
            
        case 'SUBSCRIPTION_RENEWED':
            // 定期購入が更新された
            handleSubscriptionRenewed($subscriptionId, $purchaseToken);
            break;
            
        case 'SUBSCRIPTION_REVOKED':
            // 返金または管理者によるキャンセル
            handleSubscriptionRevoked($subscriptionId, $purchaseToken);
            break;
            
        case 'SUBSCRIPTION_RESTARTED':
            // ユーザーがキャンセル後に再開
            handleSubscriptionRestarted($subscriptionId, $purchaseToken);
            break;
            
        default:
            // 未知の通知タイプ
            http_response_code(400);
            exit;
    }
    
    // 成功レスポンス
    http_response_code(200);
}

// 返金処理(SUBSCRIPTION_REVOKED)
function handleSubscriptionRevoked($subscriptionId, $purchaseToken) {
    // 1. Google Play Developer APIを使用して購入情報を取得
    $purchaseInfo = getSubscriptionPurchaseInfo($subscriptionId, $purchaseToken);
    
    // 2. 返金理由を確認
    $revocationReason = $purchaseInfo['revocationReason'] ?? null;
    
    // 3. ユーザーアカウントを更新(アクセス権限を削除など)
    $userId = getUserIdFromPurchaseToken($purchaseToken);
    revokeUserAccess($userId);
    
    // 4. データベースに返金記録を保存
    logRefund($userId, $subscriptionId, $purchaseToken, $revocationReason);
    
    // 5. 必要に応じてユーザーに通知
    sendRefundNotification($userId);
}

// Google Play Developer APIから購入情報を取得
function getSubscriptionPurchaseInfo($subscriptionId, $purchaseToken) {
    // 実際にはここでGoogle Play Developer APIを呼び出す
    // 必要な認証情報などを設定
    
    // 簡易実装(実際にはAPI呼び出しが必要)
    return [
        'revocationReason' => 1, // 1: 返金, 2: 管理者によるキャンセル
        // その他の購入情報...
    ];
}

// その他のヘルパー関数...

// メイン処理を実行
handleRtdnNotification();