1. 程式人生 > 其它 >Android_Google Play結算庫(應用內支付)billing 3.0接入實戰

Android_Google Play結算庫(應用內支付)billing 3.0接入實戰

一、接入摘要
時間:2021-04-19
版本:billing 3.0
語言:java
內容:一次性消耗型商品
老版本比較:當前客戶端接入版本對比aidl方式區別巨大,支付透傳欄位也被廢棄,需要開發者做好訂單和google訂單關聯;服務端支付驗證也改了流程,開發者需要做更多的操作。
關於google 支付新版本,瞭解到好多開發者還是用的老版本,為什麼不更新?哈哈,因為太坑了。不過現在強制更新,給了具體的時間限制


二、接入流程
Google Play開發者後臺建立應用(Google Play)
開發者後臺對應專案配置相關資訊
安卓端接入(結算庫接入文件)
後端商品驗證
三、客戶端接入
整合依賴庫
module的 build.gradle 新增下面程式碼

dependencies {
...
implementation "com.android.billingclient:billing:3.0.3"

dependencies {
    ...
    implementation "com.android.billingclient:billing:3.0.3"
    ...
}


支付流程
初始化 BillingClient
與 Google Play 建立連線
自己服務端生成訂單再調起 google 購買操作
購買成功拿到相關資訊去服務端驗證購買合法性
服務端驗證商品後進行發貨並返回客戶端資訊,客戶端消耗商品
初始化 BillingClient
/**

/**
 * 初始化billingClient
 */
private void initBillingClient() {
    mBillingClient = BillingClient.newBuilder(this)
            .setListener(new PurchasesUpdatedListener() {
                @Override
                public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<
Purchase> purchases) { //交易更新將會在這裡回撥 int responseCode = billingResult.getResponseCode(); if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null) { for (Purchase purchase : purchases) { String googlePayOrderId = purchase.getOrderId(); String purchaseToken = purchase.getPurchaseToken(); //伺服器驗證 verifyPayment(orderId, googlePayOrderId, productId, purchaseToken); } } else if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) { //取消支付 } else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) { //已存在這個未完成訂單,查詢訂單驗證然後消耗掉 queryPurchases(); } else { //還有很多其他狀態,判斷進行相應處理 } } }) .enablePendingPurchases() .build(); }

與 Google Play 建立連線
/**
* 與Google Play建立連線
*/

private void startConnection() {
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
//連結成功最好去查詢訂單,做掉單處理
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
queryPurchases();
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
//建議斷開時重連或在使用時判斷連線狀態,沒有連線就手動再調一次 startConnection,確保在執行任何方法時都與 BillingClient 保持連線。
}
});

發起購買
先展示商品再發起購買

/**
* 購買商品
*/

private void purchase() {
//先展示商品
List<String> skuList = new ArrayList<>();
skuList.add(productId);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);//INAPP應用內支付
mBillingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
for (SkuDetails skuDetails : skuDetailsList) {
String sku = skuDetails.getSku();
if (productId.equals(sku)) {
//啟動購買
BillingFlowParams purchaseParams =
BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
mBillingClient.launchBillingFlow(TestBilling.this, purchaseParams);
//購買狀態將在PurchasesUpdatedListener.onPurchasesUpdated返回
}
}
}
}
});
//
}

服務端驗證
這一步驟具體邏輯在服務端,接入是跟服務端人員溝通配合完成

/**
* 驗證支付,需要後端處理
*
* @param orderId 我們自己的訂單號,一般客戶端調起支付前會在自己服務端下單
* @param gpOrderId google商品訂單號
* @param productId 商品id
* @param purchaseToken 商品token
*/
private void verifyPayment(String orderId, String gpOrderId, String productId, String purchaseToken) {
//這一步操作就是寫個網路請求,把支付相關資訊傳到後端進行驗證合法性,後端驗證返回客戶端,驗證成功將消耗商品

}

查詢購買
/**
* 查詢購買交易,以確保所有購買交易都得到成功處理,如購買未發貨,或者未消耗
*/
private void queryPurchases() {
if (mBillingClient != null && mBillingClient.isReady()) {
Purchase.PurchasesResult result = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
List<Purchase> purchasesList = result.getPurchasesList();
if (purchasesList != null) {
for (int i = 0; i < purchasesList.size(); i++) {
if (purchasesList.get(i).isAcknowledged()) {
//已確認/已驗證,消耗即可
consume(purchasesList.get(i).getPurchaseToken());
} else {
//TODO 因google支付新版沒有透傳欄位,所以我們的訂單號需要手動關聯,
// 資料庫查詢gp訂單對應的我方訂單號或者服務端進行訂單關聯
// 關於這一塊後續看是否google有新的改動優化
Purchase purchase = purchasesList.get(i);
String gpOrderId = purchase.getOrderId();
String orderId = "";//我們自己的訂單號,如果是程式剛啟動進來補單的,那麼我們的訂單就拿不到,因為google不攜帶透傳,自己做處理吧
String sku = purchase.getSku();
String purchaseToken = purchase.getPurchaseToken();
verifyPayment(orderId, gpOrderId, sku, purchaseToken);
}
}
}

}
}

消耗商品
/**
* 消耗商品
*
* @param purchaseToken 商品token
*/
private void consume(String purchaseToken) {
if (mBillingClient != null && mBillingClient.isReady()) {
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchaseToken)
.build();
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// Handle the success of the consume operation.
}
}
};
mBillingClient.consumeAsync(consumeParams, listener);
}
}

斷開連線
@Override
public void onDestroy(Activity activity) {
if (mBillingClient!= null && mBillingClient.isReady()) {
mBillingClient.endConnection();
}
}

四、服務端驗證流程
初次接入新版本也是相當麻煩,如不懂流程在官方文件上會摸不著頭腦
總結有幾個步驟

1、建立 API 專案
Google Play 開發者後臺對應專案新建API專案(Google Play Developer API)並啟動API服務和關聯到Google Play 專案

參考地址:https://developers.google.cn/android-publisher/getting_started

2、建立 OAuth 客戶端
API 專案下新建OAuth 客戶端,應用型別選擇網頁應用,其他的看情況填寫,確認建立之後獲取到客戶端ID(client_id)和客戶端祕鑰(client_secret)還有client_secret_xxx.json檔案,內容如下:

{
"web":{
"client_id":"916683014888-479gobska5u85hho2hnb03296lcr9pii.apps.googleusercontent.com",
"project_id":"api-8972885819834575872-739232",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
"client_secret":"5J8jjMVx4o5bvvkqzku1DzMl",
"redirect_uris":[
"xxx"
],
"javascript_origins":[
"xxx"
]
}
}

以上引數都是用在服務端呼叫google api,客戶端ID和客戶端祕鑰並非Android客戶端引數,服務端這裡的所以引數都跟Android端沒有啥關係

3、獲取code
這一步的操作是需要開發者賬號登入網頁,然後在網頁開啟這個連結(https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=xxx&client_id=xxx)進行授權,redirect_uri就是建立 OAuth 客戶端填寫的重定向連結。授權成功後會把code通過redirect_uri返回。如果redirect_uri是隨便填的,訪問出現404頁面或者無法訪問的提示,這時候請將位址列中的連結地址複製出來,把code=4/xxx的值取出來,這裡就獲得了code的值4/wtedvcqw-yui5CNNb-m2iI83KQx1d.yp6198ti5Zc7dJ3UXOl0T3aRLxWrtgbn

4、 通過 code 獲取 refresh_token
重要的事情說三遍 儲存 儲存 儲存 第一次授權的時候才會返回refresh_token(長令牌,一般情況下永久有效) ,請妥善儲存。
不過在除錯階段沒儲存的小夥伴也不用太過擔心,可以在 OAuth 客戶端選擇重置祕鑰或者新建 OAuth 客戶端,記得服務端把引數替換哦。這時候再走一遍流程就可以再次授權獲得refresh_token

POST請求到https://accounts.google.com/o/oauth2/token

請求引數:

grant_type:authorization_code
code:步驟 3 獲取到的code
client_id:客戶端id
client_secret:客戶端祕鑰
redirect_uri:重定向地址

結果返回:

{
"access_token":"xxx",
"expires_in":3599,
"refresh_token":"1//xxx",
"scope":"https://www.googleapis.com/auth/androidpublisher",
"token_type":"Bearer",
"created":16193255555
}

5、通過 refresh_token 獲取 access_token
POST方式呼叫https://accounts.google.com/o/oauth2/token

請求引數:

grant_type:refresh_token
client_id:客戶端id
client_secret:客戶端祕鑰
refresh_token:步驟 4 獲取的refresh_token值

結果返回:

{
"access_token":"xxx",
"token_type":"Bearer",
"expires_in":3600
}

6、查詢訂單資訊
其實就是所謂的驗證
一次性消耗型商品參考官網地址:https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.products/get

GET方法呼叫以下介面: https://www.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}?access_token=access_token

packageName:該應用的包名, 如com.google.demo

productId:商品ID

token:Android端充值獲取的token值

access_token:步驟 5 獲取的access_token

如果訂單有效會返回相關資訊

{
"kind": “androidpublisher#productPurchase”,
"purchaseTimeMillis": “支付時間”,
"purchaseState": 0,// 是否付費: 0 已支付, 1 取消
"consumptionState": 0, // 是否被消費: 0 未消費, 1 已消費,
"developerPayload": "",//透傳欄位,新版這個客戶端沒法傳了
"orderId": “GPA-XXX”,//google的訂單號
"purchaseType": 0, // 支付型別: 0 測試, 1 真實
"acknowledgementState": 0,//商品的確認狀態, 0 尚未被確認, 1 確認
"purchaseToken": "token",//購買令牌,即客戶端支付商品token
"productId": “sss”,//商品id
"quantity": 1,//數量
"obfuscatedExternalAccountId": “”,
"obfuscatedExternalProfileId": “”,
"regionCode": “”
}


可根據返回的資訊作相應的驗證

如果訂單無效會返回400錯誤

如果返回403錯誤
1、檢查api專案啟動狀態
2、檢查api專案關聯狀態
3、OAuth 客戶端引數與服務端使用是否一致
4、谷歌服務的 bug,這時候只要在該應用的商店內,隨意增加一個內購商品,再試一次看看是否可行,新增的內購商品可以刪除。

{
"error": {
"errors": [{
"domain": "androidpublisher",
"reason": "projectNotLinked",
"message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
}],
"code": 403,
"message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
}
}

五、收工
————————————————
版權宣告:本文為CSDN博主「kincai」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/hqiong208/article/details/116162203