159 lines
6.9 KiB
PHP
159 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\ShorakaFinancing;
|
|
use App\Http\Controllers\GadaianController;
|
|
use App\Http\Controllers\AliranTunaiController;
|
|
use App\Events\Dashboard\GadaiNotify;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Throwable;
|
|
|
|
class ShorakaFinancingService
|
|
{
|
|
public function ensureReady(): void
|
|
{
|
|
// Check local migration before any registration side effect.
|
|
ShorakaFinancing::where('kodcaw', Auth::user()->cawangan)->exists();
|
|
$response = $this->api()->get(rtrim(config('api.url'), '/') . '/api/shoraka/readiness')->throw()->json();
|
|
abort_unless(!empty($response['ready']), 503, 'Shoraka belum dikonfigurasi. Pembiayaan baharu ditahan.');
|
|
}
|
|
|
|
public function api()
|
|
{
|
|
return Http::withOptions(['verify' => config('app.api_verify')])
|
|
->acceptJson()->timeout(75);
|
|
}
|
|
|
|
public function url(string $reference, string $suffix = ''): string
|
|
{
|
|
return rtrim(config('api.url'), '/') . '/api/shoraka/gadai/'
|
|
. rawurlencode(Auth::user()->cawangan) . '/' . rawurlencode($reference) . $suffix;
|
|
}
|
|
|
|
public function local(string $reference)
|
|
{
|
|
return ShorakaFinancing::where('kodcaw', Auth::user()->cawangan)
|
|
->where('norujukan', $reference)->first();
|
|
}
|
|
|
|
public function hold(Request $request, string $reference, string $action, array $extra = [])
|
|
{
|
|
$user = Auth::user();
|
|
$record = ShorakaFinancing::firstOrCreate(
|
|
['kodcaw' => $user->cawangan, 'norujukan' => $reference],
|
|
[
|
|
'teller' => $user->name, 'action' => $action, 'state' => 'WAITING',
|
|
'context' => array_merge($request->except(['_token', 'password']), $extra),
|
|
]
|
|
);
|
|
// A duplicate request only opens the saved workflow; it never replays registration.
|
|
if ($record->wasRecentlyCreated) {
|
|
try {
|
|
$this->api()->post($this->url($reference, '/submit'));
|
|
} catch (Throwable $exception) {
|
|
// The persisted workflow remains available after timeouts.
|
|
}
|
|
}
|
|
return $this->pendingResponse($request, $reference);
|
|
}
|
|
|
|
public function pendingResponse(Request $request, string $reference)
|
|
{
|
|
$url = route('shoraka.show', ['norujukan' => $reference]);
|
|
if ($request->ajax() || $request->expectsJson()) {
|
|
return response()->json(['shoraka_pending' => true, 'redirect' => $url], 202);
|
|
}
|
|
return redirect($url);
|
|
}
|
|
|
|
public function finish(Request $request, string $reference)
|
|
{
|
|
$record = $this->local($reference);
|
|
abort_unless($record && $record->teller === Auth::user()->name, 403,
|
|
'Pembiayaan perlu diselesaikan oleh teller asal.');
|
|
if ($record->state === 'DONE') {
|
|
return redirect()->route('shoraka.show', ['norujukan' => $reference]);
|
|
}
|
|
abort_unless($record->state === 'WAITING', 409,
|
|
'Penyelesaian telah diproses atau memerlukan semakan operasi. Jangan ulang bayaran.');
|
|
|
|
$this->api()->post($this->url($reference, '/refresh'))->throw();
|
|
$snapshot = $this->api()->get($this->url($reference))->throw()->json()['data'];
|
|
abort_unless(!empty($snapshot['can_complete']), 409,
|
|
'Bayaran ditahan sehingga Shoraka Completed dan amaun sepadan.');
|
|
|
|
abort_unless(
|
|
number_format((float) ($record->context['pinjaman'] ?? 0), 2, '.', '')
|
|
=== number_format((float) ($snapshot['order_amount'] ?? 0), 2, '.', ''),
|
|
409, 'Amaun pembiayaan berubah. Semakan operasi diperlukan sebelum bayaran.'
|
|
);
|
|
|
|
$claimed = ShorakaFinancing::where('id', $record->id)->where('state', 'WAITING')
|
|
->update(['state' => 'PROCESSING', 'updated_at' => now()]);
|
|
abort_unless($claimed === 1, 409, 'Penyelesaian sedang diproses.');
|
|
|
|
$saved = new Request($record->context);
|
|
$saved->setLaravelSession($request->session());
|
|
try {
|
|
if ($record->action === 'gadai') {
|
|
$result = app(GadaianController::class)->completeShorakaFinancing($saved);
|
|
} else {
|
|
if (!empty($record->context['update_history'])) {
|
|
$this->api()->put(rtrim(config('api.url'), '/') . '/api/gadai/updatehistory/'
|
|
. rawurlencode($record->kodcaw) . '/' . rawurlencode($reference), [
|
|
'historygadaian' => $saved->norujukan,
|
|
'jenistebus' => $saved->jenistebus,
|
|
])->throw();
|
|
}
|
|
$cash = app(AliranTunaiController::class);
|
|
if ($record->action === 'autotawarruq') {
|
|
$cash->alirantunaiTebusAutoTawarruq($saved);
|
|
} else {
|
|
$cash->alirantunaiTebus($saved);
|
|
}
|
|
event(new \App\Events\Dashboard\TebusNotify('lulus', $record->kodcaw));
|
|
event(new GadaiNotify('lulus', $record->kodcaw));
|
|
$result = redirect()->route('shoraka.show', ['norujukan' => $reference]);
|
|
}
|
|
$record->update(['state' => 'DONE', 'context' => []]);
|
|
return $result;
|
|
} catch (Throwable $exception) {
|
|
// A failed remote payment response can be ambiguous. Never retry automatically.
|
|
$record->update(['state' => 'REVIEW']);
|
|
throw $exception;
|
|
}
|
|
}
|
|
|
|
public function printData(string $reference): ?array
|
|
{
|
|
$response = $this->api()->get($this->url($reference))->throw()->json()['data'];
|
|
$order = $response['order'] ?? null;
|
|
if (!$order) {
|
|
abort_if($this->local($reference), 409, 'SAG ditahan: transaksi Shoraka belum tersedia.');
|
|
return null; // Historical financing keeps its historical print path.
|
|
}
|
|
abort_unless(!empty($response['can_complete']), 409, 'SAG ditahan: Shoraka belum Completed.');
|
|
$details = $response['details'] ?? [];
|
|
// Shoraka may return a list of pipe-delimited certificate details.
|
|
$certificateDetails = $details['certificateDetails1'] ?? '';
|
|
if (is_array($certificateDetails)) {
|
|
$certificateDetails = $certificateDetails[0] ?? '';
|
|
}
|
|
$certificate = is_string($certificateDetails) ? trim(explode('|', $certificateDetails)[0]) : '';
|
|
abort_unless($certificate !== '' && isset($details['unit'], $details['volume'], $details['commodityType']),
|
|
409, 'Maklumat sijil belum lengkap. Semak status Shoraka dahulu.');
|
|
return [
|
|
'komoditi' => ['no_sijil' => $certificate],
|
|
'product' => [
|
|
'kodproduk' => $details['commodityType'],
|
|
'description' => $details['commodityType'] === '000-COPPER' ? 'Copper' : 'Plastic resin',
|
|
'unit' => $details['unit'],
|
|
],
|
|
'kuantiti' => $details['volume'],
|
|
];
|
|
}
|
|
}
|