Google Play支付的流程,卡单和掉单问题处理....

官方文档

1.前言

发布app到海外市场,内购不免要使用Google支付,然后发现集成Google支付有些流程和注意事项比起国内的支付宝、微信要复杂一些,在此记录一下.

2.准备工作

(1)FQ工具是必须的.
(2)Google支付还要求手机安装Google三件套,如果没有安装,如下:
Google Play支付的流程,卡单和掉单问题处理....
(3)准备开发者账号 Google开发者管理中心
Google Play支付的流程,卡单和掉单问题处理....
(4)创建应用和提交测试版本(一般为Beta测试版),创建完后,要为应用添加商品,在添加测试人员账号,点击生成的链接即可,记录密匙
Google Play支付的流程,卡单和掉单问题处理....
Google Play支付的流程,卡单和掉单问题处理....
(5)关联google API 和 后台配置客户端id
Google Play支付的流程,卡单和掉单问题处理....
这2个有问题会出现

    "reason": "projectNotLinked",
    "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."

(6)初始化的时候需要上面的**
Google Play支付的流程,卡单和掉单问题处理....

3.Google支付大致流程

Google Play支付的流程,卡单和掉单问题处理....

4.代码-支付

受管理商品和不受管理商品:
1.受管理商品即不可重复购买的,例如:解锁关卡,**游戏等(需要消耗,没有消耗就会出现卡单情况)。
2.不受管理商品即可重复购买的,例如:购买金币,购买药水,等消耗品。
3.订阅商品,由于项目中没有涉及到,如有需要的可以翻阅一下Google Billing文档。

(1)(2)初始化

	mHelper = new IabHelper(this, key);
	mHelper.enableDebugLogging(true);

(3)(4)(5)拉起支付弹窗

	//SKU_GAS:商品id,需要与控制台设置的应用内商品id一致
	//RC_REQUEST:返回值
	//order:订单号
	try {
		mHelper.launchPurchaseFlow(this, SKU_GAS, RC_REQUEST,
				mPurchaseFinishedListener, order);
	} catch (IabHelper.IabAsyncInProgressException e) {
		complain("Error launching purchase flow. Another async operation in progress.");
	}

(6)支付完成的回调, 如果是受管理的商品在此回调中直接可以将道具给用户

	IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
	public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
		// if we were disposed of in the meantime, quit.
		if (mHelper == null)
			return;
		if (result.isFailure()) {
			//防止卡单 
			 if (result.getResponse() == -1003||result.getResponse() == 7) {//严正声明失败,
                    if (purchase.getSku().equals(SKU_GAS)) {
                    	try
						{
							mHelper.consumeAsync(purchase, mConsumeFinishedListener);
						} catch (IabAsyncInProgressException e)
						{
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
                    }
                }
		
                return;
		}

		if (purchase.getSku().equals(SKU_GAS)) {
			try {
				//消耗商品
				mHelper.consumeAsync(purchase, mConsumeFinishedListener);	
				//(7)通知本地服务器  游戏购买商品成功
				String info = purchase.getOriginalJson();
				googleVerify(info);					
			} catch (IabHelper.IabAsyncInProgressException e) {
				complain("Error consuming gas. Another async operation in progress.");
				return;
			}
		}
	}
};

// Called when consumption is complete  消耗完成的回调,商品被成功消耗进入此回调,此时将不受管理的商品给用户
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
	public void onConsumeFinished(Purchase purchase, IabResult result) {
		if (mHelper == null)
			return;
		if (result.isSuccess()) {
			// successfully consumed, so we apply the effects of the item in
			// game world's logic, which in our case means filling the gas
			// tank a bit
			// saveData();

		} else {
			complain("Error while consuming: " + result);
		}
		// updateUi();
		// setWaitScreen(false);
	}
};	

5.代码-卡单与掉单

//RestoreOrder卡单和补单的时候用  当我们完成查询我们拥有的项目的订阅,订单 有订单支付完成 但是道具没有给用户 掉单(一般发生在支付后与本地服务器验证时网络波动或者(5)到(6)之间程序闪退,没有验证成功)
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
	public void onQueryInventoryFinished(IabResult result,
			Inventory inventory) {
		// Have we been disposed of in the meantime? If so, quit.
		if (mHelper == null)
			return;

		// Is it a failure?
		if (result.isFailure()) {
			//卡单处理: 失败的订单 需要消耗掉 不然无法进行下次支付 
			String faString =PreSharedManager.getString("failPurchaseData", ZeroPayActivity.this);
			String sign=PreSharedManager.getString("failSignature", context);
			 if (!TextUtils.isEmpty(faString) && !TextUtils.isEmpty(sign)) {
                    try {
                        Purchase p = new Purchase("ITEM_TYPE_INAPP",faString, sign);
                        mHelper.consumeAsync(p, mConsumeFinishedListener);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    //向本地服务器请求订单号
                    googleProcess();
                }
             
			return;
		}

		//查询是否有未未完成订单或者掉单发生 消耗产品 与 通知本地服务器用户已购买商品 出现掉单后,再次点击同个商品id的商品支付按钮,即可补单成功.
		if (inventory.hasPurchase(SKU_GAS)) {
			try {
				//掉单处理
				Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
				String info = gasPurchase.getOriginalJson();
				mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
						mConsumeFinishedListener);
				googleVerify(info);
			} catch (IabHelper.IabAsyncInProgressException e) {
				complain("Error consuming. Another async operation in progress.");
			}
			return;
		}else {
			//普通支付流程 
			googleProcess();
		}
	}
};

//获取失败的订单信息  打开IabHelper.java
 int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException {
    // Query purchases
    logDebug("Querying owned items, item type: " + itemType);
    logDebug("Package name: " + mContext.getPackageName());
    boolean verificationFailed = false;
    String continueToken = null;

    do {
        logDebug("Calling getPurchases with continuation token: " + continueToken);
        Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(),
                itemType, continueToken);

        int response = getResponseCodeFromBundle(ownedItems);
        logDebug("Owned items response: " + String.valueOf(response));
        if (response != BILLING_RESPONSE_RESULT_OK) {
            logDebug("getPurchases() failed: " + getResponseDesc(response));
            return response;
        }
        if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST)
                || !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST)
                || !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) {
            logError("Bundle returned from getPurchases() doesn't contain required fields.");
            return IABHELPER_BAD_RESPONSE;
        }

        ArrayList<String> ownedSkus = ownedItems.getStringArrayList(
                RESPONSE_INAPP_ITEM_LIST);
        ArrayList<String> purchaseDataList = ownedItems.getStringArrayList(
                RESPONSE_INAPP_PURCHASE_DATA_LIST);
        ArrayList<String> signatureList = ownedItems.getStringArrayList(
                RESPONSE_INAPP_SIGNATURE_LIST);

        for (int i = 0; i < purchaseDataList.size(); ++i) {
            String purchaseData = purchaseDataList.get(i);
            String signature = signatureList.get(i);
            String sku = ownedSkus.get(i);
            if (Security.verifyPurchase(mSignatureBase64, purchaseData, signature)) {
                logDebug("Sku is owned: " + sku);
                Purchase purchase = new Purchase(itemType, purchaseData, signature);

                if (TextUtils.isEmpty(purchase.getToken())) {
                    logWarn("BUG: empty/null token!");
                    logDebug("Purchase data: " + purchaseData);
                }

                // Record ownership and token
                inv.addPurchase(purchase);
            }
            else {	
            	//查询失败时 获取当前失败的订单信息
            	failPurchaseData = purchaseData;
            	failSignature = signature;
            	PreSharedManager.setString("failPurchaseData", purchaseData, mContext);
            	PreSharedManager.setString("failSignature", signature, mContext);
                logWarn("Purchase signature verification **FAILED**. Not adding item.");
                logDebug("   Purchase data: " + purchaseData);
                logDebug("   Signature: " + signature);
                verificationFailed = true;
            }
        }