在写《google应用内支付 in-app billing(二)》的时候我并没有写这篇文章的前传《google应用内支付 in-app billing(一)》,留到后面再写吧.......下面进入正题。
一、in-app billing 的完整接入流程如下:
1、添加 in-app billing library;
2、完善AndroidManifest.xml,添加所需权限等;
3、创建ServiceConnection,并绑定到IInAppBillingService;
4、从我们的APP发送支付request请求到IInAppBillingService;
5、从google play 获得response
二、把AIDL文件导入到项目中,为项目和google play通信做准备
获得AIDL文件的方法:
1、打开android SDK manager
扫描二维码关注公众号,回复: 2855259 查看本文章2、选中extras,并选中google play billing library
3、最后点击install package按钮
4、在自己项目的src下建立包名为com.android.vending.billing的包,并把刚才下载好的IInAppBillingService.aidl文件考入改包下(IInAppBillingService.aidl在sdk/extras/google/play_billing下)
5、build一下你的project,你会发现在/gen下有一个名为IInAppBillingService.java的文件,接下来你就可以在项目中调用它
三、完善AndroidManifest.xml文件
in-app billing是依赖于google play的,google play相当于一个我们项目与google play server的通信中介。而要使用google play就必须要声明如下权限:
<uses-permission android.name="com.android.vending.BILLING"/>
四、创建ServiceConnection,并绑定到IInAppBillingService
为了实现自己的app和google play进行通信,必须实现一个ServiceConnection。主要步骤如下:
1、把自己的项目bind到IInAppBillingService;
2、以IPC的方式发送支付request;
3、处理google play返回的response
(一)把自己的项目bind到IInAppBillingService
(1)创建ServiceConnection,并获得IInAppBillingService示例
在Activity中示例化一个ServerConnection,并实现它的两个方法onServiceConnected()和onServiceDisconnectrd(),并在onServiceConnection()方法里获得IInAppBillingService。
private IInAppServiceConnection mService;
private ServiceConnection mServiceConn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service){
//获得IInAppBillingService
mService = IInAppServiceConnection.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name){
mService = null;
}
}
(2)在Activity的onCreate()中bindService。根据官网的解释,在bind时候要在Intent中明确要启动google play Service(即setPackage("com.android.vending")),这样能够避免request被恶意截取。详情如下:
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent serviceIntent = new Intent("com.android.vending.billing.IInAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
}
接下来你就可以使用mService与google play Service进行通信了。
注意:在退出程序的时候一定要记得unbindService,要不然会使你的设备性能降低。
@Override
public void onDestroy(){
super.onDestroy();
if(mService != null){
unbindService(mServiceConn);
}
}
五、发送支付请求
在in-app billing中,我们的app不必直接去管理每一个订单,当某一个物品被用户所购买,google play会自动识别该物品被某一用户做拥有,并阻止他重复购买该物品,除非他已经消费完了。你能做的就是在你的app中控制每一个物品的消费方式,然后通知google play,告诉它该物品是否可以再次购买。你还可以再app里向google play查询该用户都购买了什么产品 。
(一)查询商品详情(注意:此为网络请求,因此不要放在主线程中)
主要做法是创建一个Bundle对象,然后Bundle里携带一个装载有商品ids的ArrayList,最后调用mService.getSkuDetails()来查询商品详情。具体如下:
ArrayList<String> skuList = new ArrayList<String>();
skuList.add("premiumUpgrade");
skuList.add("gas");
Bundle querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
接着再调用:
Bundle skuDetails = mService.getSkuDetails(3, getPackageName(), "inapp", qureySkus);//第一个参数3是指API版本号,"inapp"是指支付方式。
(二)获取商品详情
如果请求成功,则返回的id是BILLING_RESPONSE_RESULT_OK(即0),返回的商品详情是以JSON的形式放在一个ArrayList中,改ArrayList的key是DETAIL_LIST。以下是解析的代码:
int response = skuDetails.getInt("RESPONSE_CODE");
if(response == 0){
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
for(String thisResponse : responseList){
JSONObject object = new JSONObject(thisResponse);
String sku = object.getString("productId");
String price = object.getString("price");
if(sku.equals("premiumUpgrade")){
mPemiumUpgradePrice = price;
}else if(sku.equals("gas")){
mGasPrice = price;
}
}
}
(三)购买商品
(1)发送购买请求
Bundle buyIntentBundle = mService.getBuyIntentBundle(3, getPackageName(), sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
参数解释:3为API版本号;sku为产品id;inapp是支付方式;最后一个参数是developerpayload(我理解的是这个参数也是随机凑合的一个参数,用于验证和对比以确认购买商品的请求是否是自己发送,因为后面购买成功后也会返回一个developerpayload,google官网是这样解释的The developerPayload String is used to specify any additional arguments that you want Google Play to send back along with the purchase information.)
(2)解析请求结果
购买请求发送后google play会返回一个Bundle结果(即上面的buyIntentBundle),如果请求成功则返回BILLING_RESPONSE_RESULT_OK(0),还有一个PendingIntent(key为BUY_INTENT),获取PendingIntent的方法如下:
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
参数解释:1001是一个随机的request code;
接着实现Activity中的onActivityResult()方法来接收google play返回的结果:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == 1001){//上面的随机requestCode在此用到了
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if(resultCode == RESULT_OK){
try{
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
}catch(JSONException e){
e.printStackTrace();
}
}
}
}
返回结果如下所示:
'{ "orderId":"12999763169054705758.1371079406387615", "packageName":"com.example.app", "productId":"exampleSku", "purchaseTime":1345678900000, "purchaseState":0, "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ", "purchaseToken":"opaque-token-up-to-1000-characters" }'注意:1、google play返回的purchaseToken会在其他方法中用到(比如消费产品的时候),所以要保存起来。
2、google上有这么一段安全方面的提示,但我不是很了解,所以摘抄如下:
Security Recommendation: When you send a purchase request, create a String token that uniquely identifies this purchase request and include this token in the
developerPayload
.You can use a randomly generated string as the token. When you receive the purchase response from Google Play, make sure to check the returned data signature, theorderId
, and thedeveloperPayload
String. For added security, you should perform the checking on your own secure server. Make sure to verify that theorderId
is a unique value that you have not previously processed, and thedeveloperPayload
String matches the token that you sent previously with the purchase request.
六、查询已购产品
根据google官网的说法,正常情况下,每一次查询最多只能查询出700个已购产品,如果要查更多则需要另外做一些处理,此处不再给出......下面是查询例子:
Bundle ownedItems = mService.getPurchase(3, getPackageName, "inapp", null);
int response = ownedItems.getInt("RESPONSE_CODE");
if(response == 0){
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
ArrayList<String> continuationToken = ownedItems.getStringArrayList("INAPP_CONTINUATION_TOKEN");
for(int i=0; i<purchaseDataList.size; i++){
String sku = ownedSkus.get(i);
String purchaseData = purchaseDataList.get(i);
String signature = signatureList.get(i);
}
}
七、消费已购产品
int response = mService.consumePurchase(3, getPackageName(), token);
注意:1、该请求属于网络请求,不要放在主线程之中;
2、在你进行消费之前,你必须执行消费请求,确保返回的是有效商品
八、订阅式购买
注意:订阅式购买与其它方式不同的是其购买方式是“subs”而不是“inapp”
(一)购买
Bundle bundle = mService.getBuyIntent(3, getPackageName(), sku, "subs", developerPayload);
PendingIntent pendingIntent = bundle.getParcelable("RESPONSE_BUY_INTENT");
if(bundle.getInt("RESPONSE_CODE") == BILLING_RESPONSE_RESULT_OK){
//该方法会唤醒google play的支付Dialog
startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}
(二)查询已订阅
Bundle activeSubs = mService.getPurchase(3, getPackageName(), "subs", token);
(九)结束语
这是第一篇真正意义上的博文,内容都是翻译至Android开发文档的,为自己的小进步贺喜一下,也希望有不足的地方大家帮忙提出改进!谢谢!!