2

I am a novice Android developer who has never used in-app billing before. So I have followed the steps outlined in Google's own documentation at http://developer.android.com/google/play/billing/billing_integrate.html.

I added the IInAppBillingService.aidl file under main/aidl/com.android.vending.billing and my structure matches countless screenshots I've seen online.

I added code very similar to the sample code to a part of my app that interacts with my online marketplace. A simplified version of that code, with extraneous stuff removed, is:

    imports...
    import android.content.ServiceConnection;

    import com.android.vending.billing.IInAppBillingService;


    public class intMarket extends Activity {
        IInAppBillingService mService;
        public static final String SHARED_PREFS_NAME="myAppSettings";
        public static String purchaseToken = "";

        ServiceConnection mServiceConn = new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mService = null;
            }

            @Override
            public void onServiceConnected(ComponentName name,
                                           IBinder service) {
                mService = IInAppBillingService.Stub.asInterface(service);
            }
        };

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.intbrowser);

            Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
            serviceIntent.setPackage("com.android.vending");
            bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);

            WebView myWebView = (WebView) findViewById(R.id.webview);
            myWebView.setWebViewClient(new WebViewClient());
            WebSettings webSettings = myWebView.getSettings();
            webSettings.setJavaScriptEnabled(true);
            myWebView.addJavascriptInterface(new WebAppInterface(this), "Android");

            String tUrl = getString(R.string.urlMarket);
            SharedPreferences settings = getSharedPreferences(SHARED_PREFS_NAME, 0);
            myWebView.loadUrl(tUrl);
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            if (mService != null) {
                unbindService(mServiceConn);
            }
        }

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == 1001) {
                int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
                String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
                String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");

                if (resultCode == RESULT_OK) {
                    try {
                        JSONObject jo = new JSONObject(purchaseData);
                        String sku = jo.getString("productId");
                        purchaseToken = jo.getString("purchaseToken");
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        public class  WebAppInterface {
            Context mContext;

            /** Instantiate the interface and set the context */
            WebAppInterface(Context c) {
                mContext = c;
            }

            /** Show a toast from the web page */
            @JavascriptInterface
            public void showToast(String toast) {
                Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
            }

             @JavascriptInterface
            public void purchaseItem(Integer itemID, String itemName, BigDecimal itemPrice) {
                // Credit Purchase
                 ArrayList<String> skuList = new ArrayList<String> ();
                 Bundle querySkus = new Bundle();
                 querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
                 String purchaseCreditsResponse = "Success";
                 try {
                     Bundle skuDetails = mService.(3, getPackageName(), "inapp", querySkus);
                     Log.d("WPS", skuDetails.toString());

                     int response = skuDetails.getInt("RESPONSE_CODE");
                     Log.d("WPS", Integer.toString(response));

                     if (response == 0) {
                         ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
                         String thisItemID = "0";

                         for (String thisResponse : responseList) {
                             JSONObject object = new JSONObject(thisResponse);
                             String itemPrice = object.getString("price");
                             if (itemPrice.equals(itemPrice.toString()))
                                 thisItemID = object.getString("productId");
                         }

                         if (Integer.parseInt(thisItemID) > 0) {
                             Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), thisItemID, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
                             if (buyIntentBundle.getInt("RESPONSE_CODE") == 0) {
                                 PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
                                 //startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
                                 // purchaseToken
                                 purchaseCreditsResponse = "Failure";
                                 Toast.makeText(mContext, "Buying: " + itemName, Toast.LENGTH_LONG).show();
                             }
                         } else {
                             purchaseCreditsResponse = "Failure";
                             Toast.makeText(mContext, "Sorry, but there was a problem with your purchase.  Please try again.  If the problem persists, contact support.  Error Code: 001.", Toast.LENGTH_LONG).show();
                         }
                     } else {
                         purchaseCreditsResponse = "Failure";
                         Toast.makeText(mContext, "Sorry, but there was a problem with your purchase.  Please try again.  If the problem persists, contact support.  Error Code: 002.", Toast.LENGTH_LONG).show();
                     }
                 } catch (RemoteException e) {
                     purchaseCreditsResponse = "Failure";
                     Toast.makeText(mContext, "Sorry, but there was a problem with your purchase.  Please try again.  If the problem persists, contact support.  Error Code: 003.", Toast.LENGTH_LONG).show();
                 } catch (JSONException e) {
                     purchaseCreditsResponse = "Failure";
                     Toast.makeText(mContext, "Sorry, but there was a problem with your purchase.  Please try again.  If the problem persists, contact support.  Error Code: 004.", Toast.LENGTH_LONG).show();
                 }// catch (IntentSender.SendIntentException e) {
                 //    purchaseCreditsResponse = "Failure";
                 //    Toast.makeText(mContext, "Sorry, but there was a problem with your purchase.  Please try again.  If the problem persists, contact support.  Error Code: 005.", Toast.LENGTH_LONG).show();
                 //}



                // Purchase Report
                // Report purchase and get response into purchaseResponse
                // Todo


                // Credit Consumption
                // Todo
            }
        }
    }

I then compiled a version of this and uploaded the APK to Google Play as an alpha test case and published it. I have been making updates and using ADB to install the new compiled APKs to my phone, but I continually get the following from this code in LogCat:

    03-23 15:07:55.325    2958-3009/? D/WPS﹕ Bundle[mParcelledData.dataSize=48]
    03-23 15:07:55.325    2958-3009/? D/WPS﹕ 5

Which, of course, indicates: BILLING_RESPONSE_RESULT_DEVELOPER_ERROR

I read in another SO post that you cannot test billing on a device whose main account is the same as the developer's account. So I spent several hours yesterday tracking down another phone and getting it setup using a completely separate account (and the only account on the device). I uploaded a new build to Google Play as an alpha test and published it. I waited for this other phone to get the new version and tried testing billing again. The phone reported this error message that I had built in:

 Sorry, but there was a problem with your purchase.  Please try again.  If the problem persists, contact support.  Error Code: 002.

Which indicates the same error I was running into before. This implies that there is a problem with my code, which would not surprise me.

I have not done anything with the big giant license code that appears in the Google Play developer panel because the above referenced documentation says nothing about it being required. But is it? If not, what about my code is causing this problem?

Nicholas
  • 1,974
  • 4
  • 20
  • 46
  • Have you turned `purchases` on in developers console? – Kevin Crain Mar 31 '15 at 08:07
  • @KevinCrain I've added 3 in-app products and activated them. Is there somewhere else to turn on 'purchases' in general? – Nicholas Mar 31 '15 at 12:52
  • Would suggest to first try out TrivialDrive sample app that gives you step by step instructions for in app billing http://developer.android.com/training/in-app-billing/preparing-iab-app.html#GetSample – random Apr 09 '15 at 05:51

2 Answers2

0

Verify that com.android.vending.BILLING is added to your manifest permissions.

Also verify that your app is signed with your production key and the version on Google Play should be the same as the version you are testing with.

Also have a look at this lightweight, straight forward library you can use on Github below: https://github.com/anjlab/android-inapp-billing-v3

Kevin Crain
  • 1,905
  • 1
  • 19
  • 28
  • Thank you for the suggestion. AndroidManifest has the line "". I created the install file by opening Android Studio and choosing "Generate Signed APK". Then I made a new keystore for this app and uploaded the resultant file to Google. I do this each time I create a build for testing. I assume I don't need to provide anything from that keystore to Google? – Nicholas Mar 31 '15 at 14:01
  • No not that I am aware of, I know to use certain API's they need the SHA1 fingerprint. – Kevin Crain Mar 31 '15 at 14:30
0

People comment that this is a bug in Google's billing API (maybe Google considers it a feature?). Seems like you have to consume the purchaseToken, just as the guy here does:

String purchaseToken = o.optString("token", o.optString("purchaseToken"));
// Consume purchaseToken, handling any errors
mService.consumePurchase(3, getPackageName(), purchaseToken);
Community
  • 1
  • 1
Mikel Pascual
  • 2,202
  • 18
  • 27
  • But I don't get to the consumption stage; this error occurs on the initial purchase before I can even try to consume. – Nicholas Apr 03 '15 at 12:38