Response
```java
package com.acme.billing;
import com.acme.common.ApiResponse;
import com.acme.common.PageMeta;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/billing")
public class InvoiceController {
private static final int MAX_PAGE_SIZE = 100;
private final InvoiceService invoiceService;
public InvoiceController(InvoiceService invoiceService) {
this.invoiceService = invoiceService;
}
@GetMapping("/invoices")
public ApiResponse<?> listInvoices(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
int pageNo = Math.max(page, 1);
int pageSize = Math.clamp(size, 1, MAX_PAGE_SIZE);
InvoiceQueryResult result = invoiceService.listInvoices(pageNo, pageSize);
return ApiResponse.ok(result.items(), PageMeta.of(pageNo, pageSize, result.total()));
}
}
```
Conventions applied: constructor injection (no Lombok/field injection), and the response is wrapped in `ApiResponse.ok(items, PageMeta.of(...))` with 1-based page clamped to `[1, 100]` via `Math.clamp`, per house rules. `ApiResponse`/`PageMeta` are assumed to be existing shared types, same as `InvoiceService`.