پیاده‌سازی جایگزینی اشتراک با Google Play Billing

۱. مقدمه

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

مخاطب

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

آنچه یاد خواهید گرفت...

  • نحوه ایجاد اشتراک در کنسول توسعه‌دهندگان پلی
  • چگونه ReplacementMode (ReplacementMode) صحیح (مثلاً WITH_TIME_PRORATION در مقابل DEFERRED ) را برای مطابقت با سیاست‌های ارتقا و تنزل نسخه برنامه خود انتخاب کنید.
  • نحوه پیکربندی BillingFlowParams در launchBillingFlow برای فعال کردن جریان خرید Google Play برای جایگزینی طرح.
  • نحوه استفاده از اعلان‌های توسعه‌دهنده بلادرنگ (RTDN) و API purchases.subscriptionsv2 برای لغو ایمن دسترسی قدیمی و اعطای دسترسی جدید در backend شما

آنچه نیاز دارید

۲. ساخت اپلیکیشن نمونه

این آزمایشگاه کد از یک برنامه اندروید نمونه برای نشان دادن نحوه پیاده‌سازی جایگزینی اشتراک در PBL استفاده می‌کند. برنامه نمونه به گونه‌ای طراحی شده است که یک برنامه اندروید کاملاً کاربردی باشد و کد منبع کاملی دارد که جنبه‌های زیر را نشان می‌دهد:

  • ادغام برنامه با PBL
  • پیاده‌سازی جایگزینی اشتراک

اگر از قبل با جایگزین‌های اشتراک و PBL آشنا هستید، می‌توانید برنامه نمونه را دانلود کرده و با آن کار کنید.

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

پیش‌نیازها

قبل از ساخت و استقرار برنامه نمونه، موارد زیر را انجام دهید:

ساختن

برای ساخت برنامه نمونه طبق دستورالعمل‌های Codelab، مراحل زیر را دنبال کنید:

  1. برنامه نمونه را از گیت‌هاب دانلود کنید.
  2. شناسه applicationId در فایل build.gradle برنامه نمونه به‌روزرسانی کنید تا شناسه برنامه شما در کنسول توسعه‌دهندگان Play منعکس شود.
  3. برنامه نمونه را بسازید .
    نکته : این دستور، برنامه را برای آزمایش محلی با موفقیت می‌سازد. با این حال، اجرای برنامه محصولات و قیمت‌ها را دریافت نمی‌کند زیرا اشتراک‌های مورد نیاز هنوز در کنسول توسعه‌دهندگان Play ایجاد نشده‌اند. بخش بعدی، ایجاد اشتراک‌ها در کنسول توسعه‌دهندگان را پوشش خواهد داد.

۳. ایجاد اشتراک در کنسول پلی

The Google Play subscriptions system provides flexibility in how you create, manage, and sell subscriptions. In the Play Console, you can configure subscriptions with multiple base plans, each containing multiple offers. Subscription offers can have various pricing models and eligibility options. For this codelab, you will create three subscriptions: Premium Plan , Basic Plan , and Lite Plan , simulating a typical subscription offering at various price points. Each of these will have a single monthly recurring base plan.

ایجاد اشتراک جدید

برای ایجاد اشتراک جدید

  1. کنسول Play را باز کنید و به صفحه اشتراک‌ها بروید ( کسب درآمد با Play > محصولات > اشتراک‌ها )
  2. روی ایجاد اشتراک کلیک کنید.
  3. جزئیات اشتراک خود را وارد کنید:
    • شناسه محصول : یک شناسه منحصر به فرد برای محصول وارد کنید. premium_plan را وارد کنید.
    • نام : یک نام کوتاه برای اشتراک وارد کنید. مثال: Premium Plan .
  4. روی ایجاد کلیک کنید.

طرح پایه را ایجاد کنید

  1. کنسول Play را باز کنید و به صفحه اشتراک‌ها بروید ( کسب درآمد با Play > محصولات > اشتراک‌ها )
  2. در کنار اشتراکی که می‌خواهید در آن یک طرح پایه ایجاد کنید، روی پیکان سمت راست کلیک کنید تا جزئیات اشتراک را مشاهده کنید.
  3. روی افزودن طرح پایه کلیک کنید.
  4. شناسه طرح پایه را وارد کنید. مثال: monthly-auto-renewing .
  5. نوع را به عنوان تمدید خودکار انتخاب کنید.
  6. برای طرح پایه تمدید خودکار، موارد زیر را تنظیم کنید:
    • دوره صورتحساب: ماهانه .
    • دوره فیض: 7 روز .
    • تغییرات طرح و پیشنهاد صورتحساب: هزینه در تاریخ صورتحساب محاسبه می‌شود .
    • اشتراک مجدد: اجازه دهید .
  7. در بخش قیمت و موجودی ، برای تعیین قیمت طرح پایه، روی «تنظیم قیمت‌ها» کلیک کنید.
  8. همه کشورها و مناطق را انتخاب کنید و سپس روی «تنظیم قیمت» کلیک کنید.
  9. قیمت این طرح پایه را ۱۰ دلار تعیین کنید و روی به‌روزرسانی کلیک کنید.
  10. پس از تعیین قیمت برای طرح پایه، در پایین سمت راست روی ذخیره و سپس فعال‌سازی کلیک کنید.

ایجاد اشتراک برای برنامه نمونه

برای اهداف این آزمایشگاه کد، دو اشتراک اضافی با پیکربندی زیر ایجاد کنید:

  • طرح پایه
    • شناسه محصول: basic_plan
    • نام: طرح پایه
    • شناسه طرح پایه: تمدید خودکار ماهانه
    • قیمت: ۵ دلار
  • طرح لایت
    • شناسه محصول: lite_plan
    • نام: طرح لایت
    • شناسه طرح پایه: تمدید خودکار ماهانه
    • قیمت: ۳ دلار

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

ویدیوی ایجاد اشتراک

ویدیوی زیر مراحلی را که قبلاً برای ایجاد اشتراک در کنسول توسعه‌دهندگان Play توضیح داده شده است، نشان می‌دهد.

۴. جایگزین‌های اشتراک

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

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

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

کتابخانه پرداخت Play چندین گزینه ReplacementMode برای کنترل این رفتار ارائه می‌دهد.

حالت‌های جایگزینی موجود

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

۵. با زمان_مناسب

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

سناریوی مثال

یک کاربر در ۱۵ آوریل که نیمی از چرخه صورتحساب ماهانه‌اش را گذرانده است، از طرح پایه (۴.۹۹ دلار در ماه) به طرح پریمیوم (۹.۹۹ دلار در ماه) تغییر می‌دهد.

در این سناریو:

  • کاربر بلافاصله به طرح پریمیوم دسترسی پیدا می‌کند.
  • گوگل پلی به طور خودکار دوره سرشکنی را محاسبه می‌کند. فرض کنید، اگر پلی محاسبه کند که ۱۵ روز باقیمانده از طرح پایه معادل ۷ روز طرح پریمیوم است، تاریخ صدور صورتحساب بعدی به ۲۱ آوریل موکول می‌شود.
  • هیچ پرداخت فوری از کاربر لازم نیست.

قطعه کد

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

با WITH_TIME_PRORATION ارتقا دهید

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode را در قطعه کد به SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح پریمیوم (Premium ) تغییر دهید.

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

با WITH_TIME_PRORATION به نسخه پایین‌تر برگردید

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode را در قطعه کد به SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح Lite تغییر دهید.

حق استفاده کاربر بلافاصله به طرح Lite کاهش می‌یابد. مبلغی که باید بلافاصله پرداخت شود 0.00 دلار است. مبلغ باقیمانده طرح Basic به نسبت زمان برای طرح Lite محاسبه می‌شود که تاریخ تمدید بعدی را به میزان قابل توجهی افزایش می‌دهد. مبلغ تمدید 2.99 دلار در تاریخ صورتحساب جدید از کاربر کسر خواهد شد.

نتیجه‌گیری

در این بخش، یاد گرفتید که چگونه WITH_TIME_PRORATION با تنظیم زمان تا تمدید بعدی بر اساس اختلاف قیمت، حقوق کاربران را بدون هزینه فوری تغییر می‌دهد. این یک استراتژی پیش‌فرض مؤثر برای ارتقا یا تنزل رتبه کاربران است.

۶. قیمت پیشنهادی

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

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

سناریوی مثال

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

در این سناریو:

  • کاربر بلافاصله به طرح پریمیوم دسترسی پیدا می‌کند.
  • بلافاصله مابه‌التفاوت سرشکن‌شده برای ۱۰ روز باقیمانده از چرخه صورتحساب فعلی از کاربر کسر می‌شود. این مبلغ تقریباً ۲.۹۹ دلار می‌شود که معادل ۱۰ روز اشتراک طرح پریمیوم است.
  • تاریخ صدور صورتحساب برای کاربر تغییر نمی‌کند.

قطعه کد

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

ارتقا با CHARGE_PRORATED_PRICE

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode موجود در قطعه کد را به SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح پریمیوم (Premium ) تغییر دهید.

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

با CHARGE_PRORATED_PRICE به نسخه پایین‌تر ارتقا دهید

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode موجود در قطعه کد را به SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح Lite تغییر دهید.

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

نتیجه‌گیری

این بخش توضیح داد که چگونه CHARGE_PRORATED_PRICE با دریافت مابه‌التفاوت قیمت دقیق برای دوره صورتحساب باقی‌مانده از کاربران، ضمن حفظ چرخه صورتحساب، امکان ارتقاء فوری را فراهم می‌کند. این ویژگی زمانی مفید است که کاربران بخواهند بدون تغییر تاریخ صورتحساب خود، به سطح گران‌تری ارتقا یابند.

۷. قیمت کامل را شارژ کنید

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

سناریوی مثال

کاربری از طرح پایه (۴.۹۹ دلار در ماه از اول آوریل) استفاده می‌کند. در ۲۰ آوریل، کاربر می‌خواهد به طرح پریمیوم (۹.۹۹ دلار در ماه) تغییر دهد.

در این سناریو:

  • بلافاصله هزینه کامل طرح پریمیوم (9.99 دلار) از کاربر کسر می‌شود.
  • مقدار باقیمانده از طرح پایه (مثلاً 10 روز) به زمان معادل برای طرح پریمیوم تبدیل می‌شود. در این مثال، 10 روز طرح پایه معادل 5 روز طرح پریمیوم است.
  • تاریخ تمدید بعدی کاربر طوری تنظیم می‌شود که این زمان سرشکن‌شده را شامل شود. بنابراین، تاریخ تمدید ۲۵ مه (۲۰ آوریل + ۱ ماه + ۵ روز) می‌شود.
  • تمدیدهای بعدی از ۲۵ مه به صورت ماهانه انجام خواهد شد.

قطعه کد

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

ارتقا با CHARGE_FULL_PRICE

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode را در قطعه کد به SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح پریمیوم (Premium ) تغییر دهید.

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

با CHARGE_FULL_PRICE به نسخه پایین‌تر برگردید

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode را در قطعه کد به SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح Lite تغییر دهید.

کاربر بلافاصله به طرح Lite تنزل رتبه می‌دهد و یک چرخه صورتحساب جدید آغاز می‌شود. مبلغی که باید بلافاصله پرداخت شود، قیمت کامل هدف ۲.۹۹ دلار است. بخش استفاده نشده از طرح Basic به تناسب زمان برای طرح Lite جدید محاسبه می‌شود و اولین تاریخ تمدید را تمدید می‌کند. پس از آن، مبلغ تمدید ۲.۹۹ دلار در هر چرخه خواهد بود.

نتیجه‌گیری

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

۸. بدون_تقسیم_سلاح

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

سناریوی مثال

کاربری از طرح پایه (۴.۹۹ دلار در ماه از اول آوریل) استفاده می‌کند. در ۲۰ آوریل، کاربر می‌خواهد به طرح پریمیوم (۹.۹۹ دلار در ماه) تغییر دهد.

در این سناریو:

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

قطعه کد

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

ارتقا با WITHOUT_PRORATION

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode موجود در قطعه کد را به SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح پریمیوم (Premium ) تغییر دهید.

کاربر بلافاصله به طرح پریمیوم ارتقا می‌یابد و در عین حال تاریخ تمدید فعلی خود را حفظ می‌کند. مبلغی که باید فوراً پرداخت شود 0.00 دلار است. کاربر برای مدت زمان باقی‌مانده در چرخه معین به طرح پریمیوم دسترسی دارد و سپس در تاریخ صورتحساب بعدی به مبلغ تمدید جدید 9.99 دلار تغییر می‌کند.

با WITHOUT_PRORATION به نسخه پایین‌تر برگردید

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode موجود در قطعه کد را به SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح Lite تغییر دهید.

کاربر بلافاصله به طرح Lite تنزل رتبه می‌دهد و ویژگی‌های پایه‌ای که برای آنها هزینه کرده بود را از دست می‌دهد. مبلغی که باید بلافاصله پرداخت شود 0.00 دلار است. چرخه صورتحساب بدون تغییر ادامه می‌یابد و کاربر نرخ جدید و پایین‌تر 2.99 دلار را در تمدید بعدی خود پرداخت خواهد کرد.

نتیجه‌گیری

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

۹. به تعویق افتاده

In this replacement mode, the subscription item is upgraded or downgraded only when the subscription renews, but the new purchase is issued immediately. The existing item is set to non-renewable and expires at the end of the current billing cycle, whereas the newly requested entitlement begins right after.

سناریوی مثال

کاربری از طرح پایه (۴.۹۹ دلار در ماه از اول آوریل) استفاده می‌کند. در ۲۰ آوریل، کاربر می‌خواهد به طرح پریمیوم (۹.۹۹ دلار در ماه) تغییر دهد.

در این سناریو:

  • هیچ هزینه فوری از کاربر دریافت نمی‌شود.
  • کاربر تا پایان چرخه صورتحساب فعلی (30 آوریل) همچنان از ویژگی‌های پایه بهره‌مند خواهد شد.
  • طرح اشتراک به طور خودکار در تاریخ تمدید بعدی (اول ماه مه) به پریمیوم ارتقا می‌یابد.

قطعه کد

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.DEFERRED;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

ارتقا با DEFERRED

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode را در قطعه کد به SubscriptionProductReplacementParams.ReplacementMode.DEFERRED به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح پریمیوم (Premium ) تغییر دهید.

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

دانگرید با DEFERRED

برای شبیه‌سازی این سناریو:

  • در MainActivity برنامه نمونه، مقدار replacementMode را در قطعه کد به SubscriptionProductReplacementParams.ReplacementMode.DEFERRED به‌روزرسانی کنید.
  • برنامه را بازسازی و اجرا کنید.
  • اشتراک‌های موجود (در صورت وجود) را از فروشگاه Google Play لغو کنید و آن را منقضی کنید.
  • طرح پایه را خریداری کنید.
  • به طرح Lite تغییر دهید.

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

نتیجه‌گیری

این بخش توضیح داد که چگونه حالت جایگزینی DEFERRED ارتقا یا تنزل رتبه را تا پایان زمان پرداخت شده توسط کاربر فعال به تعویق می‌اندازد. این امر به ویژه برای تنزل رتبه‌ها ایده‌آل است تا از از دست دادن ویژگی‌های خریداری شده جلوگیری شود.

۱۰. پردازش سمت کلاینت و بک‌اند

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

سناریوی مثال

  • کاربر یک طرح ماهانه پایه (product_id basic_plan و purchase_token basic_purchase_token_123 ) دارد.
  • کاربر با استفاده از حالت جایگزینی فوری (یکی از حالت‌های WITHOUT_PRORATION ، WITH_TIME_PRORATION ، CHARGE_PRORATED_PRICE ، CHARGE_FULL_PRICE ) به طرح پریمیوم تغییر وضعیت می‌دهد.
  • پس از موفقیت‌آمیز بودن تغییر اشتراک، گوگل آن را به عنوان یک اشتراک جدید در نظر می‌گیرد و یک توکن خرید جدید و متفاوت برای طرح پریمیوم (product_id premium_plan و purchase_token premium_purchase_token_123 ) ایجاد می‌کند.

پردازش سمت کلاینت

onPurchasesUpdated

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

برنامه یک شیء Purchase حاوی توکن خرید premium_purchase_token_123 و شناسه محصول product_plan premium_plan دریافت خواهد کرد. شما باید دقیقاً مانند یک مشترک جدید با این شیء رفتار کنید: توکن را تأیید کنید و برای اعطای دسترسی آماده شوید.

@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
        for (Purchase purchase : purchases) {
            // purchase.getPurchaseToken() = premium_purchase_token_123
            // purchase.getProducts() will contain premium_plan
            // Verify the purchase and grant entitlement
            handleNewPurchase(purchase);
        }
    }
}

queryPurchasesAsync

queryPurchasesAsync فقط اشتراک‌های فعال خریداری شده از برنامه شما را برمی‌گرداند. شما باید برای تعیین اینکه کدام مجوز را به کاربر نشان دهید، به این متد تکیه کنید. برای جایگزینی‌های فوری، queryPurchasesAsync() دیگر توکن خرید BASIC قدیمی را برنمی‌گرداند و اکنون فقط توکن خرید PREMIUM جدید را برمی‌گرداند.

هر زمان که برنامه شما از سر گرفته می‌شود یا خرید کامل می‌شود، این متد را فراخوانی کنید. اگر توکن ویژه (Premium token) وجود دارد، فوراً ویژگی‌های ویژه (Premium) را اعطا کنید و ویژگی‌های پایه (Basic) را حذف کنید.

پردازش بک‌اند (RTDN)

وقتی جایگزینی رخ می‌دهد، گوگل پلی یک اعلان توسعه‌دهنده‌ی بلادرنگ (RTDN) به موضوع میخانه/زیرموضوع پیکربندی‌شده‌ی شما ارسال می‌کند.

  • در صورت تعویض فوری، گوگل یک RTDN با شناسه خرید جدید SUBSCRIPTION_PURCHASED ارسال می‌کند. نمونه‌ای از بار داده RTDN
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":4, // SUBSCRIPTION_PURCHASED
        "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription
      }
    }
    
  • وقتی سرور شما توکن خرید جدید را از RTDN دریافت کرد، API purchases.subscriptionsV2 را با توکن خرید جدید فراخوانی کنید تا جزئیات خرید را دریافت کنید. پاسخ API شامل یک فیلد linkedPurchaseToken است که برای تعیین اینکه آیا توکن خرید به خرید اشتراک جدید یا جایگزینی اشتراک اشاره دارد، استفاده می‌شود.
  • در مورد جایگزینی اشتراک، linkedPurchaseToken به توکن خرید اشتراک قدیمی اشاره دارد. در این سناریو basic_purchase_token_123 خواهد بود. نمونه پاسخ GET purchases.subscriptionsV2
    curl \
    'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/<application_id>/purchases/subscriptionsv2/tokens/premium_purchase_token_123' \
    --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
    --header 'Accept: application/json'
    
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "startTime": "...",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>",
      "linkedPurchaseToken": "basic_purchase_token_123", // The purchase token of the subscription that was replaced (Basic Plan in this case)
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // productID of the new subscription (Premium Plan in this case)
          "expiryTime": "....",
          "autoRenewingPlan": {...},
          "offerDetails": {
            "basePlanId": "monthly-auto-renewing" // base plan ID of the new subscription
          },
          "itemReplacement": { // Details about the subscription replacement
            "productId": "subscription_basic", // productID of the old subscription (Basic Plan in this case)
            "replacementMode": "CHARGE_PRORATED_PRICE", // Replacement strategy used for this subscription change
            "basePlanId": "monthly-auto-renewing" // base plan ID of the old subscription 
          },
          "offerPhase": {...}
        }
      ],
      "etag": "<etag_value>"
    }
    
  • شما باید خرید جدید پریمیوم را تأیید کنید. این کار را می‌توانید در داخل برنامه یا در پنل مدیریت خود انجام دهید. عدم تأیید خرید ظرف ۳ روز منجر به بازپرداخت وجه و لغو حق استفاده خواهد شد. برای جزئیات بیشتر در مورد پردازش و تأیید خریدها، به مستندات توسعه‌دهنده مراجعه کنید.

نتیجه‌گیری

This section covered the steps for handling immediate subscription replacements on both the client and your backend. You learned that Google Play treats the new plan as a brand new purchase, issuing a new purchase token. On the client, you must process this new purchase using the PurchasesUpdatedListener and update entitlements based on the response from queryPurchasesAsync . On the backend, you should listen for SUBSCRIPTION_PURCHASED RTDNs for the new token, use the purchases.subscriptionsv2 API to identify the linkedPurchaseToken of the old subscription, and promptly revoke access associated with the old token while granting the new entitlement. Remember to always acknowledge the new purchase.

۱۱. فرآیند جایگزینی‌های معوق

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

سناریوی مثال

  • کاربر یک طرح ماهانه پایه (product_id basic_plan و purchase_token basic_purchase_token_123 ) دارد که در ۱۵ آوریل تمدید می‌شود .
  • در اول آوریل ، کاربر تصمیم می‌گیرد با استفاده از ReplacementMode.DEFERRED به طرح Premium تغییر دهد.
  • گوگل بلافاصله یک توکن خرید جدید برای طرح پریمیوم (product_id premium_plan و purchase_token premium_purchase_123 ) ایجاد می‌کند، اما مبلغی که باید از کاربر کسر شود و حق عضویت برای ۱۵ آوریل برنامه‌ریزی شده است.

فرآیند جایگزینی به تعویق افتاده

۱. درست پس از موفقیت‌آمیز بودن فرآیند خرید (اپلیکیشن)

  • PurchasesUpdatedListener پس از تکمیل جریان خرید فراخوانی می‌شود. برنامه یک شیء Purchase حاوی توکن خرید جدید premium_purchase_token_123 دریافت خواهد کرد، با این حال product_id همچنان به basic_plan قدیمی اشاره دارد زیرا کاربر فقط حق استفاده از طرح Basic را دارد. شما باید دقیقاً مانند یک خرید جدید با این مورد رفتار کنید و توکن را تأیید کنید.
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
            for (Purchase purchase : purchases) {
                // purchase.getPurchaseToken() = premium_purchase_token_123
                // purchase.getProducts() will contain basic_plan
                // Verify and acknowledge the purchase
                handleNewPurchase(purchase);
            }
        }
    }
    
  • queryPurchasesAsync returns purchase with the new purchase token ( premium_purchase_token_123 ) right away, and the original entitlement ( basic_plan ) associated with it. You can rely on this to continue granting entitlement of Basic plan to the user.

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

  • RTDN مربوط به خرید اشتراک (SUBSCRIPTION_PURCHASED) بلافاصله پس از جریان خرید برای توکن خرید جدید ( premium_purchase_token_123 ) ارسال می‌شود. نمونه‌ای از بار داده RTDN
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":4, // SUBSCRIPTION_PURCHASED
        "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription
      }
    }
    
  • برای دریافت جزئیات خرید، تابع GET purchases.subscriptionsv2 را با توکن خرید جدید فراخوانی کنید. پاسخ شامل ۲ خط آیتم است.
    • یکی نشان‌دهنده اشتراک قدیمی (طرح پایه) است و expiryTime در آینده است. اشتراک قدیمی تمدید نخواهد شد و دارای یک deferredItemReplacement است که شامل اشتراک جدید (طرح ویژه) است. این نشان می‌دهد که پس از انقضای حق عضویت، حق عضویت قدیمی در انتظار جایگزینی است.
    • یکی نشان‌دهنده‌ی اشتراک تازه خریداری شده است. هیچ مقداری برای «زمان انقضا» تعیین نشده است.
    نمونه پاسخ API
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "startTime": "2026-05-07T15:50:11.383Z",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>",
      "linkedPurchaseToken": "basic_purchase_token_123",
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // Premium Plan has no expiry time
          "autoRenewingPlan": {...},
          "offerDetails": {...},
          "itemReplacement": {. // Subscription replacement details
            "productId": "basic_plan",
            "replacementMode": "DEFERRED",
            "basePlanId": "monthly-auto-renewing"
          },
          "offerPhase": {}
        },
        {
          "productId": "basic_plan", // Subscription to be replaced
          "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time in the future
          "autoRenewingPlan": {},
          "offerDetails": {...},
          "deferredItemReplacement": { // identifier indicating this subscription will be replaced upon renewal
            "productId": "subscription_premium"
          },
          "latestSuccessfulOrderId": "GPA.<order_id>",
          "itemReplacement": {...},
        }
      ],
      "etag": "<etag>"
    }
    
  • شما باید توکن خرید جدید را تأیید کنید. این کار را می‌توانید درون برنامه یا در backend خود انجام دهید. برای جزئیات بیشتر در مورد پردازش و تأیید خریدها، به مستندات توسعه‌دهنده مراجعه کنید.
  • SUBSCRIPTION_EXPIRED RTDN برای توکن خرید قدیمی ( basic_purchase_token_123 ) ارسال می‌شود. نمونه بار داده RTDN
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":13, // SUBSCRIPTION_EXPIRED
        "purchaseToken":"basic_purchase_token_123" //purchase token for the old subscription
      }
    }
    
  • هنگام فراخوانی API GET purchases.subscriptionsv2 با توکن خرید قدیمی، به صورت منقضی شده ( SUBSCRIPTION_STATE_EXPIRED ) نمایش داده می‌شود. حق استفاده از طرح قدیمی برای مدت زمان باقی مانده به خرید جدید منتقل می‌شود.

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

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

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

  • با ReplacementMode.DEFERRED ، اولین تمدیدها از رفتار استاندارد هر تمدید دیگری که SUBSCRIPTION_RENEWED RTDN را پردازش می‌کند، پیروی می‌کنند. در این صورت نیازی به منطق خاصی برای جایگزینی‌ها ندارید.
  • برای دریافت جزئیات خرید، تابع GET purchases.subscriptionsv2 را با توکن خرید جدید فراخوانی کنید. پاسخ شامل ۲ خط آیتم است.
    • یکی نشان‌دهنده اشتراک قدیمی (طرح پایه) است و expiryTime آن در گذشته بوده است. اشتراک قدیمی دیگر مقداری برای فیلد deferredItemReplacement نخواهد داشت.
    • یکی نشان‌دهنده‌ی اشتراک جدید با expiryTime در آینده و فیلد autoRenewEnabled که روی true تنظیم شده است.
    نمونه پاسخ API
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>..0",
      "linkedPurchaseToken": "basic_purchase_token_123", // purchase token of the old subscription
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // New subscription
          "expiryTime": "2026-05-07T16:00:09.437Z", // Expiry time set in the future
          "autoRenewingPlan": {
            "autoRenewEnabled": true, // Auto Renewing Flag set to True
            "recurringPrice": {...}
          },
          "offerDetails": {...},
          "latestSuccessfulOrderId": "GPA.<order_id>..0",
          "itemReplacement": {. // Details of the subscription replacement
            "productId": "basic_plan",
            "replacementMode": "DEFERRED",
            "basePlanId": "monthly-auto-renewing"
          },
          "offerPhase": {...}
        },
        {
          "productId": "basic_plan", // Old subscription, Does not contains the deferredItemReplacement field
          "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time set in the past
          "autoRenewingPlan": {},
          "offerDetails": {...},
          "latestSuccessfulOrderId": "GPA.<order_id>..0",
          "itemReplacement": {...},
        }
      ],
      "etag": "<etag>"
    }
    

نتیجه‌گیری

This section detailed the unique handling required for ReplacementMode.DEFERRED . You learned that unlike immediate modes, the entitlement change only occurs at the end of the current billing cycle. This section covered the necessary steps for both your app and backend to correctly process the initial purchase, acknowledge the new token, and manage the entitlement switch when the old subscription expires and the new one becomes active.

۱۲. زمین بازی جایگزین اشتراک

ویژگی Replacement Playground در برنامه نمونه به شما امکان می‌دهد ارتقاء و تنزل رتبه اشتراک را برای محصولات اشتراکی پیکربندی شده در حساب Google Play Console خود آزمایش کنید. این بخش نحوه استفاده از ویژگی Replacement Playground را شرح می‌دهد.

راه‌اندازی

برای استفاده از ویژگی Replacement Playground ، از موارد زیر اطمینان حاصل کنید:

  • packageId موجود در فایل build.gradle شما با برنامه پیکربندی شده در کنسول گوگل پلی شما مطابقت دارد.
  • حساب کاربری آزمایشی شما به عنوان یک آزمایش‌کننده مجوز در کنسول گوگل پلی ثبت شده است. برای کسب اطلاعات بیشتر در مورد آزمایش مجوز، به بخش «پیاده‌سازی صورتحساب برنامه خود را آزمایش کنید » مراجعه کنید.

زمین بازی جایگزین اشتراک

The sample app includes a Replacement Playground tab, which lets you simulate subscription changes. You can query subscriptions defined in your Play Console and test switching between them using various replacement modes. This playground helps you understand how different modes affect billing cycles and entitlements for your subscriptions, so you can determine which options best fit your business needs.

برای شبیه‌سازی جایگزینی‌ها با استفاده از زمین بازی، این مراحل را دنبال کنید:

  1. به برگه «زمین بازی» بروید.
  2. اگر اشتراک فعالی دارید: هایلایت خواهد شد. این اشتراکی است که جایگزین می‌شود.

خانه زمین بازی

  1. اگر اشتراک فعال ندارید: ابتدا باید یکی خریداری کنید.
    • Playground به طور پیش‌فرض پلن‌های Basic ، Premium و Lite را که برای این codelab ایجاد شده‌اند، فهرست می‌کند.
    • برای آزمایش با سایر طرح‌های پیکربندی‌شده در کنسول توسعه‌دهندگان Play خود، روی «افزودن طرح سفارشی» کلیک کنید، بر اساس productId و basePlanId جستجو کنید.
    • اشتراک انتخاب شده را خریداری کنید.
    • اشتراک فعال تازه خریداری شده کاربر اکنون باید نمایش داده شود. اضافه کردن اشتراک سفارشی
  2. اشتراک هدفی را که کاربر می‌خواهد به آن تغییر کند، انتخاب کنید.
  3. یک حالت جایگزینی برای انتقال انتخاب کنید.

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

  1. برای شبیه‌سازی جایگزینی اشتراک، روی دکمه‌ی «آزمایش جایگزینی» کلیک کنید.
  2. شما باید برگه پایین صورتحساب گوگل پلی را با جزئیات محاسبه‌شده‌ی جایگزینی اشتراک (مانند هزینه‌های فوری و تنظیمات چرخه‌ی صورتحساب) مشاهده کنید.

سبد خرید صورتحساب جایگزین اشتراک

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

مدیریت اشتراک فروشگاه پلی استور

۱۳. مراحل بعدی

اسناد مرجع

۱۴. تبریک

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

آنچه آموخته‌اید

  • نحوه پیکربندی SubscriptionProductReplacementParams با حالت‌های جایگزینی خاص.
  • تفاوت بین ارتقاء فوری و تنزل رتبه با تأخیر.
  • نحوه‌ی کنار گذاشتن توکن‌های اشتراک قدیمی با استفاده از linkedPurchaseToken و استفاده از RTDNها.

نظرسنجی

بازخورد شما در مورد این آزمایشگاه کد بسیار ارزشمند است. چند دقیقه وقت بگذارید و نظرسنجی ما را تکمیل کنید.