SDK Java - com.itzenata:efact-java
SDK officiel EFact pour Java 21+. Conçu pour Spring Boot mais utilisable dans tout projet Java.
Installation
Maven
pom.xml
<dependency>
<groupId>com.itzenata</groupId>
<artifactId>efact-java</artifactId>
<version>0.1.0</version>
</dependency>Gradle
build.gradle
implementation 'com.itzenata:efact-java:0.1.0'Initialisation
Basique
Basic Initialization
import com.itzenata.efact.Itzenata;
Itzenata itz = new Itzenata(System.getenv("ITZENATA_SECRET_KEY"));Configuration avancée
Advanced Configuration
Itzenata itz = Itzenata.builder()
.apiKey(System.getenv("ITZENATA_SECRET_KEY"))
.apiVersion("v1")
.baseUrl("https://api.efact.itzenata.com")
.timeout(Duration.ofSeconds(30))
.maxRetries(2)
.build();Injection Spring (recommandé)
Spring Configuration
@Configuration
public class EFactConfig {
@Value("${itzenata.secret-key}")
private String secretKey;
@Bean
public Itzenata itzenata() {
return new Itzenata(secretKey);
}
}application.yml
# application.yml
itzenata:
secret-key: ${ITZENATA_SECRET_KEY}invoices().create(params)
Create Invoice
import com.itzenata.efact.model.Invoice;
import com.itzenata.efact.param.InvoiceCreateParams;
import com.itzenata.efact.param.InvoiceCreateParams.Customer;
import com.itzenata.efact.param.InvoiceCreateParams.Line;
@Autowired
private Itzenata itz;
public String createInvoice() {
Invoice invoice = itz.invoices().create(
InvoiceCreateParams.builder()
.amount(24000L)
.currency("MAD")
.customer(Customer.builder()
.ice("002345678901234")
.name("Société Exemple SARL")
.address("12 Rue Hassan II, Casablanca")
.email("finance@exemple.ma")
.build())
.addLine(Line.builder()
.description("Licence logiciel annuelle")
.quantity(2)
.unitPrice(10000L)
.taxRate(20)
.build())
.addLine(Line.builder()
.description("Formation (exonéré TVA)")
.quantity(1)
.unitPrice(4000L)
.taxRate(0)
.build())
.putMetadata("internal_id", "INV-2026-043")
.putMetadata("client_ref", "CLI-789")
.build()
);
return invoice.getClientSecret(); // à retourner au frontend
}invoices().retrieve(id)
Retrieve Invoice
Invoice invoice = itz.invoices().retrieve("inv_01H8XYZABC");
switch (invoice.getStatus()) {
case ACCEPTED -> {
String dgiId = invoice.getDgiId();
String xmlUrl = invoice.getXmlUrl();
String pdfUrl = invoice.getPdfUrl();
invoiceRepository.markAsAccepted(
invoice.getMetadata().get("internal_id"),
dgiId, xmlUrl, pdfUrl
);
}
case REJECTED -> {
String reason = invoice.getRejectionReason();
notificationService.alertAccountant(reason);
}
}invoices().list(params?)
List Invoices
import com.itzenata.efact.param.InvoiceListParams;
import com.itzenata.efact.model.InvoiceCollection;
InvoiceCollection collection = itz.invoices().list(
InvoiceListParams.builder()
.status("accepted")
.limit(50L)
.build()
);
for (Invoice invoice : collection.getData()) {
System.out.println(invoice.getId() + " - " + invoice.getMetadata().get("internal_id"));
}
// Pagination
if (collection.getHasMore()) {
InvoiceCollection nextPage = itz.invoices().list(
InvoiceListParams.builder()
.startingAfter(collection.getNextCursor())
.build()
);
}webhooks().constructEvent(payload, signature, secret)
Webhook Handler
import com.itzenata.efact.model.Event;
import com.itzenata.efact.exception.SignatureVerificationException;
@PostMapping("/webhooks/efact")
public ResponseEntity<String> handleWebhook(
@RequestBody String payload,
@RequestHeader("Itzenata-Signature") String signature) {
Event event;
try {
event = itz.webhooks().constructEvent(
payload,
signature,
System.getenv("ITZENATA_WEBHOOK_SECRET")
);
} catch (SignatureVerificationException e) {
log.warn("Webhook signature invalide : {}", e.getMessage());
return ResponseEntity.badRequest().body("Invalid signature");
}
switch (event.getType()) {
case "invoice.accepted" -> {
var data = event.getDataAs(InvoiceAcceptedData.class);
invoiceRepository.markAsAccepted(
data.getMetadata().get("internal_id"),
data.getDgiId()
);
}
case "invoice.rejected" -> {
var data = event.getDataAs(InvoiceRejectedData.class);
notificationService.notifyAccountant(data.getRejectionReason());
}
default -> log.info("Event non géré : {}", event.getType());
}
return ResponseEntity.ok("ok");
}Gestion des exceptions
Exception Handling
import com.itzenata.efact.exception.*;
try {
Invoice invoice = itz.invoices().create(params);
} catch (AuthenticationException e) {
// 401 - clé API invalide
log.error("Clé API EFact invalide", e);
} catch (InvalidRequestException e) {
// 400 / 422 - payload invalide
log.error("Champ invalide : {} - {}", e.getParam(), e.getMessage());
} catch (ApiException e) {
// 5xx - erreur serveur EFact
log.error("Erreur serveur EFact : {}", e.getStatus(), e);
} catch (EFactException e) {
// Base exception pour tous les cas
log.error("Erreur EFact", e);
}Hiérarchie des exceptions
| Exception | Code HTTP | Description |
|---|---|---|
AuthenticationException | 401 | Clé API invalide ou manquante |
PermissionException | 403 | Scope insuffisant |
ResourceNotFoundException | 404 | Ressource introuvable |
InvalidRequestException | 400 / 422 | Payload invalide |
IdempotencyException | 409 | Conflit clé d'idempotency |
RateLimitException | 429 | Rate limit dépassé |
ApiException | 5xx | Erreur côté EFact |
NetworkException | - | Timeout ou erreur réseau |
Exemple contrôleur Spring Boot complet
InvoiceController.java
@RestController
@RequestMapping("/api/invoices")
@RequiredArgsConstructor
public class InvoiceController {
private final Itzenata itz;
private final InvoiceService invoiceService;
@PostMapping
public ResponseEntity<Map<String, String>> createInvoice(
@RequestBody @Valid CreateInvoiceRequest req) {
Invoice invoice = itz.invoices().create(
InvoiceCreateParams.builder()
.amount(req.getAmountCentimes())
.currency("MAD")
.customer(Customer.builder()
.ice(req.getCustomerIce())
.name(req.getCustomerName())
.build())
.addLines(req.getLines().stream()
.map(l -> Line.builder()
.description(l.getDescription())
.quantity(l.getQuantity())
.unitPrice(l.getUnitPriceCentimes())
.taxRate(l.getTaxRate())
.build())
.toList())
.putMetadata("internal_id", req.getInvoiceId())
.build()
);
// Sauvegarder l'ID EFact dans votre base
invoiceService.saveEFactId(req.getInvoiceId(), invoice.getId());
return ResponseEntity.ok(Map.of("clientSecret", invoice.getClientSecret()));
}
}Compatibilité
| Environnement | Support |
|---|---|
| Java 21+ | Supporté |
| Spring Boot 3.x | Supporté |
| Spring Boot 2.7.x | Supporté |
| Quarkus | Supporté |
| Micronaut | Supporté |
| Jakarta EE 10 | Supporté |