}> */ public function activePricesFor(string $productId): array; /** * Every active Product on the account, with its metadata. * * The other half of the orphan question, and the more consequential one. A * run that created a Product and died before storing its id leaves an orphan * exactly as a Price does — but a second Product is worse than a duplicate * Price, because activePricesFor() is then asked about the wrong one and the * Price recognition goes blind for that whole family. * * The WHOLE active list, with no metadata filter: Stripe cannot search * metadata, and an account holds a handful of Products. Which of them is * ours is App\Services\Billing\AdoptStripeProduct's decision. * * @return array}> */ public function activeProducts(): array; /** * Write metadata onto a Price that already exists. * * Metadata is one of the few fields a Stripe Price lets you change — the * same property activatePrice() rests on, and the reason the metadata format * is NOT part of a Price's identity. An adopted orphan is brought up to * today's metadata rather than replaced, which is exactly what was done by * hand after 2026-07-29 and keeps Stripe's own dashboard readable. */ public function updatePriceMetadata(string $priceId, array $metadata): void; /** Stop a Price being offered. The Price itself stays, as Stripe requires. */ public function archivePrice(string $priceId): void; /** * Offer an archived Price again. * * The other half of archivePrice(), and it was missing. A rate that moves and * then moves back finds the Price for the figure it has come back to already * in our register: the sync brings that row back rather than minting a second * Price for one amount, which is right — but the Price was archived AT STRIPE, * and only this makes it sellable again. Without it the catalogue pointed at * an inactive Price and the failure was a customer meeting a refused checkout. */ public function activatePrice(string $priceId): void; /** * Move a subscription onto another Price. * * The ITEM is what carries a price, not the subscription, so the swap needs * both ids. `$prorationBehaviour` is one of the constants above and is * always passed explicitly: Stripe's own default settles a proration * silently, and a default deciding what a customer is charged is exactly * the kind of thing that must be written down at the call site. */ public function updateSubscriptionPrice( string $subscriptionId, string $itemId, string $priceId, string $prorationBehaviour, ): void; /** * The id of the item a subscription bills its PACKAGE through, or null if it * has none. * * For contracts opened before the item id was stored: Stripe knows it, we * did not ask, and asking once is cheaper than being unable to move them. * The first item is the package, because that is the one the checkout * created; modules are added afterwards and carry their own ids, which are * stored on the bookings themselves. */ public function subscriptionItemId(string $subscriptionId): ?string; /** * Put a module on the subscription as an item of its own, and return the * item's id. * * `$prorationBehaviour` is passed explicitly for the same reason as above. * A module booked in the middle of a term is charged PRORATE_IMMEDIATELY: * Stripe works out the days that are left, raises an invoice for exactly * that and takes the money there and then — which is the invoice the * customer is owed a document for. */ public function addSubscriptionItem( string $subscriptionId, string $priceId, int $quantity, string $prorationBehaviour, ?string $idempotencyKey = null, ): string; /** * Change how many of a module the subscription bills for. * * A pack is sold by the unit, so a second one is a quantity of two on one * item rather than a second item at the same price — two items on one price * is a thing Stripe allows and nothing could later tell apart. */ public function updateSubscriptionItemQuantity( string $itemId, int $quantity, string $prorationBehaviour, ): void; /** * Take a module off the subscription. * * Removed with PRORATE_NONE by everything that calls it: a cancelled module * is kept until the end of the term the customer has already paid for and * earns no credit, so nothing is to be settled — only the next cycle is * smaller. */ public function removeSubscriptionItem(string $itemId, string $prorationBehaviour): void; /** * Tell Stripe to stop charging for a contract that is over. * * The one call this platform could not make. Nothing here cancelled a Stripe * subscription at all: a customer's cancellation wrote a date on the * instance, a withdrawal marked the contract cancelled and sent the money * back, and in both cases Stripe went on taking the next month, and the next, * for a service that had ended — and every one of those payments arrived as * `invoice.paid` and drew a number out of the gapless Austrian series. * * `$when` is one of the CANCEL_* constants and is always passed explicitly, * for the same reason `$prorationBehaviour` is above: the two are different * promises to the customer, and which one a caller means has to be legible at * the call site rather than inherited from a default written down here. * * `$idempotencyKey` because a retry after a timeout cannot tell whether the * first attempt landed. Cancelling twice is harmless at Stripe, but the same * key makes the retry provably a no-op instead of merely a likely one. */ public function cancelSubscription( string $subscriptionId, string $when, ?string $idempotencyKey = null, ): void; /** * Send money back, against the payment that took it. * * The only outgoing money movement this platform makes, and it exists * because a consumer who withdraws within fourteen days is owed one — see * App\Actions\WithdrawContract. `$amountCents` is the whole of what was * taken: the owner has decided a withdrawing consumer gets everything back, * not the amount less the days the cloud ran. It stays an explicit figure * rather than "refund the payment" because a Storno states an amount and the * money has to be the amount the Storno states. * * `$paymentReference` is a PaymentIntent (`pi_…`) or a Charge (`ch_…`) — * whichever Stripe reported for the payment. Both are accepted rather than * one normalised: which of the two an account produces depends on the API * version it is pinned to, and a refund that silently does nothing because * the wrong field was read is the worst possible failure here. * * `$idempotencyKey` is not optional in practice. A refund is the one call * whose retry after a timeout would send the customer's money twice. * * @return string the refund's id */ public function refund( string $paymentReference, ?int $amountCents = null, ?string $idempotencyKey = null, ): string; /** * The payment behind a Stripe invoice, or null if it has none. * * A subscription checkout charges through an invoice, so the invoice id is * usually all we have written down — and a refund cannot be issued against * an invoice. This is the one hop between the two. */ public function invoicePaymentReference(string $invoiceId): ?string; /** * Every line on a Stripe invoice, for the document we owe it. * * The webhook payload carries the lines already; this is for the invoice * that has more of them than Stripe puts in an event. A document missing a * line that was charged is worse than one nobody sent, so the complete list * is worth an extra call. * * @return array> */ public function invoiceLines(string $invoiceId): array; /** * Ein SetupIntent, mit dem der Browser eine Karte hinterlegt. * * `usage: off_session`, weil die nächste Abbuchung ohne den Kunden läuft: * ohne dieses Feld verlangt Stripe bei jeder Verlängerung eine Freigabe, * die zu dem Zeitpunkt niemand geben kann. * * @return array{id: string, client_secret: string} */ public function createSetupIntent(string $customerId): array; /** * Das Zahlungsmittel, mit dem künftig abgebucht wird. * * Eine Karte zu hinterlegen genügt nicht — ohne diesen Schritt bucht * Stripe weiter mit der alten ab. */ public function setDefaultPaymentMethod(string $customerId, string $paymentMethodId): void; /** * Die hinterlegte Karte, oder null. * * @return array{id: string, brand: string, last4: string, exp_month: int, exp_year: int}|null */ public function defaultPaymentMethod(string $customerId): ?array; /** * Die finalisierten, unbezahlten Rechnungen dieses Kunden. * * @return array */ public function openInvoices(string $customerId): array; /** * Eine offene Rechnung mit dem hinterlegten Zahlungsmittel einziehen. * * Wirft NICHT bei einer abgelehnten Karte: das ist die häufigste Antwort * überhaupt und gehört auf die Seite, nicht in einen 500er. * * @return array{paid: bool, status: string, failure: ?string} */ public function payInvoice(string $invoiceId): array; /** * Eine einmalige Rechnung über einen Betrag, die Stripe selbst einzieht. * * Für Mahngebühren: eine gescheiterte Abo-Rechnung ist bereits finalisiert * und nimmt keinen Posten mehr auf. Die Gebühr wird deshalb eine eigene * Rechnung daneben — keine Stornierung, keine Rechnungsnummer wird * angefasst, und jede Mahnstufe kann ihre eigene bekommen. * * @return string die Stripe-ID der Rechnung */ public function createFeeInvoice( string $customerId, int $amountCents, string $currency, string $description, ?string $idempotencyKey = null, ): string; }