Fasáda Purchases
RoundlyConsulting\Purchases\Facades\Purchases je odporúčaný vstupný bod. result() overí a dekóduje požiadavku do výsledku ProviderResult nezávislého od poskytovateľa a nič nezapisuje; handle() urobí to isté, potom uloží model a spustí udalosti životného cyklu. sync() a replay() uložia výsledky, ktoré neprišli ako webhook, a for($owner) obmedzí dopyty na jedného vlastníka:
use RoundlyConsulting\Purchases\Facades\Purchases;
$result = Purchases::result('stripe', $request); // verify + decode, no writes
$result->type(); // ResultType::Subscription
$result->status(); // Status enum
$result->providerId(); // provider-side id
$model = Purchases::handle('stripe', $request); // verify + decode + persist + events
$model = Purchases::sync($result); // persist a result you already hold (?Model)
$model = Purchases::replay($notification); // re-run one audited notification (?Model)
Purchases::for($user)->subscribedTo('pro'); // one owner's purchases and subscriptions
Purchases::provider('google'); // Provider (throws UnknownProviderException if absent)
Purchases::providers(); // Collection<string, Provider>
Purchases::has('apple'); // bool
Purchases::ids(); // ['apple', 'google', 'stripe']Prehľad metód
| Metóda | Vracia | Čo robí |
|---|---|---|
result($id, $request) | ProviderResult | Overí a dekóduje požiadavku — nič nezapisuje. |
handle($id, $request) | Model | Overí, dekóduje, uloží a spustí udalosti; rešpektuje queue.enabled. |
sync(ProviderResult $result) | ?Model | Uloží výsledok, ktorý už máte, s auditom ako pri webhooku, vždy synchrónne. |
replay(PurchaseNotification|int $notification) | ?Model | Znova spracuje jednu uloženú auditnú notifikáciu a označí ju ako spracovanú. |
for(Model $owner) | OwnerPurchases | Nákupy a predplatné jedného vlastníka — len čítanie. |
provider($id) | Provider | Vráti poskytovateľa; ak nie je nakonfigurovaný, UnknownProviderException. |
providers() | Collection<string, Provider> | Všetci poskytovatelia podľa id. |
has($id) | bool | Či je poskytovateľ zaregistrovaný. |
ids() | list<string> | Identifikátory registrovaných poskytovateľov. |
fake() | PurchasesFake | Nasadí zaznamenávajúci spy — pozrite Testovanie. |
Čo vracia handle()
- Vždy — požiadavka sa najprv synchrónne overí; neplatný podpis vyhodí VerificationException ešte pred akýmkoľvek zápisom.
- Fronta vypnutá (predvolene) — uložený Purchase, Subscription alebo PurchaseRefund.
- Fronta zapnutá — auditný záznam PurchaseNotification, ktorý opisuje, čo sa zaradilo do fronty, alebo neuložený zástupný objekt, ak je auditný log vypnutý.
- Informatívny výsledok (zmena preferencie obnovy v Apple, odklad v Google, TEST, nenamapovaná udalosť Stripe …) — nič sa neuloží, žiadna udalosť sa nespustí a vráti sa spracovaná auditná notifikácia.
Uloženie výsledku, ktorý už máte
sync() uloží ProviderResult presne tak ako webhook — s auditom, premietnutý do Purchase, Subscription alebo PurchaseRefund, so spustenými udalosťami a auditným záznamom označeným ako spracovaný — a vždy synchrónne. Použite ho pre účtenku, ktorú vaša aplikácia overila sama, alebo pre GenericResult zostavený pri dopĺňaní histórie. Nič znova neoveruje, preto mu nikdy neodovzdávajte neoverené dáta z klienta; auditný záznam to uvádza pravdivo — signature_verified je false a origin je NotificationOrigin::Host:
use RoundlyConsulting\Purchases\Enum\ResultType;
use RoundlyConsulting\Purchases\Enum\Status;
use RoundlyConsulting\Purchases\Facades\Purchases;
use RoundlyConsulting\Purchases\Results\GenericResult;
// A Google Play purchase token your app sent up, checked against the Play Developer API
// (product() throws VerificationException unless the purchase is in a purchased state).
$purchase = Purchases::provider('google')->product($productId, $token);
$model = Purchases::sync(new GenericResult( // ?Model — null for an informational result
provider: 'google',
type: ResultType::Purchase,
providerId: $purchase->orderId ?? $token, // the key Google's own notifications use
status: Status::Completed,
transactionId: $purchase->orderId,
name: $productId,
productId: $productId,
));Ak uloženie zlyhá (napríklad chybou listenera), auditný záznam zostane nespracovaný, takže ho replay() môže dokončiť neskôr.
Opätovné spracovanie jednej notifikácie
replay() obnoví výsledok z uloženej PurchaseNotification, znova ho uloží a označí ju ako spracovanú. Ukladanie je zoradené podľa času udalosti (pozrite Udalosti), takže opätovné spracovanie notifikácie — či celého logu — nikdy nevráti nákup ani predplatné do staršieho stavu a nikdy nespustí udalosť životného cyklu pre niečo, čo už bolo uplatnené. Spracuje overenú notifikáciu poskytovateľa aj záznam s pôvodom host zapísaný cez sync(); záznam poskytovateľa, ktorého podpis sa neoveril, soft-deleted alebo neuloženú notifikáciu či snapshot, ktorý sa už nedá obnoviť, odmietne výnimkou InvalidProviderNotificationException a neznáme id výnimkou ModelNotFoundException:
Purchases::replay($notification); // ?Model — the PurchaseNotification model …
Purchases::replay(42); // … or its idNákupy jedného vlastníka
for($owner) vráti objekt OwnerPurchases obmedzený presne na daného vlastníka — jeho morph typ aj kľúč —, takže vlastník iného modelu s rovnakým id sa do výsledkov nedostane a neuložený vlastník nevidí nič. Model vlastníka nepotrebuje žiadny trait:
use RoundlyConsulting\Purchases\Facades\Purchases;
Purchases::for($user)->subscribedTo('pro'); // bool
Purchases::for($user)->activeSubscription('pro'); // ?Subscription (latest active)
Purchases::for($user)->purchases()->latest()->get(); // Builder<Purchase>
Purchases::for($user)->subscriptions()->count(); // Builder<Subscription>| Metóda | Vracia | Popis |
|---|---|---|
purchases() | Builder<Purchase> | Nákupy vlastníka, pripravené na ďalšie reťazenie. |
subscriptions() | Builder<Subscription> | Predplatné vlastníka, pripravené na ďalšie reťazenie. |
activeSubscription(?string $name = null) | ?Subscription | Posledné aktívne predplatné, voliteľne podľa názvu plánu. |
subscribedTo(string $name) | bool | Či má vlastník aktívne predplatné daného plánu. |
Vlastníka nastavujete vy. Notifikácia obchodu nikdy neuvádza, ktorému z vašich používateľov patrí, preto každý záznam uložený cez webhook, handle(), sync() či replay() začína bez vlastníka — for($user) ani HasPurchases ho neuvidia, kým ho neprepojíte, zvyčajne v listeneri (pozrite Udalosti).
Názvy plánov
subscribedTo($name) a activeSubscription($name) porovnávajú Subscription::$name. Apple a Google pomenujú predplatné podľa identifikátora produktu (com.example.pro) pri každej notifikácii, takže upgrade ho premenuje. Stripe plán nepomenúva: nové predplatné zo Stripe dostane názov podľa produktu svojej ceny (prod_…) a už sa nepremenuje, takže názov, ktorý mu dáte vy — napríklad pro —, vydrží aj všetky ďalšie udalosti.
Použitie vo vlastnej route
Pribalená webhook routa je voliteľná. Ak chcete mať endpoint plne pod kontrolou, volajte handle() z vlastnej routy a neúspešné overenie premeňte na 400 — presne to robí pribalený controller. Obchody posielajú požiadavky bez CSRF tokenu, preto routu umiestnite mimo CSRF ochrany:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use RoundlyConsulting\Purchases\Exceptions\VerificationException;
use RoundlyConsulting\Purchases\Facades\Purchases;
Route::post('billing/stripe/webhook', function (Request $request) {
try {
Purchases::handle('stripe', $request);
} catch (VerificationException) {
abort(400);
}
return response()->noContent();
});Vyhľadanie poskytovateľov
use RoundlyConsulting\Purchases\Facades\Purchases;
if (Purchases::has('google')) {
$google = Purchases::provider('google'); // RoundlyConsulting\Purchases\Providers\Google\Google
}
foreach (Purchases::providers() as $id => $provider) {
// 'apple' => Apple, 'google' => Google, 'stripe' => Stripe
}Poskytovatelia sa vytvárajú cez kontajner a sú kľúčovaní podľa id() — pre vstavaných apple, google a stripe. provider() vyhodí UnknownProviderException pre identifikátor, ktorý nie je zaregistrovaný.
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.