Skip to content

Commit 46b2798

Browse files
committed
#229 best-effort guessing of subscription end date
1 parent 8b10f6d commit 46b2798

File tree

11 files changed

+283
-97
lines changed

11 files changed

+283
-97
lines changed

gdx-pay-android-googlebilling/src/com/badlogic/gdx/pay/android/googlebilling/Iso8601DurationStringToFreeTrialPeriodConverter.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.badlogic.gdx.pay.android.googlebilling;
22

3-
import com.badlogic.gdx.pay.FreeTrialPeriod;
4-
import com.badlogic.gdx.pay.FreeTrialPeriod.PeriodUnit;
3+
import com.badlogic.gdx.pay.SubscriptionPeriod;
4+
import com.badlogic.gdx.pay.SubscriptionPeriod.PeriodUnit;
55

66
import javax.annotation.Nonnull;
77

@@ -14,10 +14,10 @@ class Iso8601DurationStringToFreeTrialPeriodConverter {
1414
* the spec</a>
1515
*/
1616
@Nonnull
17-
public static FreeTrialPeriod convertToFreeTrialPeriod(@Nonnull String iso8601Duration) {
17+
public static SubscriptionPeriod convertToFreeTrialPeriod(@Nonnull String iso8601Duration) {
1818
final int numberOfUnits = Integer.parseInt(iso8601Duration.substring(1, iso8601Duration.length() -1 ));
1919
final PeriodUnit unit = PeriodUnit.parse(iso8601Duration.substring(iso8601Duration.length() - 1).charAt(0));
2020

21-
return new FreeTrialPeriod(numberOfUnits, unit);
21+
return new SubscriptionPeriod(numberOfUnits, unit);
2222
}
2323
}

gdx-pay-android-googlebilling/src/com/badlogic/gdx/pay/android/googlebilling/PurchaseManagerGoogleBilling.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ private Information convertSkuDetailsToInformation(SkuDetails skuDetails) {
170170
String priceString = skuDetails.getPrice();
171171
return Information.newBuilder()
172172
.localName(skuDetails.getTitle())
173-
.freeTrialPeriod(convertToFreeTrialPeriod(skuDetails.getFreeTrialPeriod()))
173+
.freeTrialPeriod(convertToSubscriptionPeriod(skuDetails.getFreeTrialPeriod()))
174+
.subscriptionPeriod(convertToSubscriptionPeriod(skuDetails.getSubscriptionPeriod()))
174175
.localDescription(skuDetails.getDescription())
175176
.localPricing(priceString)
176177
.priceCurrencyCode(skuDetails.getPriceCurrencyCode())
@@ -183,7 +184,7 @@ private Information convertSkuDetailsToInformation(SkuDetails skuDetails) {
183184
* @param iso8601Duration in ISO 8601 format.
184185
*/
185186
@Nullable
186-
private FreeTrialPeriod convertToFreeTrialPeriod(@Nullable String iso8601Duration) {
187+
private SubscriptionPeriod convertToSubscriptionPeriod(@Nullable String iso8601Duration) {
187188
if (iso8601Duration == null || iso8601Duration.isEmpty()) {
188189
return null;
189190
}
@@ -293,6 +294,7 @@ private void handlePurchase(List<Purchase> purchases, boolean fromRestore) {
293294
transaction.setStoreName(PurchaseManagerConfig.STORE_NAME_ANDROID_GOOGLE);
294295
transaction.setPurchaseTime(new Date(purchase.getPurchaseTime()));
295296
transaction.setPurchaseText("Purchased: " + purchase.getSku());
297+
transaction.setInformation(getInformation(purchase.getSku()));
296298
transaction.setReversalTime(null);
297299
transaction.setReversalText(null);
298300
transaction.setTransactionData(purchase.getOriginalJson());
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package com.badlogic.gdx.pay.android.googlebilling;
22

3-
import com.badlogic.gdx.pay.FreeTrialPeriod;
4-
import com.badlogic.gdx.pay.FreeTrialPeriod.PeriodUnit;
3+
import com.badlogic.gdx.pay.SubscriptionPeriod;
4+
import com.badlogic.gdx.pay.SubscriptionPeriod.PeriodUnit;
55
import org.junit.Test;
66

77
import static org.junit.Assert.*;
88

9-
public class Iso8601DurationStringToFreeTrialPeriodConverterTest {
9+
public class Iso8601DurationStringToSubscriptionPeriodConverterTest {
1010

1111
@Test
1212
public void convertsStringWithFewDays() {
1313

14-
final FreeTrialPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P3D");
14+
final SubscriptionPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P3D");
1515

1616
assertEquals(3, period.getNumberOfUnits());
1717
assertEquals(PeriodUnit.DAY, period.getUnit());
@@ -20,7 +20,7 @@ public void convertsStringWithFewDays() {
2020
@Test
2121
public void convertsStringWithMoreThenTenDays() {
2222

23-
final FreeTrialPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P14D");
23+
final SubscriptionPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P14D");
2424

2525
assertEquals(14, period.getNumberOfUnits());
2626
assertEquals(PeriodUnit.DAY, period.getUnit());
@@ -29,7 +29,7 @@ public void convertsStringWithMoreThenTenDays() {
2929
@Test
3030
public void convertsStringWitSixMonths() {
3131

32-
final FreeTrialPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P6M");
32+
final SubscriptionPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P6M");
3333

3434
assertEquals(6, period.getNumberOfUnits());
3535
assertEquals(PeriodUnit.MONTH, period.getUnit());
@@ -38,7 +38,7 @@ public void convertsStringWitSixMonths() {
3838
@Test
3939
public void convertsStringWithOneYear() {
4040

41-
final FreeTrialPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P1Y");
41+
final SubscriptionPeriod period = Iso8601DurationStringToFreeTrialPeriodConverter.convertToFreeTrialPeriod("P1Y");
4242

4343
assertEquals(1, period.getNumberOfUnits());
4444
assertEquals(PeriodUnit.YEAR, period.getUnit());

gdx-pay-iosrobovm-apple/README.md

+45-2
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,48 @@ Next to other ways, I find the easiest way to test the IAP the following:
1919
* your build installed from TestFlight will have working IAPs that are not charged to the users
2020

2121

22-
(1) In order for you to use your actual IAP in your app you also have to fill some information regarding your App Store Connect account. Normally you will see a warning if something is missing, but sometimes when your app is marked as distributed for free, the warnings won't show. In case of IAP, make sure that you have filled required information in following section:
23-
My Apps -> Agreements, Tax, and Banking -> Paid Apps. It's status should be "Active". As stated there: "The Paid Apps agreement alllows your organization to sell apps on the App Store or **offer in-app purchases.**"
22+
(1) In order for you to use your actual IAP in your app you also have to fill some information regarding your App Store
23+
Connect account. Normally you will see a warning if something is missing, but sometimes when your app is marked as
24+
distributed for free, the warnings won't show. In case of IAP, make sure that you have filled required information in
25+
following section: My Apps -> Agreements, Tax, and Banking -> Paid Apps. It's status should be "Active". As stated
26+
there: "The Paid Apps agreement alllows your organization to sell apps on the App Store or **offer in-app purchases.**"
27+
28+
29+
## Subscriptions
30+
31+
To verify if the user has a valid subscription we recommend server-side validation.
32+
33+
If you do not want to user server-side validation, it can be done by parsing receipt in the App. GdxPay has not
34+
implemented that. Pull requests are welcome :).
35+
36+
It is still possible to find out if user has a valid subscription.
37+
38+
iOS keeps expired Transactions from subscriptions in it's SkPaymentTransaction queues (as apposed to Google Play, which
39+
does not return them in the list of purchases).
40+
41+
All Transactions, including historical transactions, are passed through to the `PurchaseObserver`.
42+
43+
For example, if a user has a subscription with monthly period going on for 6 months and restores purchases,
44+
6 Transactions will be passed too in PurchaseObserver#handleRestore().
45+
46+
Filter out expired purchases manually. Some pointers:
47+
48+
* Start time of Transaction: `Transaction#getPurchaseTime()`
49+
* Reference to Product Information: `Transaction#getInformation()`
50+
* Reference to Subscription period: `Information#getSubscriptionPeriod()`
51+
52+
Putting that together, you can calculate Transaction purchaseEndTime.
53+
54+
If you have Billing Grace Period enabled in App Store Connect, you should add those days to the purchaseEndTime.
55+
56+
This logic is covered by `Transaction#calculateSubscriptionEndDate(int billingGracePeriodInDays)`
57+
58+
If there are zero transactions with your calculated purchaseEndTime available, the user has cancelled his subscription
59+
and should resubscribe.
60+
61+
Limitations of this method:
62+
63+
* payment cancellations cannot be detected
64+
* free trial periods cannot be detected; if someone decides to start a free trial of 3 days of a one-year subscription,
65+
the user will get the full year for free if he cancels after the first day.
66+

gdx-pay-iosrobovm-apple/src/main/java/com/badlogic/gdx/pay/ios/apple/IosVersion.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
enum IosVersion {
66
;
77

8-
static boolean isIos_7_0_orAbove() {
8+
static boolean is_7_0_orAbove() {
99
return Foundation.getMajorSystemVersion() >= 7;
1010
}
1111

0 commit comments

Comments
 (0)