Offene Rechnungen bis zum Ende blaettern (Codex P2)
parent
0944de7cfa
commit
d6fd760a8b
|
|
@ -586,17 +586,34 @@ class HttpStripeClient implements StripeClient
|
|||
|
||||
public function openInvoices(string $customerId): array
|
||||
{
|
||||
$invoices = $this->request()
|
||||
->get($this->url('invoices'), [
|
||||
'customer' => $customerId,
|
||||
// Finalisiert und unbezahlt. Ohne den Filter käme die ganze
|
||||
// Historie zurück, und neben längst beglichenen Rechnungen
|
||||
// stünde ein Bezahlknopf.
|
||||
'status' => 'open',
|
||||
'limit' => 100,
|
||||
])
|
||||
->throw()
|
||||
->json('data', []);
|
||||
$invoices = [];
|
||||
$after = null;
|
||||
|
||||
// Bis zum Ende geblättert, wie invoiceLines() darunter. Stripe deckelt
|
||||
// eine Seite bei hundert, und dieses Projekt hat die abgeschnittene
|
||||
// erste Seite schon einmal bezahlt: der alte Hetzner-Client fand einen
|
||||
// Eintrag jenseits der hundert nicht und legte einen zweiten an. Hier
|
||||
// ginge es ums Geld — eine ältere offene Rechnung auf Seite zwei wäre
|
||||
// für den Kunden nicht bezahlbar, weil sie ihm nie angezeigt wird.
|
||||
// (Codex-Review 2026-07-31, P2.)
|
||||
do {
|
||||
$page = $this->request()
|
||||
->get($this->url('invoices'), array_filter([
|
||||
'customer' => $customerId,
|
||||
// Finalisiert und unbezahlt. Ohne den Filter käme die ganze
|
||||
// Historie zurück, und neben längst beglichenen Rechnungen
|
||||
// stünde ein Bezahlknopf.
|
||||
'status' => 'open',
|
||||
'limit' => 100,
|
||||
'starting_after' => $after,
|
||||
]))
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
$data = $page['data'] ?? [];
|
||||
$invoices = array_merge($invoices, $data);
|
||||
$after = $data === [] ? null : (string) end($data)['id'];
|
||||
} while (($page['has_more'] ?? false) && $after !== null);
|
||||
|
||||
return array_map(fn (array $invoice) => [
|
||||
'id' => (string) $invoice['id'],
|
||||
|
|
|
|||
|
|
@ -62,3 +62,30 @@ it('reports a successful charge', function () {
|
|||
|
||||
expect(app(HttpStripeClient::class)->payInvoice('in_1')['paid'])->toBeTrue();
|
||||
});
|
||||
|
||||
it('pages to the end instead of stopping at the first hundred', function () {
|
||||
// Dieselbe Falle, die diesem Projekt schon einmal teuer kam: der alte
|
||||
// Hetzner-Client las nur die erste Seite und legte deshalb einen zweiten
|
||||
// A-Eintrag an. `invoiceLines()` direkt daneben blättert längst bis zum
|
||||
// Ende. Hier gilt es dem Geld: eine ältere offene Rechnung, die auf Seite
|
||||
// zwei liegt, wäre für den Kunden schlicht nicht bezahlbar.
|
||||
$seite = fn (int $von, bool $weiter) => Http::response([
|
||||
'has_more' => $weiter,
|
||||
'data' => collect(range($von, $von + 99))
|
||||
->map(fn (int $i) => [
|
||||
'id' => 'in_'.$i, 'number' => 'R-'.$i,
|
||||
'amount_due' => 100, 'currency' => 'eur', 'created' => 1750000000,
|
||||
])->all(),
|
||||
]);
|
||||
|
||||
// Nach der Seite gefragt wird über `starting_after`, nicht über eine
|
||||
// Seitenzahl — daran wird hier auch unterschieden, sonst beantwortete die
|
||||
// Attrappe beide Aufrufe gleich und der Test liefe gegen den Fehler grün.
|
||||
Http::fake(function ($request) use ($seite) {
|
||||
parse_str((string) parse_url($request->url(), PHP_URL_QUERY), $query);
|
||||
|
||||
return isset($query['starting_after']) ? $seite(101, false) : $seite(1, true);
|
||||
});
|
||||
|
||||
expect(app(HttpStripeClient::class)->openInvoices('cus_42'))->toHaveCount(200);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue