درباره اشتراک ها

این سند نحوه مدیریت رویدادهای چرخه عمر اشتراک، مانند تمدید و انقضا، را شرح می‌دهد. همچنین ویژگی‌های اشتراک اضافی مانند ارائه تبلیغات و امکان مدیریت اشتراک‌های کاربران را شرح می‌دهد.

اگر محصولات اشتراکی را برای برنامه خود پیکربندی نکرده‌اید، به بخش ایجاد و پیکربندی محصولات خود مراجعه کنید.

مرور کلی اشتراک‌ها

اشتراک یک تراکنش تکرارشونده است که به کاربران امتیازات خاصی اعطا می‌کند. این امتیازات نشان‌دهنده مجموعه‌ای از مزایایی است که کاربران می‌توانند در یک دوره زمانی مشخص به آنها دسترسی داشته باشند. به عنوان مثال، یک اشتراک ممکن است به یک کاربر حق دسترسی ویژه (پریمیوم) بدهد.

از طریق طرح‌ها و پیشنهادهای پایه، می‌توانید چندین پیکربندی برای یک محصول اشتراکی ایجاد کنید. به عنوان مثال، می‌توانید یک پیشنهاد مقدماتی برای کاربرانی که هرگز در برنامه شما مشترک نشده‌اند ایجاد کنید. به طور مشابه، می‌توانید یک پیشنهاد ارتقاء برای کاربرانی که قبلاً مشترک شده‌اند ایجاد کنید.

برای مشاهده‌ی جزئیات محصولات اشتراکی، طرح‌های پایه و پیشنهادها، به مستندات موجود در مرکز راهنمایی کنسول Play مراجعه کنید.

کتابخانه پرداخت Play از انواع اشتراک‌های زیر پشتیبانی می‌کند:

  • اشتراک تک‌موردی - در این نوع، یک مورد مربوط به یک حق امتیاز است. به عنوان مثال، اشتراک در یک سرویس پخش موسیقی.

  • اشتراک با افزونه‌ها - در این نوع، یک خرید می‌تواند چندین حق اشتراک مجزا را در یک خرید واحد داشته باشد. به عنوان مثال، اشتراک در هر دو سرویس پخش موسیقی و اشتراک ویدیو. برای اطلاعات خاص در مورد اشتراک با افزونه‌ها، به اشتراک با افزونه‌ها مراجعه کنید.

ادغام طرح‌های پیش‌پرداخت

طرح‌های پیش‌پرداخت پس از انقضا به‌طور خودکار تمدید نمی‌شوند . برای تمدید بدون وقفه حق اشتراک، کاربر باید یک طرح پیش‌پرداخت برای همان اشتراک را شارژ کند .

برای افزایش موجودی، جریان صورتحساب را مانند خرید اصلی شروع کنید. نیازی نیست که مشخص کنید خرید، افزایش موجودی است.

شارژهای طرح پیش‌پرداخت همیشه از حالت جایگزینی CHARGE_FULL_PRICE استفاده می‌کنند و نیازی به تنظیم صریح این حالت نیست. کاربر بلافاصله برای یک دوره کامل صورتحساب، شارژ می‌شود و اعتبار او به مدت زمان مشخص شده در شارژ، افزایش می‌یابد.

پس از شارژ حساب، فیلدهای زیر در شیء نتیجه Purchase به‌روزرسانی می‌شوند تا آخرین خرید شارژ را نشان دهند:

  • شناسه سفارش
  • زمان خرید
  • امضا
  • خرید توکن
  • تصدیق شده

فیلدهای Purchase زیر همیشه حاوی همان داده‌های موجود در خرید اصلی هستند:

  • نام بسته
  • وضعیت خرید
  • محصولات
  • تمدید خودکار

تایید خرید پیش‌پرداخت

مشابه اشتراک‌های تمدید خودکار، شما باید پس از خرید، طرح‌های پیش‌پرداخت را تأیید کنید. هم خرید اولیه و هم هرگونه افزایش اعتبار باید تأیید شوند. برای اطلاعات بیشتر، به پردازش خریدها مراجعه کنید.

با توجه به احتمال کوتاه بودن مدت زمان طرح‌های پیش‌پرداخت، تأیید خرید در اسرع وقت بسیار مهم است.

طرح‌های پیش‌پرداخت با مدت زمان یک هفته یا بیشتر باید ظرف سه روز تأیید شوند.

طرح‌های پیش‌پرداخت با مدت زمان کمتر از یک هفته باید ظرف نیمی از مدت زمان طرح تأیید شوند. به عنوان مثال، توسعه‌دهندگان ۱.۵ روز فرصت دارند تا یک طرح پیش‌پرداخت سه روزه را تأیید کنند.

ادغام اشتراک‌های اقساطی

اشتراک اقساطی نوعی اشتراک است که در آن کاربران به جای پرداخت کل هزینه اشتراک از ابتدا، هزینه اشتراک را در چندین قسط و در یک دوره زمانی پرداخت می‌کنند.

ملاحظات اضافی برای اشتراک‌های اقساطی:

  • دسترسی به کشور : ویژگی اشتراک اقساطی فقط در برزیل، فرانسه، ایتالیا و اسپانیا موجود است (برای اطلاع از آخرین وضعیت دسترسی، به کنسول مراجعه کنید).
  • تنظیم قیمت : هنگام تنظیم قیمت برای اشتراک اقساطی در کنسول، قیمت نشان‌دهنده مبلغ پرداخت ماهانه است. این، همراه با دوره تعهد تعیین‌شده، مبلغ کل اشتراک را در صفحه خرید ایجاد می‌کند.
  • دوره تعهد : کل مدت تعهد اولیه اشتراک، که در طی آن پرداخت‌های ماهانه الزامی است. برای مثال، اگر یک طرح پایه دارای دوره تعهد ۱۵ ماهه باشد، کاربر در این دوره ۱۵ پرداخت ماهانه انجام خواهد داد.
  • تمدیدها : در زمینه اشتراک‌های اقساطی، «تمدید» به معنای پایان یک دوره تعهد، چه دوره تعهد اولیه و چه دوره تعهد بعدی، است. پس از ثبت نام اولیه، اولین تمدید پس از اتمام کل دوره تعهد اولیه انجام می‌شود. تمدیدهای بعدی پس از اتمام هر دوره تعهد بعدی انجام می‌شود. انواع تمدید برای اشتراک‌های اقساطی می‌تواند «تمدید خودکار ماهانه» یا «تمدید خودکار برای مدت مشابه» باشد. برای «تمدید خودکار ماهانه»، هیچ تعهد بعدی وجود ندارد و طرح مانند یک اشتراک ماهانه عمل می‌کند که در آن هر هزینه اشتراک ماهانه یک تمدید محسوب می‌شود.
  • دوره صورتحساب : در زمینه اشتراک‌های اقساطی، این به فاصله زمانی تکرارشونده‌ای اشاره دارد که در آن پرداخت‌های جداگانه، همانطور که در طرح پایه مشخص شده است، انجام می‌شود.
  • رفتارهای تغییر طرح در مقابل تغییر قیمت : برای تغییرات قیمت و لغو، تعهد قطعی است. این بدان معناست که اگر کاربری بخواهد لغو کند یا توسعه‌دهنده‌ای بخواهد قیمت را تغییر دهد، تغییر در پایان دوره تعهد اعمال می‌شود. برای تغییرات طرح، تعهد قطعی نیست. این بدان معناست که تغییر طرح لازم نیست تا پایان دوره تعهد منتظر بماند، بلکه بلافاصله یا در تاریخ پرداخت بعدی بر اساس حالت جایگزینی تعیین‌شده اعمال می‌شود.
  • تغییر طرح اشتراک یکسان : تغییر طرح از طرح پایه اقساطی به طرح پایه غیراقساطی برای همان محصول اشتراکی مجاز نیست.
  • اعلان‌های توسعه‌دهنده بلادرنگ (RTDN) : یک RTDN SUBSCRIPTION_CANCELLATION_SCHEDULED بلافاصله پس از لغو درخواستی کاربر، زمانی که پرداخت‌ها برای دوره تعهد باقی مانده باشد، ارسال می‌شود. لغو در حال بررسی است و فقط در پایان دوره تعهد اعمال می‌شود. سپس، اگر توسط کاربر بازیابی نشود، RTDN های SUBSCRIPTION_CANCELED و SUBSCRIPTION_EXPIRED در پایان دوره تعهد ارسال می‌شوند.

  • پرداخت‌ها / تحقق درآمد : پرداخت‌ها به توسعه‌دهندگان همزمان با پرداخت‌های ماهانه کاربران، و با رعایت همان شرایط سایر اشتراک‌ها، انجام خواهد شد. هنگام ثبت‌نام کاربر برای اشتراک اقساطی، به توسعه‌دهندگان از قبل مبلغی پرداخت نمی‌شود.

  • وصول اقساط پرداخت نشده : اگر کاربری نتواند هیچ یک از پرداخت‌های قسطی اشتراک را انجام دهد، نه گوگل و نه توسعه‌دهنده هیچ تلاشی برای وصول چنین پرداخت‌های از دست رفته یا معوقه‌ای از کاربر نخواهند کرد، مگر اینکه گوگل ممکن است به صورت دوره‌ای در طول هر دوره مهلت یا دوره نگهداری حساب مربوطه، مطابق با رویه‌های معمول خود برای پرداخت مجدد، پرداخت را دوباره انجام دهد. گوگل هیچ مسئولیتی در قبال توسعه‌دهنده در قبال هرگونه پرداخت اقساطی پرداخت نشده باقی مانده نخواهد داشت.

  • در دسترس بودن کتابخانه صورتحساب Play : فیلد installmentDetails فقط برای PBL نسخه ۷ یا بالاتر در دسترس است. برای PBL نسخه ۵ و بالاتر، اشتراک اقساط با استفاده از queryProductDetails() برگردانده می‌شود، اما اشتراک شامل اطلاعات دقیق اقساط مانند تعداد پرداخت‌های تعهد شده طرح نخواهد بود.

از لینک‌های عمیق برای مدیریت اشتراک توسط کاربران استفاده کنید

برنامه شما باید شامل لینکی در صفحه تنظیمات یا ترجیحات باشد که به کاربران امکان مدیریت اشتراک‌هایشان را بدهد، که می‌توانید آن را در ظاهر و حس طبیعی برنامه خود بگنجانید.

شما می‌توانید یک لینک عمیق از برنامه خود به مرکز اشتراک‌های گوگل پلی برای اشتراک‌های منقضی نشده اضافه کنید، که می‌توانید با استفاده از فیلد subscriptionState از منبع اشتراک، آن را تعیین کنید. بر این اساس، روش‌های مختلفی برای ایجاد لینک عمیق به مرکز اشتراک‌های پلی استور وجود دارد.

از آدرس اینترنتی زیر برای هدایت کاربران به صفحه‌ای که تمام اشتراک‌های آنها را نشان می‌دهد، همانطور که در شکل‌های ۱ و ۲ نشان داده شده است، استفاده کنید:

https://play.google.com/store/account/subscriptions
صفحه اشتراک‌های فروشگاه Play وضعیت همه اشتراک‌های کاربر که از طریق Google Play ثبت شده‌اند را نشان می‌دهد.
شکل ۱. صفحه اشتراک‌های پلی استور، وضعیت همه اشتراک‌های ثبت‌شده توسط کاربر در گوگل پلی را نشان می‌دهد.


برای مشاهده جزئیات بیشتر، روی اشتراک مورد نظر ضربه بزنید.
شکل ۲. برای مشاهده جزئیات بیشتر، روی اشتراک مورد نظر ضربه بزنید.

این لینک عمیق می‌تواند برای کمک به کاربر در بازیابی اشتراک لغو شده از مرکز اشتراک‌های فروشگاه پلی استور مفید باشد.

برای پیوند مستقیم به صفحه مدیریت اشتراک منقضی نشده، نام بسته و productId مرتبط با اشتراک خریداری شده را مشخص کنید. برای تعیین productId برای اشتراک موجود به صورت برنامه‌نویسی، از backend برنامه خود پرس‌وجو کنید یا BillingClient.queryPurchasesAsync() برای لیستی از اشتراک‌های مرتبط با یک کاربر خاص فراخوانی کنید. هر اشتراک شامل productId مربوطه به عنوان بخشی از اطلاعات وضعیت اشتراک است. هر شیء SubscriptionPurchaseLineItem مرتبط با خرید اشتراک، حاوی مقدار productId مرتبط با اشتراکی است که کاربر در آن ردیف خریداری کرده است.

از URL زیر برای هدایت کاربران به یک صفحه مدیریت اشتراک خاص استفاده کنید و به جای "your-sub-product-id" و "your-app-package" به ترتیب از productId و نام بسته برنامه استفاده کنید:

https://play.google.com/store/account/subscriptions?sku=your-sub-product-id&package=your-app-package

سپس کاربر می‌تواند روش‌های پرداخت خود را مدیریت کند و به ویژگی‌هایی از جمله لغو، اشتراک مجدد و توقف دسترسی داشته باشد.

به کاربران اجازه دهید اشتراک خود را ارتقا، کاهش یا تغییر دهند

شما می‌توانید گزینه‌های مختلفی را در اختیار مشترکین فعلی خود قرار دهید تا طرح اشتراک خود را تغییر دهند و نیازهایشان را بهتر برآورده کنند:

  • اگر چندین سطح اشتراک، مانند اشتراک‌های «پایه» و «پریمیوم» می‌فروشید، می‌توانید به کاربران اجازه دهید با خرید طرح پایه یا پیشنهاد اشتراک متفاوت، سطح اشتراک خود را تغییر دهند.
  • شما می‌توانید به کاربران اجازه دهید دوره صورتحساب فعلی خود را تغییر دهند، مانند تغییر از طرح ماهانه به سالانه.
  • همچنین می‌توانید به کاربران اجازه دهید بین طرح‌های تمدید خودکار و پیش‌پرداخت جابه‌جا شوند.

شما می‌توانید با ارائه پیشنهادهای اشتراک برای ارائه تخفیف به کاربران واجد شرایط، هر یک از این تغییرات را تشویق کنید. به عنوان مثال، می‌توانید پیشنهادی ایجاد کنید که هنگام تغییر از طرح ماهانه به سالانه، 50٪ تخفیف در سال اول ارائه می‌دهد و این پیشنهاد را به کاربرانی که در طرح ماهانه مشترک هستند و این پیشنهاد را خریداری نکرده‌اند، محدود کنید. اطلاعات بیشتر در مورد معیارهای واجد شرایط بودن پیشنهاد در مرکز راهنما موجود است.

شکل ۳ یک برنامه نمونه با سه طرح مختلف را نشان می‌دهد:

این برنامه دارای سه سطح اشتراک است..
شکل ۳. این برنامه دارای سه سطح اشتراک است.

برنامه شما می‌تواند صفحه‌ای مشابه شکل ۳ را نشان دهد و به کاربران گزینه‌هایی برای تغییر اشتراک خود ارائه دهد. در همه موارد، باید برای کاربران مشخص باشد که طرح اشتراک فعلی آنها چیست و چه گزینه‌هایی برای تغییر آن دارند.

وقتی کاربران تصمیم به ارتقا، کاهش یا تغییر اشتراک خود می‌گیرند، شما یک حالت جایگزینی مشخص می‌کنید که نحوه اعمال ارزش سرشکن‌شده دوره صورتحساب پرداخت‌شده فعلی و زمان وقوع هرگونه تغییر در حق عضویت را تعیین می‌کند.

حالت‌های جایگزینی

جدول زیر حالت‌های جایگزینی موجود و مثال‌هایی از کاربرد و تعداد پرداخت‌های انجام‌شده را فهرست می‌کند.

حالت جایگزینی

توضیحات

مثال استفاده

پرداخت‌های تعهد شده به عنوان پرداخت شده ثبت شده‌اند (برای جایگزینی اشتراک اقساطی)

WITH_TIME_PRORATION

آیتم اشتراک بلافاصله ارتقا یا کاهش می‌یابد. زمان باقی‌مانده بر اساس اختلاف قیمت تنظیم می‌شود و با جلو بردن تاریخ صورتحساب بعدی، به اشتراک جدید اضافه می‌شود. این رفتار پیش‌فرض است.

بدون هیچ گونه پرداخت اضافی فوری، به یک سطح گران‌تر ارتقا دهید.

0

CHARGE_PRORATED_PRICE

کالای اشتراکی بلافاصله ارتقا می‌یابد و چرخه صورتحساب ثابت می‌ماند. سپس مابه‌التفاوت قیمت برای دوره باقیمانده از کاربر دریافت می‌شود.

توجه: این گزینه فقط برای ارتقاء یک آیتم اشتراکی در دسترس است، که در آن قیمت در واحد زمان افزایش می‌یابد.

بدون تغییر تاریخ صورتحساب، به یک سطح گران‌تر ارتقا دهید.

۱

CHARGE_FULL_PRICE

کالای اشتراکی بلافاصله ارتقا یا کاهش می‌یابد و قیمت کامل حق اشتراک جدید بلافاصله از کاربر دریافت می‌شود. مبلغ باقیمانده از اشتراک قبلی یا به همان حق اشتراک منتقل می‌شود، یا هنگام تغییر به حق اشتراک متفاوت، با زمان متناسب می‌شود.

توجه: اگر اشتراک جدید دارای دوره آزمایشی رایگان یا پیشنهاد مقدماتی باشد، در زمان ارتقا یا تنزل رتبه، هزینه اشتراک به میزان 0 دلار یا معادل قیمت پیشنهاد مقدماتی، هر کدام که اعمال شود، از کاربر دریافت می‌شود.

دوره صورتحساب را از کوتاه‌تر به طولانی‌تر ارتقا دهید.

۱ (توجه: اگر اشتراک جدید دوره آزمایشی رایگان دارد، ۰.)

WITHOUT_PRORATION

کالای اشتراکی بلافاصله ارتقا یا کاهش می‌یابد و قیمت جدید هنگام تمدید اشتراک محاسبه می‌شود. چرخه صورتحساب به همان شکل باقی می‌ماند.

با حفظ دوره رایگان باقی‌مانده، به سطح اشتراک بالاتر ارتقا دهید.

0

DEFERRED

کالای اشتراکی فقط زمانی ارتقا یا کاهش می‌یابد که اشتراک تمدید شود، اما خرید جدید بلافاصله با دو کالای زیر صادر می‌شود:

  • کالای موجود با قابلیت تمدید خودکار غیرفعال و زمان انقضا روی پایان چرخه صورتحساب فعلی تنظیم شده است.
  • حق دسترسی جدیدی که پس از انقضای مورد موجود شروع می‌شود. می‌توانید به کاربران اجازه دهید در صورت تمایل تغییرات بیشتری ایجاد کنند. به عنوان مثال، کاربران می‌توانند به طرح اولیه برگردند یا تغییر طرح معوق جدیدی را آغاز کنند.

توجه: برای اشتراک‌های اقساطی، تغییر طرح در ابتدای تاریخ پرداخت بعدی رخ می‌دهد.

به یک سطح ارزان‌تر ارتقا دهید.

۱

KEEP_EXISTING

برنامه پرداخت برای کالای اشتراکی در نسخه جایگزین بدون تغییر باقی می‌ماند.

وقتی یک مورد خاص باید بدون تغییر باشد، مورد اشتراک را از اشتراک با افزونه‌ها اضافه یا حذف کنید.

ناموجود

برای کسب اطلاعات بیشتر در مورد کاربردهای مختلف افزایش فروش و بازگشت به حالت اولیه (upsell) در پیشنهادات ارتقا یا کاهش سطح، راهنمای پیشنهادات و تبلیغات را مطالعه کنید.

حالت جایگزینی را برای خرید تنظیم کنید

شما می‌توانید بر اساس تنظیمات برگزیده و منطق کسب‌وکار خود، از حالت‌های جایگزینی مختلفی برای انواع مختلف انتقال اشتراک استفاده کنید. این بخش نحوه تنظیم حالت جایگزینی برای تغییر در اشتراک و محدودیت‌های اعمال‌شده را توضیح می‌دهد.

اشتراک مجدد یا تغییر طرح‌ها در همان اشتراک

شما می‌توانید یک حالت جایگزینی پیش‌فرض را در کنسول گوگل پلی مشخص کنید. این تنظیم به شما امکان می‌دهد انتخاب کنید که اگر مشترکین فعلی طرح پایه یا پیشنهادی متفاوت برای همان اشتراک خریداری کنند، چه زمانی از آنها هزینه کسر شود یا پس از لغو اشتراک، دوباره مشترک شوند. گزینه‌های موجود عبارتند از «هزینه بلافاصله » که معادل CHARGE_FULL_PRICE است و «هزینه در تاریخ صدور صورتحساب بعدی » که معادل WITHOUT_PRORATION است. اینها تنها حالت‌های جایگزینی مرتبط هنگام تغییر طرح‌های پایه در همان اشتراک هستند.

برای مثال، اگر پس از لغو اشتراک توسط کاربر، اما قبل از پایان اشتراک، پیشنهاد بازگشت وجه برای همان طرح را پیاده‌سازی می‌کنید، می‌توانید خرید جدید را به عنوان یک خرید معمولی و بدون مشخص کردن هیچ مقداری در SubscriptionUpdateParams پردازش کنید. سیستم از حالت جایگزینی پیش‌فرض که در اشتراک پیکربندی کرده‌اید استفاده می‌کند و به طور خودکار انتقال طرح از خرید قدیمی به خرید جدید را مدیریت می‌کند.

طرح‌ها را بین اشتراک‌ها تغییر دهید، یا حالت جایگزینی پیش‌فرض را لغو کنید

اگر کاربر در حال تغییر محصولات اشتراکی است - خرید اشتراک دیگری - یا اگر به هر دلیلی می‌خواهید حالت جایگزینی پیش‌فرض را لغو کنید، نرخ سرشکنی را در زمان اجرا به عنوان بخشی از پارامترهای جریان خرید مشخص می‌کنید.

برای ارائه صحیح ReplacementMode در SubscriptionProductReplacementParams یا SubscriptionUpdateParams به عنوان بخشی از پیکربندی جریان خرید زمان اجرا، به محدودیت‌های زیر توجه کنید:

  • هنگام ارتقا، کاهش یا شروع تغییر اشتراک یکسان به یک طرح پیش‌پرداخت از یک طرح پیش‌پرداخت، طرح تمدید خودکار یا طرح اقساطی، تنها حالت جایگزینی مجاز CHARGE_FULL_PRICE است. اگر هر حالت جایگزینی دیگری را مشخص کنید، خرید ناموفق بوده و خطایی به کاربر نشان داده می‌شود.
  • هنگام تغییر طرح‌ها در یک اشتراک به یک طرح تمدید خودکار، چه از یک طرح پیش‌پرداخت و چه از یک طرح تمدید خودکار، حالت‌های سرشکنی معتبر CHARGE_FULL_PRICE و WITHOUT_PRORATION هستند. اگر هر حالت سرشکنی دیگری را مشخص کنید، خرید ناموفق بوده و خطایی به کاربر نشان داده می‌شود.
  • تغییر طرح‌ها در یک محصول اشتراکی یکسان از طرح پایه اقساطی به طرح پایه غیراقساطی مجاز نیست.
  • هنگام استفاده از حالت جایگزینی KEEP_EXISTING در SubscriptionProductReplacementParams برای حفظ پرداخت یک کالا در طول جایگزینی بدون تغییر، شناسه محصول قدیمی باید با شناسه محصول محصول جدید یکسان باشد. حالت KEEP_EXISTING در SubscriptionUpdateParams پشتیبانی نمی‌شود.

مثال‌ها و رفتارهای جایگزین

برای درک نحوه عملکرد هر حالت تناسب، سناریوی زیر را در نظر بگیرید:

سم‌وایز اشتراک محتوای آنلاین اپلیکیشن Country Gardener را دارد. او اشتراک ماهانه نسخه Tier 1 این محتوا را دارد که فقط متن است. این اشتراک برای او ماهی ۲ دلار هزینه دارد و اول ماه تمدید می‌شود.

در ۱۵ آوریل، سم‌وایز تصمیم گرفت اشتراک خود را به نسخه سالانه Tier 2 ارتقا دهد که شامل به‌روزرسانی‌های ویدیویی می‌شود و سالانه ۳۶ دلار هزینه دارد.

هنگام ارتقاء اشتراک، توسعه‌دهنده یک حالت تناسب انتخاب می‌کند. لیست زیر نحوه تأثیر هر حالت تناسب بر اشتراک Samwise را شرح می‌دهد:

WITH_TIME_PRORATION

اشتراک سطح ۱ سم‌وایز بلافاصله به پایان می‌رسد. از آنجایی که او هزینه یک ماه کامل (۱ تا ۳۰ آوریل) را پرداخت کرده اما در اواسط دوره اشتراک، اشتراک خود را ارتقا داده است، نیمی از اشتراک یک ماه (۱ دلار) برای اشتراک جدید او اعمال می‌شود. با این حال، از آنجایی که هزینه اشتراک جدید سالانه ۳۶ دلار است، مانده اعتبار ۱ دلاری فقط برای ۱۰ روز (۱۶ تا ۲۵ آوریل) کافی است. بنابراین در ۲۶ آوریل، ۳۶ دلار برای اشتراک جدید و ۳۶ دلار دیگر در ۲۶ آوریل هر سال بعد از آن از او کسر می‌شود.

شما باید به محض موفقیت‌آمیز بودن خرید، PurchasesUpdatedListener برنامه خود را فراخوانی کنید و بتوانید خرید جدید را به عنوان بخشی از فراخوانی queryPurchasesAsync() بازیابی کنید. بک‌اند شما بلافاصله یک اعلان توسعه‌دهنده بلادرنگ SUBSCRIPTION_PURCHASED دریافت می‌کند.

CHARGE_PRORATED_PRICE

این حالت می‌تواند مورد استفاده قرار گیرد زیرا قیمت اشتراک سطح ۲ به ازای هر واحد زمانی (۳۶ دلار در سال = ۳ دلار در ماه) بیشتر از قیمت اشتراک سطح ۱ به ازای هر واحد زمانی (۲ دلار در ماه) است. اشتراک سطح ۱ سم‌وایز بلافاصله پایان می‌یابد. از آنجا که او هزینه یک ماه کامل را پرداخت کرده اما فقط نیمی از آن را استفاده کرده است، نیمی از اشتراک یک ماه (۱ دلار) برای اشتراک جدید او اعمال می‌شود. با این حال، از آنجا که هزینه اشتراک جدید ۳۶ دلار در سال است، ۱۵ روز باقی‌مانده ۱.۵۰ دلار هزینه دارد؛ بنابراین مابه‌التفاوت ۰.۵۰ دلار برای اشتراک جدید از او کسر می‌شود. در اول ماه مه، سم‌وایز ۳۶ دلار برای سطح اشتراک جدید خود و ۳۶ دلار دیگر در اول ماه مه هر سال بعد از آن دریافت می‌کند.

شما باید به محض موفقیت‌آمیز بودن خرید، PurchasesUpdatedListener برنامه خود را فراخوانی کنید و بتوانید خرید جدید را به عنوان بخشی از فراخوانی queryPurchasesAsync() بازیابی کنید. بک‌اند شما بلافاصله یک اعلان توسعه‌دهنده بلادرنگ SUBSCRIPTION_PURCHASED دریافت می‌کند.

WITHOUT_PRORATION

اشتراک سطح ۱ سم‌وایز بلافاصله و بدون هیچ هزینه اضافی به سطح ۲ ارتقا می‌یابد و در اول ماه مه، ۳۶ دلار برای سطح اشتراک جدیدش و ۳۶ دلار دیگر در اول ماه مه هر سال بعد از آن از او دریافت می‌شود.

شما باید به محض موفقیت‌آمیز بودن خرید، PurchasesUpdatedListener برنامه خود را فراخوانی کنید و بتوانید خرید جدید را به عنوان بخشی از فراخوانی queryPurchasesAsync() بازیابی کنید. بک‌اند شما بلافاصله یک اعلان توسعه‌دهنده بلادرنگ SUBSCRIPTION_PURCHASED دریافت می‌کند.

DEFERRED

اشتراک سطح ۱ سم‌وایز تا تاریخ ۳۰ آوریل (۱۰ اردیبهشت) اعتبار دارد. در اول ماه مه (۱۱ اردیبهشت)، اشتراک سطح ۲ اعمال می‌شود و سم‌وایز برای سطح اشتراک جدید خود ۳۶ دلار هزینه دریافت می‌کند.

شما باید به محض موفقیت خرید، PurchasesUpdatedListener برنامه خود را فراخوانی کنید و بتوانید خرید جدید را به عنوان بخشی از فراخوانی queryPurchasesAsync() بازیابی کنید. بک‌اند شما بلافاصله یک اعلان توسعه‌دهنده بلادرنگ SUBSCRIPTION_PURCHASED دریافت می‌کند. شما باید خرید را به همان روشی که هر خرید جدید دیگری را در آن مرحله پردازش می‌کنید، پردازش کنید . به طور خاص، مطمئن شوید که خرید جدید را تأیید می‌کنید. توجه داشته باشید که startTime اشتراک جدید در لحظه مؤثر شدن جایگزینی، که زمانی اتفاق می‌افتد که اشتراک قدیمی منقضی می‌شود، پر می‌شود. در آن مرحله، یک RTDN SUBSCRIPTION_RENEWED برای طرح اشتراک جدید دریافت می‌کنید. درباره رفتار ReplacementMode.DEFERRED در Handle deferred replacement بیشتر بخوانید.

CHARGE_FULL_PRICE

اشتراک سطح ۱ سم‌وایز بلافاصله پایان می‌یابد. اشتراک سطح ۲ او از امروز آغاز می‌شود و ۳۶ دلار از او کسر می‌شود. از آنجایی که او هزینه یک ماه کامل را پرداخت کرده اما فقط نیمی از آن را استفاده کرده است، نیمی از اشتراک یک ماه (۱ دلار) برای اشتراک جدید او اعمال می‌شود. از آنجایی که هزینه اشتراک جدید ۳۶ دلار در سال است، ۱/۳۶ام سال به دوره اشتراک او اضافه می‌شود (حدود ۱۰ روز). بنابراین، هزینه بعدی سم‌وایز از امروز ۱ سال و ۱۰ روز با ۳۶ دلار خواهد بود. پس از آن، هر سال ۳۶ دلار از او کسر می‌شود.

هنگام انتخاب حالت تناسب، حتماً توصیه‌های جایگزینی ما را بررسی کنید.

KEEP_EXISTING

سم‌وایز اشتراک محتوای آنلاین اپلیکیشن Country Gardener را دارد. او اشتراک ماهانه‌ی Plan 1 را برای محتوای پایه دارد. این اشتراک با قیمت اولیه‌ی ۲ دلار در ماه به مدت ۳ ماه و سپس ۴ دلار در ماه ارائه می‌شود. سم‌وایز این اشتراک را در اول آوریل خریداری کرد. اپلیکیشن Country Gardener، Plan 2 را به عنوان یک محتوای ویژه‌ی اضافی با قیمت ۳ دلار در ماه ارائه می‌دهد. در ۱۵ آوریل، سم‌وایز Plan 2 را به اشتراک اپلیکیشن Country Gardener خود اضافه کرد و در عین حال Plan 1 موجود را حفظ کرد. برنامه‌ی پرداخت سم‌وایز به شرح زیر است:

  • قیمت پیشنهادی ۱.۵۰ دلار برای طرح ۲، که قرار است در ۱۵ آوریل ارائه شود.
  • قیمت ۵ دلار در ماه برای ۲ ماه بعدی که شامل قیمت مقدماتی طرح ۱ و قیمت معمولی طرح ۲ می‌شود.
  • پس از آن، پرداخت ماهانه ثابت ۷.۰۰ دلار.

اعمال تغییرات اشتراک در برنامه

برنامه شما می‌تواند با استفاده از همان مراحل راه‌اندازی جریان خرید ، به کاربران ارتقا یا تنزل رتبه ارائه دهد. با این حال، هنگام ارتقا یا تنزل رتبه، باید جزئیات اشتراک فعلی، اشتراک آینده (ارتقاء یافته یا تنزل یافته) و حالت جایگزینی مورد استفاده را ارائه دهید.

برای جایگزینی از SubscriptionProductReplacementParams استفاده کنید (ترجیحاً)

مثال زیر نحوه به‌روزرسانی یک اشتراک را با استفاده از SubscriptionProductReplacementParams نشان می‌دهد.

  • شیء BillingFlowParams.ProductDetailsParams اکنون دارای متد setSubscriptionProductReplacementParams() برای تعیین اطلاعات جایگزینی سطح محصول است.

  • SubscriptionProductReplacementParams دو متد setter دارد:

    • setOldProductId: این محصول قدیمی است که قرار است با محصول موجود در ProductDetails.
    • setReplacementMode: این حالت جایگزینی سطح آیتم است. این حالت‌ها اساساً مشابه SubscriptionUpdateParams هستند، اما نگاشت مقدار به‌روزرسانی شده است.
  • پارامترهای به‌روزرسانی سطح خرید موجود BillingFlowParams.setSubscriptionUpdateParams() باید با setOldPurchaseToken() ساخته شوند.

  • زمانی که setSubscriptionProductReplacementParams() برای هر یک از ProductDetailsParams فراخوانی شود، SubscriptionUpdateParams.setSubscriptionReplacementMode() هیچ تاثیری نخواهد داشت.

نمونه کد زیر نحوه تغییر یک طرح اشتراک از ( old_product_1 , old_product_2 ) به ( product_1 , product_2 , product_3 ) را نشان می‌دهد. در این سناریو، product_1 جایگزین old_product_1 می‌شود، product_2 جایگزین old_product_2 می‌شود و product_3 بلافاصله به اشتراک اضافه می‌شود.

کاتلین

val billingClient: BillingClient = ...
val replacementModeForBasePlan: Int = ...
val replacementModeForAddon: Int = ...

val purchaseTokenOfExistingSubscription: String = "your_old_purchase_token"

// ProductDetails instances obtained from queryProductDetailsAsync();

val productDetailsParams1 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails1_obj) // Required: Set the ProductDetails object
        .setSubscriptionProductReplacementParams(
            SubscriptionProductReplacementParams.newBuilder()
                .setOldProductId("old_product_id_1")
                .setReplacementMode(replacementModeForBasePlan)
                .build()
        )
        .build()

val productDetailsParams2 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails2_obj) // Required: Set the ProductDetails object
        .setSubscriptionProductReplacementParams(
            SubscriptionProductReplacementParams.newBuilder()
                .setOldProductId("old_product_id_2")
                .setReplacementMode(replacementModeForAddon)
                .build()
        )
        .build()

// Example for a third item without replacement params
val productDetailsParams3 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails3_obj) // Required: Set the ProductDetails object
        .build()

val newProductDetailsList = listOf(
    productDetailsParams1,
    productDetailsParams2,
    productDetailsParams3
)

val billingFlowParams =
    BillingFlowParams.newBuilder()
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder()
                .setOldPurchaseToken(purchaseTokenOfExistingSubscription)
                .build()
        )
        .setProductDetailsParamsList(newProductDetailsList)
        .build()

// To launch the billing flow:
// billingClient.launchBillingFlow(activity, billingFlowParams)

جاوا

BillingClient billingClient = ;

int replacementModeForBasePlan =;
int replacementModeForAddon =;
// ProductDetails obtained from queryProductDetailsAsync().
ProductDetailsParams productDetails1 =
  ProductDetailsParams.newBuilder()
      .setSubscriptionProductReplacementParams(
           SubscriptionProductReplacementParams.newBuilder()
               .setOldProductId("old_product_id_1")
               .setReplacementMode(replacementModeForBasePlan))
               .build();
ProductDetailsParams productDetails2 =
  ProductDetailsParams.newBuilder()
      .setSubscriptionProductReplacementParams(
           SubscriptionProductReplacementParams.newBuilder()
               .setOldProductId("old_product_id_2")
               .setReplacementMode(replacementModeForAddon))
               .build();
ProductDetailsParams productDetails3 = ...;

ArrayList newProductDetailsList = new ArrayList<>();
newProductDetailsList.add(productDetails1);
newProductDetailsList.add(productDetails2);
newProductDetailsList.add(productDetails3);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setSubscriptionUpdateParams(
          SubscriptionUpdateParams.newBuilder()
              .setOldPurchaseToken(purchaseTokenOfExistingSubscription)
             .build())
        .setProductDetailsParamsList(productDetailsList)
        .build();

billingClient.launchBillingFlow(billingFlowParams);

تنظیم SubscriptionUpdateParams برای جایگزینی (منسوخ شده)

مثال زیر نحوه به‌روزرسانی یک اشتراک را با استفاده از SubscriptionUpdateParams نشان می‌دهد.

کاتلین

val offerToken = productDetails
        .getSubscriptionOfferDetails(selectedOfferIndex)
        .getOfferToken()

val billingParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(
       listOf(
           BillingFlowParams.ProductDetailsParams.newBuilder()
               .setProductDetails(productDetails)
               .setOfferToken(offerToken)
               .build()
       )
       ).setSubscriptionUpdateParams(
           BillingFlowParams.SubscriptionUpdateParams.newBuilder()
               .setOldPurchaseToken("old_purchase_token")
               .setSubscriptionReplacementMode(
                 BillingFlowParams.ReplacementMode.CHARGE_FULL_PRICE
               )
               .build()
       ).build()

billingClient.launchBillingFlow(
    activity,
    billingParams
   )
// ...

جاوا

String offerToken = productDetails
    .getSubscriptionOfferDetails(selectedOfferIndex)
    .getOfferToken();

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        ImmuableList.of(
            ProductDetailsParams.newBuilder()
                // fetched via queryProductDetailsAsync
                .setProductDetails(productDetails)
                // offerToken can be found in
                // ProductDetails=>SubscriptionOfferDetails
                .setOfferToken(offerToken)
                .build()))
    .setSubscriptionUpdateParams(
        SubscriptionUpdateParams.newBuilder()
            // purchaseToken can be found in Purchase#getPurchaseToken
            .setOldPurchaseToken("old_purchase_token")
            .setSubscriptionReplacementMode(ReplacementMode.CHARGE_FULL_PRICE)
            .build())
    .build();

BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
// ...

توصیه‌های جایگزینی

جدول زیر سناریوهای مختلف نسبت‌بندی را به همراه آنچه برای هر سناریو توصیه می‌کنیم، نشان می‌دهد:

سناریو حالت جایگزینی توصیه شده نتیجه
ارتقا به یک سطح گران‌تر CHARGE_PRORATED_PRICE کاربر بلافاصله دسترسی پیدا می‌کند و در همان دوره صورتحساب باقی می‌ماند.
تنزل به سطح پایین‌تر DEFERRED کاربر قبلاً هزینه سطح گران‌تر را پرداخت کرده است، بنابراین تا تاریخ صورتحساب بعدی به آن دسترسی خواهد داشت.
ارتقا در دوره آزمایشی رایگان، حفظ نسخه آزمایشی WITHOUT_PRORATION کاربر در مدت زمان باقی‌مانده از دوره آزمایشی، بدون پرداخت هزینه اضافی، به سطح بالاتر ارتقا می‌یابد.
ارتقا در دوره آزمایشی رایگان - پایان دادن به دسترسی به دوره آزمایشی رایگان CHARGE_PRORATED_PRICE کاربر بلافاصله به سطح جدید دسترسی پیدا می‌کند، مبلغ باقیمانده از دوره آزمایشی رایگان به سطح بعدی منتقل می‌شود. مبلغ قابل انتقال بر اساس قیمت‌گذاری طرح پایه محاسبه می‌شود.
بدون تغییر نگه داشتن برنامه پرداخت برخی از اقلام اشتراک در حین اضافه یا حذف کردن سایر اقلام اشتراک از اشتراک با افزونه‌ها. KEEP_EXISTING کاربر همچنان قیمت قدیمی را برای کالای بدون تغییر پرداخت می‌کند. کالاهای جدید بلافاصله اضافه می‌شوند. سایر کالاهای قدیمی را می‌توان با تعیین حالت جایگزینی جایگزین یا حذف کرد.

مدیریت خریدهای تغییر اشتراک

تغییرات طرح، خریدهای جدید برای همه شرایط و اهداف محسوب می‌شوند و باید پس از اتمام موفقیت‌آمیز جریان صورتحساب، پردازش و تأیید شوند. علاوه بر پردازش مناسب خرید جدید، باید خریدی که جایگزین می‌شود را کنار بگذارید.

رفتار درون برنامه‌ای مانند هر خرید جدیدی است. برنامه شما نتیجه خرید جدید را در PurchasesUpdatedListener دریافت می‌کند و خرید جدید در queryPurchasesAsync در دسترس است.

رابط برنامه‌نویسی کاربردی توسعه‌دهندگان گوگل پلی (Google Play Developer API) هنگامی که یک خرید جایگزین خرید موجود می‌شود، یک linkedPurchaseToken در منبع اشتراک برمی‌گرداند. حتماً توکن ارائه شده در linkedPurchaseToken را نامعتبر کنید تا مطمئن شوید که توکن قدیمی برای دسترسی به سرویس‌های شما استفاده نمی‌شود. برای اطلاعات مربوط به مدیریت خریدهای ارتقا و تنزل رتبه ، به بخش ارتقاها، تنزل رتبه‌ها و استعفاها مراجعه کنید.

وقتی توکن خرید جدید را دریافت کردید، همان فرآیند تأیید را که برای تأیید توکن خرید جدید انجام دادید ، دنبال کنید. حتماً این خریدها را با BillingClient.acknowledgePurchase() از کتابخانه صورتحساب Google Play یا Purchases.subscriptions:acknowledge از API توسعه‌دهندگان Google Play تأیید کنید.

رسیدگی به تعویض‌های معوق

حالت جایگزینی معوق به شما امکان می‌دهد تا به کاربر اجازه دهید قبل از شروع طرح جدید، از حق استفاده باقی‌مانده در طرح قدیمی خود استفاده کند.

وقتی از ReplacementMode.DEFERRED برای یک خرید جدید استفاده می‌کنید، queryPurchasesAsync() یک توکن خرید جدید را پس از جریان خرید برمی‌گرداند که تا زمان انجام جایگزینی معوق در تاریخ تمدید بعدی، با محصول قدیمی مرتبط باقی می‌ماند و پس از آن محصول جدید بازگردانده می‌شود.

در گذشته می‌توانستید این تجربه کاربری را با ProrationMode.DEFERRED منسوخ‌شده به دست آورید، اما ProrationMode.DEFERRED با Play Billing Library 6 منسوخ شده است. برای درک تفاوت‌های رفتاری، به جدول زیر مراجعه کنید:

زمان

ProrationMode.DEFERRED (منسوخ شده)

حالت جایگزینی.به تعویق افتاده

درست پس از موفقیت جریان خرید (اپلیکیشن)

PurchasesUpdatedListener پس از خرید با وضعیتی مبنی بر موفقیت‌آمیز بودن ارتقا یا تنزل رتبه فراخوانی می‌شود.

حق استفاده از طرح قدیمی تا تاریخ تمدید بعدی ادامه می‌یابد. برای اطمینان از اینکه برنامه حق استفاده صحیح را ارائه می‌دهد، queryPurchasesAsync() یک شیء Purchase را با توکن خرید اصلی و حق استفاده اصلی تا زمان جایگزینی برمی‌گرداند.

توکن خرید جدید نمایش داده نشده است، بنابراین در حال حاضر قابل پردازش نیست.

PurchasesUpdatedListener پس از خرید با وضعیتی مبنی بر موفقیت‌آمیز بودن ارتقا یا تنزل رتبه فراخوانی می‌شود.

queryPurchasesAsync() خرید را با توکن خرید جدید و حق دسترسی اصلی مرتبط با آن، فوراً برمی‌گرداند.

توکن خرید جدید نمایش داده شده است، بنابراین باید در این مرحله با در نظر گرفتن زمان جایگزینی، پردازش شود.

درست پس از موفقیت جریان خرید (بک‌اند)

SUBSCRIPTION_PURCHASED RTDN پس از جریان خرید ارسال نمی‌شود . بک‌اند هنوز از خرید جدید مطلع نشده است.

RTDN مربوط به خرید اشتراک (SUBSCRIPTION_PURCHASED) با شناسه محصول قدیمی بلافاصله پس از جریان خرید برای توکن خرید جدید ارسال می‌شود.

فراخوانی متد purchases.subscriptionsv2.get با توکن خرید جدید، خریدی را برمی‌گرداند که دارای یک 'startTime' است که زمان خرید را با دو آیتم خطی نشان می‌دهد:

  • یکی نشان دهنده حق دسترسی قدیمی است و دارای یک «زمان انقضا» در آینده است. حق دسترسی قدیمی تمدید نخواهد شد و دارای یک DeferredItemReplacement است که حاوی حاصل حق دسترسی جدید است. این نشان دهنده جایگزینی در انتظار حق دسترسی قدیمی پس از انقضای آن است.
  • یکی که نشان‌دهنده‌ی حق امتیاز تازه خریداری شده است . هیچ مقداری برای «زمان انقضا» تعیین نشده است.

SUBSCRIPTION_EXPIRED برای توکن خرید قدیمی ارسال شد. هنگام فراخوانی متد purchases.subscriptionsv2.get با توکن خرید قدیمی ، به صورت منقضی شده نمایش داده می‌شود (حق استفاده از طرح قدیمی برای مدت زمان باقی مانده به خرید جدید منتقل می‌شود).

در زمان تعویض - اولین تمدید پس از جریان خرید (اپلیکیشن)

queryPurchasesAsync() یک شیء خرید جدید با توکن خرید و حق استفاده جدید برمی‌گرداند.

توکن خرید جدید اکنون نمایش داده شده است، بنابراین باید پردازش شود.

queryPurchasesAsync() بلافاصله خرید با توکن خرید جدید و حق دسترسی جدید مرتبط با آن را برمی‌گرداند.

خرید جدید باید از قبل و زمانی که جریان خرید با موفقیت انجام شد، پردازش شده باشد، بنابراین برنامه نباید هیچ اقدام خاصی انجام دهد، جز اینکه مطمئن شود حق خرید به درستی اعطا شده است.

در زمان جایگزینی - اولین تمدید پس از جریان خرید (پشتیبانی)

اکنون خرید جدید می‌تواند با ارسال اولین SUBSCRIPTION_RENEWED RTDN پردازش و تأیید شود.

linkedPurchaseToken در منبع اشتراک می‌توان برای تعیین اینکه کدام کاربر در backend اشتراک شما، در صورت لزوم، باید با مجوز جدید به‌روزرسانی شود، استفاده کرد.

خرید جدید پردازش و تأیید شد زمانی که SUBSCRIPTION_PURCHASED RTDN برای توکن خرید جدید ارسال شد و به عنوان 'startTime' ثبت شد.

با ReplacementMode.DEFERRED، اولین تمدیدها از رفتار استاندارد هر تمدید دیگری پیروی می‌کنند و نیازی نیست هنگام وقوع این رویداد، منطق خاصی را برای جایگزینی‌ها مدیریت کنید.

هنگام فراخوانی متد purchases.subscriptionsv2.get با توکن خرید جدید، یک خرید با دو آیتم خطی برمی‌گرداند:

  • یکی نشان‌دهنده‌ی حق دسترسی قدیمی ، با یک `expiryTime` در گذشته و بدون مقدار تعیین‌شده برای DeferredItemReplacement .
  • یکی نشان‌دهنده‌ی حق دسترسی جدید ، با `expiryTime` در آینده و پرچم auto_renewing_enabled روشن.

ReplacementMode.DEFERRED should be used from now on instead of the deprecated ProrationMode.DEFERRED, as it presents the same behavior regarding entitlement changes, but offers a way to manage the purchase that is more consistent with behaviors for other new purchases.

مدیریت مشتری

Using Real-time developer notifications, you can detect in real time when a user decides to cancel. When a user cancels, but before their subscription has expired, you can send them push notifications or in-app messages to ask them to resubscribe.

After a user has cancelled their subscription, you can try to win them back either in your app, or through the Play store. The following table describes various subscription scenarios along with associated winback actions and app requirements.

Before subscription expiration After subscription expiration
In-app In Play Store In-app In Play Store
Winback feature In-app subscription بازیابی In-app subscription اشتراک مجدد
User goes through checkout flow بله خیر بله بله
User subscription remains associated with the same SKU User can sign up for same or different SKU بله User can sign up for same or different SKU بله
Creates new purchase token بله خیر بله بله
به طور پیش‌فرض فعال است خیر Yes, support required for all devs خیر

Apps without Billing Library 2.0+: No

Apps with Billing Library 2.0+: Yes. Devs can opt-out in Console.

When user is charged

If using same SKU: end of current billing period.

If using different SKU: depends on proration mode.

End of current billing period بلافاصله بلافاصله
Implementation required Provide a re-signup UI in your app

Detect change in subscription state

Deep-link to Play Store

Provide a re-signup UI in your app Handle out-of-app purchases

Before subscription expiration - in-app

For subscriptions that have been canceled but have not yet expired, you can allow subscribers to restore their subscription within your app by applying the same in-app product purchase flow as for new subscribers. Ensure your UI reflects that the user has an existing subscription. For example, you might want to display the user's current expiration date and recurring price with a Reactivate button.

Most of the time, you will want to offer the user the same price and SKU they were already subscribed to, as follows:

  • Initiate a new subscription purchase with the same SKU.
  • The new subscription replaces the old one and renews on the same expiration date. The old subscription is immediately marked as expired.
  • As an example, Achilles has a subscription to Example Music App, and the subscription is due to expire on August 1. On July 10, he resubscribes to the one-month subscription at the same price per month. The new subscription is prorated with the remaining credit, is immediately active, and still renews on August 1.

If you would like to offer a different price—for example a new free trial or a winback discount—you can instead offer a different SKU to the user:

  • Initiate an upgrade or downgrade with the different SKU using the replacement mode WITHOUT_PRORATION .
  • The new subscription replaces the old one and renews on the same expiration date. The user is charged the price of the new SKU, including any introductory prices, on the original expiration date. If the old subscription was created using an obfuscated account ID, that same ID should be passed to the BillingFlowParams for upgrades and downgrades.
  • As an example, Achilles has a subscription to Example Music App, and the subscription is due to expire on August 1. On July 10, he resubscribes to an annual subscription with an introductory price. The new subscription is immediately active, and the user is charged the introductory price on August 1.
  • If you decide to include a free trial or intro price in your winback SKU, ensure that the user is eligible by unchecking the Allow one free trial per app box in the Google Play Console, which restricts the user to getting one free trial per app.

When you receive the purchase token, process the purchase just as you would with a new subscription. Additionally, the Google Play Developer API returns a linkedPurchaseToken in the subscription resource. Be sure to invalidate the token provided in the linkedPurchaseToken to ensure that the old token is not used to gain access to your services.

Before subscription expiration - in Play Store

While the subscription is canceled but still active, users can restore the subscription in the Google Play subscriptions center by clicking Resubscribe (previously Restore ). This keeps the same subscription and purchase token.

subscriptions section in the google play store app showing a
            cancelled subscription with a resubscribe button
Figure 8. Account > Subscriptions section in the Google Play Store app showing a cancelled subscription with a Resubscribe button.

For more information on restoring subscriptions, see Restorations .

After subscription expiration - in-app

You can allow expired subscribers to resubscribe within your app by applying the same in-app product purchase flow as for new subscribers. Note the following:

  • To offer users a discount, you might want to offer a product ID with special pricing for your subscription, also called a winback SKU . You can provide the offer in your app, or you can notify the user of the offer outside of the app, such as in email.
  • To start a winback subscription, launch the purchase flow in your Android app using the Google Play Billing Library. This is the same process as with a new subscription, but you can determine the SKU that is available to the user.
  • If you decide to include a free trial or intro price in your winback SKU, ensure that the user is eligible by unchecking the Allow one free trial per app box in the Google Play Console, which restricts the user to getting one free trial per app.
  • If the user resubscribes to the same SKU, they are no longer eligible for free trials or introductory price. Ensure that your UI reflects this.

When you receive the purchase token, process the purchase just as you would with a new subscription. You won't receive a linkedPurchaseToken in the subscription resource.

After subscription expiration - in Play Store

If enabled, users can resubscribe to the same SKU for up to one year after expiration by clicking Resubscribe in the Google Play subscriptions center. This generates a new subscription and purchase token.

subscriptions section in the google play store app showing a
            cancelled and expired subscription with resubscribe and remove
            buttons
Figure 9. Account > Subscriptions section in the Google Play Store app showing a cancelled and expired subscription with Resubscribe and Remove buttons.

Re-subscribing is considered an out-of-app purchase, so be sure to follow best practices for handling purchases made from outside your app .

Promote your subscription

You can create promotion codes to give selected users an extended free trial to an existing subscription. To learn more, see Promo codes .

For free trials, Google Play verifies that the user has a valid payment method before starting the free trial. Some users may see this verification as a hold or charge on their payment method. This hold or charge is temporary and is later reversed or refunded.

After the trial period ends, the user's payment method is charged for the full subscription amount.

If a user cancels a subscription at any time during the free trial, the subscription remains active until the end of the trial, and they aren't charged when the free trial period ends.

Cancel or revoke

You can use the Google Play Developer API to cancel or revoke a subscription. This functionality is also available in the Google Play Console .

  • Cancel : Users can cancel a subscription on Google Play. You can also provide an option for users to cancel in your app or on your website. Your app should handle these cancellations as described in Cancellations .

  • Revoke : When you revoke, the user immediately loses access to the subscription. This can be used if, for example, there was a technical error that prevented the user from accessing your product, and the user does not want to continue using the product. Your app should handle these cancellations as described in Revocations .

The following table illustrates the differences between cancel and revoke.

Stops renewal لغو دسترسی
لغو بله خیر
لغو بله بله

Defer billing for a subscriber

You can advance the next billing date for an auto-renewing subscriber by using Purchases.subscriptions:defer from the Google Play Developer API. During the deferral period, the user is subscribed to your content with full access but is not charged. The subscription renewal date is updated to reflect the new date.

For prepaid plans, you can use the defer billing API to defer the expiration time.

Deferred billing lets you do the following:

  • Give users free access as a special offer, such as giving one week free for purchasing a movie.
  • Give free access to customers as a gesture of goodwill.

Billing can be deferred by as little as one day and by as long as one year per API call. To defer the billing even further, you can call the API again before the new billing date arrives.

As an example, Darcy has a monthly subscription to online content for the Fishing Quarterly app. She is normally billed £1.25 on the first of each month. In March, she participated in an online survey for the app publisher. The publisher rewards her with six free weeks by deferring the next payment until May 15, which is six weeks after her previously scheduled billing date of April

  1. Darcy is not charged for April or the beginning of May and still has access to the content. On May 15, she is charged the normal £1.25 subscription fee for the month. Her next renewal date is now June 15.

When deferring, you might want to notify the user by email or within the app to notify them that their billing date has changed.

Handling payment declines

If there are payment issues with a subscription renewal, Google will periodically attempt to renew the subscription for some time before canceling. This recovery period can consists of a grace period, followed by an account hold period. During this time, Google sends the user emails and notifications prompting them to update their payment method.

Upon payment decline, the subscription enters a grace period if one is configured. During the grace period, you should ensure the user still has access to the subscription entitlements.

After any grace period has ended, the subscription enters an account hold period. During account hold, you should ensure the user does not have access to the subscription entitlements.

You can specify the length of each auto-renewing base plan's grace period and account hold in the Google Play Console. Specifying lengths less than the default values may reduce the number of subscriptions recovered from payment declines.

To maximize the likelihood of subscription recovery during a payment decline, you can inform your user of a payment issue and ask them to fix it.

You can either do this yourself, as described in the grace period and account hold sections, or you can implement the in-app messaging API, where Google shows a message to users in your app.

In-app messaging

If you've enabled in-app messaging with InAppMessageCategoryId.TRANSACTIONAL , Google Play will show users messaging during grace period and account hold once per day and provide them an opportunity to fix their payment without leaving the app.

Snackbar notifying the user to fix their payment
Figure 20. Snackbar notifying the user to fix their payment.

We recommend that you call this API whenever the user opens the app to determine whether the message should be shown.

If the user successfully recovered their subscription, you will receive a response code of SUBSCRIPTION_STATUS_UPDATED along with a purchase token. You should then use this purchase token to call the Google Play Developer API and refresh the subscription status in your app.

Integrate in-app messaging

To show in-app messaging to user, use BillingClient.showInAppMessages() .

Here is an example of triggering the in-app messaging flow:

کاتلین

val inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build()

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        object : InAppMessageResponseListener() {
            override fun onInAppMessageResponse(inAppMessageResult: InAppMessageResult) {
                if (inAppMessageResult.responseCode == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        })

جاوا

InAppMessageParams inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build();

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        new InAppMessageResponseListener() {
            @Override
            public void onInAppMessageResponse(InAppMessageResult inAppMessageResult) {
                if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        });

Handle subscription pending transactions

Pending transactions can happen in initial purchase, top-up, upgrade or downgrade. The subscription purchase starts with the SUBSCRIPTION_STATE_PENDING state before transitioning to SUBSCRIPTION_STATE_ACTIVE . If the transaction is expired or canceled by the user, it goes to SUBSCRIPTION_STATE_PENDING_PURCHASE_EXPIRED . You must and should only update the user's entitlement after the transaction is completed.

Subscription state change for initial purchase with pending transactions is straightforward. Your app receives a Purchase with PENDING state when the user initiates a pending transaction. When the transaction is completed, your app receives the Purchase again with state updated to PURCHASED . A SubscriptionNotification message with type SUBSCRIPTION_PURCHASED is sent to your RTDN client. Follow the normal process to verify the purchase, give the user access to the content and acknowledge the purchase. If the transaction expires or is canceled, a SubscriptionNotification message with type SUBSCRIPTION_PENDING_PURCHASE_CANCELED is sent to your RTDN client. In such cases, the user should never have gained access to the content.

Top-up, upgrade or downgrade with pending transactions involves state changes for both the old and new subscriptions. When the user initiates a pending top-up, upgrade or downgrade transaction, your app receives a Purchase for the old subscription with a PendingPurchaseUpdate object. At this time, the user is still owning the old subscription and has not gained the new subscription yet. Calling getProducts() and getPurchaseToken() on the PendingPurchaseUpdate object returns the product ids and purchase token of the new subscription. When the transaction is completed, your app receives a Purchase with the top-level purchase token set for the new subscription and the state set to PURCHASED . A SubscriptionNotification message with type SUBSCRIPTION_PURCHASED is sent to your RTDN client. Only at this time, you should replace the old purchase token with the new purchase token and update the user's access to the content. If the transaction expires or is canceled, a SubscriptionNotification message with type SUBSCRIPTION_PENDING_PURCHASE_CANCELED is sent to your RTDN client. In such cases, the user should still have access to the content of the old subscription.