Android支付接入(七):Google In-app-Billing
前段时间有事请耽搁了,今天跟大家一起看下Google的in-app Billing V3支付。
注意:类型根据VPN而定,我用的是L2TP/IPSec PSK,选择此类型时,编辑只需填写名称,服务器地址和IPSec预共享密钥即可,然后连接的时候填写帐号和密码。当打开Google商店能看到付费软件表名VPN已成功连接,如果显示VPN已连接但还看不到付费软件时,进入应用程序管理器分别清除Google Play服务和Google Play商店数据之后再打开Google商店即可。
3.集成Google Billing。
(2).Consuming In-app Products,消耗产品时的通信过程
(1).测试支付官方文档链接http://developer.android.com/google/play/billing/billing_testing.html
(2).Testing with static responses,静态测试,即当支付状态为一下四种情况时游戏逻辑是否正确。
一:受管理商品和不受管理商品
1.IabHelper.OnIabPurchaseFinishedListener 支付完成的回调,如果是受管理的商品在此回调中直接可以将道具给用户
2.IabHelper.OnConsumeFinishedListener 消耗完成的回调,当不受管理的商品被成功消耗进入此回调,此时将不受管理的商品给用户
3.IabHelper.QueryInventoryFinishedListener 查询完成的回调,Restore Order的时候用,当有订单成功付款但由于种种原因(突然断网、断电等)没收到Google支付成功的回调时,在这里可以查询到此订单,此时需要对订单进行处理(给用户道具等)。
四:测试用的app一定要跟上传到Google的测试版的包名、版本code、name、签名一致,否则无法进行支付测试。
1.当签名不一致或者版本code、版本name不一致时错误界面如下:
2.当包名不一致时错误界面如下:
接下来跟大家一起看一下代码具体实现:
1.下载in-app-billing-v03,下载地址:http://pan.baidu.com/share/link?shareid=1387554851&uk=473193131将下载后的压缩包解压:
将src目录下两个包及包中的java文件引入工程,例如:
2.添加权限:<uses-permission android:name="com.android.vending.BILLING" />
String base64EncodedPublicKey = "";此处填写Google控制台添加新应用程序后的appid
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(false);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
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;
}
iap_is_ok = true;
// Hooray, IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
}
});
调用支付接口:
if (iap_is_ok) {
mHelper.launchPurchaseFlow(MainActivity.this, skus[1], RC_REQUEST, mPurchaseFinishedListener);
}else {
showMessage("提示", "Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");
}
调用查询接口:
mHelper.queryInventoryAsync(mGotInventoryListener);
调用获取道具价格接口:(因Google市场是根据不同国家显示不同货币价格,所以显示到游戏道具列表中的价格不是定值,而是动态获取的)
billingservice = mHelper.getService();
Bundle querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skus);
try {
Bundle skuDetails = billingservice.getSkuDetails(3, MainActivity.this.getPackageName(),"inapp", querySkus);
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
if (null!=responseList) {
for (String thisResponse : responseList) {
try {
SkuDetails d = new SkuDetails(thisResponse);
for (int i = 0; i < sku_list.size(); i++) {
if (sku_list.get(i).equals(d.getSku())) {
price_list.set(i, d.getPrice());
}
}
iapHandler.sendEmptyMessage(0);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
三个回调:
// 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 (result.isFailure()) {
// Oh noes!
complain("Error purchasing: " + result);
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")) {
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}else if (purchase.getSku().equals("double_income")) {
//受管理的商品,开启双倍经验
showMessage("支付成功", "成功购买双倍经验");
}
}
};
// 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);
// 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
if (purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")) {
showMessage("支付成功", "成功购买100猫币");
}
}
else {
complain("Error while consuming: " + result);
}
}
};
// Listener that's called when we finish querying the items we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
if (inventory.hasPurchase("double_income")) {
//查询到有受管理的商品支付成功需要将道具给用户
showMessage("成功Restore双倍金币", "查询到有双倍金币需要恢复");
}else if(inventory.hasPurchase("cions_100")){
//查询到不受管理的商品支付成功需要将道具消耗掉
showMessage("成功Restore100金币","查询到有100金币需要恢复" );
}
}
};
处理返回Activity后的数据:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
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:
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
最后附上MainActivity.java完整文件,源码下载地址:http://pan.baidu.com/share/link?shareid=1579953623&uk=473193131:
package cn.catcap.together;
import java.util.ArrayList;
import org.json.JSONException;
import com.android.vending.billing.IInAppBillingService;
import com.example.android.trivialdrivesample.util.IabHelper;
import com.example.android.trivialdrivesample.util.IabResult;
import com.example.android.trivialdrivesample.util.Inventory;
import com.example.android.trivialdrivesample.util.Purchase;
import com.example.android.trivialdrivesample.util.SkuDetails;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
// The helper object
IabHelper mHelper;
// Debug tag, for logging
static final String TAG = "TrivialDrive";
// Current amount of gas in tank, in units
int mTank;
// (arbitrary) request code for the purchase flow请求码
static final int RC_REQUEST = 10001;
private boolean iap_is_ok = false;
//double_income为受管理商品,coins_100为不受管理商品
private String[] skus = {"android.test.purchased","double_income","coins_100"};
private ArrayList<String> sku_list;
private ArrayList<String> price_list;
private IInAppBillingService billingservice;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String base64EncodedPublicKey = "";//此处填写自己的appid
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(false);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
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;
}
iap_is_ok = true;
// Hooray, IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
}
});
//购买双倍金币(受管理商品)
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
iapHandler.sendEmptyMessage(1);
}
});
//购买100猫币(不受管理商品)
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
iapHandler.sendEmptyMessage(2);
}
});
//Restore Order
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (iap_is_ok) {
mHelper.queryInventoryAsync(mGotInventoryListener);
}else {
showMessage("提示", "Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");
}
}
});
//获取价格
findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
sku_list = new ArrayList<String>();
price_list = new ArrayList<String>();
//添加默认值
sku_list.add("double_income");
price_list.add("HK$40");
sku_list.add("coins_100");
price_list.add("HK$8");
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
getPrice();
}
}).start();
}
});
//测试订单
findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
iapHandler.sendEmptyMessage(3);
}
});
//显示价格
tv = (TextView) findViewById(R.id.text);
}
//获取价格
private void getPrice(){
ArrayList<String> skus = new ArrayList<String>();
skus.add("double_income");
skus.add("coins_100");
billingservice = mHelper.getService();
Bundle querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skus);
try {
Bundle skuDetails = billingservice.getSkuDetails(3, MainActivity.this.getPackageName(),"inapp", querySkus);
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
if (null!=responseList) {
for (String thisResponse : responseList) {
try {
SkuDetails d = new SkuDetails(thisResponse);
for (int i = 0; i < sku_list.size(); i++) {
if (sku_list.get(i).equals(d.getSku())) {
price_list.set(i, d.getPrice());
}
}
iapHandler.sendEmptyMessage(0);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Handler iapHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch(msg.what){
case 0:
tv.setText(price_list.get(0)+"\n"+price_list.get(1));
break;
case 1:
if (iap_is_ok) {
mHelper.launchPurchaseFlow(MainActivity.this, skus[1], RC_REQUEST, mPurchaseFinishedListener);
}else {
showMessage("提示", "Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");
}
break;
case 2:
if (iap_is_ok) {
mHelper.launchPurchaseFlow(MainActivity.this, skus[2], RC_REQUEST, mPurchaseFinishedListener);
}else {
showMessage("提示", "Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");
}
break;
case 3:
if (iap_is_ok) {
mHelper.launchPurchaseFlow(MainActivity.this, skus[0], RC_REQUEST, mPurchaseFinishedListener);
}else {
showMessage("提示", "Google Play初始化失败,当前无法进行支付,请确定您所在地区支持Google Play支付或重启游戏再试!");
}
break;
default:
break;
}
};
};
// 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 (result.isFailure()) {
// Oh noes!
complain("Error purchasing: " + result);
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")) {
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}else if (purchase.getSku().equals("double_income")) {
//受管理的商品,开启双倍经验
showMessage("支付成功", "成功购买双倍经验");
}
}
};
// 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);
// 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
if (purchase.getSku().equals("coins_100")||purchase.getSku().equals("android.test.purchased")) {
showMessage("支付成功", "成功购买100猫币");
}
}
else {
complain("Error while consuming: " + result);
}
}
};
// Listener that's called when we finish querying the items we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
if (inventory.hasPurchase("double_income")) {
//查询到有受管理的商品支付成功需要将道具给用户
showMessage("成功Restore双倍金币", "查询到有双倍金币需要恢复");
}else if(inventory.hasPurchase("cions_100")){
//查询到不受管理的商品支付成功需要将道具消耗掉
showMessage("成功Restore100金币","查询到有100金币需要恢复" );
}
}
};
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
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.");
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
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 void showMessage(String title,String message){
new AlertDialog.Builder(MainActivity.this).setTitle(title).setMessage(message).setPositiveButton("确定", null).show();
}
}
以上就是完整的Google in-app Billing接入过程,接下来会跟大家一起走一遍亚马逊支付,如有疑问请留言。
Android支付接入(七):Google In-app-Billing的更多相关文章
- Android支付接入之Google In-app-Billing
原文链接:http://www.mobile-open.com/2016/966337.html 因为公司需要接入Google的应用内支付(即Google的in-app Billing V3),接入过 ...
- Android支付接入(7):Google In-app-Billing
今天跟大家一起看下Google的in-app Billing V3支付. 如果没有GooglePlay此处附上安装Google Play的一键安装器的链接(需要Root权限):http://ww ...
- Android支付接入(五):机锋网
原地址:http://blog.csdn.net/simdanfeg/article/details/9012083 前边已经陆续跟大家走了一遍运营商和支付宝付费接入,今天跟大家一起看看机锋网的支付接 ...
- Android支付接入(四):联通VAC计费
原地址:http://blog.csdn.net/simdanfeg/article/details/9012031 注意事项: 1.联通支付是不需要自己标识软硬计费点的,当平台申请计费点的时候会提交 ...
- Android支付接入(三):电信爱游戏支付
原地址:http://blog.csdn.net/simdanfeg/article/details/9011977 注意事项: 1.电信要求必须先启动电信的闪屏界面 2.非网络游戏不允许有Inter ...
- Android支付接入(二):移动游戏基地
原地址:http://blog.csdn.net/simdanfeg/article/details/9011863 上篇博文跟大家一起走了一遍支付宝支付,今天我们来看看移动支付.众所周知目前付费通道 ...
- Android支付接入(一):支付宝
原地址:http://blog.csdn.net/simdanfeg/article/details/9011603 转载之前我想深深地感谢屌丝哥 相信相同过App获取利润的都会需要接入计费SDK,下 ...
- Android支付接入(八):Amazon亚马逊支付
下面跟大家一起走一遍Amazon亚马逊的支付,亚马逊目前刚把业务拓展到大陆市场,但这并不代表Amazon支付不成熟,恰恰相反,Amazon的支付流程,支付结果获取及测试另人称赞,支付流程.测试流程简洁 ...
- Android Q 接入 MQTT
Android Q 接入 MQTT 首先在APP 下引入mqtt的库 implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1 ...
随机推荐
- 最大似然预计(Maximum likelihood estimation)
一.定义 最大似然预计是一种依据样本来预计模型參数的方法.其思想是,对于已知的样本,如果它服从某种模型,预计模型中未知的參数,使该模型出现这些样本的概率最大.这样就得到了未知參数的预计值. 二 ...
- C++辛格尔顿
设计模式是编程的焦点.经常在面试时进行审查,Singleton模式是最简单的.最常见的.大部分的主模式.所以大部分的采访是测试考试的Singleton设计模式. 以下我们就来看看单例模式怎样实现(C+ ...
- Eclipse用法和技巧十一:分栏显示
在编码的时候,有时候需要同时看到两个文件的代码.或者在代码走读的时候,能同时看到两个文件的代码能加快我们对代码的理解.来看看如何在eclipse中同时显示两个文件的代码. 步骤一:拖住一 ...
- C#中System.Globalization.DateTimeFormatInfo.InvariantInfo怎么用
原文 C#中System.Globalization.DateTimeFormatInfo.InvariantInfo怎么用 在开发的时候,碰到下面这样一个问题: 在程序中显示当前系统时间,但是有一 ...
- 一道c++小编程题,
题目: 编写一个小程序,从标准输入读入一系列string对象,寻找连续重复出现的单词,程序应该找出满足以下条件的单词的输入位置:该单词的后面紧跟着再次出现自己本身,跟 踪重复次数量多的单词及其重复次数 ...
- checkbox之checked的方法(attr和prop)区别
1. $('#checkbox').click(function(){ if($('#checkbox').is(':checked')) { $(".sendmailhui"). ...
- HBase数据存储格式
好的数据结构,对于检索数据,插入数据的效率就会很高. 常见的数据结构 B+树 根节点和枝节点非常easy,分别记录每一个叶子节点的最小值,并用一个指针指向叶子节点. 叶子节点里每一个键值都指向真正的 ...
- 浅谈C#中的泛型
1.什么是泛型? 泛型是程序设计语言的一种特性.允许程序员在强类型程序设计语言中编写 代码时定义一些可变部分,那些部分在使用前必须作出指明.各种程序设计语言和其编译器.运行环境对泛型的支持均不一样.将 ...
- 9.7寸RK3188瑞芯微四核爱立顺M33平板电脑 - 深圳吉祥星晨科技有限公司 - 华强商情网
9.7寸RK3188瑞芯微四核爱立顺M33平板电脑 - 深圳吉祥星晨科技有限公司 - 华强商情网 欢迎加入 2000人超级QQ群,平板电脑行业交流群:221371451,平板电脑产品及报价群:5765 ...
- SilkTest Q&A 7
Q61.有一个用Dotnet开发的应用,有1000个为测它而录制的case,一直都运行的很正常,直到有一天… 有人改变了该应用命名空间,由于现在有一个新的window或是panel出现,所以测试脚本一 ...