您好。我是zm soft,去年(2023年底)註冊成為開發者並開始發布應用程式。我們也計劃發布一款面向開發者的應用程式,透過開發者之間的合作一起通過封閉測試,歡迎有興趣的朋友查看。
大家有沒有實現應用程式內購買呢?對於應用程式開發者來說,讓用戶滿意並獲得相應報酬是最好的事,也是開發努力得到回報的時刻。這次我想寫一篇關於如何實現這一點的文章。
什麼是應用程式內購買
如大家所知,應用程式內購買分為以下兩類:
- 消耗型商品
- 訂閱型商品
兩者都透過Google Play Billing Library存取Play Store中預先設定的商品,並將獲得收入的一部分作為手續費支付給Google。從實作角度來看兩者類似,但訂閱型商品有其獨特的注意事項。我將結合個人實際經驗來說明注意事項。
Play Store設定
商品註冊
首先,請註冊商品,因為沒有註冊就無法顯示。價格、名稱等以及是否實際使用該商品都可以後續更改,所以填寫臨時資料也沒關係。但是,只有ID無法更改,因此如果您考慮將測試用商品修改後直接用於生產環境,最好使用[product_1,2,3...]或[sub_1,2,3...]這樣可重複使用的ID進行註冊。註冊可在[Monetize]中進行,其中[In-app products]和[Subscriptions]分別對應消耗型商品和訂閱型商品。每個商品可透過[Create xx]按鈕進行註冊。

基本上按照輸入介面的指引進行設定即可,但讓我困惑的是訂閱型商品的價格設定。起初我不知道如何一次性為所有地區設定價格。我以為必須逐個地區分別輸入,但實際上可以按[Set prices] - [Country / region] - [Set price]的順序操作。

點擊按鈕後會彈出以下對話框,可以一次性進行設定。

測試帳號註冊
接下來註冊測試帳號。如果沒有註冊就進行測試購買,將會產生實際費用,請務必注意。測試帳號的註冊透過註冊郵件清單來完成。可以在登入Play Console後的第一個介面(開發者介面)透過[Settings] - [License testing]進行設定。

使用郵件清單中註冊的用戶執行應用程式購買流程時,付款資訊會顯示為「測試卡」,可以在不產生實際費用的情況下進行測試。
實作工作
應用程式端的實作需要以下工作:
- 引入函式庫
- 連接商店並取得商品資訊
- 實作商品展示介面
- 發起購買請求
- 購買完成時的處理
以下將分別詳細說明。
引入函式庫
引入函式庫需要以下操作:
- 修改build.gradle
- 修改android.manifest
build.gradle修改範例:
dependencies {
implementation "com.android.billingclient:6.0.0"
}
manifest修改範例:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.android.vending.BILLING" />
<application
關於版本等資訊,請參考官方文件。
連接商店並取得商品資訊
初始化BillingClient並執行startConnection後,即可與商店進行通訊。可透過queryProductDetailsAsync取得各商品資訊。詳情請參考官方文件。我的情況是,不知為何批量取得無法正常運作,因此先取得商品清單,再透過queryProductDetailAsync逐個取得商品詳情。取得到的商品(SKU)是消耗型還是訂閱型,可以透過其類型inapp或subs來區分。
實作商品展示介面
取得商品資訊後,以清單形式展示供用戶選擇購買。在處理訂閱型商品時,需要符合Subscription Policy,我的情況是因以下兩個原因導致應用程式更新連續被拒:
- 價格和條款的本地化不完整
- 優惠條款說明不完整
首先是價格和條款的本地化不完整:原因是期限的顯示方式(例:$10/month)。問題在於,在不支援翻譯的語言的地區設定下執行時,「month」部分未被翻譯。透過BillingClient取得資訊時,價格金額以包含單位資訊的formattedPrice字串形式取得,無需在應用程式中進行翻譯。而期限資訊以_P1M_這樣的ISO 8601格式通知,需要在應用程式中進行翻譯。透過為每種語言新增如下轉換處理來解決:
fun formatBillingPeriod(billingPeriod: String, languageCode: String): String {
return when(languageCode) {
"en" -> {
when (billingPeriod) {
"P1W" -> "weekly"
"P1M" -> "monthly"
"P3M" -> "every 3 months"
"P6M" -> "every 6 months"
"P1Y" -> "annually"
else -> "unknown"
}
}
"ja" -> {
when (billingPeriod) {
"P1W" -> "週間"
"P1M" -> "月額"
"P3M" -> "3ヶ月ごと"
"P6M" -> "6ヶ月ごと"
"P1Y" -> "年額"
else -> "不明"
}
}
"fr" -> {
when (billingPeriod) {
"P1W" -> "hebdomadaire"
"P1M" -> "mensuel"
"P3M" -> "tous les 3 mois"
"P6M" -> "tous les 6 mois"
"P1Y" -> "annuel"
else -> "inconnu"
}
}
"es" -> {
when (billingPeriod) {
"P1W" -> "semanal"
"P1M" -> "mensual"
"P3M" -> "cada 3 meses"
"P6M" -> "cada 6 meses"
"P1Y" -> "anual"
else -> "desconocido"
}
}
"de" -> {
when (billingPeriod) {
"P1W" -> "wöchentlich"
"P1M" -> "monatlich"
"P3M" -> "alle 3 Monate"
"P6M" -> "alle 6 Monate"
"P1Y" -> "jährlich"
else -> "unbekannt"
}
}
else -> "unknown"
}
}
其次是優惠條款說明不完整:

我理解了問題所在,但不知道具體應該寫什麼樣的措辭來解決。以下是我收到拒絕通知的郵件原文的摘錄:
Issue found: Violation of Subscriptions policy
Your app does not comply with the Subscriptions policy.
- Your offer does not clearly and accurately describe the terms of your trial offer or introductory pricing, including when a free trial will convert to a paid subscription, how much the paid subscription will cost, and that a user can cancel if they do not want to convert to a paid subscription.
作為解決方案,我參考了其他應用程式的措辭。因為這大部分看起來都是固定套語,所以我認為參照已有成功案例比從零開始思考更好。
發起購買請求
用戶在介面上選好商品後,終於到了商品購買處理環節。對取得到的商品執行購買流程,即可呼叫實際的購買介面。
測試
使用最初準備的測試帳號登入後進行測試,可以在不產生實際費用的情況下完成測試。這裡有以下兩個注意點:
- 購買流程需在已在商店註冊的應用程式中執行
- 訂閱型商品的期限會被自訂設定
首先,關於購買流程的執行,如果應用程式是debug建置版本,購買介面會顯示錯誤,無法執行流程。
其次,關於訂閱型商品,進入實際購買介面後會顯示測試卡的付款資訊。此時購買介面上會顯示續費期限,但顯示的期限與商品實際設定的期限不同。我的情況是顯示了5分鐘。這似乎是為了訂閱續費行為測試而顯示極短固定期限的規格設計。(我以為自己哪裡設定錯了,回顧了一段時間的設定。)
總結
起初看起來很難,但一旦理解整體框架,應用程式內購買本身可以透過官方文件等公開資訊出人意料地輕鬆實現。但是,對於訂閱型商品,存在一些不實際嘗試就無法發現的坑,希望我踩過的那些坑能對今後實現該功能的開發者有所參考。