メインコンテンツにスキップ

ArtinTech Solution

MikroTik billing automation in Linkhive: routers page with online status, customer count, RouterOS version and CPU load

インターネット事業者には、課金システムとルーターという2つの情報源があります。MikroTik課金自動化とは両者を一致させ続ける仕事であり、高くつく不具合はその隙間に潜んでいます。支払い済みなのに切断されたままの顧客、未払いなのに接続し続ける顧客、決済ゲートウェイのWebhook再送で二重に適用される更新です。ブロードバンド・Wi-Fi事業者向けのセルフホスト型Laravel課金基盤Linkhiveを作るとき、この3つの障害はどの機能よりも設計を左右しました。

MikroTik課金自動化は2回実行しても安全な処理から始まる

Linkhiveの課金サイクルは順序付きの処理の並びです。更新請求書の作成、プリペイド残高からの自動更新、支払い済み請求書の処理、リマインダー送信、期限切れと停止、そしてバウチャーの処理です。スケジューラーが15分ごとに実行し、cronのないホスティングでは同じサイクルがWebアクセスを契機に最大15分ごとに動きます。

頻繁な実行が安全なのは、2回目の実行で何も変わらない場合だけです。そのため各処理は、作業が済んでいるかを最初に確認します。更新請求の前に、その顧客、パッケージ、期間の請求書を探します。リマインダーの前に、今日送信済みのものがないかメッセージログを確認します。各処理は個別のtry/catch内で動くため、失敗は記録され、残りの処理は続行します。1件の不正なデータがネットワーク全体の停止処理を止めることはありません。

タイミングは設定できます。既定では期限の3日前に請求書を発行し、3日前と1日前にリマインダーを送り、2日の猶予後にサービスを停止します。

Linkhiveの顧客アカウント。パッケージ、有効期限、自動更新が有効なプリペイド残高、請求履歴を表示
期限まで2日のデモ顧客。自動更新が有効で、次の請求書も発行済みです。

1回のアトミックな更新で二重更新を防ぐ

1件の支払いが3つの経路で同時に届くことがあります。ゲートウェイから戻る顧客のブラウザー、ゲートウェイのWebhook、支払い済み請求書を拾う課金サイクルです。各経路が「更新済みか」を確認してから更新すると、2つが同時に確認を通過し、顧客は1か月分の料金で2か月分を得てしまいます。

確認してから書き込むとこの隙間が残るため、確認と書き込みを1つの文にしました。

$claimed = Invoice::whereKey($invoice->id)
    ->whereNull('fulfilled_at')
    ->update(['fulfilled_at' => now()]);

if ($claimed === 0) {
    return false; // another request got here first
}

どのリクエストが先に届いても、その行に一致するUPDATEは1つだけです。他のリクエストは影響行数0を見て停止します。支払いも同様で、approvePayment()はトランザクション内でlockForUpdate()により行をロックするため、2回呼んでも入金は1回だけです。

ブラウザーではなくゲートウェイに確認する

戻りURLに届く内容は偽造できるため、手がかりとしてのみ扱います。StripeではLinkhiveが決済開始時にCheckoutセッションIDを保存し、戻ってきた際にStripe APIからそのセッションを取得します。セッションが一致し、Stripeが支払い済みと報告し、参照番号、金額、通貨がすべて正しい場合にだけ入金を計上します。PayPalも同じ方式で、SSLCommerz、bKash、Nagadのドライバーはそれぞれの APIで金額と参照番号を確認します。

Linkhiveの請求書一覧。請求書番号、顧客、支払期限、残高、未払いステータスを表示
スタッフの承認かゲートウェイのAPIによる確認があるまで、請求書は未払いのままです。

オフラインのルーターが支払いを取り消してはならない

決済トランザクションの中でルーターと通信する設計は魅力的に見えますが、ルーターの再起動で成功したカード決済がエラー画面に変わってしまいます。Linkhiveではルーターへの変更はすべてProvisionerという1つのクラスを通り、例外を投げません。入金が先に記録されます。ルーターに接続できない場合、失敗はプロビジョニングログに書かれ、契約が再試行対象として印を付けられます。

1時間ごとのジョブが、印の付いた契約を最大3日間再試行します。古いコマンドを再生するのではなく契約の現在の状態を読み取り、有効な契約は有効化し、それ以外は停止します。ルーター停止中に顧客が支払った場合、再試行は再接続を行い、古い停止を適用しません。

MikroTik課金自動化では、ドライバーは自作のクライアントでRouterOS APIを使い、ポート8728またはTLSの8729で通信します。PPPoEなら/ppp/secret、ホットスポットなら/ip/hotspot/userを更新し、その後アクティブなセッションを切断して、顧客が正しいプロファイルで再接続するようにします。FreeRADIUSも同じドライバーインターフェースの背後にあるため、課金コードは事業者がどちらを使うかを意識しません。

LinkhiveのMikroTik課金自動化。状態、顧客数、RouterOSバージョン、CPU負荷を示すルーター画面
ルーターの状態は5分ごとに確認されます。デモのルーターはシミュレーションです。

ルールを攻撃するテストで証明する

こうしたルールは静かに壊れるため、MikroTik課金自動化にはそれを破ろうとするテストが必要です。Linkhiveには、SQLiteとMySQL 8.4の両方で通過する147件の自動テストが含まれます。サイクルを繰り返し実行して冪等性を確認し、偽造した戻りを送り、金額や通貨の不一致を試します。Larastanレベル5の静的解析でもエラーはありません。

ダークテーマのLinkhive管理ダッシュボード。有効顧客、入金、継続収益、未回収残高を表示
ダークテーマで見たデモネットワークの運用画面。

要点

  • 2回目の実行で何も変わらないように課金処理を書き、頻繁に実行する。
  • 読み取りと書き込みに分けず、1つのアトミックなUPDATEで作業を確保する。
  • 金額と通貨を含め、すべての支払いをゲートウェイのAPIで確認する。
  • ルーター呼び出しを決済トランザクションから外し、失敗を記録して再試行する。
  • 古いコマンドの再生ではなく、現在の状態から再試行する。

すべての動きはLinkhiveのライブデモで確認できます。Charming Themeの機能紹介を読むか、Vibe96でLinkhiveを入手してください。

ブロードバンドやWi-Fi網を運営していて、表計算では追いつかなくなっていませんか。ご相談ください。ArtinTech SolutionはLaravel開発サービスでこうした課金と連携を構築しています。

Share this article