Konfigurácia
Balík funguje bez akejkoľvek konfigurácie, no žiadny poskytovateľ nič neoverí, kým nenastavíte jeho prístupové údaje. Publikovaný config/purchases.php v plnom rozsahu:
/*
| Every on/off switch below is read from the environment as a string, so each is
| read strictly: 1/true/on/yes are on, 0/false/off/no are off, unset or blank
| (KEY=) keeps the documented default (for push authentication that is ON —
| fail-closed), and anything else throws an InvalidConfigurationException naming
| the key. Every other setting is strict too: blank means not set, so the default
| applies, while a duration such as the Stripe tolerance must be a whole number
| and URLs and queue names must be strings.
*/
return [
'models' => [
'purchase' => \RoundlyConsulting\Purchases\Models\Purchase::class,
'purchase-item' => \RoundlyConsulting\Purchases\Models\PurchaseItem::class,
'purchase-refund' => \RoundlyConsulting\Purchases\Models\PurchaseRefund::class,
'purchase-notification' => \RoundlyConsulting\Purchases\Models\PurchaseNotification::class,
'subscription' => \RoundlyConsulting\Purchases\Models\Subscription::class,
'subscription-item' => \RoundlyConsulting\Purchases\Models\SubscriptionItem::class,
],
'providers' => [
\RoundlyConsulting\Purchases\Providers\Apple\Apple::class,
\RoundlyConsulting\Purchases\Providers\Google\Google::class,
\RoundlyConsulting\Purchases\Providers\Stripe\Stripe::class,
],
// Owner morph key type: "bigint", "uuid" or "ulid" — anything else throws.
'key_type' => env('PURCHASES_KEY_TYPE', 'bigint'),
// Log every verified raw payload to purchase_notifications before reducing it.
'audit' => [
'enabled' => env('PURCHASES_AUDIT_ENABLED', true),
],
// Persist verified notifications on a queue; the webhook still 204s immediately.
'queue' => [
'enabled' => env('PURCHASES_QUEUE_ENABLED', false),
'connection' => env('PURCHASES_QUEUE_CONNECTION'),
'queue' => env('PURCHASES_QUEUE_NAME'),
],
'routes' => [
'enabled' => env('PURCHASES_ROUTES_ENABLED', false),
'prefix' => env('PURCHASES_ROUTES_PREFIX', 'purchases'),
'middleware' => ['api'],
],
'settings' => [
'apple' => [
// The one App Store app this host accepts notifications and transactions for.
'bundle_id' => env('PURCHASES_APPLE_BUNDLE_ID'),
'app_apple_id' => env('PURCHASES_APPLE_APP_APPLE_ID'),
// Production unless switched on.
'sandbox' => env('PURCHASES_APPLE_SANDBOX', false),
'url' => [
'live' => env('PURCHASES_APPLE_LIVE_URL', 'https://buy.itunes.apple.com'),
'sandbox' => env('PURCHASES_APPLE_SANDBOX_URL', 'https://sandbox.itunes.apple.com'),
],
'password' => env('PURCHASES_APPLE_PASSWORD'),
'certificate_clock_skew' => env('PURCHASES_APPLE_CERTIFICATE_CLOCK_SKEW', 60),
'api' => [
'key_id' => env('PURCHASES_APPLE_KEY_ID'),
'issuer_id' => env('PURCHASES_APPLE_ISSUER_ID'),
'private_key' => env('PURCHASES_APPLE_PRIVATE_KEY'),
'url' => [
'live' => env('PURCHASES_APPLE_API_LIVE_URL', 'https://api.storekit.itunes.apple.com'),
'sandbox' => env('PURCHASES_APPLE_API_SANDBOX_URL', 'https://api.storekit-sandbox.itunes.apple.com'),
],
],
],
'google' => [
'package_name' => env('PURCHASES_GOOGLE_PACKAGE_NAME'),
'service_account' => [
'client_email' => env('PURCHASES_GOOGLE_CLIENT_EMAIL'),
'private_key' => env('PURCHASES_GOOGLE_PRIVATE_KEY'),
'token_uri' => env('PURCHASES_GOOGLE_TOKEN_URI', 'https://oauth2.googleapis.com/token'),
],
'base_url' => env('PURCHASES_GOOGLE_BASE_URL', 'https://androidpublisher.googleapis.com'),
'acknowledge' => env('PURCHASES_GOOGLE_ACKNOWLEDGE', true),
// Pub/Sub pushes are authenticated before they are read — fail-closed.
'push' => [
'authenticate' => env('PURCHASES_GOOGLE_PUSH_AUTHENTICATE', true),
'audience' => env('PURCHASES_GOOGLE_PUSH_AUDIENCE'),
'service_account_email' => env('PURCHASES_GOOGLE_PUSH_SERVICE_ACCOUNT'),
'token' => env('PURCHASES_GOOGLE_PUSH_TOKEN'),
'jwks_url' => env('PURCHASES_GOOGLE_PUSH_JWKS_URL', 'https://www.googleapis.com/oauth2/v3/certs'),
'jwks_cache_ttl' => env('PURCHASES_GOOGLE_PUSH_JWKS_CACHE_TTL', 3600),
],
],
'stripe' => [
'secret' => env('PURCHASES_STRIPE_SECRET'),
'webhook_secret' => env('PURCHASES_STRIPE_WEBHOOK_SECRET'),
'api_version' => env('PURCHASES_STRIPE_API_VERSION', '2026-05-27.dahlia'),
'base_url' => env('PURCHASES_STRIPE_BASE_URL', 'https://api.stripe.com/v1'),
'tolerance' => env('PURCHASES_STRIPE_TOLERANCE', 300),
],
],
];Kľúče balíka
| Kľúč | Predvolené | Účel |
|---|---|---|
models.* | modely balíka | Eloquent model pre každý typ záznamu — nahraďte ho vlastnou podtriedou; iná trieda vyhodí výnimku. |
providers | Apple, Google, Stripe | Registrované triedy poskytovateľov, vyhľadávané podľa id(). Musí to byť zoznam tried implementujúcich Provider. |
key_type | bigint | Typ kľúča polymorfného vlastníka: bigint, uuid alebo ulid; čokoľvek iné vyhodí výnimku pri spustení migrácií. |
audit.enabled | true | Zapisovať každý overený payload do purchase_notifications. |
queue.enabled | false | Overiť synchrónne, uložiť cez frontu. |
queue.connection | null | Spojenie fronty pre asynchrónne ukladanie; nenastavené alebo prázdne = predvolené spojenie. |
queue.queue | null | Názov fronty pre asynchrónne ukladanie; nenastavené alebo prázdne = predvolená fronta. |
routes.enabled | false | Zaregistrovať pribalenú webhook routu. |
routes.prefix | purchases | URI prefix webhook routy. |
routes.middleware | ['api'] | Middleware webhook routy — zoznam názvov. |
Apple — settings.apple
| Kľúč | Env | Predvolené |
|---|---|---|
bundle_id | PURCHASES_APPLE_BUNDLE_ID | — |
app_apple_id | PURCHASES_APPLE_APP_APPLE_ID | — |
sandbox | PURCHASES_APPLE_SANDBOX | false |
url.live | PURCHASES_APPLE_LIVE_URL | buy.itunes.apple.com |
url.sandbox | PURCHASES_APPLE_SANDBOX_URL | sandbox.itunes.apple.com |
password | PURCHASES_APPLE_PASSWORD | — |
certificate_clock_skew | PURCHASES_APPLE_CERTIFICATE_CLOCK_SKEW | 60 |
api.key_id | PURCHASES_APPLE_KEY_ID | — |
api.issuer_id | PURCHASES_APPLE_ISSUER_ID | — |
api.private_key | PURCHASES_APPLE_PRIVATE_KEY | — |
api.url.live | PURCHASES_APPLE_API_LIVE_URL | api.storekit.itunes.apple.com |
api.url.sandbox | PURCHASES_APPLE_API_SANDBOX_URL | api.storekit-sandbox.itunes.apple.com |
bundle_id je pre Apple povinné: každá notifikácia App Store — aj každá transakcia, ktorú vráti App Store Server API — ho musí uvádzať, inak sa odmietne; zároveň ním balík podpisuje požiadavky na App Store Server API. app_apple_id (App Store Connect → App Information) je povinné v produkcii, kde ho notifikácia musí niesť. sandbox je predvolene false — produkcia; na true ho nastavte len na serveri, ktorý prijíma notifikácie zo Sandboxu, čím sa aj verifyReceipt a App Store Server API prepnú na sandbox adresy. api.* zapína App Store Server API, modernú náhradu za verifyReceipt; password je zdieľaný kľúč pre staršie účtenky. certificate_clock_skew je tolerancia v sekundách (0–3600) na oboch koncoch obdobia platnosti každého certifikátu — hodnota mimo rozsahu vyhodí InvalidConfigurationException, takže preklep kontrolu nikdy nevypne, kým prázdna hodnota sa považuje za nenastavenú a platí 60.
Google — settings.google
| Kľúč | Env | Predvolené |
|---|---|---|
package_name | PURCHASES_GOOGLE_PACKAGE_NAME | — |
service_account.client_email | PURCHASES_GOOGLE_CLIENT_EMAIL | — |
service_account.private_key | PURCHASES_GOOGLE_PRIVATE_KEY | — |
service_account.token_uri | PURCHASES_GOOGLE_TOKEN_URI | oauth2.googleapis.com/token |
base_url | PURCHASES_GOOGLE_BASE_URL | androidpublisher.googleapis.com |
acknowledge | PURCHASES_GOOGLE_ACKNOWLEDGE | true |
push.authenticate | PURCHASES_GOOGLE_PUSH_AUTHENTICATE | true |
push.audience | PURCHASES_GOOGLE_PUSH_AUDIENCE | — |
push.service_account_email | PURCHASES_GOOGLE_PUSH_SERVICE_ACCOUNT | — |
push.token | PURCHASES_GOOGLE_PUSH_TOKEN | — |
push.jwks_url | PURCHASES_GOOGLE_PUSH_JWKS_URL | www.googleapis.com/oauth2/v3/certs |
push.jwks_cache_ttl | PURCHASES_GOOGLE_PUSH_JWKS_CACHE_TTL | 3600 |
acknowledge určuje, či sa overené nákupy v Google Play automaticky potvrdia (acknowledge). push.* autentifikuje real-time developer notifikácie a bez konfigurácie ich odmieta — pozrite Google Play. push.jwks_cache_ttl, doba uloženia podpisových kľúčov Google v cache v sekundách, musí byť najmenej 1.
Stripe — settings.stripe
| Kľúč | Env | Predvolené |
|---|---|---|
secret | PURCHASES_STRIPE_SECRET | — |
webhook_secret | PURCHASES_STRIPE_WEBHOOK_SECRET | — |
api_version | PURCHASES_STRIPE_API_VERSION | 2026-05-27.dahlia |
base_url | PURCHASES_STRIPE_BASE_URL | api.stripe.com/v1 |
tolerance | PURCHASES_STRIPE_TOLERANCE | 300 |
tolerance je tolerancia časovej pečiatky webhooku v sekundách (najmenej 1, predvolene 300), takže ochranu proti replayu preklep nikdy nevypne; api_version sa posiela v hlavičke Stripe-Version pri každom REST volaní. secret je potrebný na čítanie cez REST vrátane kontroly faktúry pri každej udalosti payment_intent.* v aktuálnych verziách API (pozrite Stripe).
Prostredie (env)
Všetky prístupové údaje sa riadia cez env, takže konfiguráciu publikujete len zriedka. Konfiguračný súbor odovzdáva hodnoty z env bez úprav a balík ich číta striktne. Prepínače zapnuté/vypnuté — PURCHASES_AUDIT_ENABLED, PURCHASES_QUEUE_ENABLED, PURCHASES_ROUTES_ENABLED, PURCHASES_APPLE_SANDBOX, PURCHASES_GOOGLE_ACKNOWLEDGE a PURCHASES_GOOGLE_PUSH_AUTHENTICATE — prijímajú true/false, 1/0, on/off alebo yes/no; nenastavený alebo prázdny prepínač (KEY=) si ponechá zdokumentovanú predvolenú hodnotu (pri autentifikácii pushov je to zapnuté — bezpečne odmietajúci stav). Čokoľvek iné, napríklad PURCHASES_KEY_TYPE mimo bigint, uuid a ulid, vyhodí InvalidConfigurationException s názvom kľúča — preklep sa nikdy potichu nezmení na predvolenú hodnotu:
# Apple — your app, App Store Server API (+ legacy verifyReceipt shared secret)
PURCHASES_APPLE_BUNDLE_ID=com.example.app
PURCHASES_APPLE_APP_APPLE_ID=1234567890
PURCHASES_APPLE_SANDBOX=false
PURCHASES_APPLE_KEY_ID=
PURCHASES_APPLE_ISSUER_ID=
PURCHASES_APPLE_PRIVATE_KEY=
PURCHASES_APPLE_PASSWORD=
PURCHASES_APPLE_CERTIFICATE_CLOCK_SKEW=60
# Google Play — service account
PURCHASES_GOOGLE_PACKAGE_NAME=
PURCHASES_GOOGLE_CLIENT_EMAIL=
PURCHASES_GOOGLE_PRIVATE_KEY=
PURCHASES_GOOGLE_ACKNOWLEDGE=true
PURCHASES_GOOGLE_PUSH_AUDIENCE=https://your-app.test/purchases/webhooks/google
PURCHASES_GOOGLE_PUSH_SERVICE_ACCOUNT=[email protected]
# Stripe
PURCHASES_STRIPE_SECRET=
PURCHASES_STRIPE_WEBHOOK_SECRET=
PURCHASES_STRIPE_TOLERANCE=300
# Package behaviour
PURCHASES_KEY_TYPE=bigint
PURCHASES_AUDIT_ENABLED=true
PURCHASES_QUEUE_ENABLED=false
PURCHASES_ROUTES_ENABLED=false
PURCHASES_ROUTES_PREFIX=purchasesStriktné nastavenia
Rovnako striktné sú aj ostatné nastavenia. Premenná, ktorá nie je nastavená — chýba alebo je prázdna, napríklad PURCHASES_STRIPE_TOLERANCE= —, dostane predvolenú hodnotu; premenná nastavená v nesprávnom tvare vyhodí RoundlyConsulting\Purchases\Exceptions\InvalidConfigurationException s názvom kľúča:
- Trvania — PURCHASES_STRIPE_TOLERANCE, PURCHASES_GOOGLE_PUSH_JWKS_CACHE_TTL a PURCHASES_APPLE_CERTIFICATE_CLOCK_SKEW — prijímajú celé číslo, napríklad 300; five či 300.5 vyhodia výnimku namiesto toho, aby sa z nich stala 0.
- URL adresy, verzia API, prefix routy a spojenie aj názov fronty musia byť reťazce. Pre predvolené spojenie a frontu nechajte premenné fronty nenastavené alebo prázdne.
- purchases.providers a purchases.routes.middleware musia byť zoznamy a každý poskytovateľ musí implementovať RoundlyConsulting\Purchases\Providers\Provider.
- Prázdny prístupový údaj (PURCHASES_GOOGLE_PUSH_TOKEN=) sa považuje za nenastavený, takže sa číta ako nenakonfigurovaný; údaj, ktorý nie je reťazec, vyhodí výnimku.
Tajné kľúče poskytovateľov sa čítajú len z konfigurácie a env, sú označené #[SensitiveParameter], takže sa nikdy neobjavia v stack trace, a nikdy sa nelogujú. php artisan about ich zobrazí len ako SET alebo MISSING a chybné trvanie či zoznam poskytovateľov ako INVALID.
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.