因为公司业务需求,需要使用google的登录和支付。google支付分为订阅和应用内购买两种,笔者使用的是应用内购买这种方式,这里将整个google支付和支付验证的流程记录下来。
def billing_version = "4.0.0"
implementation "com.android.billingclient:billing-ktx:$billing_version"
流程:
这时google支付的准备工作已完成,下面就可以发起支付了
下面咋们上代码
step1
初始化并连接到google服务
// init方法
public synchronized void init(Activity mActivity){
//创建BillingClient 对面,查询 消费 支付都会使用这个对象
this.mBillingClient = BillingClient.newBuilder(mActivity)
.setListener(new PurchasesUpdatedListener() {//设置支付回调,这里其实是商品状态发生变化时就会回调
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
LogUtils.d("call onPurchasesUpdated");
if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {//支付成功
for (Purchase purchase : purchases) {
if(purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) continue;
OrderManager.getInstance().paySuccess(purchase);
//通知服务器支付成功,服务端验证后,消费商品
}
//TODO客户端同步回调支付成功
} else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {//支付取消
} else {//支付失败
}
}
})
.enablePendingPurchases()
.build();
//链接到google play
this.connectBillPay();
}
private void connectBillPay(){
mBillingClient.startConnection(new BillConnectListener());
}
class BillConnectListener implements BillingClientStateListener {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
//链接到google服务
payEnable = true;
queryPurchases();
}
}
@Override
public void onBillingServiceDisconnected() {
//未链接到google服务
payEnable = false;
connectBillPay();
}
}
setp2
查询已支付的商品,并通知服务器后消费(google的支付里面,没有消费的商品,不能再次购买)
private void queryPurchases(){
PurchasesResponseListener mPurchasesResponseListener = new PurchasesResponseListener() {
@Override
public void onQueryPurchasesResponse(@NonNull BillingResult billingResult, @NonNull List<Purchase> purchasesResult) {
if(billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK || purchasesResult == null) return;
for (Purchase purchase : purchasesResult) {
if(purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) continue;
OrderManager.getInstance().paySuccess(purchase);
//这里处理已经支付过的订单,通知服务器去验证
}
}
};
mBillingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP, mPurchasesResponseListener);
}
setp3
发起支付
/**
*
* @param cpOrder 你自己的订单号或者用户id,用于关联到对应的用户,发放道具时使用
* @param productId google后台配置产品ID
*/
public void pay(final String cpOrder, final String productId) {
if(mBillingClient == null || wrActivity.get() == null || !payEnable){
//TODO客户端同步回调支付失败,原因是为链接到google或者google的支付服务不能使用
return;
}
//查询商品详情
querySkuDetailsAsync(cpOrder, productId);
}
//查询商品详情
void querySkuDetailsAsync(final String cpOrder, final String productId){
List<String> skuList = new ArrayList<>();
skuList.add(productId);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
mBillingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
if (skuDetailsList != null && billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK){
for(SkuDetails skuDetails : skuDetailsList){
if(productId.equals(skuDetails.getSku())){
//发起支付
launchBillingFlow(cpOrder, skuDetails);
}
}
}
}
});
}
//吊起google支付页面
void launchBillingFlow(String cpOrder, SkuDetails skuDetails){
mBillingClient.launchBillingFlow(
wrActivity.get(),
BillingFlowParams
.newBuilder()
.setSkuDetails(skuDetails)
.setObfuscatedAccountId(cpOrder)//这里本来的意思存放用户信息,类似于国内的透传参数,我这里传的我们的订单号。老版本使用DeveloperPayload字段,最新版本中这个字段已不可用了
.build()
);
}
服务器支付验证操作较为复杂,咋们在下面单独提出来做一个小节
setp5
消费商品
public void consumePurchase(final Purchase purchase){
if(mBillingClient == null || purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) return;
LogUtils.d("消耗商品:\n商品id:" + purchase.getSkus() + "\n商品OrderId:" + purchase.getOrderId() + "\ntoken:" + purchase.getPurchaseToken());
LogUtils.d("消耗商品:" + purchase.getAccountIdentifiers().getObfuscatedAccountId());
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingResponseCode.ERROR) {
//消费失败将商品重新放入消费队列
OrderManager.getInstance().consumeFinal(purchase);
return;
}
LogUtils.d("消费成功");
}
};
mBillingClient.consumeAsync(consumeParams, listener);
}
做服务端验证前,需要做一下准备工作
下面咋们上操作截图
setp1
创建api项目
setp2
开启Google Play Android Developer API
Api和服务菜单
库菜单
搜索“Google Play Android Developer API”
image.png
开启“Google Play Android Developer API”
image.png
setp3
开启同意屏幕
填上必填项
这里填上必填项就行了,这个授权同意屏幕,请求code时拉起来给咋们开发人员开的,填啥都无所谓
setp4
创建oauth2客户端id
image.png
创建页面和创建成功后的修改页面可以获取到clientId和clientSecret
image.png
到这里api项目就已经创建好了
setp5
google play后台关联api项目
image.png
setp6
获取code
将上面的{XX}替换成创建api项目时填写的重定向地址,和clientId,然后将连接放到浏览器中打开,就会吊起授权界面,使用你的开发者账号授权登录
请求方式:浏览器中打开
这里可以看到,重定向地址上有两个参数code和scope,我们只需要code就行了,这里的code是urlencode后的,使用时需要decode
setp7
使用code换取refreshToken
地址:https://accounts.google.com/o/oauth2/token
请求方式:post
参数:grant_type=authorization_code
code=获取到的code(需要看看code中是否有%号,如果有需要urldecode)
client_id=创建api项目是的clientId(客户端ID)
client_secret=创建api项目时的clientSecret(客户端密钥)
redirect_uri=创建api项目时的重定向地址
image.png
这里就获取到refreshToken了,重点重点重点,refreshToken保存下来,它只会在第一次请求中返回,后续用在发一样的请求不会返回refreshtoken,如果不慎弄丢了,需要去重新创建一个WebClientId
setp8
使用refreshToken获取accessToken
地址:https://accounts.google.com/o/oauth2/token
请求方式:post
参数:grant_type=refresh_token
refresh_token=刚刚获取到的refreshToken
client_id=创建api项目是的clientId(客户端ID)
client_secret=创建api项目时的clientSecret(客户端密钥)
image.png
setp9
查询订单状态
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}?access_token={access_token}
packageName:app包名,必须是创建登录api项目时,创建android客户端Id使用包名
productId:对应购买商品的商品ID
token:购买成功后Purchase对象的getPurchaseToken()
access_token:上面咋们获取到的accessToken
请求方式:get
返回值:
{
"purchaseTimeMillis": "1623980699933",//购买产品的时间,自纪元(1970 年 1 月 1 日)以来的毫秒数。
"purchaseState": 0,//订单的购买状态。可能的值为:0. 已购买 1. 已取消 2. 待定
"consumptionState": 0,//产品的消费状态。可能的值为: 0. 尚未消耗 1. 已消耗
"developerPayload": "",
"orderId": "GPA.3398-6726-1036-80298",//google订单号
"purchaseType": 0,
"acknowledgementState": 0,
"kind": "androidpublisher#productPurchase",
"obfuscatedExternalAccountId": "SDK2106180944530041",//上面客户支付时的透传字段,google指导是用来存放用户信息的,不能过长,否则客户端不能支付
"obfuscatedExternalProfileId": "",
"regionCode": "HK"
}
到这里整个支付验证流程就已经走完了,这里总结哈笔者这次试用过程中走过的一些坑:
未经允许不得转载:肥猫博客 » Google支付和服务端验证