/**
   * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
   * which will involve bringing up the Google Play screen. The calling activity will be paused
   * while the user interacts with Google Play, and the result will be delivered via the activity's
   * {@link android.app.Activity#onActivityResult} method, at which point you must call this
   * object's {@link #handleActivityResult} method to continue the purchase flow. This method MUST
   * be called from the UI thread of the Activity.
   *
   * @param act The calling activity.
   * @param sku The sku of the item to purchase.
   * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or
   *     ITEM_TYPE_SUBS)
   * @param requestCode A request code (to differentiate from other responses -- as in {@link
   *     android.app.Activity#startActivityForResult}).
   * @param listener The listener to notify when the purchase process finishes
   * @param extraData Extra data (developer payload), which will be returned with the purchase data
   *     when the purchase completes. This extra data will be permanently bound to that purchase and
   *     will always be returned when the purchase is queried.
   */
  public void launchPurchaseFlow(
      Activity act,
      String sku,
      String itemType,
      int requestCode,
      OnIabPurchaseFinishedListener listener,
      String extraData) {
    checkSetupDone("launchPurchaseFlow");
    flagStartAsync("launchPurchaseFlow");
    IabResult result;

    if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {
      IabResult r =
          new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE, "Subscriptions are not available.");
      if (listener != null) listener.onIabPurchaseFinished(r, null);
      return;
    }

    try {
      logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);
      Bundle buyIntentBundle =
          mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType, extraData);
      int response = getResponseCodeFromBundle(buyIntentBundle);
      if (response != BILLING_RESPONSE_RESULT_OK) {
        logError("Unable to buy item, Error response: " + getResponseDesc(response));

        result = new IabResult(response, "Unable to buy item");
        if (listener != null) listener.onIabPurchaseFinished(result, null);
        return;
      }

      PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
      logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
      mRequestCode = requestCode;
      mPurchaseListener = listener;
      mPurchasingItemType = itemType;
      act.startIntentSenderForResult(
          pendingIntent.getIntentSender(),
          requestCode,
          new Intent(),
          Integer.valueOf(0),
          Integer.valueOf(0),
          Integer.valueOf(0));
    } catch (SendIntentException e) {
      logError("SendIntentException while launching purchase flow for sku " + sku);
      e.printStackTrace();

      result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.");
      if (listener != null) listener.onIabPurchaseFinished(result, null);
    } catch (RemoteException e) {
      logError("RemoteException while launching purchase flow for sku " + sku);
      e.printStackTrace();

      result =
          new IabResult(
              IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow");
      if (listener != null) listener.onIabPurchaseFinished(result, null);
    }
  }
Beispiel #2
0
  /**
   * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
   * which will involve bringing up the Google Play screen. The calling activity will be paused
   * while the user interacts with Google Play, and the result will be delivered via the activity's
   * {@link android.app.Activity#onActivityResult} method, at which point you must call this
   * object's {@link #handleActivityResult} method to continue the purchase flow. This method MUST
   * be called from the UI thread of the Activity.
   *
   * @param act The calling activity.
   * @param sku The sku of the item to purchase.
   * @param requestCode A request code (to differentiate from other responses -- as in {@link
   *     android.app.Activity#startActivityForResult}).
   * @param listener The listener to notify when the purchase process finishes
   * @param extraData Extra data (developer payload), which will be returned with the purchase data
   *     when the purchase completes. This extra data will be permanently bound to that purchase and
   *     will always be returned when the purchase is queried.
   */
  public void launchPurchaseFlow(
      Activity act,
      String sku,
      int requestCode,
      OnIabPurchaseFinishedListener listener,
      String extraData) {
    checkSetupDone("launchPurchaseFlow");
    flagStartAsync("launchPurchaseFlow");
    IabResult result;

    try {
      logDebug("Constructing buy intent for " + sku);
      Bundle buyIntentBundle =
          mService.getBuyIntent(
              BILLING_API_VERSION, mContext.getPackageName(), sku, ITEM_TYPE_INAPP, extraData);
      int response = getResponseCodeFromBundle(buyIntentBundle);
      if (response != BILLING_RESPONSE_RESULT_OK) {
        logError("Unable to buy item, Error response: " + getResponseDesc(response));

        result = new IabResult(response, "Unable to buy item", requestCode);
        if (listener != null) listener.onIabPurchaseFinished(result, null);
      } else {
        PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
        logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
        SYSLOG("MoSync Launching buy intent for " + sku + ". Request code: " + requestCode);
        mRequestCode = requestCode;
        mPurchaseListener = listener;
        act.startIntentSenderForResult(
            pendingIntent.getIntentSender(),
            requestCode,
            new Intent(Consts.METHOD_REQUEST_PURCHASE),
            0,
            0,
            0);
      }
    } catch (SendIntentException e) {
      logError("SendIntentException while launching purchase flow for sku " + sku);
      SYSLOG("MoSync SendIntentException while launching purchase flow for sku " + sku);
      e.printStackTrace();

      result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.", requestCode);
      if (listener != null) listener.onIabPurchaseFinished(result, null);
    } catch (RemoteException e) {
      logError("RemoteException while launching purchase flow for sku " + sku);
      SYSLOG("MoSync RemoteException while launching purchase flow for sku " + sku);
      e.printStackTrace();

      result =
          new IabResult(
              IABHELPER_REMOTE_EXCEPTION,
              "Remote exception while starting purchase flow",
              requestCode);
      if (listener != null) listener.onIabPurchaseFinished(result, null);
    }
  }
Beispiel #3
0
  /**
   * Handles an activity result that's part of the purchase flow in in-app billing. If you are
   * calling {@link #launchPurchaseFlow}, then you must call this method from your Activity's {@link
   * android.app.Activity@onActivityResult} method. This method MUST be called from the UI thread of
   * the Activity.
   *
   * @param requestCode The requestCode as you received it.
   * @param resultCode The resultCode as you received it.
   * @param data The data (Intent) as you received it.
   * @return Returns true if the result was related to a purchase flow and was handled; false if the
   *     result was not related to a purchase, in which case you should handle it normally.
   */
  public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
    IabResult result;
    if (requestCode != mRequestCode) return false;

    checkSetupDone("handleActivityResult");

    // end of async purchase operation
    flagEndAsync();

    if (data == null) {
      logError("Null data in IAB activity result.");
      result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result", requestCode);
      if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
      return true;
    }

    int responseCode = getResponseCodeFromIntent(data);
    String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
    String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

    if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) {
      logDebug("Successful resultcode from purchase activity.");
      logDebug("Purchase data: " + purchaseData);
      logDebug("Data signature: " + dataSignature);
      logDebug("Extras: " + data.getExtras());

      if (purchaseData == null || dataSignature == null) {
        logError("BUG: either purchaseData or dataSignature is null.");
        logDebug("Extras: " + data.getExtras().toString());
        result =
            new IabResult(
                IABHELPER_UNKNOWN_ERROR,
                "IAB returned null purchaseData or dataSignature",
                requestCode);
        if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
        return true;
      }

      Purchase purchase = null;
      try {
        purchase = new Purchase(purchaseData, dataSignature);
        String sku = purchase.getSku();

        // Verify signature
        if (!Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) {
          logError("Purchase signature verification FAILED for sku " + sku);
          result =
              new IabResult(
                  IABHELPER_VERIFICATION_FAILED,
                  "Signature verification failed for sku " + sku,
                  requestCode);
          if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase);
          return true;
        }
        logDebug("Purchase signature successfully verified.");
      } catch (JSONException e) {
        logError("Failed to parse purchase data.");
        e.printStackTrace();
        result =
            new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data.", requestCode);
        if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
        return true;
      }

      if (mPurchaseListener != null) {
        mPurchaseListener.onIabPurchaseFinished(
            new IabResult(BILLING_RESPONSE_RESULT_OK, "Success", requestCode), purchase);
      }
    } else if (resultCode == Activity.RESULT_OK) {
      // result code was OK, but in-app billing response was not OK.
      logDebug(
          "Result code was OK but in-app billing response was not OK: "
              + getResponseDesc(responseCode));
      if (mPurchaseListener != null) {
        result = new IabResult(responseCode, "Problem purchashing item.", requestCode);
        mPurchaseListener.onIabPurchaseFinished(result, null);
      }
    } else if (resultCode == Activity.RESULT_CANCELED) {
      logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
      result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.", requestCode);
      if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
    } else {
      logError(
          "Purchase failed. Result code: "
              + Integer.toString(resultCode)
              + ". Response: "
              + getResponseDesc(responseCode));
      result =
          new IabResult(
              IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response.", requestCode);
      if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
    }
    return true;
  }