110 lines
4.2 KiB
PHP
110 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Stripe;
|
|
|
|
/**
|
|
* The bit of Stripe we drive ourselves: the catalogue.
|
|
*
|
|
* Everything else about recurring billing — retries, dunning, off-session SCA,
|
|
* invoice numbering — is Stripe's job, and we learn about it through webhooks.
|
|
* Our side owns capability, because Stripe does not know how big the VM should
|
|
* be.
|
|
*/
|
|
interface StripeClient
|
|
{
|
|
/**
|
|
* Settle an upgrade at once: Stripe works out the days left in the term,
|
|
* raises its own invoice for the difference and charges it there and then.
|
|
*
|
|
* Chosen over letting the proration ride on the next cycle invoice because
|
|
* the cycle invoice is the one WE issue a document for, from the contract's
|
|
* frozen price. A renewal invoice that said one figure while Stripe took
|
|
* that figure plus a proration would be a document that does not match the
|
|
* money — see App\Actions\MoveStripeSubscriptionPrice.
|
|
*/
|
|
public const PRORATE_IMMEDIATELY = 'always_invoice';
|
|
|
|
/**
|
|
* Change the price and settle nothing. What the next cycle is billed at
|
|
* changes; no credit and no charge is raised for the switch itself.
|
|
*/
|
|
public const PRORATE_NONE = 'none';
|
|
|
|
/** Whether an account is actually configured. */
|
|
public function isConfigured(): bool;
|
|
|
|
/**
|
|
* Open a hosted checkout for one plan Price and return the URL to send the
|
|
* customer to.
|
|
*
|
|
* Hosted, not a payment form of ours: card details, 3-D Secure and every
|
|
* local payment method are Stripe's problem, and the moment a page of ours
|
|
* touches a card number the whole installation is in PCI scope.
|
|
*
|
|
* `$metadata` is what comes back on the webhook and decides what is built —
|
|
* plan, plan version and datacenter. It is set on the SESSION and on the
|
|
* subscription: the session's copy is what StripeWebhookController reads,
|
|
* the subscription's is what any later billing event carries.
|
|
*/
|
|
public function createCheckoutSession(
|
|
string $priceId,
|
|
string $successUrl,
|
|
string $cancelUrl,
|
|
array $metadata = [],
|
|
?string $customerEmail = null,
|
|
?string $idempotencyKey = null,
|
|
): string;
|
|
|
|
/**
|
|
* Create a Product for a plan family; returns its Stripe id.
|
|
*
|
|
* `$idempotencyKey` is Stripe's own guard: a crash between their creating
|
|
* the object and our storing the id would otherwise mint a second one on
|
|
* the next run, and there is no way to tell duplicates apart afterwards.
|
|
*/
|
|
public function createProduct(string $name, array $metadata = [], ?string $idempotencyKey = null): string;
|
|
|
|
/**
|
|
* Create a Price. Stripe Prices are immutable and carry their own interval,
|
|
* which is why one exists per plan version AND term.
|
|
*/
|
|
public function createPrice(
|
|
string $productId,
|
|
int $amountCents,
|
|
string $currency,
|
|
string $interval,
|
|
array $metadata = [],
|
|
?string $idempotencyKey = null,
|
|
): string;
|
|
|
|
/** Stop a Price being offered. The Price itself stays, as Stripe requires. */
|
|
public function archivePrice(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 single item on a subscription, 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.
|
|
* One item, because one contract bills one package — a subscription with
|
|
* several would need a rule for which of them the package is, and there is
|
|
* no such thing to build one from.
|
|
*/
|
|
public function subscriptionItemId(string $subscriptionId): ?string;
|
|
}
|