Android支付接入(七):Google In-app-Billing_as 接入谷歌支付-程序员宅基地

技术标签: google内支付  google play sdk  google play  Andriod支付系列  in-app-billing-v03  

今天跟大家一起看下Google的in-app Billing V3支付。
     如果没有GooglePlay此处附上安装Google Play的一键安装器的链接(需要Root权限): http://www.muzhiwan.com/com.muzhiwan.gsfinstaller-86095.html
     之前用过Google BillingV2,感觉不太爽。V2版支付走的是异步通知,不能即时得到支付结果,支付、查询接口太过复杂,还有就是没找到RestoreOrder接口,因此选择使用V3版,网上大部分都是介绍GoogleBilling的付费机制原理,具体针对某个功能点的很模糊,所以如果大家想了解GoogleBilling付费机制的这篇博文大可不看,网上转载多的是,我写这篇博文的目的是想让大家快速的将Google支付集成到自己项目中。
     集成Google in-appBilling的前提是你已经正确连接上VPN(大陆用户),基本上每个公司都会有VPN的,这个找项目组长去要。
    也正式由于天朝的封锁使得在大陆接Google的支付加大了难度。所以要想接in-appBilling首先要有一个稳定的vpn,PS:非大陆的就行,最好是美、日的。
VPN设置: Android支付接入(七):Google <wbr>In-app-Billing Android支付接入(七):Google <wbr>In-app-Billing Android支付接入(七):Google <wbr>In-app-Billing Android支付接入(七):Google <wbr>In-app-Billing Android支付接入(七):Google <wbr>In-app-Billing Android支付接入(七):Google <wbr>In-app-Billing

注意:类型根据VPN而定,我用的是L2TP/IPSecPSK,选择此类型时,编辑只需填写名称,服务器地址和IPSec预共享密钥即可,然后连接的时候填写帐号和密码。当打开Google商店能看到付费软件表名VPN已成功连接,如果显示VPN已连接但还看不到付费软件时,进入应用程序管理器分别清除GooglePlay服务和Google Play商店数据之后再打开Google商店即可。

     注:源码导入工程是不可用的,需将包名、版本号、版本code、签名改为你上传至Google控制台测试应用的包名、版本号、版本code、签名,且将MainActivity.java中的 Stringbase64EncodedPublicKey = "" ; 填写上你应用程序的签名。PS:签名即Eclipse->AndroidTools->Export Signed Application Package...
     网盘分享中的DemoForGoogleBilling.apk文件是可支付的,但支付会将钱打到我们公司账户上,因为我用的是上线app的key。

一:接入流程:
     1.申请Google开发者帐号,开发人员控制台左侧选择“设置”填写测试人员帐号。


     2.添加新应用,此处有个“上传APK”,此处上传的apk上传到Bate版或者ALPHA版,但包名、版本code、版本name、签名需跟最终上线的产品保持一致。此处上传测试版本的目的是当你支付接入完毕后测试时用。

      3.集成Google Billing。
        (1).Purchasing Items,购买商品时的通信过程

Android支付接入(七):Google <wbr>In-app-Billing
          (2).Consuming In-app Products,消耗产品时的通信过程


      4.测试支付。
          (1).测试支付官方文档链接 http://developer.android.com/google/play/billing/billing_testing.html
          (2).Testing with staticresponses,静态测试,即当支付状态为一下四种情况时游戏逻辑是否正确。
官方给出的4种如下:
There are four reserved product IDs for testing static In-appBilling responses:
android.test.purchased
When you make an In-app Billing request with this product ID,Google Play responds as though you successfully purchased an item.The response includes a JSON string, which contains fake purchaseinformation (for example, a fake order ID). In some cases, the JSONstring is signed and the response includes the signature so you cantest your signature verification implementation using theseresponses.
android.test.canceled
When you make an In-app Billing request with this product IDGoogle Play responds as though the purchase was canceled. This canoccur when an error is encountered in the order process, such as aninvalid credit card, or when you cancel a user's order before it ischarged.
android.test.refunded
When you make an In-app Billing request with this product ID,Google Play responds as though the purchase was refunded. Refundscannot be initiated through Google Play's in-app billing service.Refunds must be initiated by you (the merchant). After you processa refund request through your Google Wallet merchant account, arefund message is sent to your application by Google Play. Thisoccurs only when Google Play gets notification from Google Walletthat a refund has been made. For more information about refunds,see Handling IN_APP_NOTIFY messages and In-app BillingPricing.
android.test.item_unavailable
When you make an In-app Billing request with this product ID,Google Play responds as though the item being purchased was notlisted in your application's product list.
          例:当配置为 android.test.purchased时
[java]  view plain copy
  1. mHelper.launchPurchaseFlow(MainActivity.this,“android.test.purchased”,RC_REQUEST,mPurchaseFinishedListener);  

Android支付接入(七):Google <wbr>In-app-Billing Android支付接入(七):Google <wbr>In-app-Billing
          (3).当游戏逻辑测试通过后,进行支付测试。测试时手机设备上绑定的Google帐号必须是在开发者控制台中配置的测试帐号,绑定非大陆的信用卡,支付后会在开发者控制台看到支付的订单,由于是测试订单,可以将测试产生的费用返还给绑定的信用卡。
        测试效果图如下(由于本人没有非大陆的信用卡,所以只能看到这个界面,上线项目“蘑菇帮”测试ok)
Android支付接入(七):Google <wbr>In-app-Billing

支付接入过程中涉及到的接口及名词:

一:受管理商品和不受管理商品
     1.受管理商品即不可重复购买的,例如:解锁关卡,激活游戏等。
     2.不受管理商品即可重复购买的,例如:购买金币,购买药水,等消耗品。
     3.订阅商品,由于项目中没有涉及到,如有需要的可以翻阅一下Google Billing文档。
Google in-app BillingV3将所有商品默认为受管理的,如果有不受管理商品则需要调用 consumeAsync去消耗调,(或者你可以通俗的理解为,当你成功购买一个道具,Google后台会将此道具加上标记,当你调用 consumeAsync将订单消耗掉时,该订单标记被取消 )。
二:三个回调

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

 

  2.IabHelper.OnConsumeFinishedListener 消耗完成的回调,当不受管理的商品被成功消耗进入此回调,此时将不受管理的商品给用户

 

    3.IabHelper.QueryInventoryFinishedListener 查询完成的回调,RestoreOrder的时候用,当有订单成功付款但由于种种原因(突然断网、断电等)没收到Google支付成功的回调时,在这里可以查询到此订单,此时需要对订单进行处理(给用户道具等)。

四:测试用的app一定要跟上传到Google的测试版的包名、版本code、name、签名一致,否则无法进行支付测试。

    1.当签名不一致或者版本code、版本name不一致时错误界面如下:

Android支付接入(七):Google <wbr>In-app-Billing

      2.当包名不一致时错误界面如下:

Android支付接入(七):Google <wbr>In-app-Billing

接下来跟大家一起看一下代码具体实现:
1.下载in-app-billing-v03,下载地址: http://pan.baidu.com/share/link?shareid=1387554851&uk=473193131将下载后的压缩包解压:

将src目录下两个包及包中的java文件引入工程,例如:

2.添加权限:
[html]  view plain copy
  1. <uses-permissionandroid:nameuses-permissionandroid:name="com.android.vending.BILLING"/>  

3.添加支付代码:
    初始化IapHelper:

[java]  view plain copy
  1. String base64EncodedPublicKey ="";此处填写Google控制台添加新应用程序后的appid  
  2.       mHelper =new IabHelper(this, base64EncodedPublicKey);  
  3.        // enable debuglogging (for a production application, you should set this tofalse).  
  4.       mHelper.enableDebugLogging(false);  
  5.     // Start setup. This is asynchronous andthe specified listener  
  6.        // will be called oncesetup completes.  
  7.        Log.d(TAG,"Starting setup.");  
  8.       mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener(){  
  9.           publicvoid onIabSetupFinished(IabResult result){  
  10.               Log.d(TAG,"Setupfinished.");  
  11.              if (!result.isSuccess()) {  
  12.                  // Ohnoes, there was a problem.  
  13.                  complain("Problemsetting up in-app billing: " + result);  
  14.                 return;  
  15.               }  
  16.              iap_is_ok = true;  
  17.               //Hooray, IAB is fully set up. Now, let's get an inventory of stuffwe own.  
  18.              Log.d(TAG, "Setup successful. Queryinginventory.");  
  19.            }  
  20.        });  

  调用支付接口:

[java]  view plain copy
  1. if(iap_is_ok){  
  2. mHelper.launchPurchaseFlow(MainActivity.this,skus[1],RC_REQUEST, mPurchaseFinishedListener);  
  3. }else {  
  4. showMessage("提示""GooglePlay初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");  
  5. }  

  调用查询接口:

 

[java]  view plain copy
  1. mHelper.queryInventoryAsync(mGotInventoryListener);  

  调用获取道具价格接口:(因Google市场是根据不同国家显示不同货币价格,所以显示到游戏道具列表中的价格不是定值,而是动态获取的)

 

[java]  view plain copy
  1. billingservice =mHelper.getService();  
  2. Bundle querySkus = newBundle();  
  3.    querySkus.putStringArrayList("ITEM_ID_LIST", skus);  
  4. try {  
  5. Bundle skuDetails = billingservice.getSkuDetails(3,MainActivity.this.getPackageName(),"inapp", querySkus);  
  6. ArrayList<String> responseList =skuDetails.getStringArrayList("DETAILS_LIST");  
  7. if (null!=responseList) {  
  8. for (String thisResponse :responseList) {  
  9.           try {  
  10. SkuDetails d = newSkuDetails(thisResponse);  
  11.    
  12. for (int i = 0; i <sku_list.size(); i++) {  
  13. if(sku_list.get(i).equals(d.getSku())) {  
  14. price_list.set(i, d.getPrice());  
  15. }  
  16. }  
  17. iapHandler.sendEmptyMessage(0);  
  18.    
  19. }catch (JSONException e) {  
  20. // TODO Auto-generated catchblock  
  21. e.printStackTrace();  
  22. }  
  23.             
  24.        }  
  25. }  
  26. }catch (RemoteException e) {  
  27. // TODO Auto-generated catchblock  
  28. e.printStackTrace();  
  29. }  

  三个回调:

 

[java]  view plain copy
  1. // Callback for when a purchase is finished  
  2. IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener =newIabHelper.OnIabPurchaseFinishedListener() {  
  3.       publicvoidonIabPurchaseFinished(IabResult result, Purchase purchase) {  
  4.            Log.d(TAG,"Purchase finished: " + result+", purchase: " +purchase);  
  5.           if (result.isFailure()) {  
  6.              // Oh noes!  
  7.               complain("Error purchasing: " + result);  
  8.              return;  
  9.            }  
  10.   
  11.            Log.d(TAG,"Purchase successful.");  
  12.           if(purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")){  
  13.           mHelper.consumeAsync(purchase, mConsumeFinishedListener);  
  14. }elseif (purchase.getSku().equals("double_income")) {  
  15. //受管理的商品,开启双倍经验  
  16. showMessage("支付成功","成功购买双倍经验");  
  17. }  
  18.        }  
  19.     };  
  20. // Called when consumption is complete  
  21. IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =newIabHelper.OnConsumeFinishedListener() {  
  22.       publicvoid onConsumeFinished(Purchasepurchase, IabResult result) {  
  23.           Log.d(TAG,"Consumption finished. Purchase: "+purchase + ", result: " +result);  
  24.   
  25.           // We know this is the "gas"sku because it's the only onewe consume,  
  26.           // so we don't check whichsku was consumed. If you havemore than one  
  27.           //sku, you probably shouldcheck...  
  28.           if (result.isSuccess()) {  
  29.               //successfully consumed, so we apply the effects of the item inour  
  30.               //game world's logic, which in our case means filling the gas tank abit  
  31.           if(purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")){  
  32.           showMessage("支付成功","成功购买100猫币");  
  33.     }  
  34.            }  
  35.           else {  
  36.               complain("Error while consuming: " + result);  
  37.            }  
  38.        }  
  39.     };  
  40.       
  41. // Listener that's called when we finish querying the items weown  
  42. IabHelper.QueryInventoryFinishedListener mGotInventoryListener =newIabHelper.QueryInventoryFinishedListener() {  
  43.       publicvoidonQueryInventoryFinished(IabResult result, Inventory inventory){  
  44.           Log.d(TAG,"Query inventory finished.");  
  45.           if (result.isFailure()) {  
  46.               complain("Failed to query inventory: " +result);  
  47.              return;  
  48.            }  
  49.   
  50.           Log.d(TAG,"Query inventory was successful.");  
  51.   
  52.           if(inventory.hasPurchase("double_income")) {  
  53.          //查询到有受管理的商品支付成功需要将道具给用户  
  54. showMessage("成功Restore双倍金币""查询到有双倍金币需要恢复");  
  55. }elseif(inventory.hasPurchase("cions_100")){  
  56. //查询到不受管理的商品支付成功需要将道具消耗掉  
  57. showMessage("成功Restore100金币","查询到有100金币需要恢复");  
  58. }  
  59.        }  
  60.     };  

  处理返回Activity后的数据:

[java]  view plain copy
  1. @Override  
  2. protectedvoid onActivityResult(int requestCode,int resultCode, Intent data) {  
  3. // TODO Auto-generated methodstub  
  4. Log.d(TAG, "onActivityResult("+ requestCode + "," +resultCode +"," + data);  
  5.   
  6.        // Pass on theactivity result to the helper for handling  
  7.       if(!mHelper.handleActivityResult(requestCode, resultCode, data)){  
  8.           // not handled, so handle it ourselves(here's where you'd  
  9.           // perform any handling of activityresults not related to in-app  
  10.           // billing...  
  11.           super.onActivityResult(requestCode,resultCode, data);  
  12.        }  
  13.       else {  
  14.           Log.d(TAG,"onActivityResult handled by IABUtil.");  
  15.        }  
  16. }  

  退出游戏后销毁IabHelper:

 

[java]  view plain copy
  1. @Override  
  2. protectedvoid onDestroy() {  
  3. // TODO Auto-generated methodstub  
  4. super.onDestroy();  
  5. if (mHelper !=null) mHelper.dispose();  
  6.       mHelper =null;  
  7. }  

  最后附上MainActivity.java完整文件,源码下载地址:http://pan.baidu.com/share/link?shareid=1579953623&uk=473193131

[java]  view plain copy
  1. packagecn.catcap.together;  
  2.   
  3. importjava.util.ArrayList;  
  4. importorg.json.JSONException;  
  5. importcom.android.vending.billing.IInAppBillingService;  
  6. importcom.example.android.trivialdrivesample.util.IabHelper;  
  7. importcom.example.android.trivialdrivesample.util.IabResult;  
  8. importcom.example.android.trivialdrivesample.util.Inventory;  
  9. importcom.example.android.trivialdrivesample.util.Purchase;  
  10. importcom.example.android.trivialdrivesample.util.SkuDetails;  
  11. importandroid.os.Bundle;  
  12. importandroid.os.Handler;  
  13. importandroid.os.RemoteException;  
  14. importandroid.app.Activity;  
  15. importandroid.app.AlertDialog;  
  16. importandroid.content.Intent;  
  17. importandroid.util.Log;  
  18. importandroid.view.View;  
  19. importandroid.widget.TextView;  
  20.   
  21. public class MainActivityextends Activity {  
  22.    
  23. // The helper object  
  24.    IabHelper mHelper;  
  25.  // Debugtag, for logging  
  26.    static final String TAG = "TrivialDrive";  
  27.  //Current amount of gas in tank, in units  
  28.    int mTank;  
  29.  //(arbitrary) request code for the purchase flow请求码  
  30.    static final int RC_REQUEST = 10001;  
  31.    private boolean iap_is_ok = false;  
  32.    //double_income为受管理商品,coins_100为不受管理商品  
  33.    private String[] skus ={ "android.test.purchased","double_income","coins_100"};  
  34.      
  35.    privateArrayList<String> sku_list;  
  36.    privateArrayList<String> price_list;  
  37.      
  38.    private IInAppBillingService billingservice;  
  39.    private TextView tv;  
  40.      
  41.   
  42. @Override  
  43. protected voidonCreate(Bundle savedInstanceState) {  
  44. super.onCreate(savedInstanceState);  
  45. setContentView(R.layout.activity_main);  
  46. Stringbase64EncodedPublicKey = "";//此处填写自己的appid  
  47.        mHelper =new IabHelper(this, base64EncodedPublicKey);  
  48.        // enabledebug logging (for a production application, you should set this tofalse).  
  49.       mHelper.enableDebugLogging(false);  
  50.     // Start setup. This isasynchronous and the specified listener  
  51.        // will becalled once setup completes.  
  52.        Log.d(TAG,"Starting setup.");  
  53.       mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {  
  54.           public voidonIabSetupFinished(IabResult result) {  
  55.              Log.d(TAG, "Setup finished.");  
  56.              if (!result.isSuccess()) {  
  57.                  // Ohnoes, there was a problem.  
  58.                 complain("Problem setting up in-app billing: " + result);  
  59.                 return;  
  60.              }  
  61.              iap_is_ok = true;  
  62.              // Hooray, IAB is fully set up. Now, let's getan inventory of stuff we own.  
  63.              Log.d(TAG, "Setup successful. Queryinginventory.");  
  64.           }  
  65.        });  
  66.       //购买双倍金币(受管理商品)  
  67.       findViewById(R.id.button1).setOnClickListener(newView.OnClickListener() {  
  68.    
  69. @Override  
  70. public void onClick(Viewv) {  
  71. // TODO Auto-generatedmethod stub  
  72. iapHandler.sendEmptyMessage(1);  
  73. }  
  74. });  
  75.       //购买100猫币(不受管理商品)  
  76.       findViewById(R.id.button2).setOnClickListener(newView.OnClickListener() {  
  77.    
  78. @Override  
  79. public void onClick(Viewv) {  
  80. // TODO Auto-generatedmethod stub  
  81. iapHandler.sendEmptyMessage(2);  
  82. }  
  83. });  
  84.        //RestoreOrder  
  85.       findViewById(R.id.button3).setOnClickListener(newView.OnClickListener() {  
  86.    
  87. @Override  
  88. public void onClick(Viewv) {  
  89. // TODO Auto-generatedmethod stub  
  90. if (iap_is_ok) {  
  91. mHelper.queryInventoryAsync(mGotInventoryListener);  
  92. }else {  
  93. showMessage("提示""GooglePlay初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");  
  94. }  
  95.    
  96. }  
  97. });  
  98.        //获取价格  
  99.       findViewById(R.id.button4).setOnClickListener(newView.OnClickListener() {  
  100.    
  101. @Override  
  102. public void onClick(Viewv) {  
  103. // TODO Auto-generatedmethod stub  
  104. sku_list = newArrayList<String>();  
  105. price_list = newArrayList<String>();  
  106. //添加默认值  
  107. sku_list.add("double_income");  
  108. price_list.add("HK$40");  
  109. sku_list.add("coins_100");  
  110. price_list.add("HK$8");  
  111. new Thread(new Runnable(){  
  112.    
  113. @Override  
  114. public void run() {  
  115. // TODO Auto-generatedmethod stub  
  116. getPrice();  
  117. }  
  118. }).start();  
  119. }  
  120. });  
  121.        //测试订单  
  122.       findViewById(R.id.button5).setOnClickListener(newView.OnClickListener() {  
  123.    
  124. @Override  
  125. public void onClick(Viewv) {  
  126. // TODO Auto-generatedmethod stub  
  127. iapHandler.sendEmptyMessage(3);  
  128. }  
  129. });  
  130.        //显示价格  
  131.        tv =(TextView) findViewById(R.id.text);  
  132. }  
  133. //获取价格  
  134. private voidgetPrice(){  
  135. ArrayList<String> skus = newArrayList<String>();  
  136. skus.add("double_income");  
  137. skus.add("coins_100");  
  138. billingservice =mHelper.getService();  
  139. Bundle querySkus = newBundle();  
  140.   querySkus.putStringArrayList("ITEM_ID_LIST",skus);  
  141. try {  
  142. Bundle skuDetails =billingservice.getSkuDetails(3,MainActivity.this.getPackageName(),"inapp", querySkus);  
  143. ArrayList<String> responseList =skuDetails.getStringArrayList("DETAILS_LIST");  
  144. if (null!=responseList){  
  145. for (String thisResponse :responseList) {  
  146.          try {  
  147. SkuDetails d = newSkuDetails(thisResponse);  
  148.    
  149. for (int i = 0; i< sku_list.size(); i++) {  
  150. if(sku_list.get(i).equals(d.getSku())) {  
  151. price_list.set(i,d.getPrice());  
  152. }  
  153. }  
  154. iapHandler.sendEmptyMessage(0);  
  155.    
  156. catch (JSONException e){  
  157. // TODO Auto-generatedcatch block  
  158. e.printStackTrace();  
  159. }  
  160.            
  161.       }  
  162. }  
  163. catch (RemoteExceptione) {  
  164. // TODO Auto-generatedcatch block  
  165. e.printStackTrace();  
  166. }  
  167. }  
  168.    
  169. Handler iapHandler = newHandler(){  
  170. public voidhandleMessage(android.os.Message msg) {  
  171. switch(msg.what){  
  172. case 0:  
  173. tv.setText(price_list.get(0)+"\n"+price_list.get(1));  
  174. break;  
  175. case 1:  
  176. if (iap_is_ok) {  
  177. mHelper.launchPurchaseFlow(MainActivity.this, skus[1], RC_REQUEST,        mPurchaseFinishedListener);  
  178. }else {  
  179. showMessage("提示""GooglePlay初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");  
  180. }  
  181. break;  
  182. case 2:  
  183. if (iap_is_ok) {  
  184. mHelper.launchPurchaseFlow(MainActivity.this, skus[2], RC_REQUEST,mPurchaseFinishedListener);  
  185. }else {  
  186. showMessage("提示""GooglePlay初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");  
  187. }  
  188. break;  
  189. case 3:  
  190. if (iap_is_ok) {  
  191. mHelper.launchPurchaseFlow(MainActivity.this, skus[0], RC_REQUEST,mPurchaseFinishedListener);  
  192. }else {  
  193. showMessage("提示""GooglePlay初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");  
  194. }  
  195.    
  196. break;  
  197. default:  
  198. break;  
  199. }  
  200. };  
  201. };  
  202.    
  203. // Callback for when apurchase is finished  
  204.    IabHelper.OnIabPurchaseFinishedListenermPurchaseFinishedListener = newIabHelper.OnIabPurchaseFinishedListener() {  
  205.        publicvoid onIabPurchaseFinished(IabResult result, Purchase purchase){  
  206.           Log.d(TAG, "Purchasefinished: " + result + ", purchase: " + purchase);  
  207.           if (result.isFailure()) {  
  208.              // Oh noes!  
  209.              complain("Error purchasing: " + result);  
  210.              return;  
  211.           }  
  212.   
  213.           Log.d(TAG, "Purchasesuccessful.");  
  214.           if(purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")){  
  215.          mHelper.consumeAsync(purchase, mConsumeFinishedListener);  
  216. }else if(purchase.getSku().equals("double_income")) {  
  217. //受管理的商品,开启双倍经验  
  218. showMessage("支付成功","成功购买双倍经验");  
  219. }  
  220.        }  
  221.    };  
  222.    // Called when consumption is complete  
  223.    IabHelper.OnConsumeFinishedListenermConsumeFinishedListener = newIabHelper.OnConsumeFinishedListener() {  
  224.        publicvoid onConsumeFinished(Purchase purchase, IabResult result) {  
  225.           Log.d(TAG, "Consumptionfinished. Purchase: " + purchase + ", result: " + result);  
  226.   
  227.           // We know this is the "gas"sku because it's the only one we consume,  
  228.           // so we don't check whichsku was consumed. If you have more than one  
  229.           // sku, you probably shouldcheck...  
  230.           if (result.isSuccess()) {  
  231.              // successfully consumed, so we apply theeffects of the item in our  
  232.              // game world's logic, which in our case meansfilling the gas tank a bit  
  233.           if(purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")){  
  234.           showMessage("支付成功","成功购买100猫币");  
  235.    }  
  236.           }  
  237.           else {  
  238.              complain("Error while consuming: " +result);  
  239.           }  
  240.        }  
  241.    };  
  242.      
  243.    // Listener that's called when we finishquerying the items we own  
  244.    IabHelper.QueryInventoryFinishedListenermGotInventoryListener = newIabHelper.QueryInventoryFinishedListener() {  
  245.        publicvoid onQueryInventoryFinished(IabResult result, Inventoryinventory) {  
  246.           Log.d(TAG, "Query inventoryfinished.");  
  247.           if (result.isFailure()) {  
  248.              complain("Failed to query inventory: " +result);  
  249.              return;  
  250.           }  
  251.   
  252.           Log.d(TAG, "Query inventorywas successful.");  
  253.   
  254.           if(inventory.hasPurchase("double_income")) {  
  255.           //查询到有受管理的商品支付成功需要将道具给用户  
  256. showMessage("成功Restore双倍金币""查询到有双倍金币需要恢复");  
  257. }elseif(inventory.hasPurchase("cions_100")){  
  258. //查询到不受管理的商品支付成功需要将道具消耗掉  
  259. showMessage("成功Restore100金币","查询到有100金币需要恢复" );  
  260. }  
  261.        }  
  262.    };  
  263.    @Override  
  264. protected voidonActivityResult(int requestCode, int resultCode, Intent data){  
  265. // TODO Auto-generatedmethod stub  
  266. Log.d(TAG,"onActivityResult(" + requestCode + "," + resultCode + "," +data);  
  267.   
  268.        // Pass onthe activity result to the helper for handling  
  269.        if(!mHelper.handleActivityResult(requestCode, resultCode, data)){  
  270.           // not handled, so handle itourselves (here's where you'd  
  271.           // perform any handling ofactivity results not related to in-app  
  272.           // billing...  
  273.          super.onActivityResult(requestCode, resultCode, data);  
  274.        }  
  275.        else {  
  276.           Log.d(TAG, "onActivityResulthandled by IABUtil.");  
  277.        }  
  278. }  
  279.    @Override  
  280. protected void onDestroy(){  
  281. // TODO Auto-generatedmethod stub  
  282. super.onDestroy();  
  283. if (mHelper != null)mHelper.dispose();  
  284.        mHelper =null;  
  285. }  
  286. void complain(Stringmessage) {  
  287.        Log.e(TAG,"**** TrivialDrive Error: " + message);  
  288.       alert("Error: " + message);  
  289.    }  
  290. void alert(String message){  
  291.       AlertDialog.Builder bld = new AlertDialog.Builder(this);  
  292.       bld.setMessage(message);  
  293.       bld.setNeutralButton("OK"null);  
  294.        Log.d(TAG,"Showing alert dialog: " + message);  
  295.       bld.create().show();  
  296.    }  
  297. private voidshowMessage(String title,String message){  
  298. newAlertDialog.Builder(MainActivity.this).setTitle(title).setMessage(message).setPositiveButton("确定",null).show();  
  299. }  
  300. }  

以上就是完整的Google in-appBilling接入过程,接下来会跟大家一起走一遍亚马逊支付,如有疑问请留言。


文章转载自Android支付接入(七):Google In-app-Billing,感谢Michael-Liu提供好文章

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Yxingyeyouyu/article/details/43200279

智能推荐

《第一行代码》(第二版)广播的问题及其解决_代码里的广播错误-程序员宅基地

文章浏览阅读2.6k次,点赞5次,收藏13次。1)5.2.1弹出两次已连接或者未连接这是因为你同时打开了流量和WiFi,他就会发出两次广播。2)5.3.1中发送自定义广播问题标准广播未能弹出消息:Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");sendBroadcast(intent);上述已经失效了。修改:Intent intent=new Intent("com.example.broadcasttest...._代码里的广播错误

K8s 学习者绝对不能错过的最全知识图谱(内含 58个知识点链接)-程序员宅基地

文章浏览阅读249次。作者 |平名 阿里服务端开发技术专家导读:Kubernetes 作为云原生时代的“操作系统”,熟悉和使用它是每名用户的必备技能。本篇文章概述了容器服务 Kubernet..._k8知识库

TencentOS3.1安装PHP+Nginx+redis测试系统_tencentos-3.1-程序员宅基地

文章浏览阅读923次。分别是etc/pear.conf,etc/php-fpm.conf, etc/php-fpm.d/www.conf,lib/php.ini。php8安装基本一致,因为一个服务期内有2个版本,所以注意修改不同的安装目录和端口号。可以直接使用sbin下的nginx命令启动服务。完成编译安装需要gcc支持,如果没有,使用如下命令安装。安装过程基本一致,下面是安装7.1.33的步骤。执行如下命令,检查已经安装的包和可安装的包。执行如下命令,检查已经安装的包和可安装的包。执行如下命令,检查已经安装的包和可安装的包。_tencentos-3.1

urllib.request.urlopen()基本使用_urllib.request.urlopen(url)-程序员宅基地

文章浏览阅读3.1w次,点赞21次,收藏75次。import urllib.requesturl = 'https://www.python.org'# 方式一response = urllib.request.urlopen(url)print(type(response)) # <class 'http.client.HTTPResponse'># 方式二request = urllib.request.Req..._urllib.request.urlopen(url)

如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?-程序员宅基地

文章浏览阅读1.5k次,点赞12次,收藏15次。如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?

RS485总线常识_rs485 差分走綫間距-程序员宅基地

文章浏览阅读1.2k次。RS485总线常识 2010-10-12 15:56:36| 分类: 知识储备 | 标签:rs485 总线 传输 差分 |字号大中小 订阅RS485总线RS485采用平衡发送和差分接收方式实现通信:发送端将串行口的TTL电平信号转换成差分信号A,B两路输出,经过线缆传输之后在接收端将差分信号还原成TTL电平信号。由于传输线通常使用双绞线,又是差分传输,所_rs485 差分走綫間距

随便推点

移植、制作uboot、Linux(一)_uboot制作-程序员宅基地

文章浏览阅读621次。u-boot、linux烧录_uboot制作

windows下安装git和gitbash安装教程_64-bit git for windows setup.-程序员宅基地

文章浏览阅读1.2w次,点赞10次,收藏44次。windos上git安装,git bash安装_64-bit git for windows setup.

环形链表(算法java)_java 实现环形链表-程序员宅基地

文章浏览阅读196次。环形链表(算法java)的两种解决方法_java 实现环形链表

docker部署Airflow(修改URL-path、更换postgres -->myslq数据库、LDAP登录)_airflow docker-程序员宅基地

文章浏览阅读5.7k次。Airflow什么是 Airflow?Airflow 的架构Airflow 解决哪些问题一、docker-compose 安装airflow(postgres)1、创建启动文件airflow-docker-compose.yml.1.1、添加挂载卷,需要修改airflow-docker-compose.yml的位置2、创建本地配置文件airflow.cfg2.1、如果想修改WEB URL地址,需要修改airflow.cfg中以下两个地方3、之后up -d直接启动即可web访问地址:二、存储数据库更换post_airflow docker

计算机毕业设计springboot高校教务管理系统532k79【附源码+数据库+部署+LW】-程序员宅基地

文章浏览阅读28次。选题背景:随着社会的发展和教育的普及,高校教务管理系统在现代高等教育中扮演着至关重要的角色。传统的手工管理方式已经无法满足高校日益增长的规模和复杂的管理需求。因此,开发一套高效、智能的教务管理系统成为了当今高校管理的迫切需求。选题意义:高校教务管理系统的开发具有重要的意义和价值。首先,它可以提高高校教务管理的效率和准确性。通过自动化处理学生选课、排课、考试安排等繁琐的事务,大大减轻了教务人员的工作负担,提高了工作效率。同时,系统可以实时更新学生信息和课程信息,减少了数据错误和冗余,保证了管理的准确性

javaint接收float_Java Integer转换double,float,int,long,string-程序员宅基地

文章浏览阅读132次。首页>基础教程>常用类>常用 Integer类Java Integer转换double,float,int,long,stringjava中Integer类可以很方便的转换成double,float,int,long,string等类型,都有固定的方法进行转换。方法double doubleValue() //以 double 类型返回该 Integer 的值。flo..._java integet接收float类型的参数

推荐文章

热门文章

相关标签