Installation:
composer require greenter/gre-api
Ensure your Laravel project meets the requirements: PHP 7.4+ and the curl extension.
Obtain SUNAT Credentials: Register in the SUNAT SOL portal to get:
client_idclient_secretFirst Use Case: Submit a Guía de Remisión (GRE) ZIP file to SUNAT:
use Greenter\Sunat\GRE\Api\AuthApi;
use Greenter\Sunat\GRE\Api\CpeApi;
use Greenter\Sunat\GRE\Model\CpeDocument;
use Greenter\Sunat\GRE\Model\CpeDocumentArchivo;
// 1. Get Token
$authApi = new AuthApi(new \GuzzleHttp\Client());
$token = $authApi->getToken(
'password', 'https://api-cpe.sunat.gob.pe',
'YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET',
'RUC123456789USER', 'YOUR_SOL_PASSWORD'
)->getAccessToken();
// 2. Submit GRE
$config = \Greenter\Sunat\GRE\Configuration::getDefaultConfiguration()
->setAccessToken($token);
$cpeApi = new CpeApi(new \GuzzleHttp\Client(), $config);
$greZip = file_get_contents('path/to/your/gre-file.zip');
$doc = (new CpeDocument())
->setArchivo((new CpeDocumentArchivo())
->setNomArchivo('RRRRRRRRRRR-TT-SSSS-NNNNNNNN.zip')
->setArcGreZip(base64_encode($greZip))
->setHashZip(hash('sha256', $greZip))
);
$response = $cpeApi->enviarCpe('RRRRRRRRRRR-TT-SSSS-NNNNNNNN', $doc);
$ticket = $response->getNumTicket(); // Store for later status checks
Check Submission Status:
$status = $cpeApi->consultarEnvio($ticket);
if ($status->getCodRespuesta() === '0') {
// Success!
}
enviarCpe() and consultarEnvio().CpeDocument (structure for GRE submissions).StatusResponse (interpreting SUNAT’s response codes like 98, 99, 0).// services.php
$app->singleton(\Greenter\Sunat\GRE\Api\AuthApi::class, function ($app) {
return new AuthApi(new \GuzzleHttp\Client());
});
// TokenService.php
class TokenService {
public function getToken(): string {
$token = cache('sunat_token');
if ($token) return $token;
$authApi = app(AuthApi::class);
$token = $authApi->getToken(
'password', 'https://api-cpe.sunat.gob.pe',
config('sunat.client_id'), config('sunat.client_secret'),
config('sunat.ruc_user'), config('sunat.sol_password')
)->getAccessToken();
cache(['sunat_token' => $token], now()->addHours(1)); // Cache for 1 hour
return $token;
}
}
use Greenter\Sunat\GRE\Api\CpeApi;
use Greenter\Sunat\GRE\Model\CpeDocument;
class GreSubmitter {
public function submit(string $filePath, string $filename): string {
$greZip = file_get_contents($filePath);
$hash = hash('sha256', $greZip);
$doc = (new CpeDocument())
->setArchivo((new CpeDocumentArchivo())
->setNomArchivo("{$filename}.zip")
->setArcGreZip(base64_encode($greZip))
->setHashZip($hash)
);
$cpeApi = new CpeApi(new \GuzzleHttp\Client(), $this->getConfig());
$response = $cpeApi->enviarCpe($filename, $doc);
return $response->getNumTicket();
}
private function getConfig(): \Greenter\Sunat\GRE\Configuration {
return \Greenter\Sunat\GRE\Configuration::getDefaultConfiguration()
->setAccessToken(app(TokenService::class)->getToken());
}
}
98 or 99) with exponential backoff.class GreStatusChecker {
public function check(string $ticket): bool {
$cpeApi = new CpeApi(new \GuzzleHttp\Client(), $this->getConfig());
$status = $cpeApi->consultarEnvio($ticket);
if ($status->getCodRespuesta() === '0') {
return true;
} elseif (in_array($status->getCodRespuesta(), ['98', '99'])) {
// Retry logic (e.g., using Laravel Queues)
dispatch(new CheckGreStatusJob($ticket))->delay(now()->addMinutes(5));
return false;
}
return false;
}
}
try {
$response = $cpeApi->enviarCpe($filename, $doc);
} catch (\Greenter\Sunat\GRE\ApiException $e) {
$error = json_decode($e->getResponseBody(), true);
if (isset($error['error'])) {
throw new \RuntimeException(
"SUNAT Error: {$error['error']['msg']} (Code: {$error['error']['cod']})",
$e->getCode()
);
}
throw $e;
}
Configuration:
Store SUNAT credentials in Laravel’s .env:
SUNAT_CLIENT_ID=your_client_id
SUNAT_CLIENT_SECRET=your_client_secret
SUNAT_RUC_USER=RUC123456789USER
SUNAT_SOL_PASSWORD=your_password
Bind them in config/sunat.php:
return [
'client_id' => env('SUNAT_CLIENT_ID'),
'client_secret' => env('SUNAT_CLIENT_SECRET'),
'ruc_user' => env('SUNAT_RUC_USER'),
'sol_password' => env('SUNAT_SOL_PASSWORD'),
];
Testing:
GuzzleHttp\HandlerStack) to simulate SUNAT responses in unit tests.$handler = HandlerStack::create();
$handler->push(Middleware::tap(function ($request) {
if ($request->getUri()->getPath() === '/clientessol/oauth2/token') {
return Response::json([
'access_token' => 'mock_token',
'token_type' => 'Bearer',
'expires_in' => 3600
]);
}
}));
$client = new \GuzzleHttp\Client(['handler' => $handler]);
$authApi = new AuthApi($client);
Logging: Log SUNAT responses for auditing:
\Log::info('SUNAT GRE Submission', [
'ticket' => $ticket,
'status' => $status->getCodRespuesta(),
'error' => $status->getError()?->getDesError(),
]);
401 Unauthorized errors gracefully.$app->middleware(function ($request, $
How can I help you explore Laravel packages today?