Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
gradlePluginPortal()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.android.tools.build:gradle:4.2.2'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.13.0'
}
Expand Down
3 changes: 0 additions & 3 deletions cashier-google-play-billing-debug/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ android {
dependencies {
api project(':cashier-google-play-billing')

compileOnly deps.autoValue
compileOnly deps.supportAnnotations
annotationProcessor deps.autoValue
annotationProcessor deps.autoParcel

testImplementation deps.robolectric
testImplementation deps.junit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.SkuDetails;
Expand Down Expand Up @@ -124,14 +126,14 @@ public void dispose() {

@Override
public int isBillingSupported(String itemType) {
return BillingClient.BillingResponse.OK;
return BillingClient.BillingResponseCode.OK;
}

@Override
public void launchBillingFlow(@NonNull Activity activity, @NonNull final String sku, final String itemType) {
public void launchBillingFlow(@NonNull Activity activity, @NonNull final String sku, final String itemType, @Nullable final String developerPayload, @Nullable final String accountId) {
for (Product product : testProducts) {
if (product.sku().equals(sku)) {
activity.startActivity(FakeGooglePlayCheckoutActivity.intent(activity, product, TEST_PRIVATE_KEY));
activity.startActivity(FakeGooglePlayCheckoutActivity.intent(activity, product, TEST_PRIVATE_KEY, developerPayload, accountId));

// Put listener to pendingPurchases map and wait until either
// notifyPurchaseSuccess or notifyPurchaseError is called from FakeGooglePlayCheckoutActivity
Expand All @@ -144,13 +146,18 @@ public void onFakePurchaseSuccess(Purchase purchase) {
} else {
testInappPurchases.add(purchase);
}
vendor.onPurchasesUpdated(BillingClient.BillingResponse.OK, Collections.singletonList(purchase));
vendor.onPurchasesUpdated(
BillingResult.newBuilder()
.setResponseCode(BillingClient.BillingResponseCode.OK)
.build(), Collections.singletonList(purchase));
}

@Override
public void onFakePurchaseError(int responseCode) {
pendingPurchases.remove(sku);
vendor.onPurchasesUpdated(responseCode, null);
vendor.onPurchasesUpdated(BillingResult.newBuilder()
.setResponseCode(responseCode)
.build(), null);
}
});
return;
Expand Down Expand Up @@ -200,7 +207,41 @@ public void run() {
mainHandler.post(new Runnable() {
@Override
public void run() {
listener.onConsumeResponse(BillingClient.BillingResponse.OK, purchaseToken);
listener.onConsumeResponse(BillingResult.newBuilder()
.setResponseCode(BillingClient.BillingResponseCode.OK)
.build(), purchaseToken);
}
});
}
}.start();
}

@Override
public void acknowledgePurchase(@NonNull String purchaseToken, @NonNull AcknowledgePurchaseResponseListener listener) {
// Use new thread to simulate network operation
new Thread() {
public void run() {
// Wait 1 second to simulate network operation
try { sleep(1000L); } catch (InterruptedException e) {}

for (Iterator<Purchase> it = testInappPurchases.iterator(); it.hasNext();) {
if (it.next().getPurchaseToken().equals(purchaseToken)) {
it.remove();
}
}
for (Iterator<Purchase> it = testSubPurchases.iterator(); it.hasNext();) {
if (it.next().getPurchaseToken().equals(purchaseToken)) {
it.remove();
}
}

// Return result on main thread
mainHandler.post(new Runnable() {
@Override
public void run() {
listener.onAcknowledgePurchaseResponse(BillingResult.newBuilder()
.setResponseCode(BillingClient.BillingResponseCode.OK)
.build());
}
});
}
Expand Down Expand Up @@ -229,7 +270,9 @@ public void run() {
mainHandler.post(new Runnable() {
@Override
public void run() {
listener.onSkuDetailsResponse(BillingClient.BillingResponse.OK, details);
listener.onSkuDetailsResponse(BillingResult.newBuilder()
.setResponseCode(BillingClient.BillingResponseCode.OK)
.build(), details);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,20 @@
public class FakeGooglePlayCheckoutActivity extends Activity {

private static final String ARGUMENT_PRODUCT = "product";
private static final String ARGUMENT_DEV_PAYLOAD = "developer_payload";
private static final String ARGUMENT_ACCOUNT_ID = "account_id";

private Product product;

public static Intent intent(Context context, Product product, String privateKey64) {
private String developerPayload;

private String accountId;

public static Intent intent(Context context, Product product, String developerPayload, String accountId, String privateKey64) {
Intent intent = new Intent(context, FakeGooglePlayCheckoutActivity.class);
intent.putExtra(ARGUMENT_PRODUCT, product);
intent.putExtra(ARGUMENT_DEV_PAYLOAD, developerPayload);
intent.putExtra(ARGUMENT_ACCOUNT_ID, accountId);
return intent;
}

Expand All @@ -38,6 +46,8 @@ protected void onCreate(Bundle savedInstanceState) {

final Intent intent = getIntent();
product = intent.getParcelableExtra(ARGUMENT_PRODUCT);
developerPayload = intent.getStringExtra(ARGUMENT_DEV_PAYLOAD);
accountId = intent.getStringExtra(ARGUMENT_ACCOUNT_ID);

final TextView productName = bind(R.id.product_name);
final TextView productDescription = bind(R.id.product_description);
Expand Down Expand Up @@ -65,14 +75,16 @@ public void onClick(View v) {
purchaseJson.put("purchaseToken", product.sku() + "_" + System.currentTimeMillis());
purchaseJson.put("purchaseState", 0);
purchaseJson.put("productId", product.sku());
purchaseJson.put("developerPayload", developerPayload);
purchaseJson.put("obfuscatedAccountId", accountId);
String json = purchaseJson.toString();
String signature = GooglePlayBillingSecurity.sign(FakeGooglePlayBillingApi.TEST_PRIVATE_KEY, json);
Purchase purchase = new Purchase(json, signature);

FakeGooglePlayBillingApi.notifyPurchaseSuccess(product.sku(), purchase);

} catch (JSONException e) {
FakeGooglePlayBillingApi.notifyPurchaseError(product.sku(), BillingClient.BillingResponse.SERVICE_UNAVAILABLE);
FakeGooglePlayBillingApi.notifyPurchaseError(product.sku(), BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE);
}
finish();
}
Expand All @@ -82,7 +94,7 @@ public void onClick(View v) {
@Override
public void onBackPressed() {
super.onBackPressed();
FakeGooglePlayBillingApi.notifyPurchaseError(product.sku(), BillingClient.BillingResponse.USER_CANCELED);
FakeGooglePlayBillingApi.notifyPurchaseError(product.sku(), BillingClient.BillingResponseCode.USER_CANCELED);
}

private SpannableString metadataField(String name, Object value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class FakeSkuDetails extends SkuDetails {
private Product product;

public FakeSkuDetails(Product product) throws JSONException {
super("{}");
super("{productId:\""+product.sku()+"\", type:\""+(product.isSubscription() ? "subs" : "inapp")+"\"}");
this.product = product;
}

Expand Down
3 changes: 0 additions & 3 deletions cashier-google-play-billing/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ dependencies {

api deps.billingClient
implementation deps.supportAnnotations
compileOnly deps.autoValue
annotationProcessor deps.autoValue
annotationProcessor deps.autoParcel

testImplementation deps.robolectric
testImplementation deps.junit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
import com.android.billingclient.api.BillingClient.SkuType;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
Expand Down Expand Up @@ -63,7 +64,7 @@ public boolean initialize(@NonNull Context context, @NonNull GooglePlayBillingVe
public abstract void getSkuDetails(@SkuType String itemType, @NonNull List<String> skus,
@NonNull SkuDetailsResponseListener listener);

public abstract void launchBillingFlow(@NonNull Activity activity, @NonNull String sku, @SkuType String itemType);
public abstract void launchBillingFlow(@NonNull Activity activity, @NonNull String sku, @SkuType String itemType, @Nullable String developerPayload, @Nullable String accountId);

@Nullable
public abstract List<Purchase> getPurchases();
Expand All @@ -73,6 +74,8 @@ public abstract void getSkuDetails(@SkuType String itemType, @NonNull List<Strin

public abstract void consumePurchase(@NonNull String purchaseToken, @NonNull ConsumeResponseListener listener);

public abstract void acknowledgePurchase(@NonNull String purchaseToken, @NonNull AcknowledgePurchaseResponseListener listener);

protected void throwIfUnavailable() {
if (packageName == null) {
throw new IllegalStateException("You did not specify the package name");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClient.BillingResponse;
import com.android.billingclient.api.BillingClient.FeatureType;
import com.android.billingclient.api.BillingClient.SkuType;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.SkuDetails;
Expand Down Expand Up @@ -95,6 +98,7 @@ private Runnable createClient(@NonNull final Context context, @NonNull final Goo
public void run() {
logSafely("Creating Google Play Billing client...");
billing = BillingClient.newBuilder(context)
.enablePendingPurchases()
.setListener(vendor)
.build();

Expand Down Expand Up @@ -126,12 +130,12 @@ public int isBillingSupported(@SkuType String itemType) {
throwIfUnavailable();

if (SkuType.INAPP.equalsIgnoreCase(itemType) && billing.isReady()) {
return BillingResponse.OK;
return BillingClient.BillingResponseCode.OK;
} else if (SkuType.SUBS.equalsIgnoreCase(itemType)) {
return billing.isFeatureSupported(FeatureType.SUBSCRIPTIONS);
return billing.isFeatureSupported(FeatureType.SUBSCRIPTIONS).getResponseCode();
}

return BillingResponse.FEATURE_NOT_SUPPORTED;
return BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED;
}

@Override
Expand All @@ -149,7 +153,7 @@ public void getSkuDetails(@SkuType String itemType, @NonNull List<String> skus,
}

@Override
public void launchBillingFlow(@NonNull final Activity activity, @NonNull String sku, @SkuType String itemType) {
public void launchBillingFlow(@NonNull final Activity activity, @NonNull String sku, @SkuType String itemType, @Nullable String developerPayload, @Nullable String accountId) {
throwIfUnavailable();
logSafely("Launching billing flow for " + sku + " with type " + itemType);

Expand All @@ -158,20 +162,28 @@ public void launchBillingFlow(@NonNull final Activity activity, @NonNull String
Collections.singletonList(sku),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
public void onSkuDetailsResponse(@NonNull BillingResult result, List<SkuDetails> skuDetailsList) {
try {
if (responseCode == BillingResponse.OK && skuDetailsList.size() > 0) {
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(0))
.build();
if (result.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList.size() > 0) {
BillingFlowParams.Builder billingFlowParamsBuilder = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(0));

if (accountId != null) {
billingFlowParamsBuilder.setObfuscatedAccountId(accountId);
}

// This will call the {@link PurchasesUpdatedListener} specified in {@link #initialize}
billing.launchBillingFlow(activity, billingFlowParams);
billing.launchBillingFlow(activity, billingFlowParamsBuilder.build());
} else {
vendor.onPurchasesUpdated(BillingResponse.ERROR, null);
vendor.onPurchasesUpdated(
BillingResult.newBuilder()
.setResponseCode(BillingClient.BillingResponseCode.ERROR)
.build(), null);
}
} catch (Exception e) {
vendor.onPurchasesUpdated(BillingResponse.ERROR, null);
vendor.onPurchasesUpdated(BillingResult.newBuilder()
.setResponseCode(BillingClient.BillingResponseCode.ERROR)
.build(), null);
}
}
}
Expand All @@ -188,18 +200,18 @@ public List<Purchase> getPurchases() {
logSafely("Querying in-app purchases...");
Purchase.PurchasesResult inAppPurchasesResult = billing.queryPurchases(SkuType.INAPP);

if (inAppPurchasesResult.getResponseCode() == BillingResponse.OK) {
if (inAppPurchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
List<Purchase> inAppPurchases = inAppPurchasesResult.getPurchasesList();
logSafely("In-app purchases: " + TextUtils.join(", ", inAppPurchases));
allPurchases.addAll(inAppPurchases);
// Check if we support subscriptions and query those purchases as well
boolean isSubscriptionSupported =
billing.isFeatureSupported(FeatureType.SUBSCRIPTIONS) == BillingResponse.OK;
billing.isFeatureSupported(FeatureType.SUBSCRIPTIONS).getResponseCode() == BillingClient.BillingResponseCode.OK;
if (isSubscriptionSupported) {
logSafely("Querying subscription purchases...");
Purchase.PurchasesResult subscriptionPurchasesResult = billing.queryPurchases(SkuType.SUBS);

if (subscriptionPurchasesResult.getResponseCode() == BillingResponse.OK) {
if (subscriptionPurchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
List<Purchase> subscriptionPurchases = subscriptionPurchasesResult.getPurchasesList();
logSafely("Subscription purchases: " + TextUtils.join(", ", subscriptionPurchases));
allPurchases.addAll(subscriptionPurchases);
Expand All @@ -224,7 +236,7 @@ public List<Purchase> getPurchases(String itemType) {
throwIfUnavailable();

Purchase.PurchasesResult purchasesResult = billing.queryPurchases(itemType);
if (purchasesResult.getResponseCode() == BillingResponse.OK) {
if (purchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
List<Purchase> purchases = purchasesResult.getPurchasesList();
logSafely(itemType + " purchases: " + TextUtils.join(", ", purchases));
return purchases;
Expand All @@ -238,14 +250,28 @@ public void consumePurchase(@NonNull String purchaseToken, @NonNull ConsumeRespo
throwIfUnavailable();

logSafely("Consuming product with purchase token: " + purchaseToken);
billing.consumeAsync(purchaseToken, listener);
ConsumeParams params = ConsumeParams.newBuilder()
.setPurchaseToken(purchaseToken)
.build();
billing.consumeAsync(params, listener);
}

@Override
public void acknowledgePurchase(@NonNull String purchaseToken, @NonNull AcknowledgePurchaseResponseListener listener) {
throwIfUnavailable();

logSafely("Acknowledging subscription with purchase token: " + purchaseToken);
AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchaseToken)
.build();
billing.acknowledgePurchase(params, listener);
}

@Override
public void onBillingSetupFinished(@BillingResponse int billingResponseCode) {
logSafely("Service setup finished and connected. Response: " + billingResponseCode);
public void onBillingSetupFinished(BillingResult result) {
logSafely("Service setup finished and connected. Response: " + result.getResponseCode());

if (billingResponseCode == BillingResponse.OK) {
if (result.getResponseCode() == BillingClient.BillingResponseCode.OK) {
isServiceConnected = true;

if (listener != null) {
Expand Down
Loading