Android集成Google支付,以及遇到的坑、坑
Google商店的应用被下架,应用内购买必须走Google支付,还要扣去百分之三十的手续费,而且有些国家还会收一定的销售税最高达27%,其实Google支付只是自己集成了Paypal支付和银行卡支付,然后Google收手续费。用户使用Google正常支付退款时间是48小时,退款只会在商家账号通知。
我们来谈一谈集成Google支付吧:
Google上给出了Demo:https://github.com/cafebazaar/TrivialDrive
里面有购买订阅和受管理产品的,大家可以下载运行一下试一试。我刚开始是按照Google开发文档去集成的,可能是我水平有限,并没有成功。最后是按Demo去搞吧,方便。Google给出的API:https://developer.android.com/google/play/billing/billing_integrate.html
要在您的应用中实现应用内购买结算,您需要执行以下操作:
- 将应用内购买结算库添加到您的项目中。
- 更新您的
AndroidManifest.xml
文件。 - 创建
ServiceConnection
并将其绑定到IInAppBillingService
。 - 从您的应用发送应用内购买结算请求至
IInAppBillingService
。 - 处理来自 Google Play 的应用内购买结算请求响应。
将 AIDL 文件添加到您的项目中
IInAppBillingService.aidl
是一种定义应用内购买结算版本 3 服务接口的 Android 接口定义语言 (AIDL) 文件。 您可以使用此接口通过调用 IPC 方法调用来发送结算请求。
要获取 AIDL 文件,请执行以下操作:
- 打开 Android SDK 管理器。
- 在 SDK 管理器中,展开
Extras
部分。 - 选择 Google Play Billing Library。
- 点击 Install packages 完成下载。
IInAppBillingService.aidl
文件将安装到
。
要将 AIDL 添加到您的项目,请执行以下操作:
- 首先,下载 Google Play Billing Library 到您的 Android 项目:
- 选择 Tools > Android > SDK Manager。
- 在 Appearance & Behavior > System Settings > Android SDK 下面,选择 SDK Tools 标签以选择并下载 Google Play Billing Library。
- 接下来,复制
IInAppBillingService.aidl
文件到您的项目。- 如果您使用的是 Android Studio,请执行以下操作:
- 导航至 Project 工具窗口中的
src/main
。 - 选择 File > New > Directory,然后在 New Directory 窗口中输入
aidl
,再选择 OK。 - 选择 File > New > Package,然后在 New Package 窗口中输入
com.android.vending.billing
,再选择 OK。 - 使用您的操作系统文件资源管理器,导航至
,复制/extras/google/play_billing/ IInAppBillingService.aidl
文件,然后将其粘贴到项目中的com.android.vending.billing
软件包。
- 导航至 Project 工具窗口中的
- 如果您在非 Android Studio 环境中开发,请执行以下操作:创建目录
/src/com/android/vending/billing
,并将IInAppBillingService.aidl
文件复制到此目录。 将 AIDL 文件添加到您的项目中并使用 Gradle 工具构建项目,从而生成IInAppBillingService.java
文件。
- 如果您使用的是 Android Studio,请执行以下操作:
- 开发您的应用。您会在项目的
/gen
目录中看到名为IInAppBillingService.java
的生成文件。
更新您的应用清单
应用内购买结算依赖于 Google Play 应用,后者将处理应用与 Google Play 服务器之间的所有通信。 要使用 Google Play 应用,您的应用必须请求适当的权限。 您可以通过将 com.android.vending.BILLING
权限添加到 AndroidManifest.xml 文件执行此操作。 如果您的应用未声明应用内购买结算权限,但试图发送结算请求,Google Play 将拒绝请求并使用错误响应。
要为您的应用授予必要的权限,请在 AndroidManifest.xml
文件中添加以下代码行:
这个权限是一定得加的。 按照例子,先把所需Google aidl放好,位置一定不能错。
IInAppBillingService.aidl
还有所需的Util,都拷贝到项目中:
然后Clean一下,不让IInAppBillingService不能用。
下面开始代码集成:
先把所需要的常量定义一下:
//google支付部分: // 声明属性The helper object private IabHelper mHelper; private String TAG = "MyLog1"; /** * Google是否初始化成功: */ boolean iap_is_ok = false; /** * Google支付需要的 * 购买产品的id */ static String purchaseId = ""; // (arbitrary) request code for the purchase flow //购买请求回调requestcode static final int RC_REQUEST = 1001; //base64EncodedPublicKey是在Google开发者后台复制过来的:要集成的应用——>服务和API——>此应用的许可密钥(自己去复制) String base64EncodedPublicKey = "MIIBIjANBgkqh******************************DAQAB";
在onCreate中初始化:
mHelper = new IabHelper(this, base64EncodedPublicKey); // enable debug logging (for a production application, you should set this to false). mHelper.enableDebugLogging(true); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { MyLog.i(TAG, "初始化完成."); if (!result.isSuccess()) { // Oh noes, there was a problem. complain("Problem setting up in-app billing:初始化失败 " + result); return; } iap_is_ok = true; if (mHelper == null) return; MyLog.i(TAG, "Google初始化成功."); if (iap_is_ok) { try { mHelper.queryInventoryAsync(mGotInventoryListener); } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } }else { MyLog.i(TAG, "Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");// toast("Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!"); } } });
初始化的时候会去查询一下Google 后台自己所建立的产品,查询监听: // Listener that's called when we finish querying the items and subscriptions we own 查询所有的产品 IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { MyLog.i(TAG, "查询库存完成."); // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // Is it a failure? if (result.isFailure()) { complain("查询库存失败: " + result); return; } MyLog.i(TAG, "查询库存成功."); /* * Check for items we own. Notice that for each purchase, we check * the developer payload to see if it's correct! See * verifyDeveloperPayload(). */ // Check for gas delivery -- if we own gas, we should fill up the tank immediately //查询你的产品是否存在没有消耗的,要是没有消耗,先去消耗,再购买 Purchase gasPurchase = inventory.getPurchase(purchaseId); if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) { try { mHelper.consumeAsync(inventory.getPurchase(purchaseId), mConsumeFinishedListener); } catch (IabHelper.IabAsyncInProgressException e) { complain("Error consuming gas. Another async operation in progress."); } return; } MyLog.i(TAG, "初始库存查询完成;启用主用户界面."); } };
然后是购买产品的点击事件中添加购买事件: /** * 去购买Google产品 * purchaseId Google产品id * * 点击购买的时候,才去初始化产品,看看是否有这个产品,是否消耗 * */ private void toBuyGooglepay(){ // launch the gas purchase UI flow. // We will be notified of completion via mPurchaseFinishedListener MyLog.i(TAG, "开始购买"); /* 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. */ //这个payload是要给Google发送的备注信息,自定义参数,购买完成之后的订单中也有该字段 String payload = Contants.userID; try { mHelper.launchPurchaseFlow(TypeActivity.this, purchaseId, RC_REQUEST,mPurchaseFinishedListener, payload); } catch (Exception e) { Toast.makeText(TypeActivity.this,"无法完成谷歌支付",Toast.LENGTH_SHORT).show(); } }
下面是几个所需的购买过程中事件监听: 1:购买完成的回调事件监听:
// Callback for when a purchase is finished购买完成的回调 IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { MyLog.i(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); return; } if (!verifyDeveloperPayload(purchase)) { complain("Error purchasing. Authenticity verification failed."); return; } MyLog.i("MyLog1", "购买完成."); //购买完成时候就能获取到订单的详细信息:purchase.getOriginalJson(),要是想要什么就去purchase中get //根据获取到产品的Id去判断是哪一项产品 if (purchase.getSku().equals(purchaseId)) { MyLog.i(TAG, "购买的是"+purchase.getSku()); try { //购买完成之后去消耗产品 mHelper.consumeAsync(purchase, mConsumeFinishedListener); } catch (IabHelper.IabAsyncInProgressException e) { complain("Error consuming gas. Another async operation in progress."); return; } } } };
购买完成事件中有产品消耗事件监听,也就是说,购买完成时候,一定要去消耗一下产品,不然不能进行下次购买。 Google给出的声明:应用内商品一经购买,就会被视为“被拥有”且无法从 Google Play 购买。 您必须对应用内商品发送消耗请求,然后 Google Play 才能允许再次购买。可以消耗托管的应用内商品,但不能消耗订阅。下面是消耗监听:
// Called when consumption is complete 消耗产品的回调 IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() { public void onConsumeFinished(Purchase purchase, IabResult result) { MyLog.i(TAG, "消耗完。购买(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 MyLog.i(TAG, "消费成功。Provisioning."); }else { complain("Error while consuming: " + result); } } };
下面是几个用到的辅助类: /** 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; } void complain(String message) { MyLog.i(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); MyLog.i(TAG, "Showing alert dialog: " + message); bld.create().show(); }
在onActivityResult中添加: @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data); if (mHelper == null) return; // Pass on the activity result to the helper for handling try { 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 { MyLog.i("MyLog1", "onActivityResult handled by IABUtil."); } } catch (Exception e) { e.printStackTrace(); } if (requestCode == 1001) { int responseCode = data.getIntExtra("RESPONSE_CODE", 0);//订单信息 String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); MyLog.i("MyLog1", "responseCode:: " + responseCode ); MyLog.i("MyLog1", "purchaseData:: " + purchaseData ); MyLog.i("MyLog1", "dataSignature:: " + dataSignature ); if (resultCode == RESULT_OK) { try { JSONObject jo = new JSONObject(purchaseData);//订单Id String sku = jo.getString("productId"); System.out.println("You have bought the " + sku + ". Excellent choice,adventurer!"); } catch (JSONException e) { MyLog.i("MyLog1","Failed to parse purchase data."); System.out.println("Failed to parse purchase data."); e.printStackTrace(); } } } }}
完成购买之后一定要解除应用内购买服务,在onDestroy中添加 @Override protected void onDestroy() { super.onDestroy(); if (mHelper != null) { try { mHelper.dispose(); } catch (Exception e) { e.printStackTrace(); } } mHelper = null; }
注:国内部分手机由于不能启动Google play service ,所以在解绑的时候可能会出错,(调用mHelper.dispose();方法时报错),解决办法: 在IabHelp.java中的300行左右把:
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
修改为:
新建一个绑定的判断:
public boolean isBound;isBound = mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
然后在dispose();方法中修改:
public void dispose() throws IabAsyncInProgressException { synchronized (mAsyncInProgressLock) { if (mAsyncInProgress) { throw new IabAsyncInProgressException("Can't dispose because an async operation " + "(" + mAsyncOperation + ") is in progress."); } } logDebug("Disposing."); mSetupDone = false; if (mServiceConn != null) { logDebug("Unbinding from service."); if (mContext != null && isBound){ mContext.unbindService(mServiceConn); isBound = false; } } mDisposed = true; mContext = null; mServiceConn = null; mService = null; mPurchaseListener = null; }
代码集成到此结束,下面是测试:
首先你得有一个能安装Google play的手机,还要有个Gmail邮箱(不能和Google开发者的Gmail邮箱一样),测试支付不产生订单id,要是需要查看订单id就得真实支付(需要支持双币的信用卡或者paypal账号)。测试用的app一定要跟上传到Google的测试版的包名、版本code、name、签名一致,否则无法进行支付测试。
在Google开发后台:设置-->信息-->设置测试许可中添加你的测试Gmail邮箱(重要,不然会真实扣费)。在应用中的APK选项中,上传一版Bata版,发布,在测试人员中设置你的测试Gmail,和测试许可中的一致
另附Google play billing下载地址:https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main
更多相关文章
- 关于Android(安卓)WebView不支持location.href打开的解决方法 小
- NFC钥匙扣对于iPhone和Android设备从中国RFID
- Android支付宝支付
- Android(安卓)Pay,能冲破第三方支付围堵吗?
- Android(安卓)针对个人开发者的bmob支付
- 微信支付宝支付调用SDK
- android 支付宝
- android App集成支付宝
- Android中集成支付宝