Google play内购 Iab

Google Play 内购 In-App-Billing在Android项目或者Cocos2dx/Unity项目中的集成.

最近在做一个游戏的海外版,需要加内购,碰到一些坑,这里记录下来,希望能对大家有个帮助。


参考教程:

Google Play In-app Billing官方教程
Google Play In-app Billing 踩过的那些坑
StackOverflow 论坛
Google Play 内购In-app-billing 总结~

开发者需要做的准备:


1.Android手机和电脑。

2.Google play 后台应用,并且把内购项目创建好并发布成功。能够得到内购产品的SKU即ProductID,和项目64位的秘钥。

3.内购产品的说明:

a.产品的id是唯一的字符串定义,比如com.engine.produce01,后台添加产品后需要激活。

b.In-app Billing 的 API 有个 v2 版本和 v3 版本,v2 版本已经不支持了,直接整 v3 版本的吧,Google Play 没有可重购买商品这个概念,所有的“商品/充值档”用户成功购买过一次之后就不允许再次购买了。所以为了实现像应用内支付充值这种可重复购买的“商品/充值档”,Google Play 提供了一个“消耗”借口(Consuming In-app Products)。用户购买完商品后,调一下“消耗”接口,这样用户下次就可以继续购买了。


使用IAB的流程:


1.首先确定你的SKU和Request值(随便填)


static final String SKU_PACKAGE1 = "android.test.purchased";
static final String SKU_PACKAGE2 = "cinderella_product02";
static final String SKU_PACKAGE3 = "cinderella_infinite";

 
   

// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001;

2.IabHelper类初始化方法,这里的base64EncodedPublicKey是googleplay后台的发布产品的时候生成提供的


mHelper=new IabHelper(this, base64EncodedPublicKey);

3.startSetup 的操作是检查是否有权限和连接到Google Billing service是否成功.


这里回调的操作是如果成功,调用queryInventoryAsync查看产品id是否可以使用,查询完成后会调用IabHelper.QueryInventoryFinishedListener 这个回调接口进行通知,在这个接口中可以获取商品的详细信息SkuDetails和Purchase信息。

		mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
			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;
				}
				// Have we been disposed of in the meantime? If so, quit.
				if (mHelper == null)
					return;

				// 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);
			}
		});

4.点击购买按钮调用方法,主要是mHelper.launchPurchaseFlow()方法


	// User clicked the "Buy Gas" button
	public void onBuyGasButtonClicked(View arg0) {
		Log.d(TAG, "Buy gas button clicked.");

		// launch the gas purchase UI flow.
		// We will be notified of completion via mPurchaseFinishedListener
		setWaitScreen(true);
		Log.d(TAG, "Launching purchase flow for gas.");

		/*
		 * 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, SKU_PACKAGE1, RC_REQUEST, mPurchaseFinishedListener, payload);
	}

5.verifyDeveloperPayload方法用来在服务器做验证的,起到确认订单的作用,小游戏就免了吧!


/** Verifies the developer payload of a purchase. */
	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;
	}

6.下面是执行完购买后的监听方法


	// Callback for when a purchase is finished
	IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
		public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
			Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
			// if we were disposed of in the meantime, quit.
			if (mHelper == null)
				return;
			if (result.isFailure()) {
				complain("Error purchasing: " + result);
				setWaitScreen(false);
				return;
			}
			if (!verifyDeveloperPayload(purchase)) {
				complain("Error purchasing. Authenticity verification failed.");
				setWaitScreen(false);
				return;
			}
			Log.d(TAG, "Purchase successful.");
			if (purchase.getSku().equals(SKU_PACKAGE1)) {
				// bought 1/4 tank of gas. So consume it.
				Log.d(TAG, "Purchase1 is gas. Starting gas consumption.");
				mHelper.consumeAsync(purchase, mConsumeFinishedListener);
			} else if (purchase.getSku().equals(SKU_PACKAGE2)) {
				// bought the premium upgrade!
				Log.d(TAG, "Purchase2 is premium upgrade. Congratulating user.");
				mHelper.consumeAsync(purchase, mConsumeFinishedListener);
			} else if (purchase.getSku().equals(SKU_PACKAGE3)) {
				// bought the premium upgrade!
				Log.d(TAG, "Purchase3 is premium upgrade. Congratulating user.");
			}
		}
	};

7.执行完购买回调后,消耗型商品需要调用消耗方法


IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener()

这句的意思就是消耗掉你刚买的商品,消耗是指在googleplay上的消耗,为什么呢?因为GooglePlay 的In-app-Billing V3.0版本,已经没有管理,非管理的商品,或者像苹果IOS那边消耗性和非消耗性的商品了,在后台新建商品的时候,你会发现全部是受管理的商品,所以在我们购买了消耗型的商品后,在代码中执行mHelper.consumeAsync(purchase,mConsumeFinishedListener);就行了,代表这个商品被消耗了,你还可以购买。下面是消耗后的回调方法:

// Called when consumption is complete
	IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
		public void onConsumeFinished(Purchase purchase, IabResult result) {
			Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);

			// if we were disposed of in the meantime, quit.
			if (mHelper == null)
				return;

			// We know this is the "gas" sku because it's the only one we
			// consume,
			// so we don't check which sku was consumed. If you have more than
			// one
			// sku, you probably should check...
			if (result.isSuccess()) {
				// successfully consumed, so we apply the effects of the item in
				// our
				// game world's logic, which in our case means filling the gas
				// tank a bit
				Log.d(TAG, "Consumption successful. Provisioning.");
				//saveData();
			} else {
				complain("Error while consuming: " + result);
			}
			updateUi();
			setWaitScreen(false);
			Log.d(TAG, "End consumption flow.");
		}
	};

8.最后补充一点,官方例子的方法设计非常合理,一些辅助方法的书写和使用,很经典,值得我们的借鉴。


// updates UI to reflect model
	public void updateUi() {
		
	}

	// Enables or disables the "please wait" screen.
	void setWaitScreen(boolean set) {
		findViewById(R.id.screen_main).setVisibility(set ? View.GONE : View.VISIBLE);
		findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE : View.GONE);
	}

	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();
	}

你可能感兴趣的:(Unity)