본문 바로가기
모바일개발(Mobile Dev)/안드로이드개발(Android)

How to determine if user cancels Google Play subscription

by 테크한스 2018. 12. 9.
반응형

[JSONObject]

AssetManager am = getActivity().getApplicationContext().getAssets();
HttpTransport httpTransport;
GoogleCredential credential;
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
try {

InputStream inputStream = am.open(certificate);
File privateKey = createFileFromInputStream(inputStream);

httpTransport = new com.google.api.client.http.javanet.NetHttpTransport();
credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(emailAddress)
.setServiceAccountPrivateKeyFromP12File(privateKey)
.setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/androidpublisher"))
.build();

(1) Subscription New (Not expiration)



json={


"autoRenewing":true,

"expiryTimeMillis":"1544329966938",

"kind":"androidpublisher#subscriptionPurchase",

"startTimeMillis":"1544329550862",

"priceCurrencyCode":"KRW",

"priceAmountMicros":"2000000000",

"countryCode":"KR",

"developerPayload":"user_id",

"paymentState":1,"

orderId":"GPA.3344-7136-XXXX-78016","

purchaseType":0


}


(2) Subscription Finish (expiration)


json={


"autoRenewing":false,

"expiryTimeMillis":"1544317534203",

"kind":"androidpublisher#subscriptionPurchase",

"startTimeMillis":"1544315438338","

priceCurrencyCode":"KRW","

priceAmountMicros":"2000000000",

"countryCode":"KR",

"developerPayload":"user_id",

"cancelReason":1,

"orderId":"GPA.3306-8789-XXXX-01440..5","

purchaseType":0

}








written by https://stackoverflow.com/questions/37635996/how-to-determine-if-user-cancels-google-play-subscription







accepted

It looks like it's now possible to receive server-side notifications about subscriptions changes (renewal, cancellation...):

The In-app Billing API provides server push notifications that give developers the capability to monitor state changes for Play-managed subscriptions.

See this page for more details: https://developer.android.com/google/play/billing/billing_subscriptions.html#realtime-notifications



According to In-App Subscription documentation there is no mechanism to detect when the user cancels the subscription. Since it is not canceled immediately. Instead it waits for the end of the cycle for the subscription to expire.

Excerpt from document (source)

When the user cancels a subscription, Google Play does not offer a refund for the current billing cycle. Instead, it allows the user to have access to the canceled subscription until the end of the current billing cycle, at which time it terminates the subscription. For example, if a user purchases a monthly subscription and cancels it on the 15th day of the cycle, Google Play will consider the subscription valid until the end of the 30th day (or other day, depending on the month)

The app won't receive any kind of notification when user cancels the subscription.

The behavior of subscription is whenever you query the inventory in the app SKU will be returned if subscription is valid. When the subscription expires the SKU won't be returned when you query the inventory.

According to the documentation in this link

It is okay to run a batch query whenever subscription is nearing the end

Excerpt from document (source):

Query for subscription status only at expiration — Once your server has retrieved the expiration date of subscription tokens, it should not query the Google Play servers for the subscription status again until the subscription is reaching or has passed the expiration date. Typically, your servers would run a batch query each day to check the status of expiring subscriptions, then update the database

The following server api lets you query the subscription status:

https://developers.google.com/android-publisher/api-ref/purchases/subscriptions#resource-representations

The variables autoRenewing and cancelReason will let you know if the subscription has been canceled.

By using the above API you would be able to implement a system wherein the subscription nearing expiration can be queried for status and then determine whether they are canceled or not.

Full details for subscription cancellation can be found at this link.

Note:
Documentation states that you should continue to provide the content as long as the user has valid subscription. If you are planning to deny access to the content if someone canceled the subscription will go against the Google Policy

Excerpt from document (source)

Important: In all cases, you must continue to offer the content that your subscribers have purchased through their subscriptions, as long any user is able to access it. That is, you must not remove any content while any user still has an active subscription to it, even if that subscription will terminate at the end of the current billing cycle.

  • 1
    Thanks Anirudha. If a user has a subscription which renews once per year, but for which they pay monthly, should we query the API for expired subscription monthly on their billing date? – Karim Varela Jun 14 '16 at 15:48
  • I don't think that Google Play allows you to pay monthly for yearly subscription. If it does can you provide the link to it ? – Anirudha Agashe Jun 15 '16 at 5:36
  • I think you're right, just saw this: "For monthly and annual subscriptions, billing cycles will always match subscription cycles, based on the purchase date." on developer.android.com/google/play/billing/… – Karim Varela Jun 15 '16 at 16:35
  • I dont know if it is correct for real subscriptions but when I test with test account and cancel the subscription, even after expiry date, it is still returned. – batmaci May 15 at 23:45


Purchases.subscriptions

For a list of methods for this resource, see the end of this page.

Resource representations

A SubscriptionPurchase resource indicates the status of a user's subscription purchase.

{
 
"kind": "androidpublisher#subscriptionPurchase",
 
"startTimeMillis": long,
 
"expiryTimeMillis": long,
 
"autoRenewing": boolean,
 
"priceCurrencyCode": string,
 
"priceAmountMicros": long,
 
"countryCode": string,
 
"developerPayload": string,
 
"paymentState": integer,
 
"cancelReason": integer,
 
"userCancellationTimeMillis": long,
 
"cancelSurveyResult": {
   
"cancelSurveyReason": integer,
   
"userInputCancelReason": string
 
},
 
"orderId": string,
 
"linkedPurchaseToken": string,
 
"purchaseType": integer,
 
"profileName": string,
 
"emailAddress": string,
 
"givenName": string,
 
"familyName": string,
 
"profileId": string
}
Property nameValueDescriptionNotes
autoRenewingbooleanWhether the subscription will automatically be renewed when it reaches its current expiry time.
cancelReasonintegerThe reason why a subscription was canceled or is not auto-renewing. Possible values are:
  1. User canceled the subscription
  2. Subscription was canceled by the system, for example because of a billing problem
  3. Subscription was replaced with a new subscription
  4. Subscription was canceled by the developer
cancelSurveyResultnested objectInformation provided by the user when they complete the subscription cancellation flow (cancellation reason survey).
cancelSurveyResult.cancelSurveyReasonintegerThe cancellation reason the user chose in the survey. Possible values are:
  1. Other
  2. I don't use this service enough
  3. Technical issues
  4. Cost-related reasons
  5. I found a better app
cancelSurveyResult.userInputCancelReasonstringThe customized input cancel reason from the user. Only present when cancelReason is 0.
countryCodestringISO 3166-1 alpha-2 billing country/region code of the user at the time the subscription was granted.
developerPayloadstringA developer-specified string that contains supplemental information about an order.
emailAddressstringThe email address of the user when the subscription was purchased. Only present for purchases made with 'Subscribe with Google'.
expiryTimeMillislongTime at which the subscription will expire, in milliseconds since the Epoch.
familyNamestringThe family name of the user when the subscription was purchased. Only present for purchases made with 'Subscribe with Google'.
givenNamestringThe given name of the user when the subscription was purchased. Only present for purchases made with 'Subscribe with Google'.
kindstringThis kind represents a subscriptionPurchase object in the androidpublisher service.
linkedPurchaseTokenstringThe purchase token of the originating purchase if this subscription is one of the following:
  • Re-signup of a canceled but non-lapsed subscription
  • Upgrade/downgrade from a previous subscription
For example, suppose a user originally signs up and you receive purchase token X, then the user cancels and goes through the resignup flow (before their subscription lapses) and you receive purchase token Y, and finally the user upgrades their subscription and you receive purchase token Z. If you call this API with purchase token Z, this field will be set to Y. If you call this API with purchase token Y, this field will be set to X. If you call this API with purchase token X, this field will not be set.
orderIdstringThe order id of the latest recurring order associated with the purchase of the subscription.
paymentStateintegerThe payment state of the subscription. Possible values are:
  1. Payment pending
  2. Payment received
  3. Free trial
priceAmountMicroslongPrice of the subscription, not including tax. Price is expressed in micro-units, where 1,000,000 micro-units represents one unit of the currency. For example, if the subscription price is €1.99, price_amount_micros is 1990000.
priceCurrencyCodestringISO 4217 currency code for the subscription price. For example, if the price is specified in British pounds sterling, price_currency_code is "GBP".
profileIdstringThe profile id of the user when the subscription was purchased. Only present for purchases made with 'Subscribe with Google'.
profileNamestringThe profile name of the user when the subscription was purchased. Only present for purchases made with 'Subscribe with Google'.
purchaseTypeintegerThe type of purchase of the subscription. This field is only set if this purchase was not made using the standard in-app billing flow. Possible values are:
  1. Test (i.e. purchased from a license testing account)
startTimeMillislongTime at which the subscription was granted, in milliseconds since the Epoch.
userCancellationTimeMillislongThe time at which the subscription was canceled by the user, in milliseconds since the epoch. Only present if cancelReason is 0.

Methods

cancel
Cancels a user's subscription purchase. The subscription remains valid until its expiration time.
defer
Defers a user's subscription purchase until a specified future expiration time.
get
Checks whether a user's subscription purchase is valid and returns its expiry time.
refund
Refunds a user's subscription purchase, but the subscription remains valid until its expiration time and it will continue to recur.
revoke
Refunds and immediately revokes a user's subscription purchase. Access to the subscription will be terminated immediately and it will stop recurring.




Google Play Developer API


Google Play Developer API는 게시 및 앱 관리 작업 수행을 위해 사용하는 REST 기반 웹 서비스입니다. 이 API를 사용하여 게시 작업을 릴리스 관리 프로세스와 통합할 수 있습니다.

모든 개발자가 이런 API를 사용할 필요는 없습니다. 대부분의 경우, Google Play Developer Console을 사용하여 계속 앱을 직접 관리할 것입니다. 하지만 관리할 APK의 개수가 많거나 사용자 구매와 구독을 추적해야 할 경우 이 API가 매우 유용함을 알 수 있을 것입니다.

Google Play Developer API를 사용하여 다음을 포함한 다양한 앱 관리 작업을 자동화할 수 있습니다.

  • 앱의 새 버전 업로드 및 출시
  • 현지화된 텍스트와 그래픽을 포함한 앱 Google Play 스토어 목록 편집
  • 인앱 상품 카탈로그, 상품 구매 상태, 앱 구독의 관리

Google Play Developer API를 사용하면 앱 디자인과 개발에 집중할 수 있는 한편, 신규 시장으로 사업을 확장할 때도 릴리스 관리에 드는 시간과 노력이 줄어듭니다.

Google Play Developer API에는 다음 두 가지 구성 요소가 포함됩니다.

  • Publishing API를 사용하여 앱을 업로드 및 게시하고 다른 게시 관련 작업을 수행할 수 있습니다.
  • Subscriptions and In-App Purchases API를 사용하면 인앱 구매와 구독을 관리할 수 있습니다. 전에는 이 API를 "Purchase Status API"로 불렀습니다.

Publishing API

Google Play Developer Publishing API를 사용하면 앱 배포와 관계가 있는 빈번한 작업을 자동화할 수 있습니다. 이 API는 개발자가 Google Play Developer Console을 통해 사용할 수 있는 것과 유사한 다음과 같은 기능을 제공합니다.

  • 앱의 새 버전 업로드
  • 다양한 트랙(알파, 베타, 단계적 출시 또는 프로덕션)에 APK를 할당하여 앱 출시
  • 현지화된 텍스트와 그래픽 및 다중 기기 스크린샷을 포함한 Google Play 스토어 목록 생성 및 수정

이러한 작업은 변경을 수행할 때 트랜잭션 접근 방식을 취하는 편집 기능을 사용하여 수행됩니다. 즉, 여러 가지 변경 사항을 단 하나의 초안 편집으로 묶은 다음, 모든 변경 사항을 한 번에 커밋합니다. (편집을 커밋해야 변경 사항이 적용됩니다.)

참고: 이 API를 사용할 필요가 없는 개발자도 있을 것입니다. 이 API에서 제공되는 모든 기능은 Google Play Developer Console을 통해서도 사용할 수 있습니다. 하지만 이 API를 사용하면 앱 및 목록 업데이트 프로세스를 기존 도구와 통합할 수 있어, 어떤 개발자에게는 매우 유용할 것입니다. 특히, 관리할 APK 수가 많거나 목록이 여러 다른 로케일로 현지화되어 있는 경우 이 API가 정말 중요하다는 생각이 들 것입니다.

Subscriptions and In-App Purchases API

이 API를 사용하면 인앱 상품과 구독으로 구성된 앱의 카탈로그를 관리할 수 있습니다. 그 밖에도, Subscriptions and In-App Purchases API에서는 표준 GET 요청을 사용하여 어떤 구매의 세부정보라도 빠르게 검색할 수 있습니다. 이 요청에서는 구매에 대한 정보, 즉 앱 패키지 이름, 구매 또는 구독 ID, 구매 토큰 정보를 제공합니다. 서버는 관련 구매 세부정보, 주문 상태, 개발자 페이로드 및 기타 정보를 설명하는 JSON 객체로 응답합니다.

개별 주문의 보고와 조정, 구매와 구독의 만료 확인 등, 여러 가지 용도로 Purchase Status API를 사용할 수 있습니다. 또한, 이 API를 사용하여 취소된 주문에 대해 파악하고 인앱 상품이 취소되기 전에 소비되었는지를 포함해, 인앱 상품의 소비 여부를 확인할 수 있습니다.

참고: Subscriptions and In-App Purchases API는 Publishing API가 사용하는 새로운 트랜잭션 "편집" 기능을 사용하지 않습니다. InappproductsPurchases.productsPurchases.subscriptions 리소스에 대한 메서드는 즉시 적용됩니다. 각 리소스의 API 참조 페이지에는 해당 리소스에 대한 메서드가 "편집" 모델을 사용하는지 여부가 구체적으로 표시됩니다.

Purchase Status API는 Google Developers Console을 통해 사용할 수 있는 Google Play Developer API v. 2.0의 일부입니다.

API 사용

API 호출을 시작하기 위해, 여러분은 Google Play Developer Console에서 직접 Google Play Developer API를 설정하고 관리할 것입니다. 이 API는 Google Play 개발자 계정의 소유자만이 관리할 수 있습니다.

이 API에 액세스하려면 다음 절차를 따르세요.

  1. 신규 또는 기존 API 프로젝트 설정
  2. 하나 이상의 인증된 클라이언트 설정(다음 중 하나일 수 있음):

전체 세부정보를 보려면 Google Play Developer API 시작하기 페이지를 참조하세요.

단계적 편집

Google Play Developer Publishing API Edits 메서드를 사용하여 Google Play 앱에 대한 변경 내용을 준비하고 커밋할 수 있습니다. 업데이트를 진행할 준비가 되면 단일 작업으로 업데이트를 배포할 수 있습니다. 다음과 같은 사항을 변경할 수 있습니다.

  • 하나 이상의 APK 업로드
  • 다양한 "트랙"에 다양한 APK 할당: 알파, 베타, 단계적 출시, 프로덕션
  • 앱용으로 현지화된 스토어 목록 생성 및 수정
  • 앱의 스토어 목록을 위한 스크린샷과 기타 이미지 업로드

원하는 모든 변경 사항이 준비되었으면 모두 단일 작업으로 커밋됩니다.

단계적 편집에 대한 자세한 내용은 Google Play Developer API Edits 페이지를 참조하세요.

참고: 새로운 트랜잭션 "편집" 기능은 Publishing API에서만 사용됩니다. Subscriptions and In-App Purchases API에 대한 메서드는 즉시 적용됩니다. 각 리소스의 API 참조 페이지에는 해당 리소스에 대한 메서드가 "편집" 모델을 사용하는지 여부가 구체적으로 표시됩니다.

API의 효율적인 사용

Google Play Developer API를 사용하는 모든 애플리케이션에 대한 고성능 환경을 보장하는 데 도움이 되도록 이 API에 대한 액세스는 적절히 조절됩니다(할당량에 기술되어 있는 바에 따름). 애플리케이션에 대한 일일 할당량을 더 많이 요청할 수 있지만, 꼭 다음과 같은 기법을 사용해 액세스를 최소화하세요.

  • 앱 업데이트 횟수 제한 — 하루에 한 번 이상 알파 또는 베타 업데이트를 게시하지 마세요. 프로덕션 앱의 업데이트 횟수는 그보다도 더 적어야 합니다. 업데이트할 때마다 사용자가 시간을 써야 하고 어쩌면 비용이 들지도 모릅니다. 너무 자주 업데이트하면 사용자가 업데이트를 무시하기 시작하거나, 심지어 앱을 제거해버릴 수도 있습니다. 물론, 앱에 중대한 문제가 있다면 바로 문제를 해결해야겠지요.
  • 새로운 구매에 대해서만 Purchase Status API를 쿼리할 것 — 구매 시, 앱이 백엔드 서버로 구매 토큰과 기타 세부정보를 전달할 수 있으며, 이를 통해 백엔드 서버는 Purchase Status API를 사용해 구매를 확인할 수 있습니다.
  • 서버에 구매 세부정보 캐시 — 가능한 범위까지는 백엔드 서버에 인앱 상품과 구독에 대한 구매 세부정보를 캐시하세요. 앱이 런타임에 백엔드 서버에 접속하여 구매 유효성을 확인하는 경우, 서버가 캐시된 세부정보를 바탕으로 구매를 확인하여 Purchase Status API의 사용을 최소화하고 사용자에게 최대한 가장 빠른 응답과 최상의 환경을 제공할 수 있습니다.
  • 서버에 구독 만료 저장 — 서버가 Purchase Status API를 사용하여 새 구독 토큰에 대한 만료일을 쿼리한 다음, 만료일을 로컬에 저장해야 합니다. 따라서 만료 시나 만료 후에만 구독 상태를 확인할 수 있습니다(아래 참조).
  • 만료 시에만 구독 상태 쿼리 — 서버가 구독 토큰의 만료일을 검색한 후, 구독 만료일이 되거나 그 날을 지날 때까지는 Google Play 서버에 구독 상태를 다시 쿼리하면 안 됩니다. 일반적으로, 서버는 매일 일괄적으로 쿼리를 실행하여 만료되는 구독의 상태를 확인한 후 데이터베이스를 업데이트합니다. 다음 사항을 참고하세요.
    • 서버가 매일 모든 구독을 쿼리하면 안 됩니다.
    • 서버가 Android 애플리케이션의 개별 요청을 바탕으로 구독 상태를 동적으로 쿼리하면 안 됩니다.

이런 일반적인 가이드라인을 따르면 구현된 앱이 사용자 입장에서 가능한 최상의 성능을 제공할 것입니다.

할당량

Google Play Developer API를 사용하는 애플리케이션은 초기에 허용되는 사용 할당량이 (애플리케이션당) 매일 200,000개의 요청으로 제한됩니다. 이 정도면 게시 활동과 일반적인 구독-유효성 검사 요구를 충족시키기에 충분한 액세스 성능이 제공될 것입니다.



반응형