CustomerInfo، کش و بازیابی خرید
چهار سیاست دریافت CustomerInfo، انقضای اشتراک، بررسی حق دسترسی و تفاوت بازیابی خودکار با sync و restore استور.
مرجع دسترسی، CustomerInfo تأییدشده و حق دسترسی فعال است؛ فلگ محلی isPaid، لینک پرداخت یا تاریخچه تلاش جای آن نیستند. خطای دریافت را از دسترسی غیرفعال جدا نگه دارید.
خواندن و پیشفرضها
| سیاست | Kotlin | Flutter موبایل | رفتار |
|---|---|---|---|
| کش یا دریافت؛ پیشفرض | CACHED_OR_FETCHED | cachedOrFetched | کش فوری؛ رفرش stale در پسزمینه؛ fetch در نبود کش |
| فقط کش | CACHE_ONLY | cacheOnly | کش یا خطا؛ بدون شبکه |
| سرور جاری | FETCH_CURRENT | fetchCurrent | انتظار سرور؛ شکست بدون fallback موفق stale |
| کش تازه یا سرور | NOT_STALE_CACHED_OR_CURRENT | notStaleCachedOrCurrent | کش تازه، وگرنه انتظار سرور |
Flutter پارامتر نامدار fetchPolicy: میگیرد؛ Kotlin enum را به getCustomerInfo(policy) میدهد. نمونه درخواست جاری:
برای خواندن پیشفرض پارامتر ندهید. برای سه حالت دیگر enum جدول را جایگزین کنید. entitlements.active['premium'] دسترسی فعال و entitlements.all['premium'] سابقه شناختهشده را میدهد. hasEntitlement، isActiveEntitlement و getEntitlement فقط آخرین snapshot را میخوانند و شبکه نمیفرستند. checkEntitlement عمومی موبایل حذف شده است.
تجربه آفلاین و تاریخ انقضا
Android داده را در foreground پس از پنج دقیقه و background پس از ۲۵ ساعت stale میداند؛ iOS فعلی آستانه پنج دقیقه دارد. stale یعنی نیاز به تازهسازی، نه تمدید یا پایان خودکار حق دسترسی. درخواستهای صریح network ممکن است شکست بخورند؛ کش تأییدشده برای حالتهای مجاز حفظ میشود. تایمر polling دائمی وجود ندارد.
اگر اشتراک ۳۰روزه امروز تأیید شود و روز دهم بدون اینترنت اپ باز شود، کش معتبر و سیاست استفاده از کش میتوانند دسترسی باقیمانده را نشان دهند. تاریخ انقضا همچنان بررسی میشود. محصول غیرمصرفی میتواند دسترسی مادامالعمر داشته باشد، اما null بودن انقضا بهتنهایی دسترسی فعال نیست؛ پرچم فعال و اعتبار داده هم لازماند. موجودی مصرفی از ledger تحویل میزبان میآید.
کش تضمین مشاهده لغو یا تغییر سمت سرور در حالت آفلاین نیست. محدودیتهای ساعت و زمان مورداعتماد SDK را با افزودن مقایسه خام ساعت گوشی یا وعده ضدتقلب تازه جایگزین نکنید.
بازیابی خودکار و متدهای دستی
| مسیر | چه زمانی؟ |
|---|---|
| بازیابی داخلی Android | configure/logIn و بازگشت foreground یا اتصال اینترنت؛ با کار ناتمام یا گذشت پنج دقیقه، با فاصله خطای حداقل سی ثانیه |
syncStorePurchases() | اقدام دستی برای بررسی خرید استور یا تلاش نامشخص؛ UI پرداخت باز نمیکند |
restoreStorePurchases() | دکمه صریح کاربر برای بازیابی اشتراک/غیرمصرفی واجد شرایط |
syncPendingConsumables() | بررسی تحویلهای تأییدشده و checkpoint مصرف؛ با handler میتواند تحویل را اجرا کند |
confirmDelivery(id) | تنها پس از grant ماندگار و یکتای میزبان |
همان نامها در Kotlin و Flutter موبایل بدون پارامتر استفاده میشوند، جز confirm که شناسه میگیرد. recover عمومی وجود ندارد. در Android حلقه lifecycle یا sync خودکار موازی اضافه نکنید. restore میتواند restored/alreadyProcessed/failed count داشته باشد؛ success کلی به معنی موفقیت همه موارد نیست و مصرفیِ قبلاً مصرفشده را دوباره grant نمیکند.
iOS و وب را جدا پیاده کنید
iOS بازیابی داخلی شروع دارد، اما observerهای foreground/connectivity اندروید را نصب نمیکند. در بازگشت مرتبط پرداخت یا resume موردنیاز، CustomerInfo جاری و تحویلهای Direct را بررسی کنید. syncStorePurchases و restoreStorePurchases در این backend فقط Direct delivery را reconcile میکنند؛ StoreKit ندارند و restoredCount استور صفر است.
در JS مستقیم getCustomerInfo() پیشفرض FETCH_CURRENT دارد و چهار enum همنام uppercase را میپذیرد. snapshot مشتری memory-only است. Flutter Web 0.2.1 fetchPolicy را عبور نمیدهد و به همین پیشفرض provider میرسد؛ وعده cacheOnly آفلاین از facade آن ندهید. وب توضیح کامل دارد.
برگشت از پرداخت و تغییر وضعیت
روی Android فقط لینکهای نشانهگذاریشده اینپیفای بازیابی را تحریک میکنند؛ intentهای شروع و جدید خودکار منتقل میشوند. router سفارشی میتواند handleDeepLink(url) را forward کند. هر deep link نباید sync ایجاد کند و marker اثبات پرداخت نیست.
برای وضعیت زنده از InappifySdk.events و snapshot یا Kotlin addEventListener و stateUpdates() استفاده و observer را هنگام پایان مالک آزاد کنید. تاریخچه تلاش Android برای رویدادهای ازدسترفته است؛ بازکردن دسترسی از CustomerInfo انجام میشود.