From c8074552697b441c5208780c6197354a8db9619d Mon Sep 17 00:00:00 2001 From: Nadir Hamid Date: Mon, 8 Jun 2026 19:58:49 +0000 Subject: [PATCH 1/2] make invoice settling workflow more versatile --- app/app/Helpers/BillingDataHelper.php | 6 ++- .../Http/Controllers/BillingController.php | 54 +++++++++++++------ app/app/Http/routes.php | 1 + app/app/UserInvoicePayment.php | 17 ++++++ ..._06_08_172008_create_invoices_payments.php | 39 ++++++++++++++ 5 files changed, 100 insertions(+), 17 deletions(-) create mode 100755 app/app/UserInvoicePayment.php create mode 100644 app/database/migrations/2026_06_08_172008_create_invoices_payments.php diff --git a/app/app/Helpers/BillingDataHelper.php b/app/app/Helpers/BillingDataHelper.php index c66d5e649..59af8a044 100755 --- a/app/app/Helpers/BillingDataHelper.php +++ b/app/app/Helpers/BillingDataHelper.php @@ -109,7 +109,11 @@ public static function getBillingInfo($user, $plan=NULL, $subscription=NULL, $wo $planCost = 0; $credits = UserCredit::where('user_id', '=',$user->id)->where('status', PaymentStatus::APPROVED)->get(); $debits = UserDebit::where('user_id', '=',$user->id)->get(); - $invoices = UserInvoice::where('user_id', '=',$user->id)->get(); + $invoices = UserInvoice::where('user_id', '=',$user->id) + ->where('status', '!=', PaymentStatus::PAID) + ->where('status', '!=', PaymentStatus::CANCELLED) + ->get(); + if (!empty($workspace) && !$plan->pay_as_you_go) { if ($subscription->billing_cycle == 'ANNUAL') { $cycleStart = (new DateTime('first day of January this year'))->format('Y-m-d 00:00:00'); diff --git a/app/app/Http/Controllers/BillingController.php b/app/app/Http/Controllers/BillingController.php index c761ff355..01620a338 100755 --- a/app/app/Http/Controllers/BillingController.php +++ b/app/app/Http/Controllers/BillingController.php @@ -18,6 +18,7 @@ use App\Helpers\BillingDataHelper; use App\Helpers\WorkspaceHelper; use App\Helpers\InvoiceHelper; +use App\Enums\PaymentStatus; use DateTime; class BillingController extends ApiAuthController @@ -149,6 +150,7 @@ public function settleInvoice(Request $request, $invoiceId) return $this->response->errorForbidden(); } $data = $request->json()->all(); + $amountToPay = $data['amount_to_pay']; $invoices = []; if (!empty($data['invoices'])) { $invoices = $data['invoices']; @@ -162,23 +164,21 @@ public function settleInvoice(Request $request, $invoiceId) ->where('workspace_id', $workspace->id) ->firstOrFail(); - foreach ($invoices as $invoiceId) { - $invoice = UserInvoice::where('id', $invoiceId) - ->where('workspace_id', $workspace->id) - ->firstOrFail(); - $message = [ - 'run_id' => 'settle_invoice_' . $invoice->id . '_' . time(), - 'action' => 'SETTLE_INVOICE', - 'invoice_id' => $invoice->id, - 'user_id' => $user->id, - 'workspace_id' => $workspace->id, - 'payment_method_id' => $card->stripe_payment_method_id, - 'card_last_4' => $card->last_4, - 'card_brand' => $card->issuer, - ]; - RabbitMQHelper::publish('billing_tasks', $message); - } + + $message = [ + 'run_id' => 'settle_invoice_multi_' . time(), + 'action' => 'SETTLE_INVOICES', + 'invoice_id' => implode(',', $invoices), + 'user_id' => $user->id, + 'workspace_id' => $workspace->id, + 'payment_method_id' => $card->stripe_payment_method_id, + 'card_last_4' => $card->last_4, + 'card_brand' => $card->issuer, + 'amount_to_pay' => $amountToPay, + ]; + + RabbitMQHelper::publish('billing_tasks', $message); return $this->response->noContent(); } @@ -247,6 +247,28 @@ public function getInvoices(Request $request) return $this->response->array(['invoices' => $invoices]); } + public function getOverdueInvoices(Request $request) + { + $user = $this->getUser($request); + $workspace = $this->getWorkspace($request); + if (!WorkspaceHelper::canPerformAction($user, $workspace, 'manage_billing')) { + return $this->response->errorForbidden(); + } + $status = $request->query('status'); + + $query = UserInvoice::where('workspace_id', $workspace->id) + ->whereIn('status', [PaymentStatus::FAILED, PaymentStatus::PENDING]); + + $invoices = $query->get()->map(function($item) { + $item['amount_in_dollars'] = MainHelper::toDollars($item['cents']); + $item['friendly_created_at'] = \Carbon\Carbon::parse($item['created_at'])->format('M d, Y'); + return $item; + }); + + return $this->response->array(['invoices' => $invoices]); + } + + public function downloadInvoice(Request $request, $invoiceId) { $user = $this->getUser($request); diff --git a/app/app/Http/routes.php b/app/app/Http/routes.php index aa35aba20..07bfadab1 100755 --- a/app/app/Http/routes.php +++ b/app/app/Http/routes.php @@ -907,6 +907,7 @@ $api->get('invoices/{invoiceId}/download', '\App\Http\Controllers\BillingController@downloadInvoice'); $api->post('invoices/settle', '\App\Http\Controllers\BillingController@settleInvoices'); $api->get('invoices', '\App\Http\Controllers\BillingController@getInvoices'); + $api->get('overdueInvoices', '\App\Http\Controllers\BillingController@getOverdueInvoices'); }); }); }); diff --git a/app/app/UserInvoicePayment.php b/app/app/UserInvoicePayment.php new file mode 100755 index 000000000..f2019cbe0 --- /dev/null +++ b/app/app/UserInvoicePayment.php @@ -0,0 +1,17 @@ +increments('id'); + $table->timestamps(); + $table->integer('user_id')->nullable()->unsigned(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('set null'); + $table->integer('workspace_id')->nullable()->unsigned(); + $table->foreign('workspace_id')->references('id')->on('workspaces')->onDelete('set null'); + + $table->integer('cents'); + $table->string('source'); + $table->string('status'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('users_invoices_payments'); + } +} From 680487a2decb6bd70dfac0ae0c5a52235f8e2841 Mon Sep 17 00:00:00 2001 From: Nadir Hamid Date: Mon, 8 Jun 2026 20:44:47 +0000 Subject: [PATCH 2/2] make invoice settling API more versatile so it can handle batch invoice payments in one event. --- app/app/Http/Controllers/BillingController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app/Http/Controllers/BillingController.php b/app/app/Http/Controllers/BillingController.php index 01620a338..48eae87c4 100755 --- a/app/app/Http/Controllers/BillingController.php +++ b/app/app/Http/Controllers/BillingController.php @@ -175,7 +175,7 @@ public function settleInvoice(Request $request, $invoiceId) 'payment_method_id' => $card->stripe_payment_method_id, 'card_last_4' => $card->last_4, 'card_brand' => $card->issuer, - 'amount_to_pay' => $amountToPay, + 'amount' => $amountToPay, ]; RabbitMQHelper::publish('billing_tasks', $message);