Menerapkan Penggantian langganan dengan Layanan Penagihan Google Play

1. Pengantar

Codelab ini mengajarkan cara menggunakan Google Play Billing Library (PBL) untuk mengelola perubahan paket langganan. Anda akan menemukan pengaruh berbagai mode penggantian terhadap harga dan hak pengguna sekaligus mempelajari cara memproses Notifikasi Developer Real-time (RTDN) backend.

Penonton

Dirancang untuk developer aplikasi Android, codelab ini memberikan panduan tentang cara menerapkan fitur pengelolaan langganan yang canggih. Panduan ini membantu Anda menawarkan pengalaman yang lancar kepada pengguna untuk mengupgrade, mendowngrade, atau beralih antar-paket langganan yang berbeda.

Yang akan Anda pelajari ...

  • Cara membuat Langganan di Konsol Play.
  • Cara memilih ReplacementMode yang benar (misalnya, WITH_TIME_PRORATION versus DEFERRED) agar sesuai dengan kebijakan upgrade dan downgrade aplikasi Anda.
  • Cara mengonfigurasi BillingFlowParams dalam launchBillingFlow untuk memicu alur pembelian Google Play untuk penggantian paket.
  • Cara menggunakan Notifikasi Developer Real-time (RTDN) dan purchases.subscriptionsv2 API untuk mencabut akses lama dan memberikan akses baru dengan aman di backend Anda

Yang Anda butuhkan

2. Mem-build aplikasi contoh

Codelab ini menggunakan aplikasi Android contoh untuk menunjukkan cara menerapkan penggantian langganan di PBL. Aplikasi contoh dirancang sebagai aplikasi Android yang berfungsi penuh dan memiliki kode sumber lengkap yang menunjukkan aspek berikut:

  • Mengintegrasikan aplikasi dengan PBL
  • Menerapkan penggantian langganan

Jika sudah memahami penggantian langganan dan PBL, Anda dapat mendownload aplikasi contoh dan mencobanya.

Video demo berikut menunjukkan tampilan dan perilaku aplikasi contoh setelah di-deploy dan dijalankan.

Prasyarat

Sebelum Anda membuat dan men-deploy aplikasi contoh, lakukan hal berikut:

Build

Untuk mem-build aplikasi contoh sesuai yang diperlukan untuk mengikuti codelab:

  1. Download aplikasi contoh dari GitHub.
  2. Perbarui applicationId dalam build.gradle aplikasi contoh agar mencerminkan ID Aplikasi aplikasi Anda di Konsol Play.
  3. Build aplikasi contoh.
    Catatan: Tindakan ini akan berhasil mem-build aplikasi untuk pengujian lokal. Namun, menjalankan aplikasi tidak mengambil produk dan harga karena langganan yang diperlukan belum dibuat di Konsol Play. Bagian berikutnya akan membahas cara membuat langganan di Konsol Developer.

3. Membuat langganan di Konsol Play

Sistem langganan Google Play memberi Anda fleksibilitas dalam cara membuat, mengelola, dan menjual langganan. Di Konsol Play, Anda dapat mengonfigurasi langganan dengan beberapa paket dasar, yang masing-masing berisi beberapa penawaran. Penawaran langganan dapat memiliki berbagai model harga dan opsi kelayakan. Untuk codelab ini, Anda akan membuat tiga langganan: Premium Plan, Basic Plan, dan Lite Plan, yang menyimulasikan penawaran langganan umum pada berbagai titik harga. Setiap langganan akan memiliki satu paket dasar berulang bulanan.

Buat langganan baru

Untuk membuat langganan baru

  1. Buka Konsol Play, lalu buka halaman Langganan (Monetisasi dengan Play > Produk > Langganan)
  2. Klik Buat langganan.
  3. Masukkan detail langganan Anda:
    • ProductID : Masukkan ID produk yang unik. Masukkan premium_plan.
    • Nama : Masukkan nama singkat untuk langganan. Contoh: Premium Plan.
  4. Klik Buat.

Buat Paket dasar

  1. Buka Konsol Play, lalu buka halaman Langganan (Monetisasi dengan Play > Produk > Langganan)
  2. Di samping langganan yang paket dasarnya ingin Anda buat, klik panah kanan untuk melihat detail langganan.
  3. Klik Tambahkan paket dasar.
  4. Masukkan ID paket dasar. Contoh monthly-auto-renewing.
  5. Pilih jenis sebagai Perpanjangan otomatis.
  6. Untuk paket dasar Perpanjangan otomatis, tetapkan hal berikut:
    • Periode penagihan: Bulanan.
    • Masa tenggang: 7 hari.
    • Perubahan penawaran dan paket penagihan: Penagihan pada tanggal penagihan.
    • Berlangganan kembali: Izinkan.
  7. Di bagian Harga dan ketersediaan, klik Tetapkan Harga untuk menetapkan harga paket dasar.
  8. Pilih semua negara dan wilayah, lalu klik Tetapkan harga.
  9. Tetapkan harga sebagai $10 untuk paket dasar ini, lalu klik Perbarui.
  10. Setelah harga untuk paket dasar ditetapkan, klik Simpan di kanan bawah, lalu klik Aktifkan.

Membuat langganan untuk aplikasi contoh

Untuk tujuan codelab ini, buat dua langganan tambahan dengan konfigurasi berikut:

  • Paket Dasar
    • ID Produk: basic_plan
    • Nama: Paket Dasar
    • ID Paket Dasar: monthly-auto-renewing
    • Harga: $5
  • Paket Lite
    • ID Produk: lite_plan
    • Nama: Lite Plan
    • ID Paket Dasar: monthly-auto-renewing
    • Harga: $3

Aplikasi contoh dikonfigurasi untuk menggunakan ID produk dan ID paket dasar ini. Anda dapat membuat langganan yang berbeda dengan konfigurasi yang berbeda. Dalam hal ini, Anda harus mengubah aplikasi contoh untuk menggunakan ID produk yang telah dibuat.

Video pembuatan langganan

Video berikut menunjukkan langkah-langkah yang dijelaskan sebelumnya untuk membuat Langganan di Konsol Developer Play.

4. Penggantian Langganan

Developer yang terintegrasi dengan PBL dapat memberi pelanggan lama berbagai opsi untuk mengubah paket langganan agar mereka dapat memenuhi kebutuhan dengan lebih baik:

  • Jika Anda menjual beberapa tingkat langganan, seperti langganan dasar dan premium, Anda dapat mengizinkan pengguna untuk beralih tingkat dengan membeli paket dasar atau penawaran langganan yang berbeda.
  • Anda dapat mengizinkan pengguna mengubah periode penagihan saat ini, seperti beralih dari paket bulanan ke tahunan.
  • Anda juga dapat mengizinkan pengguna beralih antar-paket perpanjangan otomatis dan prabayar.

Saat pengguna memutuskan untuk mengupgrade, mendowngrade, atau mengubah langganan mereka, Anda menentukan mode penggantian yang menentukan cara penerapan nilai prorata dari periode penagihan saat ini, dan kapan perubahan hak terjadi untuk pengguna.

Play Billing Library menyediakan beberapa opsi ReplacementMode untuk mengontrol perilaku ini.

Mode penggantian yang tersedia

  • WITH_TIME_PRORATION: Item langganan langsung diupgrade atau didowngrade. Semua sisa waktu disesuaikan berdasarkan perbedaan harga, dan dikreditkan ke langganan yang baru dengan memperbarui tanggal penagihan berikutnya. Ini adalah perilaku default.
  • CHARGE_PRORATED_PRICE: Item langganan langsung diupgrade, dan siklus penagihan tetap sama. Perbedaan harga untuk periode yang tersisa akan ditagihkan kepada pengguna.
  • CHARGE_FULL_PRICE: Item langganan langsung diupgrade atau didowngrade, dan pengguna akan langsung dikenai biaya penuh untuk hak baru ini. Nilai yang tersisa dari langganan sebelumnya akan dipindahkan ke hak yang sama, atau diproporsionalkan sesuai dengan waktu pada saat pelanggan beralih ke hak yang berbeda.
  • WITHOUT_PRORATION: Item langganan langsung diupgrade atau didowngrade, dan harga baru akan dikenakan saat langganan diperpanjang. Siklus penagihan tetap sama.
  • DEFERRED: Item langganan diupgrade atau didowngrade hanya saat langganan diperpanjang.

5. WITH_TIME_PRORATION

Dalam mode penggantian ini, item langganan langsung diupgrade atau didowngrade. Semua sisa waktu disesuaikan berdasarkan perbedaan harga, dan dikreditkan ke langganan yang baru dengan memajukan tanggal penagihan berikutnya. Ini merupakan perilaku default.

Contoh skenario

Pengguna beralih dari paket Basic ($4,99 per bulan) ke paket Premium ($9,99 per bulan) pada 15 April, yaitu di tengah siklus penagihan bulanan.

Dalam skenario ini:

  • Pengguna akan langsung mendapatkan akses ke paket Premium.
  • Google Play otomatis menghitung periode prorata. Misalnya, jika Play menghitung bahwa 15 hari yang tersisa dari paket Basic setara dengan 7 hari paket Premium, tanggal penagihan berikutnya akan dimajukan ke 21 April.
  • Pengguna tidak perlu melakukan pembayaran langsung.

Cuplikan kode

// 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);

Upgrade dengan WITH_TIME_PRORATION

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Premium.

Hak pengguna akan langsung diupgrade ke paket Premium. Jumlah yang harus segera dibayar oleh pengguna adalah $0,00. Nilai yang tersisa dari paket Basic dihitung secara prorata menjadi waktu untuk paket Premium, yang memajukan tanggal perpanjangan berikutnya. Pengguna akan ditagih jumlah perpanjangan sebesar $9,99 pada tanggal penagihan yang baru disesuaikan.

Turunkan versi dengan WITH_TIME_PRORATION

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Lite.

Hak pengguna akan langsung didowngrade ke paket Lite. Jumlah yang harus dibayar segera adalah $0,00. Nilai yang tersisa dari paket Basic dihitung secara prorata menjadi waktu untuk paket Lite, yang memperpanjang tanggal perpanjangan berikutnya secara signifikan. Pengguna akan ditagih jumlah perpanjangan sebesar $2,99 pada tanggal penagihan yang baru disesuaikan.

Kesimpulan

Di bagian ini, Anda telah mempelajari cara WITH_TIME_PRORATION mengubah hak pengguna tanpa biaya langsung dengan menyesuaikan waktu hingga perpanjangan berikutnya berdasarkan perbedaan harga. Strategi ini adalah strategi default yang efektif untuk meng-upgrade atau mendowngrade pengguna.

6. CHARGE_PRORATED_PRICE

Dalam mode penggantian ini, item langganan langsung diupgrade, dan siklus penagihan tetap sama. Perbedaan harga untuk periode yang tersisa akan ditagihkan kepada pengguna.

Catatan: Opsi ini hanya tersedia untuk upgrade item langganan, dengan harga per unit waktu yang dinaikkan.

Contoh skenario

Pengguna yang menggunakan paket Basic ($4,99 per bulan) memutuskan untuk mengupgrade ke paket Premium ($9,99 per bulan) pada 20 April dengan sisa sekitar 10 hari dalam siklus penagihan bulanan mereka.

Dalam skenario ini:

  • Pengguna akan langsung mendapatkan akses ke paket Premium.
  • Pengguna akan langsung ditagih selisih prorata untuk 10 hari yang tersisa dalam siklus penagihan saat ini. Jumlah ini kira-kira setara dengan Rp45.000, yang mewakili nilai paket Premium selama 10 hari.
  • Tanggal penagihan untuk pengguna tidak berubah.

Cuplikan kode

// 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);

Upgrade dengan CHARGE_PRORATED_PRICE

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Premium.

Pengguna akan langsung diupgrade ke paket Premium, dengan tetap mempertahankan tanggal perpanjangan awal. Jumlah yang harus segera dibayarkan adalah selisih prorata antara harga paket Premium dan Dasar untuk sisa hari dalam siklus saat ini. Pada tanggal perpanjangan, pengguna akan ditagih jumlah perpanjangan Premium penuh sebesar $9,99.

Turunkan versi dengan CHARGE_PRORATED_PRICE

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Lite.

Mode penggantian ini akan menghasilkan error selama downgrade karena hanya tersedia untuk upgrade item langganan dengan harga per unit waktu yang dinaikkan. Alur penagihan akan gagal dan menampilkan error kepada pengguna yang menyatakan bahwa mode penghitungan prorata tidak didukung untuk downgrade.

Kesimpulan

Bagian ini menjelaskan cara CHARGE_PRORATED_PRICE memungkinkan upgrade langsung dengan menagih pengguna selisih harga yang tepat untuk periode penagihan yang tersisa sekaligus mempertahankan siklus penagihan. Hal ini berguna jika pengguna ingin mengupgrade ke tingkatan yang lebih mahal, tanpa mengubah tanggal penagihan.

7. CHARGE_FULL_PRICE

Dalam mode penggantian ini, item langganan langsung diupgrade atau didowngrade, dan pengguna akan langsung dikenai biaya penuh untuk hak baru. Nilai yang tersisa dari langganan sebelumnya akan dipindahkan ke hak yang sama, atau diproporsionalkan sesuai dengan waktu pada saat pelanggan beralih ke hak yang berbeda.

Contoh skenario

Pengguna menggunakan paket Basic ($4,99 per bulan mulai 1 April). Pada 20 April, pengguna ingin beralih ke paket Premium ($9,99 per bulan).

Dalam skenario ini:

  • Pengguna akan langsung ditagih harga penuh paket Premium ($9,99).
  • Sisa nilai dari paket Basic (misalnya, senilai 10 hari) dikonversi menjadi waktu yang setara untuk paket Premium. Dalam contoh ini, 10 hari Dasar setara dengan 5 hari Premium.
  • Tanggal perpanjangan berikutnya pengguna disesuaikan untuk menyertakan waktu yang diprorata ini. Jadi, tanggal perpanjangan menjadi 25 Mei (20 April + 1 bulan + 5 hari).
  • Perpanjangan selanjutnya akan dilakukan setiap bulan mulai 25 Mei.

Cuplikan kode

// 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);

Upgrade dengan CHARGE_FULL_PRICE

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Premium.

Pengguna akan langsung diupgrade ke paket Premium. Jumlah yang harus dibayar segera adalah harga penuh Paket Premium - $9,99. Setiap nilai yang tersisa dari paket Dasar akan dikonversi menjadi waktu pada paket Premium baru, sehingga sedikit memperpanjang tanggal perpanjangan pertama. Setelah itu, jumlah perpanjangan akan menjadi $9,99 per siklus.

Turunkan versi dengan CHARGE_FULL_PRICE

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Lite.

Pengguna akan langsung didowngrade ke paket Lite, dan siklus penagihan baru akan dimulai. Jumlah yang harus dibayar segera adalah harga target penuh sebesar $2,99. Bagian yang tidak digunakan dari paket Basic akan dihitung secara prorata menjadi waktu untuk paket Lite yang baru, sehingga memperpanjang tanggal perpanjangan pertama. Setelah itu, jumlah perpanjangan akan menjadi $2,99 per siklus.

Kesimpulan

Di bagian ini, kita membahas cara CHARGE_FULL_PRICE menagih pengguna sepenuhnya di luar biaya yang ditanggung pada hari peralihan, dengan segera memulai siklus penagihan baru. Sisa saldo dari paket sebelumnya akan diterapkan secara linear ke tanggal perpanjangan berikutnya.

8. WITHOUT_PRORATION

Dalam mode penggantian ini, item langganan langsung diupgrade atau didowngrade, dan harga baru akan dikenakan saat langganan diperpanjang.

Contoh skenario

Pengguna menggunakan paket Basic ($4,99 per bulan mulai 1 April). Pada 20 April, pengguna ingin beralih ke paket Premium ($9,99 per bulan).

Dalam skenario ini:

  • Pengguna akan langsung mendapatkan akses ke paket Premium.
  • Pengguna tidak perlu membayar harga yang lebih tinggi, yaitu $9,99, hingga tanggal perpanjangan langganan berikutnya (1 Mei).

Cuplikan kode

// 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);

Upgrade dengan WITHOUT_PRORATION

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Premium.

Pengguna akan langsung diupgrade ke paket Premium dengan tetap mempertahankan tanggal perpanjangan yang ada. Jumlah yang harus dibayar segera adalah $0,00. Pengguna memiliki akses ke paket Premium selama sisa waktu dalam siklus yang diberikan, sebelum beralih ke jumlah perpanjangan baru sebesar $9,99 pada tanggal penagihan berikutnya.

Downgrade dengan WITHOUT_PRORATION

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Lite.

Pengguna akan langsung didowngrade ke paket Lite dan kehilangan fitur Basic yang telah dibayar. Jumlah yang harus dibayar segera adalah $0,00. Siklus penagihan akan tetap sama, dan pengguna akan membayar tarif baru yang lebih rendah sebesar $2, 99 pada perpanjangan terjadwal berikutnya.

Kesimpulan

Bagian ini menunjukkan cara WITHOUT_PRORATION langsung menukar hak pengguna tanpa biaya checkout sekaligus membiarkan siklus penagihan tidak berubah.

9. DEFERRED

Dalam mode penggantian ini, item langganan diupgrade atau didowngrade hanya saat langganan diperpanjang, tetapi pembelian baru akan segera diberikan. Item yang ada ditetapkan sebagai tidak dapat diperpanjang dan akan berakhir pada akhir siklus penagihan saat ini, sedangkan hak yang baru diminta akan dimulai tepat setelahnya.

Contoh skenario

Pengguna menggunakan paket Basic ($4,99 per bulan mulai 1 April). Pada 20 April, pengguna ingin beralih ke paket Premium ($9,99 per bulan).

Dalam skenario ini:

  • Pengguna tidak akan langsung dikenai biaya.
  • Pengguna akan terus menerima fitur Basic hingga akhir siklus penagihan saat ini (30 April).
  • Paket langganan otomatis diupgrade ke Premium pada tanggal perpanjangan berikutnya (1 Mei).

Cuplikan kode

// 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);

Upgrade dengan DEFERRED

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.DEFERRED.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Premium.

Pengguna akan tetap menggunakan paket Basic hingga akhir siklus penagihan saat ini. Jumlah yang harus dibayar segera adalah $0,00. Pada tanggal perpanjangan, haknya akan diupgrade ke paket Premium dan dia akan ditagih jumlah perpanjangan baru sebesar $9,99.

Downgrade dengan DEFERRED

Untuk menyimulasikan skenario ini:

  • Di MainActivity aplikasi contoh, perbarui replacementMode dalam cuplikan kode menjadi SubscriptionProductReplacementParams.ReplacementMode.DEFERRED.
  • Bangun ulang dan luncurkan aplikasi.
  • Batalkan langganan yang ada (jika ada) dari Google Play Store dan biarkan langganan tersebut berakhir.
  • Beli paket Basic.
  • Beralih ke paket Lite.

Pengguna akan tetap menggunakan paket Basic hingga akhir siklus penagihan saat ini. Jumlah yang harus dibayar segera adalah $0,00. Pada tanggal perpanjangan, haknya akan diupgrade ke paket Lite dan dia akan ditagih jumlah perpanjangan baru sebesar $2,99.

Kesimpulan

Bagian ini menjelaskan cara mode penggantian DEFERRED menunda upgrade atau downgrade hingga akhir waktu berbayar pengguna aktif. Hal ini menjadikannya sangat ideal untuk downgrade guna mencegah hilangnya fitur yang sudah dibeli.

10. Pemrosesan Backend dan Sisi Klien

Setelah pengguna memicu penggantian langganan yang berhasil, pastikan aplikasi dan backend Anda menangani perubahan dengan benar untuk menghindari masalah seperti gangguan layanan atau penagihan ganda.

Contoh Skenario

  • Pengguna memiliki paket bulanan Basic (product_id basic_plan dan purchase_token basic_purchase_token_123).
  • Pengguna beralih ke paket Premium menggunakan mode penggantian langsung (salah satu dari WITHOUT_PRORATION, WITH_TIME_PRORATION, CHARGE_PRORATED_PRICE, CHARGE_FULL_PRICE)
  • Setelah peralihan langganan berhasil, Google akan memperlakukannya sebagai langganan BARU dan membuat token pembelian baru yang berbeda untuk Paket Premium (product_id premium_plan dan purchase_token premium_purchase_token_123).

Pemrosesan sisi klien

onPurchasesUpdated

Saat pembelian pengganti selesai, PurchasesUpdatedListener dipicu. Meskipun ini adalah pengalihan, Google Play memperlakukan paket Premium sebagai pembelian yang benar-benar baru.

Aplikasi akan menerima objek Purchase yang berisi premium_purchase_token_123 token pembelian dan product_id premium_plan. Anda harus memperlakukannya persis seperti pelanggan baru: verifikasi token dan bersiap untuk memberikan akses

@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 hanya menampilkan langganan aktif yang dibeli dari aplikasi Anda. Anda harus mengandalkan metode ini untuk menentukan hak yang akan ditampilkan kepada pengguna. Untuk penggantian langsung, queryPurchasesAsync() akan berhenti menampilkan token pembelian BASIC lama dan sekarang hanya akan menampilkan token pembelian PREMIUM baru.

Setiap kali aplikasi Anda dilanjutkan atau pembelian selesai, panggil metode ini. Jika token Premium ada, segera berikan fitur Premium dan hapus fitur Dasar.

Pemrosesan backend (RTDN)

Saat penggantian terjadi, Google Play akan mengirimkan Notifikasi Developer Real Time (RTDN) ke topik pub/sub yang Anda konfigurasi.

  • Dalam kasus penggantian langsung, Google mengirimkan RTDN SUBSCRIPTION_PURCHASED dengan token pembelian baru.Contoh Payload 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
      }
    }
    
  • Saat server Anda menerima token pembelian baru dari RTDN, panggil API purchases.subscriptionsV2 dengan token pembelian baru untuk mengambil detail pembelian. Respons API berisi kolom linkedPurchaseToken yang digunakan untuk menentukan apakah token pembelian merujuk ke pembelian langganan baru atau penggantian langganan.
  • Dalam kasus penggantian langganan, linkedPurchaseToken mengacu pada token pembelian langganan lama. Dalam skenario ini, responsnya adalah basic_purchase_token_123.Contoh respons 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>"
    }
    
  • Anda harus mengonfirmasi pembelian Premium baru. Hal ini dapat dilakukan di dalam aplikasi atau di backend Anda. Jika pembelian tidak dikonfirmasi dalam waktu 3 hari, hak akan dikembalikan dananya dan dicabut. Untuk mengetahui detail selengkapnya tentang pemrosesan dan konfirmasi pembelian, lihat dokumentasi developer.

Kesimpulan

Bagian ini membahas langkah-langkah untuk menangani penggantian langganan langsung di klien dan backend Anda. Anda telah mempelajari bahwa Google Play memperlakukan paket baru sebagai pembelian baru, dengan mengeluarkan token pembelian baru. Di klien, Anda harus memproses pembelian baru ini menggunakan PurchasesUpdatedListener dan memperbarui hak berdasarkan respons dari queryPurchasesAsync. Di backend, Anda harus memproses RTDN SUBSCRIPTION_PURCHASED untuk token baru, menggunakan API purchases.subscriptionsv2 untuk mengidentifikasi linkedPurchaseToken langganan lama, dan segera mencabut akses yang terkait dengan token lama sambil memberikan hak baru. Ingatlah untuk selalu mengonfirmasi pembelian yang baru.

11. Memproses Penggantian yang DITANGGUHKAN

Tidak seperti mode penggantian langsung, ReplacementMode.DEFERRED menunda perubahan langganan dan pembaruan hak hingga akhir siklus penagihan saat ini. Penanganan penggantian yang ditangguhkan memerlukan logika khusus untuk memastikan pengguna menerima hak yang benar pada waktu yang tepat.

Contoh Skenario

  • Pengguna memiliki paket bulanan Basic (product_id basic_plan dan purchase_token basic_purchase_token_123) yang diperpanjang pada 15 April.
  • Pada 1 April, pengguna memutuskan untuk beralih ke paket Premium menggunakan ReplacementMode.DEFERRED.
  • Google akan langsung membuat token pembelian BARU untuk paket Premium (product_id premium_plan dan purchase_token premium_purchase_123), tetapi jumlah yang akan ditagih dari pengguna dan haknya dijadwalkan pada 15 April.

Memproses penggantian yang ditangguhkan

1. Segera setelah alur pembeliannya berhasil (aplikasi)

  • PurchasesUpdatedListener dipanggil setelah alur pembelian selesai. Aplikasi akan menerima objek Purchase yang berisi token pembelian baru premium_purchase_token_123, tetapi product_id akan tetap merujuk ke basic_plan lama karena pengguna hanya memiliki hak atas paket Basic. Anda harus memperlakukannya persis seperti pembelian baru dan mengonfirmasi token.
    @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 langsung mengembalikan pembelian dengan token pembelian baru (premium_purchase_token_123), dan hak asli (basic_plan) yang terkait dengannya. Anda dapat mengandalkan hal ini untuk terus memberikan hak paket Basic kepada pengguna.

2. Tepat setelah alur pembeliannya berhasil (backend)

  • RTDN SUBSCRIPTION_PURCHASED dikirim segera setelah alur pembelian untuk token pembelian yang baru (premium_purchase_token_123).Contoh Payload 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
      }
    }
    
  • Panggil GET purchases.subscriptionsv2 dengan token pembelian baru untuk mengambil detail pembelian. Respons berisi 2 item baris.
    • Satu item baris mewakili langganan lama (paket dasar) dan memiliki expiryTime di masa mendatang. Langganan lama tidak akan diperpanjang dan memiliki deferredItemReplacement yang berisi langganan baru (paket premium). Hal ini menunjukkan penggantian tertunda hak lama setelah masa berlakunya berakhir.
    • Satu mewakili langganan yang baru dibeli. Tidak ada nilai yang ditetapkan untuk ‘expiryTime'
    Contoh respons 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>"
    }
    
  • Anda harus mengonfirmasi token pembelian baru. Hal ini dapat dilakukan di dalam aplikasi atau di backend Anda. Untuk mengetahui detail selengkapnya tentang pemrosesan dan konfirmasi pembelian, lihat dokumentasi developer.
  • RTDN SUBSCRIPTION_EXPIRED dikirim untuk token pembelian lama (basic_purchase_token_123).Contoh Payload 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
      }
    }
    
  • Saat memanggil API GET purchases.subscriptionsv2 dengan token pembelian lama, API tersebut akan ditampilkan sebagai sudah tidak berlaku (SUBSCRIPTION_STATE_EXPIRED). Hak untuk paket lama ditransfer ke pembelian baru untuk sisa waktu yang ada.

3. Pada tanggal penggantian - perpanjangan pertama setelah alur pembelian (aplikasi)

  • queryPurchasesAsync menampilkan pembelian dengan token pembelian baru (premium_purchase_token_123), dan langganan baru yang terkait dengannya (premium_plan).
  • Pembelian yang baru seharusnya sudah diproses saat alur pembelian berhasil, Anda tidak perlu melakukan tindakan khusus apa pun selain memastikan akses ke langganan yang tepat diberikan kepada pengguna.

4. Pada tanggal penggantian - perpanjangan pertama setelah alur pembelian (backend)

  • Dengan ReplacementMode.DEFERRED, perpanjangan pertama akan mengikuti perilaku standar perpanjangan lainnya yang memproses RTDN SUBSCRIPTION_RENEWED. Anda tidak perlu memiliki logika khusus untuk penggantian saat hal ini terjadi.
  • Panggil GET purchases.subscriptionsv2 dengan token pembelian baru untuk mengambil detail pembelian. Respons berisi 2 item baris.
    • Satu item baris mewakili langganan lama (paket dasar) dan memiliki expiryTime di masa lalu. Langganan lama tidak akan lagi memiliki nilai yang ditetapkan untuk kolom deferredItemReplacement.
    • Satu lagi mewakili langganan baru dengan expiryTime pada masa mendatang dan kolom autoRenewEnabled ditetapkan ke true.
    Contoh Respons 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>"
    }
    

Kesimpulan

Bagian ini menjelaskan penanganan unik yang diperlukan untuk ReplacementMode.DEFERRED. Anda telah mempelajari bahwa tidak seperti mode langsung, perubahan hak hanya terjadi di akhir siklus penagihan saat ini. Bagian ini membahas langkah-langkah yang diperlukan agar aplikasi dan backend Anda dapat memproses pembelian awal dengan benar, mengonfirmasi token baru, dan mengelola peralihan hak saat langganan lama berakhir dan langganan baru menjadi aktif.

12. Playground Penggantian Langganan

Fitur Replacement Playground di aplikasi contoh memungkinkan Anda menguji upgrade dan downgrade langganan untuk produk langganan yang dikonfigurasi di akun Konsol Google Play Anda. Bagian ini menjelaskan cara menggunakan fitur Replacement Playground.

Penyiapan

Untuk menggunakan fitur Playground Penggantian, pastikan hal berikut:

  • packageId di file build.gradle Anda cocok dengan aplikasi yang dikonfigurasi di Konsol Google Play.
  • Akun pengguna penguji Anda terdaftar sebagai penguji lisensi di Konsol Google Play. Untuk mempelajari lebih lanjut pengujian lisensi, lihat Menguji penerapan penagihan aplikasi Anda.

Playground Penggantian Langganan

Aplikasi contoh menyertakan tab Replacement Playground, yang memungkinkan Anda menyimulasikan perubahan langganan. Anda dapat membuat kueri langganan yang ditentukan di Konsol Play dan menguji peralihan di antara langganan tersebut menggunakan berbagai mode penggantian. Playground ini membantu Anda memahami pengaruh berbagai mode terhadap siklus penagihan dan hak untuk langganan Anda, sehingga Anda dapat menentukan opsi mana yang paling sesuai dengan kebutuhan bisnis Anda.

Untuk menyimulasikan penggantian menggunakan playground, ikuti langkah-langkah berikut:

  1. Buka tab Playground.
  2. Jika Anda memiliki langganan aktif: Langganan tersebut akan ditandai. Ini adalah langganan yang akan diganti.

Beranda Playground

  1. Jika Anda tidak memiliki langganan aktif: Anda harus membelinya terlebih dahulu.
    • Playground mencantumkan paket Basic, Premium, dan Lite yang dibuat untuk codelab ini secara default.
    • Untuk menguji dengan paket lain yang dikonfigurasi di Konsol Developer Play, klik Tambahkan Paket Kustom, telusuri menurut productId dan basePlanId.
    • Beli langganan yang dipilih.
    • Langganan aktif yang baru dibeli pengguna kini akan ditampilkan. Menambahkan langganan kustom
  2. Pilih langganan target yang ingin digunakan pengguna untuk beralih.
  3. Pilih Mode Penggantian untuk transisi.

Pilih mode penggantian

  1. Klik tombol Uji Penggantian untuk menyimulasikan penggantian langganan.
  2. Anda akan melihat sheet bawah penagihan Google Play dengan detail penggantian langganan yang dihitung (seperti biaya langsung dan penyesuaian siklus penagihan).

Keranjang penagihan penggantian langganan

  1. Selesaikan transaksi.
  2. Buka halaman Kelola Langganan di aplikasi Play Store untuk melihat perubahan langganan aktif beserta detail tentang tanggal dan harga perpanjangan yang diperbarui.

Pengelolaan langganan Play Store

13. Langkah berikutnya

Dokumen referensi

14. Selamat

Selamat! Anda telah berhasil menerapkan penggantian langganan dengan berbagai mode prorata dan mengonfigurasi penanganan backend untuk transisi paket.

Yang telah Anda pelajari

  • Cara mengonfigurasi SubscriptionProductReplacementParams dengan mode penggantian tertentu.
  • Perbedaan antara upgrade langsung dan downgrade yang ditangguhkan.
  • Cara menghentikan penggunaan token langganan lama menggunakan linkedPurchaseToken menggunakan RTDN.

Survei

Masukan Anda tentang codelab ini sangat kami hargai. Luangkan waktu beberapa menit untuk mengisi survei kami.