@Test public void shouldCallPurchaseCallbackErrorWhenPurcharseIntentSenderForResultFails() throws Exception { activityBindAndConnect(); Offer offer = offerFullEditionEntitlement(); whenGetBuyIntentForIdentifierReturn(offer.getIdentifier(), buyIntentResponseOk()); doThrow(new IntentSender.SendIntentException("Intent cancelled")).when(applicationProxy) .startIntentSenderForResult(isA(IntentSender.class), eq(ACTIVITY_REQUEST_CODE), isA(Intent.class), eq(0), eq(0), eq(0)); v3InAppbillingService.startPurchaseRequest(offer.getIdentifier(), PURCHASE_TYPE_IN_APP, purchaseRequestCallback); verify(applicationProxy).startIntentSenderForResult(isA(IntentSender.class), eq(ACTIVITY_REQUEST_CODE), isA(Intent.class), eq(0), eq(0), eq(0)); verify(purchaseRequestCallback).purchaseError(isA(GdxPayException.class)); }
private void refreshSKUs() { // refresh the identifiers/SKUs: ensures we have the correct mapping in case PurchaseManagerConfig was updated if (config == null) return; for (int i = 0; i < config.getOfferCount(); i++) { Offer offer = config.getOffer(i); // map store-specific identifiers with our default identifier! String identifier = offer.getIdentifier(); Set<Map.Entry<String, String>> identifierForStores = offer.getIdentifierForStores(); for (Map.Entry<String, String> entry : identifierForStores) { String storeNameOpenIAB = storeNameToOpenIAB(entry.getKey()); if (storeNameOpenIAB != null) { // if the store name is 'null', it doesn't belong to OpenIAB & we ignore (e.g. iOS store!) String identifierForStore = entry.getValue(); if (!(SkuManager.getInstance().getStoreSku(storeNameOpenIAB, identifier).equals(identifierForStore))) { SkuManager.getInstance().mapSku(identifier, storeNameOpenIAB, identifierForStore); } } } } }
@Override protected PurchaseManagerConfig getPurchaseManagerConfig() { PurchaseManagerConfig purchaseManagerConfig = new PurchaseManagerConfig(); for (LevelPack levelPack : LevelManager.getLevelPacks()) { if (!levelPack.isFree()) { Offer offer = new Offer(); offer .setType(OfferType.ENTITLEMENT) .setIdentifier(levelPack.getGoogleSku()); offer.putIdentifierForStore( PurchaseManagerConfig.STORE_NAME_ANDROID_GOOGLE, levelPack.getGoogleSku()); purchaseManagerConfig.addOffer(offer); } } String key = PhysicsComponent.s(com.draga.shape.Circle.s( GraphicComponent.s(Circle.s) + MenuScreen.s(IngameMenuScreen.s, -3) + GraphicComponent.s(HudScreen.s) + MenuScreen.s(DeadZoneInputModifier.s, 4) + GraphicComponent.s(Joystick.s))); purchaseManagerConfig.addStoreParam( PurchaseManagerConfig.STORE_NAME_ANDROID_GOOGLE, key); return purchaseManagerConfig; }
@Override public void purchase(String identifier) { // Find the SKProduct for this identifier. Offer offer = config.getOffer(identifier); if (offer == null) { log(LOGTYPEERROR, "Invalid product identifier, " + identifier); observer.handlePurchaseError(new RuntimeException("Invalid product identifier, " + identifier)); } else { String identifierForStore = offer.getIdentifierForStore (PurchaseManagerConfig.STORE_NAME_IOS_APPLE); SKProduct product = getProductByStoreIdentifier(identifierForStore); if (product == null) { // Product with this identifier not found: load product info first and try to purchase again log(LOGTYPELOG, "Requesting product info for " + identifierForStore); NSMutableSet<String> identifierForStoreSet = (NSMutableSet<String>) NSMutableSet .alloc().initWithCapacity(1); identifierForStoreSet.addObject(identifierForStore); productsRequest = SKProductsRequest.alloc().initWithProductIdentifiers (identifierForStoreSet); productsRequest.setDelegate(new AppleProductsDelegatePurchase()); productsRequest.start(); } else { // Create a SKPayment from the product and start purchase flow log(LOGTYPELOG, "Purchasing product " + identifier + " ..."); SKPayment payment = SKPayment.paymentWithProduct(product); ((SKPaymentQueue) SKPaymentQueue.defaultQueue()).addPayment(payment); } } }
private void getPurchaseIdsByType(List<String> inAppPurchasesIds, List<String> subsPurchasesIds) { for (int i = 0; i < purchaseManagerConfig.getOfferCount(); i++) { Offer offer = purchaseManagerConfig.getOffer(i); if (offer.getType().equals(OfferType.SUBSCRIPTION)) subsPurchasesIds.add(offer.getIdentifier()); else inAppPurchasesIds.add(offer.getIdentifier()); } }
private OfferType getOfferType(String identifier) { Offer offer = purchaseManagerConfig.getOffer(identifier); if (offer == null || offer.getType() == null) { throw new IllegalStateException("No offer or offerType configured for identifier: " + identifier + ", offer: " + offer); } return offer.getType(); }
public static Bundle skuDetailsResponseResultOkProductFullEditionEntitlement() { Offer offer = OfferObjectMother.offerFullEditionEntitlement(); Information information = InformationObjectMother.informationFullEditionEntitlement(); Bundle bundle = new Bundle(3); bundle.putInt(RESPONSE_CODE, BILLING_RESPONSE_RESULT_OK.getCode()); bundle.putStringArrayList(ITEM_ID_LIST, itemIdList(offer)); bundle.putStringArrayList(DETAILS_LIST, bundleDetailList(offer, information)); return bundle; }
public static Bundle skuDetailsResponseResultOkNoPriceAmountMicrosInDetailList() { Offer offer = OfferObjectMother.offerFullEditionEntitlement(); Information information = InformationObjectMother.informationFullEditionEntitlement(); Bundle bundle = new Bundle(3); bundle.putInt(RESPONSE_CODE, BILLING_RESPONSE_RESULT_OK.getCode()); bundle.putStringArrayList(ITEM_ID_LIST, itemIdList(offer)); bundle.putStringArrayList(DETAILS_LIST, bundleDetailListNoPriceAmountMicros(offer, information)); return bundle; }
private static ArrayList<String> bundleDetailList(Offer offer, Information information) { JSONObject object = makeJsonObject(offer, information); ArrayList<String> list = new ArrayList<>(); list.add(object.toString()); return list; }
private static JSONObject makeJsonObject(Offer offer, Information information) { JSONObject object = new JSONObject(); try { object.put(GoogleBillingConstants.SKU_TITLE, information.getLocalName()); object.put(GoogleBillingConstants.SKU_DESCRIPTION, information.getLocalDescription()); object.put(GoogleBillingConstants.SKU_PRICE, information.getLocalPricing()); object.put(GoogleBillingConstants.PRODUCT_ID, offer.getIdentifier()); object.put(GoogleBillingConstants.PRICE_AMOUNT_MICROS, information.getPriceInCents() * 10_000); object.put(GoogleBillingConstants.PRICE_CURRENCY_CODE, information.getPriceCurrencyCode()); } catch(JSONException e) { throw new IllegalStateException("Failed to create json object", e); } return object; }
private static String makeInAppPurchaseJsonData() { Offer offer = offerFullEditionEntitlement(); try { return makeJsonObjectForOffer(offer); } catch(JSONException e) { throw new RuntimeException(e); } }
private static String makeJsonObjectForOffer(Offer offer) throws JSONException { JSONObject jsonObject = new JSONObject(); jsonObject.put(GoogleBillingConstants.PRODUCT_ID, offer.getIdentifier()); jsonObject.put(GoogleBillingConstants.PURCHASE_TIME, System.currentTimeMillis()); jsonObject.put(GoogleBillingConstants.ORDER_ID, "GPA.1234-5678-9012-34567"); return jsonObject.toString(); }
private void connectBindAndForFullEditionEntitlement() throws android.os.RemoteException { Offer offer = offerFullEditionEntitlement(); Information information = informationFullEditionEntitlement(); whenGetProductsDetailsReturn(offer.getIdentifier(), information); bindFetchNewConnectionAndInstallPurchaseSystem(); }
@Test public void convertsConfigWithOneOffer() throws Exception { Offer offer = offerFullEditionEntitlement(); Bundle bundle = convertProductIdsToItemIdList(singletonList(offer.getIdentifier())); assertEquals(1, bundle.size()); ArrayList<String> actualList = bundle.getStringArrayList(ITEM_ID_LIST); ArrayList<String> expectedArrayList = new ArrayList<>(singletonList(offerFullEditionEntitlement().getIdentifier())); assertEquals(expectedArrayList, actualList); }
private List<String> getIdsForEntitlementsAndConsumables() { final List<String> result = new ArrayList<String>(); final int offerCount = config.getOfferCount(); for (int i = 0; i < offerCount; i++) { Offer offer = config.getOffer(i); if (offer.getType() == OfferType.ENTITLEMENT || offer.getType() == OfferType.CONSUMABLE) { result.add(offer.getIdentifier()); } } return result; }
private List<String> getIdsForSubscriptions() { final List<String> result = new ArrayList<String>(); final int offerCount = config.getOfferCount(); for (int i = 0; i < offerCount; i++) { Offer offer = config.getOffer(i); if (offer.getType() == OfferType.SUBSCRIPTION) { result.add(offer.getIdentifier()); } } return result; }
public static Offer offerFullEditionEntitlement() { Offer offer = new Offer(); offer.setIdentifier(PRODUCT_IDENTIFIER_FULL_EDITION); offer.setType(OfferType.ENTITLEMENT); return offer; }
public static Offer offerSubscription() { Offer offer = new Offer(); offer.setIdentifier("com.appname.subscription"); offer.setType(OfferType.SUBSCRIPTION); return offer; }
public static Offer offerConsumable() { Offer offer = new Offer(); offer.setIdentifier("com.appname.consumable.100.coins"); offer.setType(OfferType.CONSUMABLE); return offer; }
private static ArrayList<String> itemIdList(Offer offer) { ArrayList<String> skuList = new ArrayList<>(); skuList.add(offer.getIdentifier()); return skuList; }
@Test public void shoulReconnectAndRetryWhenGetBuyIntentFailsWithDeadObjectException() throws Exception { activityBindAndConnect(); Offer offer = offerFullEditionEntitlement(); whenGetBuyIntentForIdentifierThrow(offer.getIdentifier(), new DeadObjectException("Purchase service died.")); v3InAppbillingService.startPurchaseRequest(offer.getIdentifier(), PURCHASE_TYPE_IN_APP, purchaseRequestCallback); verify(applicationProxy).unbindService(isA(ServiceConnection.class)); verifyAndroidApplicationBindService(2); verify(asyncExecutor).executeAsync(isA(Runnable.class), eq(RETRY_PURCHASE_DELAY_IN_MS)); }
@Test public void shouldCallGdxPurchaseCallbackErrorAndReconnectWhenGetBuyIntentFailsWithDeadObjectException() throws Exception { activityBindAndConnect(); Offer offer = offerFullEditionEntitlement(); whenGetBuyIntentForIdentifierThrow(offer.getIdentifier(), new DeadObjectException("Purchase service died.")); v3InAppbillingService.startPurchaseRequest(offer.getIdentifier(), PURCHASE_TYPE_IN_APP, purchaseRequestCallback); verifyAsyncExecutorWasCalledForRetryAndRetryRun(); verify(purchaseRequestCallback).purchaseError(isA(GdxPayException.class)); verify(applicationProxy).unbindService(isA(ServiceConnection.class)); verifyAndroidApplicationBindService(2); }
private static ArrayList<String> bundleDetailListNoPriceAmountMicros(Offer offer, Information information) { JSONObject object = makeJsonObject(offer, information); object.remove(PRICE_AMOUNT_MICROS); ArrayList<String> list = new ArrayList<>(); list.add(object.toString()); return list; }
private PurchaseRequestCallback connectBindAndPurchaseRequestForFullEditionEntitlement() throws android.os.RemoteException { Offer offer = offerFullEditionEntitlement(); Information information = informationFullEditionEntitlement(); whenGetProductsDetailsReturn(offer.getIdentifier(), information); bindFetchNewConnectionAndInstallPurchaseSystem(); String productIdentifier = offerFullEditionEntitlement().getIdentifier(); purchaseManager.purchase(productIdentifier); verify(googleInAppBillingService).startPurchaseRequest(Mockito.eq(productIdentifier), Mockito.eq(PURCHASE_TYPE_IN_APP), purchaseRequestListenerArgumentCaptor.capture()); return purchaseRequestListenerArgumentCaptor.getValue(); }
@Test public void shouldReturnSkusWhenResponseIsOk() throws Exception { whenBillingServiceGetSkuDetailsReturn(skuDetailsResponseResultOkProductFullEditionEntitlement()); activityBindAndConnect(); Offer offer = offerFullEditionEntitlement(); Map<String, Information> details = v3InAppbillingService.getProductsDetails(singletonList(offer.getIdentifier()), PURCHASE_TYPE_IN_APP); assertEquals(details, Collections.singletonMap(offer.getIdentifier(), informationFullEditionEntitlement())); }
@Test public void shouldStartSenderIntentForBuyIntentResponseOk() throws Exception { activityBindAndConnect(); Offer offer = offerFullEditionEntitlement(); whenGetBuyIntentForIdentifierReturn(offer.getIdentifier(), buyIntentResponseOk()); v3InAppbillingService.startPurchaseRequest(offer.getIdentifier(), PURCHASE_TYPE_IN_APP, purchaseRequestCallback); verify(applicationProxy).startIntentSenderForResult(isA(IntentSender.class), eq(ACTIVITY_REQUEST_CODE), isA(Intent.class), eq(0), eq(0), eq(0)); }
@Test public void shouldCallPurchaseListenerOnActivityResultAfterSuccessfulPurchaseRequest() throws Exception { Offer offer = offerFullEditionEntitlement(); bindConnectAndStartPurchaseRequest(offer); whenBillingServiceGetSkuDetailsReturn(skuDetailsResponseResultOkProductFullEditionEntitlement()); when(purchaseResponseActivityResultConverter.convertToTransaction(isA(Intent.class))) .thenReturn(transactionFullEditionEuroGooglePlay()); androidEventListener.onActivityResult(ACTIVITY_REQUEST_CODE, Activity.RESULT_OK, activityResultPurchaseFullEditionSuccess()); verify(purchaseRequestCallback).purchaseSuccess(isA(Transaction.class)); }
@Test public void shouldCallPurchaseCanceledOnResultCodeZero() throws Exception { Offer offer = offerFullEditionEntitlement(); bindConnectAndStartPurchaseRequest(offer); whenBillingServiceGetSkuDetailsReturn(skuDetailsResponseResultOkProductFullEditionEntitlement()); androidEventListener.onActivityResult(ACTIVITY_REQUEST_CODE, Activity.RESULT_CANCELED, new Intent()); verify(purchaseRequestCallback).purchaseCanceled(); }
private void bindConnectAndStartPurchaseRequest(Offer offer) throws android.os.RemoteException { activityBindAndConnect(); whenGetBuyIntentForIdentifierReturn(offer.getIdentifier(), buyIntentResponseOk()); v3InAppbillingService.startPurchaseRequest(offer.getIdentifier(), PURCHASE_TYPE_IN_APP, purchaseRequestCallback); }