private static boolean isNeutralized(@Nonnull List<Purchase> purchases, @Nonnull Purchase purchase) { Check.isTrue(purchase.state == Purchase.State.PURCHASED, "Must be PURCHASED"); for (int i = 1; i < purchases.size(); i++) { final Purchase same = purchases.get(i); if (same.sku.equals(purchase.sku)) { switch (same.state) { case PURCHASED: // found same later purchase => obviously there is a bug somewhere as user can't own // several purchases with same SKU. For now let's skip the item Billing.warning("Two purchases with same SKU found: " + purchase + " and " + same); break; case CANCELLED: case REFUNDED: case EXPIRED: // neutralization found => need to remove it purchases.remove(i); break; } return true; } } return false; }
/** * This method must be called from {@link Activity#onActivityResult(int, int, Intent)} (or * {@link android.app.Fragment#onActivityResult(int, int, Intent)}) in order to finish a started * purchase flow. * * @return true if activity result was handled (there existed a purchase flow for the given * <var>requestCode</var>) * @see Activity#onActivityResult(int, int, Intent) * @see android.app.Fragment#onActivityResult(int, int, Intent) */ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { final PurchaseFlow flow = mFlows.get(requestCode); if (flow == null) { Billing.warning("Purchase flow doesn't exist for requestCode=" + requestCode + ". Have you forgotten to create it?"); return false; } flow.onActivityResult(requestCode, resultCode, data); return true; }
@Nonnull @Override public PurchaseVerifier getPurchaseVerifier() { Billing.warning("Default purchase verification procedure is used, please read https://github.com/serso/android-checkout#purchase-verification"); return newPurchaseVerifier(getPublicKey()); }
void onCheckoutStopped() { Check.isMainThread(); synchronized (mLock) { mCheckoutCount--; if (mCheckoutCount < 0) { mCheckoutCount = 0; warning("Billing#onCheckoutStopped is called more than Billing#onCheckoutStarted"); } if (mCheckoutCount == 0 && mConfiguration.isAutoConnect()) { disconnect(); } } }
@Nonnull private Inventory.Products loadProducts(@Nonnull Inventory.Request request, @Nonnull SQLiteDatabase db) { final Inventory.Products result = new Inventory.Products(); for (String productId : ProductTypes.ALL) { final Inventory.Product product = new Inventory.Product(productId, true); final List<String> skus = request.getSkus(productId); if (!skus.isEmpty()) { product.setPurchases(loadPurchases(skus, db)); } else { Billing.warning("There are no SKUs for \"" + product.id + "\" product. No purchase information will be loaded"); } result.add(product); } return result; }
private void loadSkus(@Nonnull BillingRequests requests, @Nonnull final Product product) { final List<String> skuIds = mTask.getRequest().getSkus(product.id); if (skuIds.isEmpty()) { Billing.warning("There are no SKUs for \"" + product.id + "\" product. No SKU information will be loaded"); synchronized (mLock) { countDown(); } return; } requests.getSkus(product.id, skuIds, synchronizedListener(new RequestListener<Skus>() { @Override public void onSuccess(@Nonnull Skus skus) { product.setSkus(skus.list); countDown(); } @Override public void onError(int response, @Nonnull Exception e) { countDown(); } })); } }
/** * Connects to the Billing service. Called automatically when first request is done, * Use {@link #disconnect()} to disconnect. * It's allowed to call this method several times, if service is already connected nothing will * happen. */ public void connect() { synchronized (mLock) { if (mState == State.CONNECTED) { executePendingRequests(); return; } if (mState == State.CONNECTING) { return; } if (mConfiguration.isAutoConnect() && mCheckoutCount <= 0) { warning("Auto connection feature is turned on. There is no need in calling Billing.connect() manually. See Billing.Configuration.isAutoConnect"); } setState(State.CONNECTING); mMainThread.execute(new Runnable() { @Override public void run() { connectOnMainThread(); } }); } }