应用内结算:库存不正确;让用户再次购买

时间:2013-09-08 21:05:54

标签: android in-app-purchase in-app-billing

编辑*** 仍然在寻找我原来问题的答案。为什么Android不会看到之前购买的商品而不是让用户再次付款?设置SharedPreferences是一个好主意,但如果用户卸载该怎么办?他们将不得不再次购买。我不希望这个用户。谢谢。

我试图弄清楚为什么应用程序没有正确库存,我从TrivialDrive中取出并尝试将其设为我自己的。我相信我删除了所有的消费,我只有一件商品可以购买;高级升级。这应该只买一次,从不收取两次。当我在平板电脑上编译并运行应用程序时,我通过购买流程确定并且一切顺利,直到我使用任务管理器关闭应用程序并重新打开它。一旦重新打开,它就会要求再次购买保险费,而不是通过并执行高级功能。

以下是代码:

public class myClass extends SherlockActivity {

private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;

private CharSequence mDrawerTitle;
private CharSequence mTitle;
private String[] mPlanetTitles;

public static final int DIALOG_DOWNLOAD_PROGRESS = 0;
ProgressBar pd = null;
private ProgressDialog mProgressDialog;

Spinner spLoadFrom;
WebView wv;
private LinearLayout container;
private Button nextButton, closeButton;
private EditText findBox;

private static final String TAG = "Web";
IabHelper mHelper;

static boolean mIsPremium = false;
boolean mIsUserPremium = false;
boolean searchAllowed = false;

static final String PREM_SKU = "prem";

private ArrayAdapter<CharSequence> spinnerArrayAdapter;

String name_free[] = { };

String displayName_free[] = {  };

String name_premium[] = { };
String displayName_premium[] = {};


/** Called when the activity is first created. */
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mylayout);

    mTitle = mDrawerTitle = getTitle();

    mPlanetTitles = getResources().getStringArray(
            R.array.fMenu);

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mDrawerList = (ListView) findViewById(R.id.left_drawer);

    // set a custom shadow that overlays the main content when the drawer
    // opens
    mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,
            GravityCompat.START);
    // set up the drawer's list view with items and click listener
    mDrawerList.setAdapter(new ArrayAdapter<String>(this,
            R.layout.drawer_list_item, mPlanetTitles));
    mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

    // enable ActionBar app icon to behave as action to toggle nav drawer
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setHomeButtonEnabled(true);

    // ActionBarDrawerToggle ties together the the proper interactions
    // between the sliding drawer and the action bar app icon
    mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
    mDrawerLayout, /* DrawerLayout object */
    R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
    R.string.drawer_open, /* "open drawer" description for accessibility */
    R.string.drawer_close /* "close drawer" description for accessibility */
    ) {
        public void onDrawerClosed(View view) {
            getSupportActionBar().setTitle(mTitle);
            supportInvalidateOptionsMenu(); // creates call to
                                            // onPrepareOptionsMenu()
        }

        public void onDrawerOpened(View drawerView) {
            getSupportActionBar().setTitle(mDrawerTitle);
            supportInvalidateOptionsMenu(); // creates call to
                                            // onPrepareOptionsMenu()
        }
    };
    mDrawerLayout.setDrawerListener(mDrawerToggle);

    if (savedInstanceState == null) {
        selectItem(0);
    }

    mProgressDialog = new ProgressDialog(this);
    mProgressDialog.setIndeterminate(false);
    mProgressDialog.setMax(100);
    mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

    wv = (WebView) findViewById(R.id.webview);
    pd = (ProgressBar) findViewById(R.id.pBar);

    spLoadFrom = (Spinner) findViewById(R.id.Spinner02);

    if (mIsPremium == true) {
        spinnerArrayAdapter = new ArrayAdapter<CharSequence>(this,
                android.R.layout.simple_spinner_item, displayName_premium);
    } else {
        spinnerArrayAdapter = new ArrayAdapter<CharSequence>(this,
                android.R.layout.simple_spinner_item, displayName_free);
    }

    spinnerArrayAdapter
            .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spLoadFrom.setAdapter(spinnerArrayAdapter);
    SpinnerListener spListener = new SpinnerListener();
    spLoadFrom.setOnItemSelectedListener(spListener);

    String base64EncodedPublicKey = "hidden";
    mHelper = new IabHelper(this, base64EncodedPublicKey);

    Log.d(TAG, "Starting setup.");
    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
        @Override
        public void onIabSetupFinished(IabResult result) {
            Log.d(TAG, "Setup finished.");

            if (!result.isSuccess()) {
                // Oh noes, there was a problem.
                complain("Problem setting up in-app billing: " + result);
                return;
            }

            // Hooray, IAB is fully set up. Now, let's get an inventory of
            // stuff we own.
            Log.d(TAG, "Setup successful. Querying inventory.");
            mHelper.queryInventoryAsync(mGotInventoryListener);
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getSupportMenuInflater().inflate(R.menu.menu3, menu);
    return super.onCreateOptionsMenu(menu);
}

/* Called whenever we call invalidateOptionsMenu() */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    // If the nav drawer is open, hide action items related to the content
    // view
    boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
    return super.onPrepareOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    if (mDrawerToggle.onOptionsItemSelected(getMenuItem(item))) {
        return true;
    }

    int itemId = item.getItemId();
    if (itemId == R.id.search) {
        if (searchAllowed == false) {

    } else if (itemId == R.id.contact) {
        emailme();
    } else if (itemId == R.id.rate) {
        Uri uri = Uri.parse("market://details?id=" + getPackageName());
        Intent myAppLinkToMarket = new Intent(Intent.ACTION_VIEW, uri);
        try {
            startActivity(myAppLinkToMarket);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, "Unable to find Play Market",
                    Toast.LENGTH_SHORT).show();
        }
    }
    return super.onOptionsItemSelected(item);

}

public void buyPrem() {
    Log.d(TAG,
            "Upgrade button clicked; launching purchase flow for upgrade.");
    /*
     * TODO: for security, generate your payload here for verification. See
     * the comments on verifyDeveloperPayload() for more info. Since this is
     * a SAMPLE, we just use an empty string, but on a production app you
     * should carefully generate this.
     */
    String payload = "";

    mHelper.launchPurchaseFlow(this, PREM_SKU, 10001,
            mPurchaseFinishedListener, payload);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + ","
            + data);

    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here's where you'd
        // perform any handling of activity results not related to in-app
        // billing...
        super.onActivityResult(requestCode, resultCode, data);
    } else {
        Log.d(TAG, "onActivityResult handled by IABUtil.");
    }
}

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    @Override
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
        Log.d(TAG, "Purchase finished: " + result + ", purchase: "
                + purchase);
        if (result.isFailure()) {
            complain("Error purchasing: " + result);
            // Handle error
            return;

        }
        if (!verifyDeveloperPayload(purchase)) {
            complain("Error purchasing. Authenticity verification failed.");
            return;

        }
        Log.d(TAG, "Purchase successful.");

        if (purchase.getSku().equals(PREM_SKU)) {
            // bought the premium upgrade!
            Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
            alert("Thank you for upgrading to premium!");
            mIsPremium = true;
            mIsUserPremium = true;
            searchAllowed = true;
        }

    }
};

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    @Override
    public void onQueryInventoryFinished(IabResult result,
            Inventory inventory) {
        Log.d(TAG, "Query inventory finished.");
        if (result.isFailure()) {
            complain("Failed to query inventory: " + result);
            return;
        }

        /*if (inventory.hasPurchase(PREM_SKU)) {

            mHelper.consumeAsync(inventory.getPurchase(PREM_SKU), null);
        }*/

        Log.d(TAG, "Query inventory was successful.");

        Purchase premiumPurchase = inventory.getPurchase(PREM_SKU);
        mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
        Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));
        if (mIsPremium) {
            searchAllowed = true;
            mIsUserPremium = true;
            Log.d(TAG, "Should be premium by now...");
        }

        Log.d(TAG, "Initial inventory query finished; enabling main UI.");
    }

};

boolean verifyDeveloperPayload(Purchase p) {
    String payload = p.getDeveloperPayload();

    /*
     * TODO: verify that the developer payload of the purchase is correct.
     * It will be the same one that you sent when initiating the purchase.
     * 
     * WARNING: Locally generating a random string when starting a purchase
     * and verifying it here might seem like a good approach, but this will
     * fail in the case where the user purchases an item on one device and
     * then uses your app on a different device, because on the other device
     * you will not have access to the random string you originally
     * generated.
     * 
     * So a good developer payload has these characteristics:
     * 
     * 1. If two different users purchase an item, the payload is different
     * between them, so that one user's purchase can't be replayed to
     * another user.
     * 
     * 2. The payload must be such that you can verify it even when the app
     * wasn't the one who initiated the purchase flow (so that items
     * purchased by the user on one device work on other devices owned by
     * the user).
     * 
     * Using your own server to store and verify developer payloads across
     * app installations is recommended.
     */

    return true;
}

/*public void consumeItem() {
    mHelper.queryInventoryAsync(mReceivedInventoryListener);

}*/

IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    @Override
    public void onQueryInventoryFinished(IabResult result,
            Inventory inventory) {

        if (result.isFailure()) {
            // Handle failure
        } else {
            searchAllowed = false;
            //mHelper.consumeAsync(inventory.getPurchase(PREM_SKU),
            //      mConsumeFinishedListener);
        }
    }
};

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
    @Override
    public void onConsumeFinished(Purchase purchase, IabResult result) {

        if (result.isSuccess()) {

        } else {
            // handle error
        }
    }
};

@Override
public void onDestroy() {
    super.onDestroy();
    if (mHelper != null)
        mHelper.dispose();
    mHelper = null;
}

void complain(String message) {
    Log.e(TAG, "**** TrivialDrive Error: " + message);
    alert("Error: " + message);
}

void alert(String message) {
    AlertDialog.Builder bld = new AlertDialog.Builder(this);
    bld.setMessage(message);
    bld.setNeutralButton("OK", null);
    Log.d(TAG, "Showing alert dialog: " + message);
    bld.create().show();
}

private android.view.MenuItem getMenuItem(final MenuItem item) {
    return new android.view.MenuItem() {
        @Override
        public int getItemId() {
            return item.getItemId();
        }

        public boolean isEnabled() {
            return true;
        }

        @Override
        public boolean collapseActionView() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean expandActionView() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public ActionProvider getActionProvider() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public View getActionView() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public char getAlphabeticShortcut() {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public int getGroupId() {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public Drawable getIcon() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Intent getIntent() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public ContextMenuInfo getMenuInfo() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public char getNumericShortcut() {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public int getOrder() {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public SubMenu getSubMenu() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public CharSequence getTitle() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public CharSequence getTitleCondensed() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public boolean hasSubMenu() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean isActionViewExpanded() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean isCheckable() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean isChecked() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean isVisible() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public android.view.MenuItem setActionProvider(
                ActionProvider actionProvider) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setActionView(View view) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setActionView(int resId) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setAlphabeticShortcut(char alphaChar) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setCheckable(boolean checkable) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setChecked(boolean checked) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setEnabled(boolean enabled) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setIcon(Drawable icon) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setIcon(int iconRes) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setIntent(Intent intent) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setNumericShortcut(char numericChar) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setOnActionExpandListener(
                OnActionExpandListener listener) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setOnMenuItemClickListener(
                OnMenuItemClickListener menuItemClickListener) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setShortcut(char numericChar,
                char alphaChar) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setShowAsAction(int actionEnum) {
            // TODO Auto-generated method stub

        }

        @Override
        public android.view.MenuItem setShowAsActionFlags(int actionEnum) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setTitle(CharSequence title) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setTitle(int title) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setTitleCondensed(CharSequence title) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public android.view.MenuItem setVisible(boolean visible) {
            // TODO Auto-generated method stub
            return null;
        }
    };
}

private class DrawerItemClickListener implements
        ListView.OnItemClickListener {

    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {

        switch (position) {
        case 0:
            mDrawerLayout.closeDrawer(mDrawerList);
            break;
        case 1:
            Intent c1 = new Intent(getBaseContext(), 1.class);
            startActivity(c1);
            break;
        case 2:
            Intent c2 = new Intent(getBaseContext(), 5.class);
            startActivity(c2);
            break;
        case 3:
            Intent c3 = new Intent(getBaseContext(), 3.class);
            startActivity(c3);
            break;
        case 4:
            Intent c4 = new Intent(getBaseContext(), 4.class);
            startActivity(c4);
            break;
        default:
        }
    }
}

public void selectItem(int position) {
    switch (position) {
    case 0:
        break;
    case 1:
        setContentView(R.layout.1);
        break;
    case 2:
        setContentView(R.layout.2);
        break;
    case 3:
        setContentView(R.layout.3);
        break;
    case 4:
        setContentView(R.layout.4);
        break;
    default:
    }
}

@Override
public void setTitle(CharSequence title) {
    mTitle = title;
    getSupportActionBar().setTitle(mTitle);
}

/**
 * When using the ActionBarDrawerToggle, you must call it during
 * onPostCreate() and onConfigurationChanged()...
 */

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    // Sync the toggle state after onRestoreInstanceState has occurred.
    mDrawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // Pass any configuration change to the drawer toggls
    mDrawerToggle.onConfigurationChanged(newConfig);
}

}

3 个答案:

答案 0 :(得分:2)

关闭应用程序,删除所有变量,包括mIsPremium。

因此,你需要在某处保存mIsPremium的值,并在再次打开应用程序时加载它。

我建议您使用SharedPreferences执行此任务。

你可以这样做:

IabHelper.OnIabPurchaseFinishedListener 中,您需要保存自己的偏好:

if (purchase.getSku().equals(PREM_SKU)) {
        // bought the premium upgrade!
        Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
        alert("Thank you for upgrading to premium!");
        mIsPremium = true;
        mIsUserPremium = true;
        searchAllowed = true;
        SharedPreferences prefs = this.getBaseContext().getSharedPreferences(
                "com.example.yourapp", 0);
        prefs.edit().putBoolean("premium", true).apply;
}

在用户成功购买保费后,您可以将保费状态“true”保存到共享偏好“premium”。

onCreate()方法中,您需要加载首选项:

SharedPreferences prefs = this.getBaseContext().getSharedPreferences(
        "com.example.yourapp", 0);
mIsPremium = prefs.getBoolean("premium", false);

在这里加载首选项“premium”,如果此偏好尚不存在,则将采用“false”als默认值。

答案 1 :(得分:2)

虽然@Philip Sheard和@ Al0x提供的答案都非常出色,但我的问题通过审查我的代码来解决。我完全删除了对“消费”过程的任何引用。这是一个简单的修复,但我也整合了SharedPreferences,一切都很好。感谢所有的建议 - 你带我到了正确的地方!

答案 2 :(得分:1)

另一种解决方案是在应用内商店中添加一个按钮,允许用户恢复所有购买。 IAB v3文档中描述了此过程,Apple要求采用这种方法。