PHP code example of pristavu / laravel-anaf

1. Go to this page and download the library: Download pristavu/laravel-anaf library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

pristavu / laravel-anaf example snippets


use Pristavu\Anaf\Facades\Anaf;

Anaf::oauth(...);
Anaf::eFactura(...);
Anaf::taxPayer(...);

use function Pristavu\Anaf\anaf;

anaf()->oauth(...);
anaf()->eFactura(...);
anaf()->taxPayer(...);

[ 
  'oauth' => [
        'client_id' => env('ANAF_CLIENT_ID'),
        'client_secret' => env('ANAF_CLIENT_SECRET'),
        'redirect_uri' => env('ANAF_REDIRECT_URI', 'http://localhost/auth/anaf/callback'),
  ],
  ...
]

// you can pass the config values directly when initializing the oauth2 authenticator
$connector = Pristavu\Anaf\Facades\Anaf::oauth(
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    redirectUri: 'http://your-callback-url/auth/anaf/callback'
);

// In redirect controller method eg: '/auth/anaf/redirect'

public function __invoke(): RedirectResponse
{    
    $connector = Pristavu\Anaf\Facades\Anaf::oauth();            
    $state = Str::random(32);    
    // store the state in a scoped session (L12) or cache for later validation
    session()->cache()->put('anaf_oauth_state', $state, now()->addMinutes(5));
    
    $authorizationUrl = $connector->getAuthorizationUrl(
        state: $state, 
        additionalQueryParameters: ['token_content_type' => 'jwt']
    );
    
    return redirect()->away($authorizationUrl);
}

// In callback controller method eg: '/auth/anaf/callback'

public function __invoke(Request $request): ?RedirectResponse
{
    $code = $request->get('code');
    $state = $request->get('state');
    $expectedState = session()->cache()->get('anaf_oauth_state');
    
    if($request->has('error')){
        abort(400, 'Error from authorization server: '.$request->get('error'));
    }
    
    if (!$code || !$state) {
        abort(400, 'Invalid state or code');
    }
      
    try {
        $connector = Pristavu\Anaf\Facades\Anaf::oauth(); 
        $authenticator = $connector->getAccessToken($code, $state, $expectedState);
    } catch (\Exception $e) {
        abort(400, 'Failed to get access token: ' . $e->getMessage());
    }
    
  
    // you can store the serialized token in session or cache for later use
    session()->cache()->put('anaf_oauth_authenticator', $authenticator->serialize());
       
    // or store the token in database or other persistent storage
    Pristavu\Anaf\Models\AccessToken::create([
      'user_id' => auth()->id(),
      'provider' => Provider::ANAF,
      'access_token' => $authenticator->getAccessToken(),
      'refresh_token' => $authenticator->getRefreshToken(),
      'expires_at' => $authenticator->getExpiresAt(),
    ]);
    
    return redirect('/home'); // or wherever you want to redirect the user
}

// initialize the oauth2 authenticator
$connector = \Pristavu\Anaf\Facades\Anaf::oauth();
 
// get serialized authenticator from session or cache
$serialized = session()->cache()->get('anaf_oauth_authenticator');
$authenticator = \Saloon\Http\Auth\AccessTokenAuthenticator::unserialize($serialized);

// or retrieve it from database access token model
$accessToken = Pristavu\Anaf\Models\AccessToken::query()->where('user_id', auth()->id())->first();
$authenticator = $accessToken->authenticator();

if ($authenticator->hasExpired()) {
    // We'll refresh the access token which will return a new authenticator   
    $authenticator = $connector->refreshAccessToken($authenticator);    
    
    // Store the new token serialized in session or cache
    session()->cache()->put('anaf_oauth_authenticator', $authenticator->serialize());
    
    // or update the existing token model in database
    $accessToken->update([
        'token' => $authenticator->getAccessToken(),
        'refresh_token' => $authenticator->getRefreshToken(),
        'expires_at' => $authenticator->getExpiresAt(),
    ]);
}

// You need a valid access token to initialize the efactura connector

// retrieve access token from database or other storage
$accessToken = Pristavu\Anaf\Models\AccessToken::query()->where('user_id', auth()->id())->first()->access_token;

// initialize the efactura connector / client
$connector = Pristavu\Anaf\Facades\Anaf::eFactura(accessToken: $accessToken);

// Live (production) endpoint is used by default and is forced for certain operations eg: validateInvoice, convertInvoice
// Test mode can be used only for uploading, messages ,messagesPaginated, uploadInvoice, messageStatus, downloadInvoice
$connector->inTestMode();

// enable logging of requests and responses
// die will stop execution after logging the response
$connector->debug();  // $connector->debug(die: true); 

// Separate Debuggers
$connector->debugRequest(); // connector->debugRequest(die: true);
$connector->debugResponse(); // connector->debugResponse(die: true);


// If you want to disable caching for all operations you can do it like this:
$connector->disableCaching()->downloadInvoice(downloadId: $downloadId);
// or for invalidating cached content before downloading again:
$connector->invalidateCache()->downloadInvoice(downloadId: $downloadId);

// days - number of days between 1 and 60
// type can be one of: MessageType::{SENT/RECEIVED/ERROR/MESSAGE} -  if none provided, all types are retrieved

// eg: retrieve sent messages/invoices for cif 123456 from last 60 days
$response = $connector->messages(cif: 123456, days: 60, type: MessageType::SENT); // returns a MessagesResponse 

if($response->success){
    $response->messages->each(function(Message $message){
        // do something with $message
        $message->cif; // the cif associated with the message/invoice
        $message->upload_id; // the upload id for the message/invoice
        $message->download_id; // the download id for the message/invoice
        $message->type; // the type of message/invoice    
        $message->created_at; // Carbon instance of creation date
        $message->description; // description/details of the message/invoice
    });
} else {
    // handle error
    $response->error;

// eg: retrieve any type of messages/invoices for cif 123456 from last 10 days
$response = $connector->messages(cif: 123456, days: 10);


// period - interval must not exceed 60 days
// type can be one of: MessageType::{SENT/RECEIVED/ERROR/MESSAGE} -  if none provided, all types are retrieved

// retrieve sent messages/invoices for cif 123456 from last 60 days (paginated page 1)
$period = \Carbon\CarbonPeriod::create(now()->subDays(60), now());
$response = $connector->messagesPaginated(cif: 123456, period: $period, page: 1, type: MessageType::SENT); // returns a PaginatedMessagesResponse

if($response->success){
    $response->messages->each(function(Message $message){
        // do something with $message
        $message->cif; // the cif associated with the message/invoice
        $message->upload_id; // the upload id for the message/invoice
        $message->download_id; // the download id for the message/invoice
        $message->type; // the type of message/invoice    
        $message->created_at; // Carbon instance of creation date
        $message->description; // description/details of the message/invoice
    });
    
    // paginated response metadata
    $response->meta->total; // number of messages in selected period
    $response->meta->per_page; // messages per page (default 500)
    $response->meta->current_page; // current page number
    $response->meta->last_page; // last page number
} else {
    // handle error
    $response->error;
}


// or using the toPeriod method
// retrieve any messages/invoices  for cif 123456 from last 10 days (paginated page 2)
$period = now()->subDays(10)->toPeriod(now());
$response = $connector->messagesPaginated(cif: 123456, period: $period, page: 2);

$downloadId = 987654321; // the download_id of the message/invoice
$response = $connector->downloadInvoice(downloadId: $downloadId);

if($response->success){
    // save the zip content to a file
    Storage::disk('private')->put("/invoices/{$downloadId}.zip",$response->content);
    
    // optionally you can extract files from the zip message/invoice using the Extract helper without saving archive to disk    
    $message = Pristavu\Anaf\Support\Extract::from($response->content);
    
    // get xml invoice, signature and dto invoice objects
    $message->xmlInvoice();
    $message->signature();
    // dto invoice will be null if unzipping a non invoice message (eg: xml response error message)
    $message->dtoInvoice();       
}
else {
    // handle error
    $response->error; // array of download errors
}


$xml = Storage::disk('private')->get('invoices/12345/987654321.xml'); 
// optionally you can pass the full path to xml
$xml = Storage::disk('private')->path('invoices/12345/987654321.xml');

$response = $connector->validateInvoice(
    xml: $xml,
    standard: \Pristavu\Anaf\Enums\DocumentStandard::FCN, // optional, default is FACT1  
);

if($response->success){
   // do something with $response 
} else {
   // handle errors
   $response->errors; // array of validation errors
}


$xml = Storage::disk('private')->get('invoices/12345/987654321.xml');
// optionally you can pass the full path to xml
$xml = Storage::disk('private')->path('invoices/12345/987654321.xml');
$response = $connector->uploadInvoice(
    cif: 123456,
    xml: $xml,
    standard: \Pristavu\Anaf\Enums\XmlStandard::UBL, // optional, default is UBL
    isExternal:  false, // optional, default is false
    isSelfInvoice: false, // optional, default is false
    isLegalEnforcement: false // optional, default is false
);

if($response->success){
    // do something with $response
    $response->upload_id; // the upload id of the invoice
    
}
else {
    // handle error
    $response->error; 
}

$xml = Storage::disk('private')->get('invoices/12345/987654321.xml'); 
// optionally you can pass the full path to xml
$xml = Storage::disk('private')->path('invoices/12345/987654321.xml');
$response = $connector->convertInvoice(xml: $xml, standard: DocumentStandard::FACT1, withoutValidation: true);

if($response->success){
    // save the pdf content to a file
    Storage::disk('private')->put("/invoices/12345/987654321.pdf",$response->content);
}

$uploadId = 987654321; // the message id to check status for
$response = $connector->messageStatus(uploadId: $uploadId);

if($response->success){
   // do something with $response
   $response->status
   $response->download_id; // download id if available
}
else {
   // handle error
   $response->error;
}

// initialize the taxPayer connector / client
$connector = Pristavu\Anaf\Facades\Anaf::taxPayer();

$vatStatus = $connector->vatStatus(cif: 123456, date: '2023-12-31');

$balanceSheet = $connector->balanceSheet(cif: 123456, year: 2022);

use Saloon\Http\Faking\MockClient;
use Requests\Efactura\MessagesRequest;

test('my test', function () {
    // arrange
    $mockClient = new MockClient([
        MessagesRequest::class => MockResponse::make(
        body: [
            'mesaje' => [
                [
                    'data_creare' => 202508291153,
                    'cif' => 123456,
                    'id_solicitare' => 999999999,
                    'detalii' => 'Factura cu id_incarcare=999999999 emisa de cif_emitent=123456 pentru cif_beneficiar=987654',
                    'tip' => 'FACTURA TRIMISA',
                    'id' => 888888888,
                ],
                ...              
            ]
        ],
        status: 200
        ),
    ]);
    
    // act
    $connector = Anaf::eFactura(accessToken: 'TEST_TOKEN');
    $messages = $connector->withMockClient($mockClient)->messages(cif: 123456, days: 60);
    
    // assert
    expect($messages)->toBeArray();    
});
bash
php artisan vendor:publish --tag="anaf-migrations"
php artisan migrate
bash
php artisan vendor:publish --tag="anaf-config"