isConfigured()) { $this->error('STRIPE_SECRET ist nicht gesetzt.'); return self::FAILURE; } $dryRun = (bool) $this->option('dry-run'); $limit = (int) $this->option('limit'); $refresh = (bool) $this->option('refresh'); $query = ShoppingUser::query()->orderBy('id'); if ($refresh) { // Bereits verknüpfte Kunden neu an Stripe schreiben. $query->whereNotNull('stripe_customer_id'); } else { // Noch nicht verknüpfte Kunden, für die Stripe selbst einen // (namenlosen) Customer angelegt hat. $query->whereNull('stripe_customer_id') ->whereHas('shopping_orders.shopping_payments', function ($q) { $q->where('payment_provider', 'stripe')->whereNotNull('stripe_customer_id'); }); } $linked = 0; $updated = 0; $failed = 0; $query->chunkById(100, function ($shoppingUsers) use (&$linked, &$updated, &$failed, $customerService, $dryRun, $limit, $refresh): bool { foreach ($shoppingUsers as $shoppingUser) { $customerId = $refresh ? $shoppingUser->stripe_customer_id : $this->existingCustomerIdFor($shoppingUser); if ($customerId === null) { continue; } $this->line(sprintf( '#%d %s → %s', $shoppingUser->id, StripeCustomerService::customerNameFor($shoppingUser) ?? '(ohne Namen)', $customerId, )); if ($dryRun) { $linked++; continue; } try { if (! $refresh) { $shoppingUser->forceFill(['stripe_customer_id' => $customerId])->save(); } $linked++; $customerService->updateCustomer($customerId, $customerService->customerPayloadFor($shoppingUser)); $updated++; } catch (\Throwable $e) { $failed++; $this->error(sprintf('Kunde #%d (%s): %s', $shoppingUser->id, $customerId, $e->getMessage())); } if ($limit > 0 && $linked >= $limit) { return false; } } return true; }); $this->info(sprintf( '%s: %d %s, %d bei Stripe aktualisiert, %d fehlgeschlagen.', $dryRun ? 'Probelauf' : 'Fertig', $linked, $refresh ? 'geprüft' : 'verknüpft', $updated, $failed, )); return $failed > 0 ? self::FAILURE : self::SUCCESS; } /** * Die Stripe-Customer-ID, die Stripe bei einer früheren Zahlung dieses * Kunden selbst angelegt hat (jüngste zuerst). */ private function existingCustomerIdFor(ShoppingUser $shoppingUser): ?string { $orderIds = $shoppingUser->shopping_orders()->pluck('id'); if ($orderIds->isEmpty()) { return null; } return ShoppingPayment::whereIn('shopping_order_id', $orderIds) ->whereNotNull('stripe_customer_id') ->latest('id') ->value('stripe_customer_id'); } }