/// <summary> /// Handles an activity result that's part of the purchase flow in in-app billing. If you /// are calling <seealso cref="#launchPurchaseFlow"/>, then you must call this method from your /// Activity's <seealso cref="android.app.Activity@onActivityResult"/> method. This method /// MUST be called from the UI thread of the Activity. /// </summary> /// <param name="requestCode"> The requestCode as you received it. </param> /// <param name="resultCode"> The resultCode as you received it. </param> /// <param name="data"> The data (Intent) as you received it. </param> /// <returns> 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. </returns> public bool handleActivityResult(int requestCode, int resultCode, Intent data) { IabResult result; if (requestCode != mRequestCode) { return false; } checkNotDisposed(); checkSetupDone("handleActivityResult"); // end of async purchase operation that started on launchPurchaseFlow flagEndAsync(); if (data == null) { logError("Null data in IAB activity result."); result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result"); 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 == (int)Result.Ok && responseCode == BILLING_RESPONSE_RESULT_OK) { logDebug("Successful resultcode from purchase activity."); logDebug("Purchase data: " + purchaseData); logDebug("Data signature: " + dataSignature); logDebug("Extras: " + data.Extras); logDebug("Expected item type: " + mPurchasingItemType); if (purchaseData == null || dataSignature == null) { logError("BUG: either purchaseData or dataSignature is null."); logDebug("Extras: " + data.Extras.ToString()); result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature"); if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(result, null); } return true; } Purchase purchase = null; try { purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature); string sku = purchase.Sku; // 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); if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(result, purchase); } return true; } logDebug("Purchase signature successfully verified."); } catch (JSONException e) { logError("Failed to parse purchase data."); Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data."); if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(result, null); } return true; } if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase); } } else if (resultCode == (int)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."); mPurchaseListener.onIabPurchaseFinished(result, null); } } else if (resultCode == (int)Result.Canceled) { logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode)); result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled."); if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(result, null); } } else { logError("Purchase failed. Result code: " + Convert.ToString(resultCode) + ". Response: " + getResponseDesc(responseCode)); result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response."); if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(result, null); } } return true; }
public RunnableAnonymousInnerClassHelper2(RunnableAnonymousInnerClassHelper outerInstance, IabResult result_f, Inventory inv_f) { this.outerInstance = outerInstance; this.result_f = result_f; this.inv_f = inv_f; }
/// <summary> /// 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 /// <seealso cref="android.app.Activity#onActivityResult"/> method, at which point you must call /// this object's <seealso cref="#handleActivityResult"/> method to continue the purchase flow. This method /// MUST be called from the UI thread of the Activity. /// </summary> /// <param name="act"> The calling activity. </param> /// <param name="sku"> The sku of the item to purchase. </param> /// <param name="itemType"> indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS) </param> /// <param name="requestCode"> A request code (to differentiate from other responses -- /// as in <seealso cref="android.app.Activity#startActivityForResult"/>). </param> /// <param name="listener"> The listener to notify when the purchase process finishes </param> /// <param name="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. </param> public void launchPurchaseFlow(Activity act, string sku, string itemType, int requestCode, OnIabPurchaseFinishedListener listener, string extraData) { checkNotDisposed(); 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."); flagEndAsync(); if (listener != null) { listener.onIabPurchaseFinished(r, null); } return; } try { logDebug("Constructing buy intent for " + sku + ", item type: " + itemType); Bundle buyIntentBundle = mService.GetBuyIntent(3, mContext.PackageName, sku, itemType, extraData); int response = getResponseCodeFromBundle(buyIntentBundle); if (response != BILLING_RESPONSE_RESULT_OK) { logError("Unable to buy item, Error response: " + getResponseDesc(response)); flagEndAsync(); result = new IabResult(response, "Unable to buy item"); if (listener != null) { listener.onIabPurchaseFinished(result, null); } return; } PendingIntent 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.IntentSender, requestCode, new Intent(), ActivityFlags.BroughtToFront, ActivityFlags.BroughtToFront, Convert.ToInt32(0)); } catch (Android.Content.IntentSender.SendIntentException e) { logError("SendIntentException while launching purchase flow for sku " + sku); Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); flagEndAsync(); 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); Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); flagEndAsync(); result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow"); if (listener != null) { listener.onIabPurchaseFinished(result, null); } } }