mineoアプリ　API仕様

# はじめに
mineoのパケットギフトという機能で2回線（+家族の1回線）のパケットの無限繰り越しを毎月せっせと行っています。
いろいろ自動化していきたいと思う今日この頃、ふとパケットギフトも自動化できないかと考え、mineoアプリを活用してパケットギフトの通信方法を調べてみることにしました。

# 各APIの仕様 
:::note warn
以下で紹介するAPIはいずれも<b>非公開API</b>と考えられます。
研究・開発目的での利用に留めるほか、過度なアクセスはお控えください。
:::

# 全体
紹介するAPIは特に記述が無い限り、全て以下のパラメータを必要とします。

|パラメータ|値|
|:-:|:-:|
|appId|（任意の文字列）|
|appVersion|（任意の文字列）|
|Authorizatrion|Bearer|
|aid|（任意の文字列）|

`appId`、`appVersion`は何を設定しても動作に問題ありません。

:::note
通常、`appId`は`mineoApp`、
`appVersion`は`2.18.00`が設定されています（2024年7月現在）。
:::

`Authorization`は、[mitmproxy](https://mitmproxy.org/)などのツールで確認してください。
`Authorization`は、<b>24時間</b>の有効期限が設定されています。
不正な`Authorization`を入力すると次のエラーが返されます。
```json:Response_error
{
    "statusCode": 401,
    "message": "Invalid JWT."
}
```

`aid`は恐らくmineo側から付与された<b>IDFA/AAID</b>（広告識別子）です。
必須項目ですが、こちらも適当な文字列を入力しても通るようです。


また、`custId`を`form-urlencoded`で指定する必要があります。
`custId`は<b>回線毎</b>に固有番号を持っています。
```text:URLEncoded Form
custId: ABC12DE345
```

# 回線関係
## Get Telnum List
eoIDで契約している回線の一覧を取得します。
また、他のAPIで必要となる`custId`もここで取得できます。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/get_telnum_list|

:::note warn
以降のAPIにも当てはまることですが、URLには`GET`と書いておきながら、
実際のメソッドは`POST`であることに注意してください。
:::

このAPIは`custId`を<INS>必要としません。</INS>

```json-doc:Response_ok
{
    "messages": [
        null
    ],
    "resultCode": "00",
    "telNumList": [
        {
            "custId": "ABC12DE345",
            "lineName": "iPhone",          // 回線名称
            "telNum": "080XXXXXXXX"        // 電話番号
        },
        {
            "custId": "ABC12DE345",
            "lineName": "iPhone_sub",
            "telNum": "080XXXXXXXX"
        },
        {
            "custId": "ABC12DE345",
            "lineName": "iPad",
            "telNum": "090XXXXXXXX"
        }
    ]
}
```
パラメータが不足している場合、エラーが返されます。
```json:Response_error
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "telNumList": null
}
```

## Get Line Name
契約回線に設定した回線名称を取得します。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/get_line_name|

```json-doc:Response_ok
{
    "lineName": "iPhone",  // 回線名称
    "messages": [
        null
    ],
    "resultCode": "00"
}
```

```json-doc:Response_error
// パラメータ不足
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "lineName": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "lineName": null
}
```

# パケット関係
## Get Capacity
契約回線のパケット使用量、残量を確認するために使用されます。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/get_capacity|

```json-doc:Response_ok
{
    "lowSpeedDisp": null,  // mineoスイッチON時 -> "通信速度最大200kbps適用中"
    "messages": [
        null
    ],
    "mySokuFlg": "0",                       // 0 -> マイそくOFF, 1 -> マイそくON
    "packetInfo": {
        "baseCapacity": 5120,               // 基本データ容量
        "baseRemainingCapacity": 5120,      // 残り基本データ容量
        "chargeCapacity": 0,                // チャージ容量
        "chargeRemainingCapacity": 0,       // 残りチャージ容量
        "forwardCapacity": 67254,           // 前月繰り越し容量
        "forwardRemainingCapacity": 33563,  // 残り前月繰り越し容量
        "giftCapacity": 0,                  // ギフト容量
        "giftRemainingCapacity": 0          // 残りギフト容量
    },
    "resultCode": "00",
    "serviceName": "Dプラン デュアルタイプ (5GB)",
    "speedFlg": "1",                        // 0 -> mineoスイッチON, 1 -> mineoスイッチOFF
    "tushinSettingStatusCode": "00"         // 00 -> mineoスイッチOFF, 01 -> mineoスイッチON
}
```

```json-doc:Response_error
// パラメータ不足
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "packetInfo": null,
    "speedFlg": null,
    "tushinSettingStatusCode": null,
    "mySokuFlg": null,
    "serviceName": null,
    "lowSpeedDisp": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "packetInfo": null,
    "speedFlg": null,
    "tushinSettingStatusCode": null,
    "mySokuFlg": null,
    "serviceName": null,
    "lowSpeedDisp": null
}
```
## Get Capacity For Gift
パケットギフトのギフト可能容量を確認できます。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/get_capacity_for_gift|

```json:Response_ok
{
    "capacityForGift": 40918,
    "messages": [
        null
    ],
    "resultCode": "00"
}
```

```json-doc:Response_error
// パラメータ不足
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "capacityForGift": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "capacityForGift": null
}
```

## Change Gift
発行済みパケットギフトを受け取ります。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/change_gift|

受け取りたいパケットギフトのコードは、`form-urlencoded`で指定できます。

```text:URLEncoded Form
giftCode: ABCD1234
```

```json:Response_ok
{
    "resultCode": "00",
    "messages": [
      null
    ],
    "giftCapacity": 10
}
```

```json-doc:Response_error
// パラメータ不足
{
    "resultCode": "0098",
    "messages": [
      "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "giftCapacity": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "giftCapacity": null
}

// ギフト発行した回線と同一回線でパケットギフトを受け取ろうとした
{
    "resultCode": "0056",
    "messages": [
      "発行元と受取先が同一のため、受け取りできません。"
    ],
    "giftCapacity": null
}

// 存在しないパケットギフトを受け取ろうとした
{
    "resultCode": "0051",
    "messages": [
      "このギフトコードは存在しません。"
    ],
    "giftCapacity": null
}
```

## Issue Gift
パケットギフトを発行します。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/issue_gift|

ギフト容量は、`form-urlencoded`で指定できます。
`giftCapacity`はmineoアプリと同様、<b>10以上9999以下</b>で指定する必要があります。

```text:URLEncoded Form
giftCapacity: 1000
```

```json-doc:Response_ok
{
    "expireDate": "20240727",      // 有効期限
    "giftCapacity": 1000,          // ギフト容量
    "giftCode": "ABCD1234",        // ギフトコード
    "messages": [
        null
    ],
    "resultCode": "00"
}
```

```json-doc:Response_error
// パラメータ不足、または範囲外のギフト容量を指定した
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "giftCode": null,
    "giftCapacity": null,
    "expireDate": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "giftCode": null,
    "giftCapacity": null,
    "expireDate": null
}

//
```

# その他機能
## Change Speed
mineoスイッチのON/OFFを切り替えます。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/change_speed|

`form-urlencoded`で`speedFlg`を<b>0</b>または<b>1</b>で指定することで、変更できます。
```text:URLEncoded Form
// mineoスイッチをONに変更
speedFlg: 0

// mineoスイッチをOFFに変更
speedFlg: 1
```


```json:Response_ok
{
    "messages": [
        null
    ],
    "resultCode": "00"
}
```

```json-doc:Response_error
// パラメータ不足、またはspeedFlgを範囲外に指定した
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ]
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ]
}

// mineoスイッチのクールダウン（1分間）中に実行した
{
    "resultCode": "0025",
    "messages": [
        "エラーが発生しております。\n時間をおいてから再度お試しください。"
    ]
}
```

## Get Traffic History
過去3ヶ月分のデータ通信量、パケットギフト、マイネ王からのパケットの出入りを確認できます。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/get_traffic_history|

```json-doc:Response_ok
{
    "messages": [
        null
    ],
    "monthlyTrafficHistoryList": [
        {
            "averageInfo": {                      // 一日あたりの平均通信量
                "dataTraffic": 242,               // データ通信量（合計）
                "dataTrafficAdCountFree": 0,      // データ通信量（広告フリー）
                "dataTrafficHighSpeed": 242,      // データ通信量（高速）
                "dataTrafficLowSpeed": 0,         // データ通信量（低速・mineoスイッチON）
                "decreaseFreeTank": 0,            // フリータンクに入れた通信量・贈ったチップなど
                "decreasePacketGift": 955,        // 発行したパケットギフト
                "increaseFreeTank": 0,            // フリータンクから引き出した通信量・受け取ったチップなど
                "increasePacketGift": 1458,       // 受け取ったパケットギフト
                "targetDay": null
            },
            "dailyTrafficHistoryList": [          // 一日ごとのデータ通信量を集計
                {
                    "dataTraffic": 143,
                    "dataTrafficAdCountFree": 0,
                    "dataTrafficHighSpeed": 143,
                    "dataTrafficLowSpeed": 0,
                    "decreaseFreeTank": 0,
                    "decreasePacketGift": 0,
                    "increaseFreeTank": 0,
                    "increasePacketGift": 0,
                    "targetDay": "1"               // 日にち（ここでは5月1日を表す）
                },
                {
                    "dataTraffic": 4,
                    "dataTrafficAdCountFree": 0,
                    "dataTrafficHighSpeed": 4,
                    "dataTrafficLowSpeed": 0,
                    "decreaseFreeTank": 0,
                    "decreasePacketGift": 0,
                    "increaseFreeTank": 0,
                    "increasePacketGift": 0,
                    "targetDay": "2"
                },
                {
                    "dataTraffic": 1,
                    "dataTrafficAdCountFree": 0,
                    "dataTrafficHighSpeed": 1,
                    "dataTrafficLowSpeed": 0,
                    "decreaseFreeTank": 0,
                    "decreasePacketGift": 0,
                    "increaseFreeTank": 0,
                    "increasePacketGift": 0,
                    "targetDay": "3"
                },
                // 以下、月最終日（targetDay->31）まで省略
            ],
            "sumInfo": {                             // データ通信量の月合計
                "dataTraffic": 7501,
                "dataTrafficAdCountFree": 0,
                "dataTrafficHighSpeed": 7501,
                "dataTrafficLowSpeed": 0,
                "decreaseFreeTank": 0,
                "decreasePacketGift": 29598,
                "increaseFreeTank": 0,
                "increasePacketGift": 45196,
                "targetDay": null
            },
            "targetMonth": "5"                      // 集計月
        },
        {
            "averageInfo": {                        // 6月分
                "dataTraffic": 277,
                "dataTrafficAdCountFree": 0,
                "dataTrafficHighSpeed": 277,
                "dataTrafficLowSpeed": 0,
                "decreaseFreeTank": 0,
                "decreasePacketGift": 2309,
                "increaseFreeTank": 0,
                "increasePacketGift": 2242,
                "targetDay": null
            },
            
            // 省略
            
            "sumInfo": {
                "dataTraffic": 3696,
                "dataTrafficAdCountFree": 0,
                "dataTrafficHighSpeed": 3696,
                "dataTrafficLowSpeed": 0,
                "decreaseFreeTank": 0,
                "decreasePacketGift": 0,
                "increaseFreeTank": 0,
                "increasePacketGift": 0,
                "targetDay": null
            },
            "targetMonth": "7"
        }
    ],
    "resultCode": "00"
}
```

```json-doc:Response_error
// パラメータ不足
{
    "resultCode": "0098",
    "messages": [
      "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "monthlyTrafficHistoryList": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "monthlyTrafficHistoryList": null
}
```

## Login Stamp
mineoアプリを起動するともらえるログインスタンプを確認、獲得します。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://api.eonet.jp/mineo/v1/login_stamp|

`form-urlencoded`で`updateMode`を<b>0</b>か<b>1</b>の値にし、ログインスタンプを獲得するか指定します。
既にログイン済みでもエラーは出ないため、基本的には<b>1</b>を指定しておけば問題ないでしょう。
```text:URLEncoded Form
// ログインスタンプを獲得しない
updateMode:0

// 今日未ログインだった場合、ログインスタンプを獲得する
updateMode: 1
```

```json-doc:Reponse_ok
{
    "resultCode": "00",
    "messages": [
        null
    ],
    "stampCnt": 155,      // 累計スタンプカウント
    "updateFlg": "0",     // 0 -> 何もしない、 1 -> ログインスタンプを獲得した 
    "tokutenList": [
        {
            "id": "LS00000001",
            "name": "100MB",
            "requiredStamps": 20,   // stampCnt % requiredStamps -> 残り必要スタンプ数
            "getFlg": "0"           // 0 -> 何もしない、　1 -> 特典を獲得した
        },
        {
            "id": "LS00000002",
            "name": "王国コイン",
            "requiredStamps": 100,
            "getFlg": "0"
        }
    ]
}
```

```json-doc:Response_error
// パラメータ不足、またはupdateModeを範囲外に指定した
{
    "resultCode": "0098",
    "messages": [
        "システムエラー。\n時間をおいてから再度お試しください。"
    ],
    "stampCnt": null,
    "updateFlg": null,
    "tokutenList": null
}

// 存在しないcustId
{
    "resultCode": "0022",
    "messages": [
        "認証エラー。\nログアウト後、再ログインしてください。"
    ],
    "stampCnt": null,
    "updateFlg": null,
    "tokutenList": null
}
```

# おまけ
## Authorizationについて
今まで紹介した各APIは`Authorization`が必要で、しかも24時間経過で有効期限が切れるという仕様があります。
しかし幸いなことにトークンを発行するためのAPIがあり、`refresh_token`さえ入手できれば、あとは一日おきに実行するだけでトークンを自動更新できるようになります。

|パラメータ|値|
|:-:|:-:|
|Method|POST|
|URL|https://login.eonet.jp/oidc/v1/token|

`form-urlencoded`で次のように指定します。
```text:URLEncoded Form
grant_type: refresh_token     // 固定値
refresh_token: TOKEN
client_id: 100064798          // 固定値
```
`refresh_token`はmitmproxyなどのツールを実行した状態で、mineoアプリをログアウト・ログインし、上記URLの<b>Response Body</b>を確認すると入手できます。

:::note
`grant_type`を`authorization_code`に指定し、`code`、`code_verfier`、`redirect_uri`を指定することで新規発行が可能な様です。
しかし`code`と`code_verfier`の入手方法が複雑であるため、今回は扱いが容易な`refresh_token`のみを取り扱います。
:::

```json-doc:Response_ok
{
    "access_token": "xxE05YhIxMdoJmY......",
    "expires_in": "86400",
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjNIOURJcUNXZ1R5dzdOdW1ZV1daM3cifQ......",
    "refresh_token": "VEweVyQQCzmTwPmBXd......",
    "token_type": "Bearer"
}
```
Response Bodyにある`id_token`は、今まで紹介したAPIのAuthorizationパラメータで使用することができます（有効期限24時間）。

なお、`refresh_token`は<b>ワンタイムトークン</b>であり、同じトークンで再度APIにアクセスしようとするとエラーが返されます。
```json-doc:Response_error
{
    "error" : "invalid_token",
    "error_description" : "refresh token is not exists"
}
```
もし`refresh_token`を控えるのを忘れてしまった、期限が切れてしまった場合は、mitmproxyなどのツールを使って上記手順をやり直しましょう。


# おわりに
紹介したもの以外にもまだいくつかAPIはあるのですが、いまいち用途の分からないものもあったので割愛しています。
あとマイネ王のフリータンク関係も書くつもりでしたが、長すぎたので別記事に分割しようと思います。