一、接入摘要
時間: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