Alibaba CloudのBilling APIを使ってみた

この記事は[alibabacloud Advent Calendar 2019](https://qiita.com/advent-calendar/2019/alibabacloud) 2日目の記事です。

# 1.はじめに
Alibaba CloudにはBilling APIというものがあります。
今後、料金に関する通知、集計、分析などを自動化したいという考えもあり、Billing APIを調査してみました。

なお、Alibaba Cloud日本アカウントでは、このAPIは使えないようです。
色々試しましたが、日本アカウントから杭州とシンガポールと東京のエンドポイントにアクセスしてもデータが返ってきません。

|Alibaba Cloudアカウント種類|エンドポイントのリージョン|エンドポイントURL|
|:---:|:--:|:---:|:---|
|[中国](https://cn.aliyun.com)|杭州|https://business.aliyuncs.com|
|[国際](https://www.alibabacloud.com)|シンガポール|https://business.ap-southeast-1.aliyuncs.com|
|[日本](https://jp.alibabacloud.com)|なし？|なし？|

以降は国際アカウントで試した結果となります。

# 2.OpenAPI ExplorerがAPIを試すのに便利

Alibaba CloudでAPIの動作確認をする時は、OpenAPI Explorerが便利です。
OpenAPI Explorerの特徴は以下となります。

- Alibaba Cloudのコンソールでブラウザ上でコードを実行できる
- コンソールにログインしているユーザーの権限で各APIが利用できる
- APIのサンプルコードが各言語(Java、Node.js、Go、PHP、Python、.Net、Ruby)に合わせて利用できる
- APIリファレンスへのリンクがあり、そこから仕様を確認できる

OpenAPI Explorerはまだベータ版という位置付けだからか、コンソールからは発見できませんでしたが、コンソールにログイン後、下記のURLから利用できます。

https://api.alibabacloud.com/#/

![OpenAPI_Explorer01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/1120e371-3a20-1fe9-3ac7-29c3ce9bcbf1.png)

OpenAPI Explorerの画面に行き、roductsのところをマウスオーバーすると、検索メニューが出るので、そこでbillingと入力します。
すると、Alibaba Cloud Billingと表示されるので、クリックします。


# 3.プロダクトごとの月額料金を確認する

## コンソールで確認する

どのプロダクトでどのくらいの料金がかかっているのかを確認します。
ひとまずAPIで確認する前に、コンソールで確認してみます。

月単位の指定となっているので、2019年9月を指定しました。
Detailなどをクリックしていくと、どんどん詳細情報が見れます。

<img width="1366" alt="Billing_Management_201909_01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/e0398ee6-d06b-f3be-ed05-d17c750966f7.png">

0USDとなってます。

<img width="1371" alt="Billing_Management_201909_02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/3d369242-9cc3-ba5e-008d-f0a218a9034f.png">

詳細を見ると、3.741240 USDでしたが、全額クーポンで支払われているので、結果 0 USDとなっています。

<img width="1362" alt="Billing_Management_201909_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/1b835790-6225-971a-a3d2-0baf7c3a5ac7.png">

さらに見ていくと、Instanceが2つあったことがわかります。
どちらにどのくらいの料金がかかったかも確認できます。

<img width="1233" alt="Billing_Management_201909_04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/bf627ef4-4dbd-7b5b-dff6-035f1d8cba23.png">

各インスタンスに対し、どのように料金がかかったかも確認できるようです。
1時間ごとに 0.112 かかってますが、まさに[料金表](https://www.alibabacloud.com/insert/ecs/pricing?lang=en#/?_k=085735)に載っているのと同じでした。
<img width="1207" alt="sn1nelarge.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/ff7c3a2b-9367-b0f4-29db-762aff1cf353.png">

## APIで確認する

### QueryBillでプロダクトごとの料金を出力

![OpenAPI_Explorer02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/b5ec6793-4966-e988-4572-f2d7bd2bfdba.png)

左側にずらっとAPIのリストが並んでいますが、QueryBillを選択します。
次に利用したいプログラミング言語を選択します。

![OpenAPI_Explorer03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/9d2ebae8-bf19-5c35-140b-ea6d26eabeed.png)

変数が色々あるのですが、QueryBillに関しては下記が必須でした。

- RegionId: Singapore
- BillingCycle: yyyy-mmの形式

上記のパラメータを入力すると、サンプルコードにも反映されます。
ここまでできたら、Submit Request をクリックするとコードが実行されます。

<details><summary>QueryBillの出力例</summary><div>


```JSON
{
	"Data": {
		"Items": {
			"Item": [
				{
					"RecordID": "201909000xxxxxxx",
					"PretaxAmount": 0, 
					"PaymentTransactionID": "",
					"RoundDownDiscount": 0, 
					"PaymentTime": "",
					"Item": "PayAsYouGoBill",
					"PaymentCurrency": 0,
					"ProductType": "",
					"ProductName": "Elastic Compute Service",
					"PretaxGrossAmount": 3.74124,
					"Currency": "USD",
					"InvoiceDiscount": 0, 
					"SubscriptionType": "PayAsYouGo",
					"ProductCode": "ecs",
					"PretaxAmountLocal": 0,
					"ProductDetail": "Elastic Computing (Pay-As-You-Go)",
					"SubOrderId": "",
					"Tax": 0,
					"AfterTaxAmount": 0,
					"DeductedByCoupons": 3.74124,
					"UsageStartTime": "2019-09-01 00:00:00",
					"UsageEndTime": "2019-10-01 00:00:00",
					"Status": "PayFinish",
					"PaymentAmount": 0,
					"OutstandingAmount": 0,
					"OwnerID": ""
				},
				{
					"RecordID": "201909000xxxxxxx",
					"PretaxAmount": 0,
					"PaymentTransactionID": "",
					"RoundDownDiscount": 0,
					"PaymentTime": "",
					"Item": "PayAsYouGoBill",
					"PaymentCurrency": 0,
					"ProductType": "",
					"ProductName": "Log Service",
					"PretaxGrossAmount": 0,
					"Currency": "USD",
					"InvoiceDiscount": 0,
					"SubscriptionType": "PayAsYouGo",
					"ProductCode": "sls",
					"PretaxAmountLocal": 0,
					"ProductDetail": "sls_intl",
					"SubOrderId": "",
					"Tax": 0,
					"AfterTaxAmount": 0,
					"DeductedByCoupons": 0,
					"UsageStartTime": "2019-09-01 00:00:00",
					"UsageEndTime": "2019-10-01 00:00:00",
					"Status": "PayFinish",
					"PaymentAmount": 0,
					"OutstandingAmount": 0,
					"OwnerID": ""
				}
			]
		},
		"TotalCount": 2,
		"BillingCycle": "2019-09",
		"AccountID": "xxxxxxxxxxxxxxxx",
		"PageSize": 20,
		"AccountName": "xxxxx@xxxxx",
		"PageNum": 1
	},
	"Message": "Successful!",
	"RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
	"Success": true,
	"Code": "Success"
}

```
</div></details>

プロダクト単位での出力になっていますね。
Elastic Compute ServiceとLog Serviceの２つがあります。

ECSは、税引き前金額（PretaxGrossAmount）が 3.74124ですが、
クーポンによる割引（DeductedByCoupons）も3.74124なので、
PaymentAmountは、0になってます。


# 4.インスタンスごとの月額料金を確認する

### QueryInstanceBillでインスタンス単位の料金

<details><summary>QueryBillの出力例</summary><div>


```JSON
{
	"Data": {
		"Items": {
			"Item": [
				{
					"ListPrice": "",
					"BillingItem": "",
					"NickName": "host1",
					"Usage": "",
					"Region": "Japan (Tokyo)",
					"InstanceConfig": "iz:亚太东北1 可用区A;实例规格名称:ecs.sn1ne.large;CPU:2;内存:4096;磁盘:0;带宽:1024000;OS:562949953436647;是否io优化:1",
					"Item": "PayAsYouGoBill",
					"UsageUnit": "",
					"IntranetIP": "",
					"ProductType": "",
					"Tag": "",
					"ProductName": "Elastic Compute Service",
					"Zone": "ap-northeast-1a",
					"PretaxGrossAmount": 2.8926,
					"Currency": "USD",
					"InvoiceDiscount": 0,
					"SubscriptionType": "PayAsYouGo",
					"ResourceGroup": "",
					"ProductCode": "ecs",
					"InstanceID": "i-6we9o3dvmqxxxxxxx",
					"ProductDetail": "Elastic Computing (Pay-As-You-Go)",
					"ListPriceUnit": "",
					"CostUnit": "Not Allocated",
					"ServicePeriod": "162174",
					"DeductedByResourcePackage": "",
					"InternetIP": "xx.xx.xx.xx",
					"DeductedByCoupons": 2.8926,
					"InstanceSpec": "ecs.sn1ne.large",
					"OwnerID": "xxxxxxxxxxxxxxxx",
					"BillingType": "Others"
				},
				{
					"ListPrice": "",
					"BillingItem": "",
					"NickName": "",
					"Usage": "",
					"Region": "Japan (Tokyo)",
					"InstanceConfig": "-",
					"Item": "PayAsYouGoBill",
					"UsageUnit": "",
					"IntranetIP": "",
					"ProductType": "",
					"Tag": "",
					"ProductName": "Elastic Compute Service",
					"Zone": "",
					"PretaxGrossAmount": 0.8486,
					"Currency": "USD",
					"InvoiceDiscount": 0,
					"SubscriptionType": "PayAsYouGo",
					"ResourceGroup": "",
					"ProductCode": "ecs",
					"InstanceID": "i-6we67slarfgflxxxxxxx",
					"ProductDetail": "Elastic Computing (Pay-As-You-Go)",
					"ListPriceUnit": "",
					"CostUnit": "Not Allocated",
					"ServicePeriod": "55137",
					"DeductedByResourcePackage": "",
					"InternetIP": "",
					"DeductedByCoupons": 0.8486,
					"InstanceSpec": "",
					"OwnerID": "xxxxxxxxxxxxxxxx",
					"BillingType": "Others"
				},
				{
					"ListPrice": "",
					"BillingItem": "",
					"NickName": "",
					"Usage": "",
					"Region": "",
					"InstanceConfig": "",
					"Item": "PayAsYouGoBill",
					"UsageUnit": "",
					"IntranetIP": "",
					"ProductType": "",
					"Tag": "",
					"ProductName": "Log Service",
					"Zone": "",
					"PretaxGrossAmount": 0,
					"Currency": "USD",
					"InvoiceDiscount": 0,
					"SubscriptionType": "PayAsYouGo",
					"ResourceGroup": "",
					"ProductCode": "sls",
					"InstanceID": "xxxxxxxxxxxxxxxx",
					"ProductDetail": "sls_intl",
					"ListPriceUnit": "",
					"CostUnit": "Not Allocated",
					"ServicePeriod": "86400",
					"DeductedByResourcePackage": "",
					"InternetIP": "",
					"DeductedByCoupons": 0,
					"InstanceSpec": "",
					"OwnerID": "xxxxxxxxxxxxxxxx",
					"BillingType": "Others"
				}
			]
		},
		"TotalCount": 3,
		"BillingCycle": "2019-09",
		"AccountID": "xxxxxxxxxxxxxxxx",
		"PageSize": 20,
		"AccountName": "xxxxx@xxxxx",
		"PageNum": 1
	},
	"Message": "Successful!",
	"RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
	"Success": true,
	"Code": "Success"
}
```
</div></details>

ECSインスタンスが2つとSLSインスタンスが1つ確認できます。

# 5.アカウントごとの月額料金を確認する

ざっくりとアカウントの料金を取得するには、QueryAccountBillが使えそうです。

QueryAccountBillのAPIドキュメントは中国語版しか発見できませんでしたが、itemが子アカウントに該当するようなので、itemsの中に複数の子アカウントが入る仕様になっているようです。
https://help.aliyun.com/document_detail/142608.html

もし、Alibaba CloudのEnterpriseアカウントを使っているなら、子アカウントの料金情報をまとめて取得できそうに見えます。

## クーポンで無料になる場合

<details><summary>QueryAccountBillの出力例(クーポンで払える場合)</summary><div>


```JSON
{
	"Data": {
		"Items": {
			"Item": [
				{
					"SubscriptionType": "",
					"ProductCode": "",
					"CostUnit": "Not Allocated",
					"ProductName": "",
					"OwnerID": "xxxxxxxxxxxxx",
					"Currency": "USD",
					"DeductedByCoupons": 3.7412,
					"PretaxGrossAmount": 3.7412,
					"OwnerName": "xxxx@xxxx",
					"InvoiceDiscount": 0
				}
			]
		},
		"BillingCycle": "2019-09",
		"TotalCount": 1,
		"AccountID": "xxxxxxxxxxxxx",
		"PageSize": 20,
		"AccountName": "xxxxx@xxxxx",
		"PageNum": 1
	},
	"Message": "Successful!",
	"RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
	"Success": true,
	"Code": "Success"
}
```
</div></details>

- PretaxGrossAmount: 3.7412
- DeductedByCoupons: 3.7412
- InvoiceDiscount: 0

クーポンで全部払えてますね。

## 請求が発生する場合

実際に請求が発生した月の例を下記に載せます。

<details><summary>QueryAccountBillの出力例(請求発生の場合)</summary><div>


```JSON
{
	"Data": {
		"Items": {
			"Item": [
				{
					"SubscriptionType": "",
					"ProductCode": "",
					"CostUnit": "Not Allocated",
					"ProductName": "",
					"OwnerID": "xxxxxxxxxxxxx",
					"Currency": "USD",
					"DeductedByCoupons": 216.22,
					"PretaxGrossAmount": 399,
					"OwnerName": "xxxx@xxxx",
					"InvoiceDiscount": 179.1
				}
			]
		},
		"BillingCycle": "2019-10",
		"TotalCount": 1,
		"AccountID": "xxxxxxxxxxxxxxxx",
		"PageSize": 20,
		"AccountName": "xxxx@xxxx",
		"PageNum": 1
	},
	"Message": "Successful!",
	"RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
	"Success": true,
	"Code": "Success"
}
```
</div></details>

- PretaxGrossAmount: 399
- DeductedByCoupons: 216.22
- InvoiceDiscount: 179.1

これらの金額から計算すると、3.68 USDとなります。

```
PretaxGrossAmount - DeductedByCoupons - InvoiceDiscount = 3.680000000000007
```

しかし、これは税引き前のものです。
コンソールで確認すると、税金が0.37 USDがかかっていました。
これは消費税10%のようで、それを加算すると、合計4.05 USDとなり、コンソールの数字と一致します。

<img width="1373" alt="Billing_Management_201910_01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131345/ff67d7ef-108a-c9f5-eccb-826bf97d548e.png">

## 税引後の料金を取得する

上記のように計算でだいたい出せるのですが、少数の扱いが怪しいので、ズバリな税引後の数字もAPIで取得したいものです。

再び、QueryBillを見てみると、PaymentAmountが税引き後の数字になっていました。
これが使えそうです。

<details><summary>QueryBillの出力例</summary><div>


```JSON

{
	"Data": {
		"Items": {
			"Item": [
				{
					"RecordID": "xxxxxxxxxxxxxxx",
					"PretaxAmount": 3.68,
					"PaymentTransactionID": "",
					"RoundDownDiscount": 0,
					"PaymentTime": "2019-10-12 13:52:04",
					"Item": "SubscriptionOrder",
					"PaymentCurrency": 0,
					"ProductType": "acpcourse_intl",
					"ProductName": "",
					"PretaxGrossAmount": 199,
					"Currency": "USD",
					"InvoiceDiscount": 179.1,
					"SubscriptionType": "Subscription",
					"ProductCode": "acp",
					"PretaxAmountLocal": 0,
					"ProductDetail": "Alibaba Cloud Certification Course",
					"SubOrderId": "xxxxxxxxxxxxxxx",
					"Tax": 0,
					"AfterTaxAmount": 0,
					"DeductedByCoupons": 16.22,
					"UsageStartTime": "",
					"UsageEndTime": "",
					"Status": "PayFinish",
					"PaymentAmount": 4.05,
					"OutstandingAmount": 0,
					"OwnerID": ""
				},
				{
					"RecordID": "xxxxxxxxxxxxxxx",
					"PretaxAmount": 0,
					"PaymentTransactionID": "",
					"RoundDownDiscount": 0,
					"PaymentTime": "2019-10-10 00:21:49",
					"Item": "SubscriptionOrder",
					"PaymentCurrency": 0,
					"ProductType": "",
					"ProductName": "",
					"PretaxGrossAmount": 200,
					"Currency": "USD",
					"InvoiceDiscount": 0,
					"SubscriptionType": "Subscription",
					"ProductCode": "acp",
					"PretaxAmountLocal": 0,
					"ProductDetail": "ACP Certification",
					"SubOrderId": "xxxxxxxxxxxxxxx",
					"Tax": 0,
					"AfterTaxAmount": 0,
					"DeductedByCoupons": 200,
					"UsageStartTime": "",
					"UsageEndTime": "",
					"Status": "PayFinish",
					"PaymentAmount": 0,
					"OutstandingAmount": 0,
					"OwnerID": ""
				}
			]
		},
		"BillingCycle": "2019-10",
		"TotalCount": 2,
		"AccountID": "xxxxxxxxxxxxxxx",
		"PageSize": 20,
		"AccountName": "xxxx@xxxx",
		"PageNum": 1
	},
	"Message": "Successful!",
	"RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
	"Success": true,
	"Code": "Success"
}
```
</div></details>

> "PretaxAmount": 3.68
> "PaymentAmount": 4.05

アカウントごとの税引き後の料金を出すには、QueryBillで全itemのPaymentAmountを足してあげれば良さそうです。

> "Tax": 0,

税金そのものの表示は正しくない気もしますが。

# 6.まとめ

- APIのお試しには、OpenAPI Explorerが便利。
- 国際アカウントのBilling APIは、シンガポールがエンドポイント
- 税引き後の金額はQueryBillの各itemのPaymentAmountの合計値