注意
このページはlaravel/cashier-mollie
パッケージのREADMEを翻訳したものです。Laravel
Cashierの日本での利用者が少ないのかビュー数が低いため、このページへ頻繁に原文の変更を取り込む予定はありません。最新版をチェックしたい方は、オリジナル英文のREADMEを直接参照してください。
Laravel CashierはMollieの光り放つサービスを使った、サブスクリプションの読み書きしやすいインターフェイスを提供します。
インストール
このパッケージはComposerでインストールできます。
composer require laravel/cashier-mollie "^1.0"
準備
パッケージを入れ終わったら、次のように実行します。
1. php artisan cashier:install
を実行してください。
2. 以下のフィールドをBillableモデルのマイグレーションへ追加してください。(通常は、デフォルトの"create_user_table"マイグレーションです。)
$table->string('mollie_customer_id')->nullable();
$table->string('mollie_mandate_id')->nullable();
$table->decimal('tax_percentage', 6, 4)->default(0); // オプション
$table->dateTime('trial_ends_at')->nullable(); // オプション
$table->text('extra_billing_information')->nullable(); // オプション
3.
php artisan migrate
でマイグレーションを実行します。
4. .envファイルでMOLLIE_KEY
を設定します。MollieのダッシュボードからAPIキーを取得できます。
MOLLIE_KEY="test_xxxxxxxxxxx"
5. 設定ファイルを用意します。
config/cashier_plans.php
へ、最低でも1つのサブスクリプションプランを設定してください。config/cashier_coupons.php
で、全クーポンを管理します。デフォルトでクーポン例が有効になっています。実働環境へデプロイする前に無効へ設定するように注意してください。基本的な設定は
config/cashier
です。変更する場合は注意してください。ほとんどの場合は変更の必要がありません。
6. Billableモデルを用意します。(通常はデフォルトのLaravel Userモデルです。)
Laravel\Cashier\Billable
トレイトを追加します。オプションとしてモバイルの顧客を生成時に、Billableモデルのどのフィールドを保存するかを設定するため、
mollieCustomerFields()
メソッドをオーバーライドすることもできます。LaravelのUserモデルはデフォルトでmollieCustomerFields()
メソッドを始めから使用しています。
public function mollieCustomerFields() {
return [
'email' => $this->email,
'name' => $this->name,
];
}
モバイルの顧客に対するデータ保管の詳細は、こちらで確認してください。
Laravel\Cashier\Order\Contracts\ProvidesInvoiceInformation
を実装します。例をご覧ください。
/**
* インボイス情報のレシーバを取得
* 通常は名前と(メール/実際の)アドレスのようなものが含まれる
*
* @return array 文字列の配列
*/
public function getInvoiceInformation()
{
return [$this->name, $this->email];
}
/**
* インボイスへ表示する追加の情報を取得(典型は顧客が提供した注釈)
*
* @return string|null
*/
public function getExtraBillingInformation()
{
return null;
}
7.
定期ジョブの実行をスケジュールする: Cashier::run()
.
$schedule->command('cashier:run')
->daily() // 好みの頻度で実行(Daily, monthly, every minuteなど)
->withoutOverlapping(); // 必ず指定する
Laravelを使用してジョブをスケジュールする詳細は、こちらにあります。
🎉 これで使い始められます。
使用法
サブスクリプションの作成
サブスクリプションを作成するには、まずBillableモデルのインスタンスを取得します。これは通常、App \ User
のインスタンスになります。
モデルインスタンスを取得したら、newSubscription
メソッドを使用してモデルのサブスクリプションを作成できます。
$user = User::find(1);
// config/cashier_plans.phpの中で確実に'premium'プランを設定してください。
$result = $user->newSubscription('main', 'premium')->create();
顧客が有効なMollieの権限をすでに持っている場合、$ result
はSubscription
になります。
顧客に有効なMollie権限がない場合、$ result
はRedirectToCheckoutResponse
になり、顧客をMollieの支払いへリダイレクトして最初の支払いを行います。支払いを受け取ると、サブスクリプションが開始されます。
以下にサブスクリプション作成の基本的なコントローラの例を紹介します。
namespace App\Http\Controllers;
use Laravel\Cashier\SubscriptionBuilder\RedirectToCheckoutResponse;
use Illuminate\Support\Facades\Auth;
class CreateSubscriptionController extends Controller
{
/**
* @param string $plan
* @return \Illuminate\Http\RedirectResponse
*/
public function __invoke(string $plan)
{
$user = Auth::user();
$name = ucfirst($plan) . ' membership';
if(!$user->subscribed($name, $plan)) {
$result = $user->newSubscription($name, $plan)->create();
if(is_a($result, RedirectToCheckoutResponse::class)) {
return $result; // Mollieの支払いへリダイレクト
}
return back()->with('status', 'Welcome to the ' . $plan . ' plan');
}
return back()->with('status', 'You are already on the ' . $plan . ' plan');
}
}
Mollieの支払いページへのリダイレクトを常に適用するには、
newSubscription
の代わりにnewSubscriptionViaMollieCheckout
メソッドを使用します。
$redirect = $user->newSubscriptionViaMollieCheckout('main', 'premium')->create(); // config/cashier_plans.phpの中で確実に'premium'プランを設定してください。
クーポン
Cashier Mollieのクーポン処理は柔軟性を考慮して設計されています。
クーポンはconfig/cashier_coupons.php
で定義できます。
\Cashier\Discount\BaseCouponHandler
を拡張し、独自のクーポンハンドラを提供できます。
最初から基本的な"FixedDiscountHandler"を用意しています。
既存サブスクリプションに対するクーポン適用
既存サブスクリプションに対しクーポンを利用するには、Billable
traitのredeemCoupon()
メソッドを使用します。
$user->redeemCoupon('your-coupon-code');
これによりクーポンコードが検証され、クーポンを利用できます。クーポンは次回の注文に適用されます。
オプションで、適用するサブスクリプションを指定できます。
$user->redeemCoupon('your-coupon-code', 'main');
デフォルトでは、そのサブスクリプションに対する他の有効な適用済みクーポンはすべて取り消されます。これを防ぐには、$revokeOtherCoupons
フラグをfalseに設定します。
$user->redeemCoupon('your-coupon-code', 'main', false);
サブスクリプション状態のチェック
ユーザーがアプリケーションのサブスクライブを開始すれば、さまざまな便利な方法を使用してサブスクリプションのステータスを簡単に確認できます。サブスクリプションが現在トライアル期間内であっても、ユーザーに有効なサブスクリプションがあれば、subscribed
メソッドはtrue
を返します。
if ($user->subscribed('main')) {
//
}
subscribed
メソッドは[ルートミドルウェア](https://www.laravel.com/docs/middleware)の優れた候補にもなり、ユーザーのサブスクリプション状態に基づいてルートとコントローラーへのアクセスをフィルターすることができます。
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed('main')) {
// このユーザーは支払いを済ませていない顧客
return redirect('billing');
}
return $next($request);
}
ユーザーがまだ試用期間内かどうかを判断する場合は、onTrial
メソッドを使用します。このメソッドは、ユーザーがまだ試用期間中であるという警告を表示したいときにべんりです。。
if ($user->subscription('main')->onTrial()) {
//
}
subscribedToPlan
メソッドは、設定したプランに基づきユーザーが指定プランを定期購入しているかを判断するために使用します。この例では、ユーザーのmain
サブスクリプションがmonthly
プランに対し、有効に定期購入されているかを判断します。
if ($user->subscribedToPlan('monthly', 'main')) {
//
}
キャンセルしたサブスクリプションの状態
そのユーザーが一度有効に定期購入した後に、キャンセルしたことを判定するには、cancelled
メソッドを使用します。
if ($user->subscription('main')->cancelled()) {
//
}
ユーザーがサブスクリプションをキャンセルしたが、それが完全に期限切れになるまで「猶予期間」が続いているかを確認することもできます。
たとえば、3月10日に期限が切れるように予定されていたサブスクリプションを3月5日にキャンセルした場合、ユーザーは3月10日まで「猶予期間」になります。この間も、subscribed
メソッドはtrue
を返します。
if ($user->subscription('main')->onGracePeriod()) {
//
}
プラン変更
ユーザーがアプリケーションを定期購入した後、新しいサブスクリプションプランに変更したくなる場合もあるでしょう。
ユーザーを新しいサブスクリプションに切り替えるには、プランの識別子をswap
またはswapNextCycle
メソッドに渡してください。
$user = App\User::find(1);
// すぐに変更
$user->subscription('main')->swap('other-plan-id');
// 現在の購入期間が終えた時点で変更
$user->subscription('main')->swapNextCycle('other-plan-id');
ユーザーが試用期間中の場合、試用期間は維持されます。また、サブスクリプションに「購入数」が存在する場合、その購入数も維持されます。
サブスクリプション購入数
サブスクリプションは「購入数」の影響を受ける場合があります。たとえば、アプリケーションでアカウントのユーザーごとに1か月あたり10ユーロを請求する場合があるでしょう。サブスクリプションの購入数を簡単に増減するには、incrementQuantity
とdecrementQuantity
メソッドを使用します:
$user = User::find(1);
$user->subscription('main')->incrementQuantity();
// 現在の購入数を5個増やす
$user->subscription('main')->incrementQuantity(5);
$user->subscription('main')->decrementQuantity();
// 現在の購入数を5個減らす
$user->subscription('main')->decrementQuantity(5);
もしくは、updateQuantity
メソッドで特定の購入数をセットできます。
$user->subscription('main')->updateQuantity(10);
サブスクリプションの税金
サブスクリプション購入でユーザーが支払う税率を指定するには、Billableモデルに対しtaxPercentage
メソッドを実装し、数値で0から100の間のパーセンテージで指定します。
public function taxPercentage() {
return 20;
}
taxPercentage
メソッドによりモデルごとの税率を適用でき、多国間に渡る複数の税率を利用するユーザーに役立つでしょう。
課税パーセントの同期
taxPercentage
メソッドが返すハードコードされた税率IDを変更しても、ユーザーの既存サブスクリプションの税率設定は同じままです。返されたtaxPercentage
値で既存サブスクリプションの税率を更新する場合は、ユーザーのサブスクリプションインスタンスに対し、syncTaxPercentage
メソッドを呼び出す必要があります。
$user->subscription('main')->syncTaxPercentage();
サブスクリプション課金日付け
まだ実装されていません。しかし、毎月特定の日付だけにCashier::run()
を実行するようにスケジュールすることで実現できます。
サブスクリプションのキャンセル
サブスクリプションをキャンセルするにはcancel
メソッドをユーザーのサブスクリプションに対して使ってください。
$user->subscription('main')->cancel();
サブスクリプションがキャンセルされるとCashierは自動的に、データベースのends_at
カラムをセットします。このカラムはいつからsubscribed
メソッドがfalse
を返し始めればよいのか、判定するために使用されています。たとえば、顧客が3月1日にキャンセルしたが、そのサブスクリプションが3月5日に終了するようにスケジュールされていれば、subscribed
メソッドは3月5日になるまでtrue
を返し続けます。
ユーザーがサブスクリプションをキャンセルしたが、まだ「猶予期間」が残っているかどうかを調べるにはonGracePeriod
メソッドを使います。
if ($user->subscription('main')->onGracePeriod()) {
//
}
サブスクリプションの再開
ユーザーがキャンセルしたサブスクリプションを、再開したいときには、resume
メソッドを使用してください。サブスクリプションを再開するには、そのユーザーに有効期間が残っている必要があります。
$user->subscription('main')->resume();
ユーザーがサブスクリプションをキャンセルし、それからそのサブスクリプションを再開する場合、そのサブスクリプションの有効期日が完全に切れていなければすぐに課金されません。そのサブスクリプションはシンプルに再度有効になり、元々の支払いサイクルにより課金されます。
顧客の支払い義務の更新
まだ実装されていません。
サブスクリプション使用期限
支払い登録あり
顧客へ試用期間を提供し、支払情報を事前に登録してもらう場合、サブスクリプションを作成するときにtrialDays
メソッドを使ってください。
$user = User::find(1);
$user->newSubscription('main', 'monthly')
->trialDays(10)
->create();
このメソッドはデータベースのサブスクリプションレコードへ、試用期間の終了日を設定します。
Note: 顧客は最初の支払い時に支払いを登録するため、Mollieの支払いページへリダイレクトされます。Cashierの設定ファイルで金額を更新できます。
Note: 顧客のサブスクリプションが試用期間の最後の日までにキャンセルされないと、期限が切れると同時に課金されます。そのため、ユーザーに試用期間の終了日を通知しておくべきでしょう。
trialUntil
メソッドにより、使用期間の終了時を指定する、Carbon
インスタンスを渡せます
use Carbon\Carbon;
$user->newSubscription('main', 'monthly')
->trialUntil(Carbon::now()->addDays(10))
->create();
ユーザーが使用期間中であるかを判定するには、ユーザーインスタンスに対しonTrial
メソッドを使うか、サブスクリプションインスタンスに対してonTrial
を使用してください。次の2つの例は、同じ目的を達します。
if ($user->onTrial('main')) {
//
}
if ($user->subscription('main')->onTrial()) {
//
}
支払い登録なし
事前にユーザーの支払い方法の情報を登録してもらうことなく、試用期間を提供する場合は、そのユーザーのレコードのtrial_ends_at
に、試用の最終日を設定するだけです。典型的な使い方は、ユーザー登録時に設定する方法でしょう。
$user = User::create([
// 他のユーザープロパティの設定…
'trial_ends_at' => now()->addDays(10),
]);
Note: モデル定義の
trial_ends_at
に対する、日付ミューテタを付け加えるのを忘れないでください。
既存のサブスクリプションと関連付けが行われていないので、Cashierでは、このタイプの試用を「包括的な試用(generic
trial)」と呼んでいます。User
インスタンスに対し、onTrial
メソッドがtrue
を返す場合、現在の日付はtrial_ends_at
の値を過ぎていません。
if ($user->onTrial()) {
// User is within their trial period...
}
とくに、ユーザーが「包括的な試用」期間中であり、まだサブスクリプションが作成されていないことを調べたい場合は、onGenericTrial
メソッドが使用できます。
if ($user->onGenericTrial()) {
// ユーザーは「包括的」な試用期間中
}
ユーザーに実際のサブスクリプションを作成する準備ができたら、通常はnewSubscription
メソッドを使います。
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create();
Webフックハンドラの定義
Cashierは課金の失敗時に、サブスクリプションを自動的にキャンセル処理します。
また、Laravel\Cashier\Events
名前空間下の以下のイベントをリッスンすることで、アプリケーションへ特定の振る舞いを追加できます。
OrderPaymentPaid
andOrderPaymentFailed
FirstPaymentPaid
andFirstPaymentFailed
一回限りの支払い
まだ実装されていません。
インボイス
Laravel\Cashier\Events
名前空間下のOrderInvoiceAvailable
イベントをリッスンしてください。新しい注文が処理されると、次のようにインボイスを取得できます。
$invoice = $event->order->invoice();
$invoice->view(); // Bladeビューの取得
$invoice->pdf(); // BladeビューのPDF取得
$invoice->download(); // PDFのダウンロードレスポンス取得
ユーザーが注文したインボイスのリストへアクセスするには、$user->orders->invoices()
をつかいます。これにはまだ処理されていない注文や、失敗した注文など全注文が含まれます。
払い戻し
まだ実装されていません。
顧客の差し引き残高
たとえばより安いプランへ変更したケースのように、顧客が払いすぎる状況は起きます。払いすぎた金額は、その顧客の差し引き残高へ加算されます。
顧客の差し引き残高は注文ごとに自動で処理します。
通貨ごとに別々の差し引き残高が保持されます。
差し引き残高を直接操作するメソッドは、多少あります。
注意深く使用してください
$credit = $user->credit('EUR');
$user->addCredit(new Amount(10, 'EUR'); // 10ユーロ追加
$user->hasCredit('EUR');
合計金額がマイナスになる注文が処理されると、その金額がユーザーの残高に加算されます。その時点でユーザーが有効なサブスクリプションを持っていない場合、BalanceTurnedStale
イベントが発生します。残りの残高を返金したい場合や、ユーザーに通知したい場合は、このイベントをリッスンしてください。
顧客のロケール
Mollieは、顧客の地域に合わせたチェックアウトを提供します。このため、訪問者のロケールを推測します。デフォルトのロケールを上書きするには、config/cashier.php
で設定します。これは単一国向けにサービスを提供するのに便利でしょう。
複数のロケールを扱う場合にMollieのデフォルト動作をオーバーライドしたいときは、BillableモデルにgetLocale()
メソッドを実装します。一般的な実現方法は、NULL値可能なlocale
フィールドをuserテーブルに追加し、その値を取得することです。
class User extends Model
{
/**
* @return string
* @link https://docs.mollie.com/reference/v2/payments-api/create-payment#parameters
* @example 'nl_NL'
*/
public function getLocale() {
return $this->locale;
}
}
Cashier全イベント
リッスンできるLaravel\Cashier\Events
名前空間下のイベントを以下に紹介します。
BalanceTurnedStale
イベント
ユーザーのアカウント残高はプラスですが、有効なサブスクリプションはありません。返金を検討してください。
CouponApplied
イベント
クーポンが注文されたアイテムに適用されました。redeemingクーポンとapplyingクーポンの違いに注意してください。redeemingクーポンは複数の注文に適用できます。たとえば単一のredeemingクーポンを使用して毎月のサブスクリプションへ6ヶ月間割引を適用できます。
FirstPaymentFailed
イベント
支払い登録を利用した、最初の支払いに失敗しました。
FirstPaymentPaid
イベント
支払い登録を利用した、最初の支払いに成功しました。
MandateClearedFromBillable
イベント
mollie_mandate_id
がBillableモデルへ作成されました。これは支払い登録が無効だったため、支払いへ失敗した場合に発生します。
MandateUpdated
イベント
Billableモデルの支払い登録が更新されました。これは通常、新しいカードが登録されたことを意味します。
OrderCreated
イベント
注文が作成されました。
OrderInvoiceAvailable
イベント
An Invoice is available on the Order. Access it using
$event->order->invoice()
.
OrderPaymentFailed
イベント
The payment for an order has failed.
OrderPaymentPaid
イベント
The payment for an order was successful.
OrderProcessed
イベント
The order has been fully processed.
SubscriptionStarted
イベント
A new subscription was started.
SubscriptionCancelled
イベント
The subscription was cancelled.
SubscriptionResumed
イベント
The subscription was resumed.
SubscriptionPlanSwapped
イベント
The subscription plan was swapped.
SubscriptionQuantityUpdated
イベント
The subscription quantity was updated.
Metered billing with variable amounts
Some business cases will require dynamic subscription amounts.
To allow for full flexibility Cashier Mollie allows you to define your own set of Subscription OrderItem preprocessors. These preprocessors are invoked when the OrderItem is due, right before being processed into a Mollie payment.
If you're using metered billing, this is a convenient place to calculate the amount based on the usage statistics and reset any counters for the next billing cycle.
You can define the preprocessors in the cashier_plans
config file.
Ok. So how does this all actually work?
This Cashier implementation schedules triggering payments from the client side, instead of relying on subscription management at Mollie. (Yes, Mollie also offers a Subscription API, but it does not support all of Cashier features, so this package provides its own subscription engine.)
From a high level perspective, this is what the process looks like:
- A
Subscription
is created using theMandatePaymentSubscriptionBuilder
(redirecting to Mollie's checkout to create aMandate
) orPremandatedSubscriptionBuilder
(using an existingMandate
). - The
Subscription
yields a scheduledOrderItem
at the beginning of each billing cycle. OrderItems
which are due are preprocessed and bundled intoOrders
whenever possible by a scheduled job (i.e. daily). This is done so your customer will receive a single payment/invoice for multiple items later on in the chain). Preprocessing theOrderItems
may involve applying dynamic discounts or metered billing, depending on your configuration.- The
Order
is processed by the same scheduled job into a payment:- First, (if available) the customer's balance is processed in the
Order
. - If the total due is positive, a Mollie payment is incurred.
- If the total due is 0, nothing happens.
- If the total due is negative, the amount is added to the user's
balance. If the user has no active subscriptions left, the
BalanceTurnedStale
event will be raised.
- First, (if available) the customer's balance is processed in the
- You can generate an
Invoice
(html/pdf) for the user.
F.A.Q. - Frequently Asked Questions
My billable model uses UUIDs, how can I get Cashier Mollie to work with this?
By default Cashier Mollie uses unsignedInteger
fields
for the billable model relationships. If required for your billable
model, modify the cashier migrations for UUIDs:
// Replace this:
$table->unsignedInteger('owner_id');
// By this:
$table->uuid('owner_id');
How is prorating handled?
Cashier Mollie applies prorating by default. With prorating, customers are billed at the start of each billing cycle.
This means that when the subscription quantity is updated or is switched to another plan:
- the billing cycle is reset
- the customer is credited for unused time, meaning that the amount that was overpaid is added to the customer's balance.
- a new billing cycle is started with the new subscription settings. An Order (and payment) is generated to deal with all of the previous, including applying the credited balance to the Order.
This does not apply to the
$subscription->swapNextCycle('other-plan')
, which simply
waits for the next billing cycle to update the subscription plan. A
common use case for this is downgrading the plan at the end of the
billing cycle.
How can I load coupons and/or plans from database?
Because Cashier Mollie uses contracts a lot it's quite easy to extend Cashier Mollie and use your own implementations. You can load coupons/plans from database, a file or even a JSON API.
For example a simple implementation of plans from the database:
Firstly you create your own implementation of the plan repository and
implement Laravel\Cashier\Plan\Contracts\PlanRepository
.
Implement the methods according to your needs and make sure you'll
return a Laravel\Cashier\Plan\Contracts\Plan
.
use App\Plan;
use Laravel\Cashier\Exceptions\PlanNotFoundException;
use Laravel\Cashier\Plan\Contracts\PlanRepository;
class DatabasePlanRepository implements PlanRepository
{
public static function find(string $name)
{
$plan = Plan::where('name', $name)->first();
if (is_null($plan)) {
return null;
}
// Return a \Laravel\Cashier\Plan\Plan by creating one from the database values
return $plan->buildCashierPlan();
// Or if your model implements the contract: \Laravel\Cashier\Plan\Contracts\Plan
return $plan;
}
public static function findOrFail(string $name)
{
if (($result = self::find($name)) === null) {
throw new PlanNotFoundException;
}
return $result;
}
}
Example Plan model (app/Plan.php) with buildCashierPlan and returns a \Laravel\Cashier\Plan\Plan
~~~~~ name); return $plan->setAmount(mollie_array_to_money($this->amount)) ->setInterval($this->interval) ->setDescription($this->description) ->setFirstPaymentMethod($this->first_payment_method) ->setFirstPaymentAmount(mollie_array_to_money($this->first_payment_amount)) ->setFirstPaymentDescription($this->first_payment_description) ->setFirstPaymentRedirectUrl($this->first_payment_redirect_url) ->setFirstPaymentWebhookUrl($this->first_payment_webhook_url) ->setOrderItemPreprocessors(Preprocessors::fromArray($this->order_item_preprocessors)); } } ~~~~~ Note: In this case you'll need to add accessors for all the values (like amount, interval, fist_payment_method etc.) to make sure you'll use the values from your defaults (config/cashier_plans.php > defaults).Then you just have to bind your implementation to the Laravel/Illuminate container by registering the binding in a service provider
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(\Laravel\Cashier\Plan\Contracts\PlanRepository::class, DatabasePlanRepository::class);
}
}
Cashier Mollie will now use your implementation of the PlanRepository. For coupons this is basically the same, just make sure you implement the CouponRepository contract and bind the contract to your own implementation.
テスト
Cashier Mollie is tested against Mollie's test API.
Start with copying phpunit.xml.dist
into
phpunit.xml
, and set these environment variables in
phpunit.xml
:
Mollie API test key
You can obtain this key from the dashboard right after signing up.
<env name="MOLLIE_KEY" value="YOUR_VALUE_HERE"/>
ID of a customer with a valid directdebit mandate
<env name="MANDATED_CUSTOMER_DIRECTDEBIT" value="YOUR_VALUE_HERE"/>
Mandate's ID (of the previously mentioned customer)
<env name="MANDATED_CUSTOMER_DIRECTDEBIT_MANDATE_ID" value="YOUR_VALUE_HERE"/>
ID of a successful ("paid) payment by the customer
Use a 1000 EUR amount. ~~~~~
ID of an unsuccessful ("failed") payment by the customer
<env name="PAYMENT_FAILED_ID" value="YOUR_VALUE_HERE"/>
Now you can run:
composer test
貢献
Please see CONTRIBUTING for details.
脆弱性
If you discover any security related issues, please email support@mollie.com instead of using the issue tracker.
クレジット
ライセンス
The MIT License (MIT). Please see License File for more information.