NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Purchases for Laravel

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ódaVraciaČo robí
result($id, $request)ProviderResultOverí a dekóduje požiadavku — nič nezapisuje.
handle($id, $request)ModelOverí, dekóduje, uloží a spustí udalosti; rešpektuje queue.enabled.
sync(ProviderResult $result)?ModelUloží výsledok, ktorý už máte, s auditom ako pri webhooku, vždy synchrónne.
replay(PurchaseNotification|int $notification)?ModelZnova spracuje jednu uloženú auditnú notifikáciu a označí ju ako spracovanú.
for(Model $owner)OwnerPurchasesNákupy a predplatné jedného vlastníka — len čítanie.
provider($id)ProviderVrá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()PurchasesFakeNasadí 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 id

Ná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ódaVraciaPopis
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)?SubscriptionPosledné 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 kryptomien

Odoslaní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.