Grenke Controllers
app/Http/Controllers/Grenke/
Drei Controller bedienen die /api/v1/grenke-Routen. Sie bleiben dünn — nur Validation, DTO-Hydration, Action-Aufruf und Response.
| Controller | Datei | Routen |
|---|---|---|
RequestController | Grenke/RequestController | gesamtes Request-API (Step-by-Step) |
RequestFlowController | Grenke/RequestFlowController | Orchestrator (One-Shot Flow) |
CalculateController | Grenke/CalculateController | Lease-Berechnungen |
RequestController
Bedient den vollständigen Lifecycle eines Grenke-Requests.
Methoden
| Methode | Route | Action(s) |
|---|---|---|
create | POST /api/v1/grenke | CreateGrenkeRequestAction |
show | GET /api/v1/grenke/{financingId} | GetGrenkeRequestAction |
patchRequest | PATCH /api/v1/grenke/{financingId} | PatchGrenkeRequestAction |
patchLessee | PATCH /api/v1/grenke/{financingId}/lessee | PatchGrenkeLesseeAction |
waitForReadyToSign | GET /api/v1/grenke/{financingId}/wait-ready-to-sign | WaitForGrenkeReadyToSignAction |
getESignatureConfiguration | GET /api/v1/grenke/{financingId}/e-signature/configuration | Get+GetESignatureConfigurationAction |
startESignature | POST /api/v1/grenke/{financingId}/e-signature | Get+StartGrenkeESignatureAction |
cancelESignature | PUT /api/v1/grenke/{financingId}/cancel-e-signature | CancelGrenkeESignatureAction |
switchToPaperContract | PUT /api/v1/grenke/{financingId}/switch-to-paper-contract | SwitchGrenkeToPaperContractAction |
getContractDocumentBase64 | GET /api/v1/grenke/{financingId}/contractdocument/base64 | Get+GetGrenkeContractDocumentAction |
getContractDocumentDownloadLink | POST /api/v1/grenke/{financingId}/contractdocument/link | Get+PostGrenkeContractDocumentAction |
Beispiel — create
public function create(CreateGrenkeRequestRequest $request): JsonResponse
{
try {
$dto = RequestPayloadData::fromArray($request->validated());
$result = $this->createGrenkeRequestAction->execute($dto);
return response()->json([
'success' => true,
'data' => $result->toArray(),
'meta' => $this->grenkeRequestStateService->buildStateMeta($result),
]);
} catch (Throwable $e) {
return $this->errorResponse($e);
}
}
Beispiel — waitForReadyToSign (mit Mail-Context)
public function waitForReadyToSign(Request $request, string $financingId): JsonResponse
{
try {
$result = $this->waitForGrenkeReadyToSignAction->execute(
financingId: $financingId,
maxAttempts: (int) $request->input('max_attempts', 10),
sleepMilliseconds: (int) $request->input('sleep_milliseconds', 1000),
mailContext: [
'technician_appointment_date' => $request->input('technician_appointment_date'),
'technician_appointment_time' => $request->input('technician_appointment_time'),
],
);
return response()->json([
'success' => true,
'data' => $result->toArray(),
'meta' => $this->grenkeRequestStateService->buildStateMeta($result),
]);
} catch (Throwable $e) {
return $this->errorResponse($e);
}
}
mailContextwird bei StatusMissingInfoan denMissingInfoMailFlowServicegereicht — siehe Mailflow.
Beispiel — getContractDocumentBase64 mit optionalem Speichern
$savedFile = null;
if ($request->boolean('store_pdf', false)) {
$base64 = $this->grenkePdfService->extractBase64FromResponse($result);
$savedFile = $this->grenkePdfService->saveBase64Pdf(
base64Content: $base64,
directory: 'grenke/contracts',
filename: $financingId . '.pdf',
);
}
errorResponse(Throwable $e)
Zentralisiert. Loggt den Fehler und liefert einheitlichen 500-Body:
{ "success": false, "message": "<exception message>" }
RequestFlowController
Wraps den RequestFlowService für One-Shot-Aufrufe — der gesamte Flow (Create → optional Patch → polling → e-signature oder cancel/paper) in einem einzigen Endpoint.
Routen
| Route | Methode | Zweck |
|---|---|---|
POST /api/v1/grenke/grenke/request-flow | store | kompletter Flow |
GET /api/v1/grenke/grenke/request-flow | index | Health-Check |
GET /api/v1/grenke/grenke/request-flow/test-flow/{financingId} | testFlow | nur Vertrag-PDF in Storage ablegen |
Request-Body (gekürzt)
{
"request_payload": { "FinancingAmount": 33877.56, "Period": 84, "Lessee": { ... }, "FinancingObjects": [ ... ] },
"e_signature_payload": { "SignerName": "Bettina Brem", "SignerEmail": "..." },
"patch_request_payload": { "PaymentMethod": "Invoice" },
"patch_lessee_payload": { "CompanyName": "..." },
"cancel_e_signature": false,
"fetch_contract_document_base64": true,
"fetch_contract_document_download_link": false,
"max_attempts": 10,
"sleep_milliseconds": 1000
}
Vollständiges Beispiel + Erklärung in Request-Flow.
CalculateController
app/Http/Controllers/Grenke/CalculateController.php
Zwei Hilfs-Endpunkte für Lease-Berechnung:
| Methode | Route | Zweck |
|---|---|---|
store | POST /api/v1/grenke/calculate | calls Grenke POST /calculate mit validiertem Body |
purchase | GET /api/v1/grenke/calculate/{monthlyInstalment?}/{period?} | inverse Berechnung: aus Monatsrate + Laufzeit → Kaufsumme |
purchase — wie funktioniert das?
Grenke\Helper::getFactor($period) liefert pro Laufzeit (48/60/72/84 Monate) einen Faktor. Aus monatlicher Rate und Laufzeit ergibt sich der Finanzierungsbetrag:
$financingAmount = ($monthlyInstalment * 100) / Helper::getFactor($period);
// e.g. (299 * 100) / 1.47 = 20340.14 für 84 Monate
Default-Werte: monthlyInstalment = 299, period = 84.
GET /api/v1/grenke/calculate/299/84
→ { "data": 20340.14, "monthlyInstalment": 299, "period": 84 }