<?php

require 'vendor/autoload.php';
use GuzzleHttp\Client;

/**
 * @author pengchen
 * @since  2023年10月13日
 * @description 本范例代码，可以通过修改参数，运行函数，查看接口效果
 */
class Example
{
    // 域名常数
    private $DOMAIN = "https://api.developer.xiaomi.com/devupload";
    private $PUSH = "/dev/push";
    private $PUSH_CHANNEL_APK = "/dev/pushChannelApk";
    private $QUERY = "/dev/query";
    private $CATEGORY = "/dev/category";
    private $pubKey;

    // x509加密常数
    const KEY_SIZE = 1024;
    const GROUP_SIZE = self::KEY_SIZE / 8;
    const ENCRYPT_GROUP_SIZE = self::GROUP_SIZE - 11;

    public function __construct()
    {
        $this->pubKey = $this->getPublicKeyByX509Cer('公钥文件路径');
    }

    /**
     * 获取公钥
     * @param $cerFilePath 公钥文件路径
     * @return false|OpenSSLAsymmetricKey 返回公钥密串
     */
    public function getPublicKeyByX509Cer($cerFilePath)
    {
        $cert = file_get_contents($cerFilePath);
        $pubKey = openssl_pkey_get_public($cert);
        return $pubKey;
    }

    /**
     * 使用公钥加密
     * @param $str 明文
     * @param $publicKey 公钥秘钥
     * @return string
     */
    function encryptByPublicKey($str, $publicKey)
    {
        $data = str_split($str, self::ENCRYPT_GROUP_SIZE);
        $baos = '';
        $cipher = openssl_get_publickey($publicKey);
        foreach ($data as $segment) {
            $result = '';
            openssl_public_encrypt($segment, $result, $cipher);
            $baos .= $result;
        }
        return bin2hex($baos);
    }

    /**
     *  获取应用分类
     * @return bool|string
     */
    public function getCatetories()
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->DOMAIN . $this->CATEGORY);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }

    /**
     * 查询应用信息
     * @param $userName 小米账号邮箱
     * @param $password 小米账号密码或者私钥
     * @param $packageName 包名
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    function query($userName, $password, $packageName) {
        $client = new Client();

        $json = [
            'packageName' => $packageName,
            'userName' => $userName
        ];

        $formBodyBuilder = [
            'RequestData' => json_encode($json)
        ];

        $sigItem = [
            'name' => 'RequestData',
            'hash' => md5(json_encode($json))
        ];

        $sig = [
            'sig' => [$sigItem],
            'password' => $password
        ];

        try {
            $formBodyBuilder['SIG'] = $this->encryptByPublicKey(json_encode($sig), $this->pubKey);
            $response = $client->post($this->DOMAIN . $this->QUERY, [
                'form_params' => $formBodyBuilder
            ]);

            return $response->getBody()->getContents();
        } catch (Exception $ex) {
            echo $ex->getMessage();
        }

        return "";
    }

    /**
     * 推送普通app
     * @param $email 小米账号邮箱
     * @param $password 小米账号密码或者私钥
     * @param $synchroType 更新类型：0=新增，1=更新包，2=内容更新
     * @param $apkFile apk文件路径
     * @param $iconFile icon文件路径
     * @param $appDetail app信息json字符串
     * @param $screenshots 截图文件路径数组
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    function push($email, $password, $synchroType, $apkFile, $iconFile, $appDetail, $screenshots) {
        $httpClient = new GuzzleHttp\Client([
            'connect_timeout' => 10,
            'timeout' => 20,
        ]);

        $json = [
            'userName' => $email,
            'appInfo' => json_encode($appDetail),
            'synchroType' => $synchroType,
        ];

        // Calculate digital signature
        $sigJSON = [];
        $paramMd5Array = [];
        $sigItem = [
            'name' => 'RequestData',
            'hash' => md5(json_encode($json)),
        ];
        $paramMd5Array[] = $sigItem;

        $multipart = [];

        if ($apkFile !== null) {
            $apk = [
                'name' => 'apk',
                'hash' => md5_file($apkFile),
            ];
            $paramMd5Array[] = $apk;
            $multipart[] = [
                'name' => 'apk',
                'contents' => fopen($apkFile, 'r'),
            ];
        }

        if ($iconFile !== null) {
            $icon = [
                'name' => 'icon',
                'hash' => md5_file($iconFile),
            ];
            $paramMd5Array[] = $icon;
            $multipart[] = [
                'name' => 'icon',
                'contents' => fopen($iconFile, 'r'),
            ];
        }

        if ($screenshots !== null && count($screenshots) > 0) {
            for ($i = 0; $i < count($screenshots); $i++) {
                $screenshotName = 'screenshot_' . ($i + 1);
                $screenshot = [
                    'name' => $screenshotName,
                    'hash' => md5_file($screenshots[$i]),
                ];
                $paramMd5Array[] = $screenshot;

                $multipart[] = [
                    'name' => $screenshotName,
                    'contents' => fopen($screenshots[$i], 'r'),
                ];
            }
        }

        $sigJSON['sig'] = $paramMd5Array;
        $sigJSON['password'] = $password;

        $multipart[] = [
            'name' => 'RequestData',
            'contents' => json_encode($json),
        ];

        $multipart[] = [
            'name' => 'SIG',
            'contents' => $this->encryptByPublicKey(json_encode($sigJSON), $this->pubKey),
        ];

        try {
            $response = $httpClient->request('POST', $this->DOMAIN . $this->PUSH, [
                'multipart' => $multipart,
            ]);

            return (string) $response->getBody();
        } catch (Exception $e) {
            echo $e->getMessage();
            return '';
        }
    }

    /**
     * @param $email 小米账号邮箱
     * @param $password 小米账号密码或者私钥
     * @param $apkChannel 渠道包的渠道号
     * @param $channelApkFile 渠道包的文件路径
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    function pushChannelApk($email, $password, $apkChannel, $channelApkFile) {
        $httpClient = new GuzzleHttp\Client();

        $json = [
            'userName' => $email,
            'apkChannel' => $apkChannel
        ];

        // Calculate the digital signature
        $paramMd5Array = [
            [
                'name' => 'RequestData',
                'hash' => md5(json_encode($json))
            ]
        ];

        $multipart = [];

        if ($channelApkFile !== null) {
            $apk = [
                'name' => 'channelApk',
                'hash' => hash_file('md5', $channelApkFile)
            ];
            $paramMd5Array[] = $apk;
            $multipart[] = [
                'name' => 'channelApk',
                'contents' => fopen($channelApkFile, 'r')
            ];
        }

        $sigJSON = [
            'sig' => $paramMd5Array,
            'password' => $password
        ];
        $multipart[] = [
            'name' => 'RequestData',
            'contents' => json_encode($json)
        ];
        $multipart[] = [
            'name' => 'SIG',
            'contents' => $this->encryptByPublicKey(json_encode($sigJSON), $this->pubKey)
        ];

        try {
            $response = $httpClient->request('POST', $this->DOMAIN. $this->PUSH_CHANNEL_APK, [
                'multipart' => $multipart
            ]);
            return (string) $response->getBody();
        } catch (Exception $e) {
            echo $e->getMessage();
            return "";
        }
    }


    function main()
    {
        $email = "pengchen3@xiaomi.com";
        $pwd = "w2sexrfwr67e4jfdb4sq00zshxveskbvn4e6eoo7gzz9237vsu";

        // 获取分类信息
        $category = $this->getCatetories();
        echo "Category: " . $category . "\n";

        // 获取应用的状态信息
        $info = $this->query($email, $pwd, "com.test.myapplication360");
        echo "Details: " . $info . "\n";

        // 推送普通应用
        $appDetail = new AppDetailBean();
        $appDetail->appName = "App Name";
        $appDetail->category = 2;
        $appDetail->desc = "App Description";
        $appDetail->packageName = "com.test.myapplication360";
        $appDetail->keyWords = "Keyword1 Keyword2";
        $appDetail->web = "App Official Website";
        $appDetail->brief = "One-line Introduction";
        $appDetail->publisherName = "Developer Name";
        $appDetail->versionName = "Version Name";
        $appDetail->privacyUrl = "http://";
        $appDetail->testAccount = "{\"zh_CN\":{\"accounts\":[{\"t\":1,\"a\":\"xxxxxx\",\"p\":\"xxxxx\",\"c\":\"xxxxx\"},{\"t\":2,\"a\":\"xxxxxx\",\"p\":\"xxxxx\",\"c\":\"xxxxx\"}],\"auditNotes\":\"xxxxxxxx\"}}";
        $appDetail->onlineTime = 1698771600000;

        $apkFile = "/Users/pengchen/developer/apk/app-release.apk";
        $iconFile = "/Users/pengchen/developer/image/icon.png";

        $screenshot_1File = "/Users/pengchen/developer/image/1.jpg";
        $screenshot_2File = "/Users/pengchen/developer/image/2.jpg";
        $screenshot_3File = "/Users/pengchen/developer/image/3.jpg";
        $screenshot_4File = "/Users/pengchen/developer/image/4.jpg";

        $screenshots = array($screenshot_1File, $screenshot_2File, $screenshot_3File, $screenshot_4File);
        $result = $this->push($email, $pwd, 0, $apkFile, $iconFile, $appDetail, $screenshots);
        echo "Result: " . $result . "\n";

        // 推送渠道包
        $apkChannel = "Channel Name";
        $channelApkFile = "/Users/pengchen/developer/apk/app-release.apk";
        $channelApkResult = $this->pushChannelApk($email, $pwd, $apkChannel, $channelApkFile);
        echo "Result: " . $channelApkResult . "\n";
    }
}

$example = new Example();
$example->main();

/**
 * app应用信息的结构体
 */
class AppDetailBean {
    public $appName;
    public $packageName;
    public $publisherName;
    public $category;
    public $keyWords;
    public $versionName;
    public $desc;
    public $web;
    public $updateDesc;
    public $price;
    public $shortDesc;
    public $brief;
    public $privacyUrl;
    public $testAccount;
    public $onlineTime;
}
?>
