google pay支付以及支付掉單問題

一、前言:

在這我先提前說這么幾個(gè)名詞:

1.消耗
消耗是什么意思:消耗就相當(dāng)于是訂單確認(rèn),如果沒有確認(rèn)google會在3天后自動退款,同時(shí)這一筆物品就重新買不了

2.重試
重試是什么意思:就是重新去做處理,去查詢谷歌這邊合是,如果谷歌這邊真的扣款成功,在去服務(wù)器這邊查看是否成功)

3.依賴包版本選擇
implementation 'com.android.billingclient:billing:3.0.0'
implementation 'com.android.billingclient:billing:4.0.0'
這2個(gè)包都是谷歌支付有關(guān)的锭环,3.0 跟4.0 最大區(qū)別是3.0不能重復(fù)購買聪全,只能一個(gè)個(gè)的購買,4.0版本可以購買多件(根據(jù)產(chǎn)品的需求 不需要一次性購買多件辅辩,我這次選擇3.0)

二难礼、google play 后臺配置:

1、接入準(zhǔn)備

  • 1.申請一個(gè)google play開發(fā)者賬號汽久,這里我是有g(shù)oogle play開發(fā)賬號的鹤竭,畢竟我們的APP是發(fā)谷歌市場的
  • 2.提前準(zhǔn)備好一個(gè)apk(不需要集成支付sdk,占位用)景醇,在google play控制臺上傳你的apk臀稚,這里你可以發(fā)封閉測試?yán)锩嫒ィ旅嫖視蠄D三痰,不懂的看圖
  • 3.發(fā)布一個(gè)alpha或者beta的版本吧寺,發(fā)布之前需要點(diǎn)亮以下選項(xiàng)(提交商品詳情內(nèi)容)(確定內(nèi)容分級)(選擇發(fā)布范圍)等,之后才能正常發(fā)布
  • 4.添加測試人員散劫,等應(yīng)用審核通過之后稚机,會得到一個(gè)地址,把地址發(fā)給對方获搏,讓對方點(diǎn)擊同意加入測試即可
  • 5.需要創(chuàng)建應(yīng)用內(nèi)商品(商品id赖条,商品描述,定價(jià))常熙,按提示填就可以了
  • 6.在賬戶詳細(xì)信息里面纬乍,添加許可測試的郵箱賬號,許可測試響應(yīng)改為 “RESPOND_NORMALLY/LICENSED”裸卫,點(diǎn)擊保存,需要一兩分鐘生效,記得弄這一步仿贬,這個(gè)很坑,你不弄墓贿,你測試人員就一直不會出現(xiàn)測試卡測試的模式
  • 7.檢查你的包名和簽名文件是否和Google Console 上面上傳的apk包是否一致
  • 8.檢查版本號是否和Google console發(fā)布的apk版本是否一致
  • 9.檢查你是否可以購買茧泪,是否綁定了銀行卡蜓氨,手機(jī)支不支持Google支付,手機(jī)是否有Google服務(wù)
  • 10.由于我是臺灣上線APP队伟,想測臺幣支付穴吹,我還得準(zhǔn)備一個(gè)vpn,能選擇線路臺灣的

2缰泡、設(shè)定定價(jià)刀荒,就是商品的定價(jià):

圖片.png

按圖所示建立價(jià)格, 我這里有4個(gè)價(jià)格棘钞。具體建立很簡單

建立產(chǎn)品:一個(gè)產(chǎn)品對應(yīng)一個(gè)定價(jià),比如我這里580臺幣對應(yīng)406點(diǎn)干毅。創(chuàng)建完后宜猜,如果沒問題,一定要啟用硝逢,不然app那邊取不到數(shù)據(jù)姨拥,另外產(chǎn)品ID就是唯一,后面用在代碼里取數(shù)據(jù)用的渠鸽。關(guān)于產(chǎn)品id的設(shè)置叫乌,谷歌API中有這么一說,建議是按他要求的來徽缚,比較好

圖片.png
圖片.png
圖片.png
圖片.png
圖片.png

3憨奸、在賬戶詳細(xì)信息里面,添加許可測試的郵箱賬號凿试,許可測試響應(yīng)改為 “RESPOND_NORMALLY/LICENSED”排宰,點(diǎn)擊保存,需要一兩分鐘生效,記得弄這一步,這個(gè)很坑那婉,你不弄板甘,你測試人員就一直不會出現(xiàn)測試卡測試的模式

圖片.png

4、關(guān)于發(fā)一個(gè)占位包详炬,跟設(shè)置測試人員

圖片.png
圖片.png

成功發(fā)布后盐类,應(yīng)用市場會出現(xiàn)

圖片.png

三、整體流程

圖片.png

看完流程圖后呛谜,我們可以簡單的總結(jié)下步驟在跳,這里業(yè)務(wù)部分我就不細(xì)說了,這東西你得根據(jù)自己業(yè)務(wù)來調(diào)整

1.進(jìn)入商品選擇列表界面呻率,選擇需要購買的物品
2.根據(jù)選擇的物品id硬毕,創(chuàng)建訂單
3.初始化google支付,如果google已經(jīng)連接礼仗,查詢這個(gè)商品ID得到商品詳情吐咳;如果沒連接googlepay逻悠,調(diào)用連接
4.購買操作
5.onPurchasesUpdated通過購買回調(diào),判斷是否購買成功
6韭脊。如果購買成功童谒,拿到Google支付返回的相關(guān)信息,在服務(wù)器進(jìn)行驗(yàn)證操作沪羔。
7.如果服務(wù)器拿到你上傳的相關(guān)信息和Google支付進(jìn)行交互驗(yàn)證饥伊,驗(yàn)證谷歌扣款成功,服務(wù)器收款成功蔫饰,說明支付成功琅豆,成功后,要做一次消耗操作篓吁,(消耗是什么意思:消耗就相當(dāng)于是訂單確認(rèn)茫因,如果沒有確認(rèn)google會在3天后自動退款,同時(shí)這一筆物品就重新買不了
8.如果服務(wù)器拿到你上傳的相關(guān)信息和Google支付進(jìn)行交互驗(yàn)證杖剪,驗(yàn)證谷歌扣款成功冻押,服務(wù)器未收款到賬,說明有問題盛嘿,需要做重試操作洛巢,(重試是什么意思:就是重新去做處理,去查詢谷歌這邊次兆,如果谷歌這邊真的扣款成功稿茉,在去服務(wù)器這邊查看是否成功)
注:這里第8步,我在這2個(gè)環(huán)境下类垦,都做了對應(yīng)的操作狈邑,增加了程序的友好體驗(yàn), 第1:如果用戶支付完成蚤认,就沒管了米苹,每次打開APP首頁,我都會去幫他們做重試操作砰琢。這種用戶是無感的蘸嘶,體驗(yàn)比較友好 第2: 如果用戶支付完成,退出當(dāng)前頁面陪汽,然后再進(jìn)來商品選擇頁面训唱,點(diǎn)擊同樣的商品,這個(gè)時(shí)候由于還沒成功挚冤,也沒做消耗處理况增,是會有提示框彈窗,已擁有該商品训挡,這個(gè)時(shí)候澳骤,我是會做重試的歧强,如果成功,他下次在點(diǎn)擊为肮,就可以重新購買了

四摊册、代碼接入

目前已經(jīng)升級到V3、V4版本颊艳,[AIDL](https://so.csdn.net/so/search?q=AIDL&spm=1001.2101.3001.7020)的方法已經(jīng)過時(shí)了茅特,并且未來的版本會將之移除,推薦使用使用 Google Play 結(jié)算庫

1棋枕、添加依賴 跟 權(quán)限

implementation 'com.android.billingclient:billing:4.0.0'
<!--谷歌商店應(yīng)用內(nèi)購買結(jié)算需要的權(quán)限-->
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR" />

2白修、創(chuàng)建連接

//連接到GooglePay
    private void connectGooglePay(String skuId) {
        //請求連接到GooglePay
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                int code = billingResult.getResponseCode();
                if (code != BillingClient.BillingResponseCode.OK) {
                    String msg = billingResult.getDebugMessage();
                    Log.e(TAG, "連接到GooglePay失敗    code = " + code + "    msg = " + msg);
                    onFail();
                    return;
                }
                Log.e(TAG, "連接到GooglePay成功");
                checkSku(skuId);
            }
 
            //連接失敗
            @Override
            public void onBillingServiceDisconnected() {
                Log.e(TAG, "連接到GooglePay失敗,請重試");
            }
        });
    }

3重斑、查詢

//查詢商品
   private void checkSku(String id) {
       List<String> skuList = new ArrayList<>();
       skuList.add(id);
       SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder()
               .setSkusList(skuList)
               .setType(BillingClient.SkuType.INAPP);
       billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
           @Override
           public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
               int code = billingResult.getResponseCode();
               if (code != BillingClient.BillingResponseCode.OK || list == null || list.isEmpty()) {
                   String msg = billingResult.getDebugMessage();
                   Log.e(TAG, "查詢商品失敗    code = " + code + "    msg = " + msg);
                   onFail();
                   return;
               }
               Log.e(TAG, "查詢商品成功");
               buyIt(list.get(0));
           }
       });
   }

4熬荆、購買

//購買
  private void buyIt(SkuDetails skuDetails) {
      BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
              .setSkuDetails(skuDetails)
              .build();
      BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
      int code = billingResult.getResponseCode();
      if (code != BillingClient.BillingResponseCode.OK) {
          String msg = billingResult.getDebugMessage();
          Log.e(TAG, "購買商品失敗    code = " + code + "    msg = " + msg);
          onFail();
          return;
      }
      Log.e(TAG, "購買商品" + skuDetails.toString());
  }

5、購買后的回調(diào)

@Override
  public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
      int code = billingResult.getResponseCode();
      String msg = billingResult.getDebugMessage();
      Log.e(TAG, "onPurchasesUpdated:code = " + code + "    msg = " + msg);
      if (list != null) {
          for (Purchase purchase : list) {
              Log.e(TAG, "onPurchasesUpdated:" + purchase.toString());
          }
      }
      if (code == BillingClient.BillingResponseCode.OK && list != null) {
          Log.e(TAG, "支付成功");
          onSuccess(list);
      } else if (code == BillingClient.BillingResponseCode.USER_CANCELED) {
          Log.e(TAG, "支付取消");
          onFail();
      } else {
          Log.e(TAG, "支付失敵窈:code = " + code + "    msg = " + msg);
          onFail();
      }
  }

6、消耗

/**
  * 消耗商品:BillingClient.BillingResponseCode.OK
  *
  *  int SERVICE_TIMEOUT = -3; //服務(wù)超時(shí)
  *  int FEATURE_NOT_SUPPORTED = -2; //不支持功能
  *  int SERVICE_DISCONNECTED = -1; //服務(wù)單元已斷開
  *  int OK = 0; //成功
  *  int USER_CANCELED = 1; //用戶按上一步或取消對話框
  *  int SERVICE_UNAVAILABLE = 2; //網(wǎng)絡(luò)連接斷開
  *  int BILLING_UNAVAILABLE = 3; //所請求的類型不支持 Google Play 結(jié)算服務(wù) AIDL 版本
  *  int ITEM_UNAVAILABLE = 4; //請求的商品已不再出售累盗。
  *  int DEVELOPER_ERROR = 5; //提供給 API 的參數(shù)無效寒矿。此錯誤也可能說明應(yīng)用未針對結(jié)算服務(wù)正確簽名或設(shè)置,或者在其清單中缺少必要的權(quán)限若债。
  *  int ERROR = 6; //API 操作期間出現(xiàn)嚴(yán)重錯誤
  *  int ITEM_ALREADY_OWNED = 7; //未能購買符相,因?yàn)橐呀?jīng)擁有此商品
  *  int ITEM_NOT_OWNED = 8; //未能消費(fèi),因?yàn)樯形磽碛写松唐?  */
 private void consume(List<Purchase> list) {
     if (list == null || list.isEmpty() || billingClient == null) {
         return;
     }
     for (Purchase purchase : list) {
         billingClient.consumeAsync(ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build(), new ConsumeResponseListener() {
             @Override
             public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
                 Log.e(TAG, "onConsumeResponse    code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage() + " , purchaseToken = " + purchaseToken);
   if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
 //消耗成功蠢琳,執(zhí)行下一步
}
             }
         });
     }
 }

7啊终、查詢失敗訂單(支付成功未消耗)

/**
  * 查詢失敗的訂單
  * skuType :,BillingClient.SkuType.INAPP;  BillingClient.SkuType.SUBS
  * 0:PurchaseState.UNSPECIFIED_STATE:未知狀態(tài)
  * 1:PurchaseState.PURCHASED:付款完成
  * 2:PurchaseState.PENDING:購買正在等待付款完成。
  */
 public void queryFailOrder(Activity activity, String skuType) {
     MyToash.Log("pay", "-----查詢需要補(bǔ)貨的商品:");
     if (mBillingClient != null) {
         Purchase.PurchasesResult result = mBillingClient.queryPurchases(skuType);
         if (BillingClient.BillingResponseCode.OK == result.getResponseCode()) {
             for (Purchase purchase : result.getPurchasesList()) {
                 //PURCHASED --已購買
                 if (Purchase.PurchaseState.PURCHASED == purchase.getPurchaseState()) {
                     //MyToash.Log("pay", "----------需要補(bǔ)貨的商品:");
                     //調(diào)用google去消費(fèi)
                     getConsumeGoods(activity, purchase);
                 }
             }
         }
     }
 }

五傲须、支付遇到的掉單問題處理:

1蓝牲、分析失敗的原因:

1、付款成功泰讽,消費(fèi)失敗例衍,查詢google未消費(fèi)訂單即可;
2已卸、付款成功佛玄,消費(fèi)失敗(服務(wù)單元已斷開-1累澡,但是消費(fèi)成功了梦抢,實(shí)際遇到的問題);
3愧哟、消費(fèi)成功了奥吩,調(diào)用本地發(fā)放書幣失敽咔;

2圈驼、我的處理掉單的流程圖

正常支付流程圖

正常支付流程圖.png

重試流程圖

重試流程圖.png

六人芽、本人的邏輯處理:

1、GooglePayCommonUtils

/**
 * google pay 公共方法調(diào)用
 */
public class GooglePayCommonUtils {
    static  Activity activity = null;
    //google play的對象
    public static GoogleBillingUtil googleBillingUtil = null;
    // 購買回調(diào)接口
    public static MyOnPurchaseFinishedListener mOnPurchaseFinishedListener = new MyOnPurchaseFinishedListener();
    // 查詢回調(diào)接口 - 返回?zé)o處理
    public static MyOnQueryFinishedListener mOnQueryFinishedListener = new MyOnQueryFinishedListener();
    // 啟動結(jié)果回調(diào)接口-返回?zé)o處理
    public static MyOnStartSetupFinishedListener mOnStartSetupFinishedListener = new  MyOnStartSetupFinishedListener();
    /**
     * 初始化google play
     */
    public static void initPlay(Activity mActivity) {
        activity = mActivity;
        googleBillingUtil = GoogleBillingUtil.getInstance()
                // 注意绩脆,監(jiān)聽器設(shè)置是可選的萤厅,視個(gè)人需求設(shè)置。
                .setOnPurchaseFinishedListener(mOnPurchaseFinishedListener)//購買回調(diào)接口
                .setOnQueryFinishedListener(mOnQueryFinishedListener) //查詢回調(diào)接口
                .setOnStartSetupFinishedListener(mOnStartSetupFinishedListener) //啟動結(jié)果回調(diào)接口
                .build(activity);
    }

    /**
     * google支付
     * google_id:谷歌ID
     * goods_id:商品ID
     */
    public static void googlePay(Activity context,String google_id,String goods_id){
        if (context != null&&googleBillingUtil!=null){
            googleBillingUtil.purchaseInApp(activity, google_id, goods_id);
        }
    }

    /**
     * 查詢未完成消耗的訂單
     */
   public static void chenkOrder(Activity context){
       if (context != null&&googleBillingUtil!=null){
           //調(diào)用線程池
           ThreadPoolManager.Companion.getInstance().addTask("googlepay", new Runnable() {
               @Override
               public void run() {
                   //1靴迫、查詢google未消費(fèi)的商品
                   googleBillingUtil.queryFailOrder(activity, BillingClient.SkuType.INAPP);
                   //2惕味、檢查本地消耗成功的商品
                   googleBillingUtil.checkRecordLoaclConsumeGoods(activity);
                   //3、查詢本地的失敗訂單玉锌;
                   googleBillingUtil.getLocalFailOrder(activity);
               }
           });
       }
   }

    /**
     * 更新用戶第一次支付失敗的狀態(tài)
     */
    public static void updateUserFailState(){
        int is_recharge = MmkvUtils.decodeInt(CommonConstantUtils.IS_RECHARGE);
        if (is_recharge<=0){
            //支付數(shù)小于0
            MmkvUtils.encode(CommonConstantUtils.FIRST_FAIL,true);
        }else {
            MmkvUtils.encode(CommonConstantUtils.FIRST_FAIL,false);
        }
    }
    /**
     * 購買商品回調(diào)接口
     */
    public static class MyOnPurchaseFinishedListener implements GoogleBillingUtil.OnPurchaseFinishedListener {
        @Override
        public void onPurchaseSuccess(Purchase purchase) {
            MyToash.Log("pay","--MyOnPurchaseFinishedListener--purchase="+purchase.toString());
            //調(diào)用本地接口
            googleBillingUtil.getConsumeGoods(activity,purchase);
        }

        @Override
        public void onPurchaseFail(int responseCode) {
            updateUserFailState();
            MyToash.Log("pay","--MyOnPurchaseFinishedListener--responseCode="+responseCode);
        }

        @Override
        public void onPurchaseError() {
            updateUserFailState();
            MyToash.Log("pay","--MyOnPurchaseFinishedListener--onPurchaseError=");
        }
    }


    /**
     * 服務(wù)初始化結(jié)果回調(diào)接口
     */
    public static class MyOnStartSetupFinishedListener implements GoogleBillingUtil.OnStartSetupFinishedListener {
        @Override
        public void onSetupSuccess() {

        }

        @Override
        public void onSetupFail(int responseCode) {

        }

        @Override
        public void onSetupError() {

        }
    }

    /**
     * 查詢商品信息回調(diào)接口
     */
    public static class MyOnQueryFinishedListener implements GoogleBillingUtil.OnQueryFinishedListener {
        @Override
        public void onQuerySuccess(String skuType, List<SkuDetails> list) {
        }

        @Override
        public void onQueryFail(int responseCode) {
            //查詢失敗
        }

        @Override
        public void onQueryError() {
            //查詢錯誤
        }
    }


    /**
     * 支付
     *
     * @param palChannelBean
     * @param palChannelBean
     */
    public static void payGood(Activity activity,PayBeen.ItemsBean itemsBean, PayBeen.ItemsBean.PalChannelBean palChannelBean) {
        if (palChannelBean != null && itemsBean != null) {
           // MyToash.Log("van---", itemsBean.google_id + "    " + itemsBean.getFat_price());
            switch (palChannelBean.getPay_type()) {
                case 1:
                    MyToash.Log("pay", "--發(fā)起內(nèi)購");
                    /**
                     * google_id: 谷歌ID
                     * goods_id:商品ID
                     */
                    googleBillingUtil.purchaseInApp(activity, itemsBean.google_id, itemsBean.goods_id);
                    break;
                case 2:
                case 4:
                    // 應(yīng)用內(nèi)使用web
                    if (palChannelBean.getGateway() != null && !TextUtils.isEmpty(palChannelBean.getGateway())) {
                        Intent intent = new Intent();
                        intent.setClass(activity, WebViewActivity.class);
                        intent.putExtra("title", palChannelBean.getTitle());
                        if (palChannelBean.getGateway().contains("?")) {
                            intent.putExtra("url", palChannelBean.getGateway() +
                                    "&token=" + UserUtils.getToken(activity) +
                                    "&goods_id=" + itemsBean.goods_id);
                        } else {
                            intent.putExtra("url", palChannelBean.getGateway() +
                                    "?token=" + UserUtils.getToken(activity) +
                                    "&goods_id=" + itemsBean.goods_id);
                        }
                        if (palChannelBean.getPay_type() == 2) {
                            intent.putExtra("is_otherBrowser", true);
                        }
                        activity.startActivity(intent);
                    }
                    break;
            }
        } else {
            MyToash.ToashError(activity, LanguageUtil.getString(activity, R.string.PayActivity_zhifucuowu));
        }
    }
}

2名挥、GoogleBillingUtil


public class GoogleBillingUtil {

    private static final String TAG = "GoogleBillingUtil";
    private static final boolean IS_DEBUG = true;
    public static final String BILLING_TYPE_INAPP = BillingClient.SkuType.INAPP;//內(nèi)購
    public static final String BILLING_TYPE_SUBS = BillingClient.SkuType.SUBS;//訂閱

    private static BillingClient mBillingClient;
    private static BillingClient.Builder builder;
    private static OnPurchaseFinishedListener mOnPurchaseFinishedListener;//購買回調(diào)接口
    private static OnStartSetupFinishedListener mOnStartSetupFinishedListener;//啟動結(jié)果回調(diào)接口
    private static OnQueryFinishedListener mOnQueryFinishedListener; //查詢回調(diào)接口
    private static OnConsumeResponseListener mOnConsumeResponseListener; //消耗商品接口

    private boolean isAutoConsumeAsync = true;//是否在購買成功后自動消耗商品

    private static final GoogleBillingUtil mGoogleBillingUtil = new GoogleBillingUtil();

    private GoogleBillingUtil() {

    }

    /**
     * 設(shè)置skus
     *
     * @param inAppSKUS 內(nèi)購id
     * @param subsSKUS  訂閱id
     */
    public static void setSkus(@Nullable String[] inAppSKUS, @Nullable String[] subsSKUS) {
      /*  if (inAppSKUS != null) {
            GoogleBillingUtil.inAppSKUS = inAppSKUS;
        }
        if (subsSKUS != null) {
            GoogleBillingUtil.subsSKUS = subsSKUS;
        }*/
    }

    public static GoogleBillingUtil getInstance() {
        cleanListener();
        return mGoogleBillingUtil;
    }

    /**
     * google play初始化
     *
     * @param context
     * @return
     */
    public GoogleBillingUtil build(Context context) {
        if (mBillingClient == null) {
            synchronized (mGoogleBillingUtil) {
                if (mBillingClient == null) {
                    //檢測GooglePlay服務(wù)是否可用
                    if (isGooglePlayServicesAvailable(context)) {
                        builder = BillingClient.newBuilder(context);
                        mBillingClient = builder.enablePendingPurchases().setListener(mGoogleBillingUtil.new MyPurchasesUpdatedListener()).build();
                    } else {
                        if (IS_DEBUG) {
                            log("警告:GooglePlay服務(wù)處于不可用狀態(tài),請檢查");
                        }
                        if (mOnStartSetupFinishedListener != null) {
                            mOnStartSetupFinishedListener.onSetupError();
                        }
                    }
                } else {
                    //Google購買商品回調(diào)接口(訂閱和內(nèi)購都走這個(gè)接口)
                    builder.setListener(mGoogleBillingUtil.new MyPurchasesUpdatedListener());
                }
            }
        } else {
            //Google購買商品回調(diào)接口(訂閱和內(nèi)購都走這個(gè)接口)
            builder.setListener(mGoogleBillingUtil.new MyPurchasesUpdatedListener());
        }
        //請求連接到GooglePay
        synchronized (mGoogleBillingUtil) {
            if (mGoogleBillingUtil.startConnection()) {
              /*  mGoogleBillingUtil.queryInventoryInApp();
                mGoogleBillingUtil.queryInventorySubs();
                mGoogleBillingUtil.queryPurchasesInApp();*/
            }
        }
        return mGoogleBillingUtil;
    }

    /**
     * 請求連接到GooglePay
     *
     * @return
     */
    public boolean startConnection() {
        if (mBillingClient == null) {
            MyToash.Log("pay", "初始化失敗:mBillingClient==null");
            return false;
        }
        MyToash.Log("pay", "--mBillingClient.isReady()-=" + mBillingClient.isReady());
        if (!mBillingClient.isReady()) {
            mBillingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(BillingResult billingResult) {
                    //  MyToash.Log("van--", "--mBillingClient.startConnection-=" + billingResult.getResponseCode() + "===" + billingResult.getDebugMessage());
                    //連接到GooglePay成功
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        if (mOnStartSetupFinishedListener != null) {
                            mOnStartSetupFinishedListener.onSetupSuccess();
                        }
                    } else {
                        MyToash.Log("pay", "初始化失敗:onSetupFail:code=" + billingResult.getResponseCode());
                        if (mOnStartSetupFinishedListener != null) {
                            mOnStartSetupFinishedListener.onSetupFail(billingResult.getResponseCode());
                        }
                    }
                }

                @Override
                public void onBillingServiceDisconnected() {
                    if (mOnStartSetupFinishedListener != null) {
                        mOnStartSetupFinishedListener.onSetupError();
                    }
                    //log("初始化失敗:onBillingServiceDisconnected");
                    MyToash.Log("pay", "初始化失敗:onBillingServiceDisconnected");
                }
            });
            return false;
        } else {
            return true;
        }
    }


    /**
     * getResponseCode值:
     * <p>
     * Google購買商品回調(diào)接口(訂閱和內(nèi)購都走這個(gè)接口)
     * int SERVICE_TIMEOUT = -3;//服務(wù)超時(shí)
     * int FEATURE_NOT_SUPPORTED = -2;//不支持功能
     * int SERVICE_DISCONNECTED = -1;//服務(wù)單元已斷開
     * int OK = 0;//成功
     * int USER_CANCELED = 1;//用戶按上一步或取消對話框
     * int SERVICE_UNAVAILABLE = 2;//網(wǎng)絡(luò)連接斷開
     * int BILLING_UNAVAILABLE = 3;//所請求的類型不支持 Google Play 結(jié)算服務(wù) AIDL 版本
     * int ITEM_UNAVAILABLE = 4;//請求的商品已不再出售主守。
     * int DEVELOPER_ERROR = 5;//提供給 API 的參數(shù)無效禀倔。此錯誤也可能說明應(yīng)用未針對結(jié)算服務(wù)正確簽名或設(shè)置,或者在其清單中缺少必要的權(quán)限参淫。
     * int ERROR = 6;//API 操作期間出現(xiàn)嚴(yán)重錯誤
     * int ITEM_ALREADY_OWNED = 7;//未能購買救湖,因?yàn)橐呀?jīng)擁有此商品
     * int ITEM_NOT_OWNED = 8;//未能消費(fèi),因?yàn)樯形磽碛写松唐?     */
    private class MyPurchasesUpdatedListener implements PurchasesUpdatedListener {
        @Override
        public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> list) {
            MyToash.Log("pay", "--MyPurchasesUpdatedListener--調(diào)用本地接口");
            //支付成功
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null && !list.isEmpty()) {
                MyToash.Log("pay", "--onPurchasesUpdated--=" + list.size());
                //回調(diào)支付成功
                if (mOnPurchaseFinishedListener != null) {
                    //消耗商品
                    Purchase purchase = list.get(0);
                    mOnPurchaseFinishedListener.onPurchaseSuccess(purchase);
                }
            } else {
                MyToash.Log("pay", "--onPurchasesUpdatedFail--=" + billingResult.getResponseCode());
                mOnPurchaseFinishedListener.onPurchaseFail(billingResult.getResponseCode());
            }
        }

    }

    /**
     * 查詢內(nèi)購商品信息
     */
    public void queryInventoryInApp() {
        queryInventory(BillingClient.SkuType.INAPP);
    }

    /**
     * 查詢訂閱商品信息
     */
    public void queryInventorySubs() {
        queryInventory(BillingClient.SkuType.SUBS);
    }

    private void queryInventory(final String skuType) {

       /* Runnable runnable = new Runnable() {
            @Override
            public void run() {
                if (mBillingClient == null) {
                    if (mOnQueryFinishedListener != null) {
                        mOnQueryFinishedListener.onQueryError();
                    }
                    return;
                }
                ArrayList<String> skuList = new ArrayList<>();
                if (skuType.equals(BillingClient.SkuType.INAPP)) {
                    Collections.addAll(skuList, inAppSKUS);
                } else if (skuType.equals(BillingClient.SkuType.SUBS)) {
                    Collections.addAll(skuList, subsSKUS);
                }
                SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
                params.setSkusList(skuList).setType(skuType);
                mBillingClient.querySkuDetailsAsync(params.build(), new MySkuDetailsResponseListener(skuType));
            }
        };
        executeServiceRequest(runnable);*/
    }


    /**
     * Google查詢商品信息回調(diào)接口
     */
    private class MySkuDetailsResponseListener implements SkuDetailsResponseListener {
        private String skuType;

        public MySkuDetailsResponseListener(String skuType) {
            this.skuType = skuType;
        }

        @Override
        public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> list) {
            if (mOnQueryFinishedListener == null) {
                if (IS_DEBUG) {
                    log("警告:接收到查詢商品回調(diào)涎才,但查詢商品接口為Null鞋既,請?jiān)O(shè)置購買接口。eg:setOnQueryFinishedListener()");
                }
                return;
            }
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) {
                mOnQueryFinishedListener.onQuerySuccess(skuType, list);
            } else {
                mOnQueryFinishedListener.onQueryFail(billingResult.getResponseCode());
            }
        }

    }

    /**
     * 發(fā)起內(nèi)購
     *
     * @param activity
     * @param skuId:googleID
     * @param goods_id:商品ID
     */
    public void purchaseInApp(Activity activity, String skuId, String goods_id) {
        //創(chuàng)建本地訂單
        WaitDialogUtils.showDialog(activity);
        getCreateOrder(activity, goods_id, "1", new CallCheckOrder() {
            @Override
            public void call(String result) {
                purchase(activity, skuId, BillingClient.SkuType.INAPP);
            }
        });

    }

    /**
     * 發(fā)起訂閱
     *
     * @param skuId
     * @return
     */
    public void purchaseSubs(Activity activity, String skuId, String goods_id) {
        //創(chuàng)建本地訂單
        WaitDialogUtils.showDialog(activity);
        getCreateOrder(activity, skuId, "1", new CallCheckOrder() {
            @Override
            public void call(String result) {
                purchase(activity, skuId, BillingClient.SkuType.SUBS);
            }
        });

    }

    /**
     * 發(fā)起google play支付
     *
     * @param activity
     * @param skuId
     * @param skuType
     */
    private void purchase(Activity activity, final String skuId, final String skuType) {
        if (mBillingClient == null) {
            if (mOnPurchaseFinishedListener != null) {
                mOnPurchaseFinishedListener.onPurchaseError();
                MyToash.Log("pay", "-purchase-onPurchaseError");
            }
            return;
        }
        if (startConnection()) {
            List<String> skuList = new ArrayList<>();
            skuList.add(skuId);//商品的id
            skuList.add("gas");// 這個(gè)參數(shù)不能為空耍铜,值隨便傳
            SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
            params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
            MyToash.Log("pay", "-開始調(diào)用google查詢商品接口");
            //querySkuDetailsAsync 查詢方法邑闺,list集合中傳入商品id,谷歌后臺-》應(yīng)用內(nèi)商品-》具體商品
            mBillingClient.querySkuDetailsAsync(params.build(),
                    new SkuDetailsResponseListener() {
                        @Override
                        public void onSkuDetailsResponse(BillingResult billingResult,
                                                         List<SkuDetails> skuDetailsList) {
                            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                                //  MyToash.Log("pay", "skuDetailsList---=" + skuDetailsList.toString());
                                for (SkuDetails skuDetailsBean : skuDetailsList) {
                                    String sku = skuDetailsBean.getSku();//商品id
                                    if (skuId.equals(sku)) {
                                        MyToash.Log("pay", "-開始調(diào)用google支付彈窗");
                                        /**
                                         * 真正的購買方法
                                         */
                                        BillingFlowParams purchaseParams =
                                                BillingFlowParams.newBuilder()
                                                        .setSkuDetails(skuDetailsBean)
                                                        .build();
                                        //發(fā)起google支付彈窗
                                        mBillingClient.launchBillingFlow(activity, purchaseParams);
                                    }
                                }
                            } else {
                                MyToash.Log("pay", "goods search fail");
                            }
                        }
                    });
        } else {
            if (mOnPurchaseFinishedListener != null) {
                MyToash.Log("pay", "---google連接失敗");
                mOnPurchaseFinishedListener.onPurchaseError();
            }
        }
    }

    /**
     * 消耗商品
     *
     * @param purchaseToken
     *  int SERVICE_TIMEOUT = -3; //服務(wù)超時(shí)
     *  int FEATURE_NOT_SUPPORTED = -2; //不支持功能
     *  int SERVICE_DISCONNECTED = -1; //服務(wù)單元已斷開
     *  int OK = 0; //成功
     *  int USER_CANCELED = 1; //用戶按上一步或取消對話框
     *  int SERVICE_UNAVAILABLE = 2; //網(wǎng)絡(luò)連接斷開
     *  int BILLING_UNAVAILABLE = 3; //所請求的類型不支持 Google Play 結(jié)算服務(wù) AIDL 版本
     *  int ITEM_UNAVAILABLE = 4; //請求的商品已不再出售棕兼。
     *  int DEVELOPER_ERROR = 5; //提供給 API 的參數(shù)無效陡舅。此錯誤也可能說明應(yīng)用未針對結(jié)算服務(wù)正確簽名或設(shè)置,或者在其清單中缺少必要的權(quán)限程储。
     *  int ERROR = 6; //API 操作期間出現(xiàn)嚴(yán)重錯誤
     *  int ITEM_ALREADY_OWNED = 7; //未能購買章鲤,因?yàn)橐呀?jīng)擁有此商品
     *  int ITEM_NOT_OWNED = 8; //未能消費(fèi)败徊,因?yàn)樯形磽碛写松唐?     */
    public void consumeAsync(Activity activity, ConsumeParams consumeParams, Purchase purchase) {
        if (mBillingClient == null) {
            return;
        }
        //本地記錄消費(fèi)的商品
        saveRecordLoaclConsumeGoods(activity, purchase, 1);
        mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
            @Override
            public void onConsumeResponse(@NonNull BillingResult billingResult, @NonNull String s) {
//                MyToash.Log("van---", "--consumeAsync--=" + billingResult.toString() + "---" + s);
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    MyToash.Log("pay", "-----消耗商品成功");
                    // ----------- 沙盒代碼 - 正式環(huán)境要上報(bào)-------- -
                    if (BaseConfig.INSTANCE.getCurrentEnvironment()){
                        String pId = purchase.getSkus().get(0);
                        String price = CommonOkHttpUtils.INSTANCE.getPayPrice(pId);
                        MyToash.Log("pay", "-----消耗商品成功 "+"價(jià)格:"+price);
                        String productToken = purchase.getPurchaseToken();
                        String token = UserUtils.getToken(activity);
                        AdjustEvent event = new AdjustEvent(Constant.MD_ADJUST_czcg);
                        event.setRevenue(Double.parseDouble(price),"USD");
                        event.addCallbackParameter("productId", pId);
                        event.addCallbackParameter("productToken", productToken);
                        // event.addCallbackParameter("goods_id",goods_id);
                        event.addCallbackParameter("token", token);

                        event.addCallbackParameter("content_type", "1");
                        event.addCallbackParameter("book_id", ShareUitls.getString(activity, BaseKey.SP_BOOKID, ""));
                        event.addCallbackParameter("packageName", purchase.getPackageName());
                        Adjust.trackEvent(event);
                    }
                    //請求本地接口
                    getHttpPay(activity, purchase);
                    //移除
                    saveRecordLoaclConsumeGoods(activity, purchase, 2);
                } else {
                    MyToash.Log("pay", "-----消耗商品失敗" + billingResult.toString() + "---" + s + "--code:" + billingResult.getResponseCode());
                }
            }
        });
    }

    /**
     * 記錄本地消費(fèi)的商品
     *
     * @param activity
     * @param purchase
     * @param type:    1:是保存;2:移除
     */
    private void saveRecordLoaclConsumeGoods(Activity activity, Purchase purchase, int type) {
        try {
            if (purchase != null) {
                String uid_key = UserUtils.getUID(activity) + CommonConstantUtils.CONSUME;
                List<String> list = new ArrayList<>();
                //獲取存儲的值
                String lastJson = MmkvUtils.decodeString(uid_key);

                String pId = purchase.getSkus().get(0);
                String purchaseToken = purchase.getPurchaseToken();
                String packageName = purchase.getPackageName();
                //鍵值對唯一的key
                String onlyKey = purchaseToken + CommonConstantUtils.CONSUME;
                if (type == 1) {  //保存
                    //不是空
                    if (!lastJson.isEmpty()) {
                        List<String> list2 = GsonUtil.GsonToList(lastJson, String.class);
                        for (int i = 0; i < list2.size(); i++) {
                            String data = list2.get(i);
                            OrderFailBean bean = GsonUtil.GsonToBean(data, OrderFailBean.class);
                            String tokenKey = bean.getPurchaseToken() + CommonConstantUtils.CONSUME;
                            //是否要保存一條數(shù)據(jù)
                            if (!tokenKey.equals(onlyKey)) {
                                list.add(data);
                            }
                        }
                    }
                    //沒有相同單據(jù)眷蜈,保存
                    HashMap map = new HashMap();
                    map.put("pId", pId);
                    map.put("purchaseToken", purchaseToken);
                    map.put("packageName", packageName);
                    list.add(GsonUtil.BeanToJson(map));
                    String json = GsonUtil.BeanToJson(list);
                    //保存上傳失敗的訂單
                    MmkvUtils.encode(uid_key, json);
                    MyToash.Log("pay", "--保存消耗的商品json:" + purchaseToken);
                    // MyToash.Log("pay", "--保存消耗的商品json:" + json);
                } else if (type == 2) { //移除
                    //不是空
                    if (!lastJson.isEmpty()) {
                        int currentIndex = -1;
                        List<String> list3 = GsonUtil.GsonToList(lastJson, String.class);
                        for (int i = 0; i < list3.size(); i++) {
                            String data = list3.get(i);
                            list.add(data);
                            OrderFailBean bean = GsonUtil.GsonToBean(data, OrderFailBean.class);
                            String tokenKey = bean.getPurchaseToken() + CommonConstantUtils.CONSUME;
                            if (tokenKey.equals(onlyKey)) {
                                currentIndex = i;
                            }
                        }
                        if (currentIndex != -1) {
                            list.remove(currentIndex);
                            String json = GsonUtil.BeanToJson(list);
                            //保存上傳失敗的訂單
                            MmkvUtils.encode(uid_key, json);
                            MyToash.Log("pay", "--移除消耗的商品json:" + json);
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查詢本地消費(fèi)商品未返回?cái)?shù)據(jù)
     *
     * @param type
     * @param activity
     * @param purchase
     */
    public void checkRecordLoaclConsumeGoods(Activity activity) {
        try {
            String uid = UserUtils.getUID(activity);
            String uid_key = UserUtils.getUID(activity) + CommonConstantUtils.CONSUME;
            List<String> list = new ArrayList<>();
            //獲取存儲的值
            String lastJson = MmkvUtils.decodeString(uid_key);
            MyToash.Log("pay", "--查詢本地消費(fèi)后商品未返回?cái)?shù)據(jù)lastJson:" + lastJson);
            if (!lastJson.isEmpty()) {
                //不是空
                list = GsonUtil.GsonToList(lastJson, String.class);
                if (list != null && list.size() > 0) {
                    //上傳
                    for (int i = 0; i < list.size(); i++) {
                        String data = list.get(i);
                        OrderFailBean bean = GsonUtil.GsonToBean(data, OrderFailBean.class);
                        String pId = bean.getpId();
                        String purchaseToken = bean.getPurchaseToken();
                        String packageName = bean.getPackageName();
                        MyToash.Log("pay", "--查詢本地消費(fèi)后商品未返回?cái)?shù)據(jù)上傳本地單據(jù):" + purchaseToken);
                        //上傳本地單據(jù)
                        UploadLocalFailOrder(activity, uid, pId, packageName, purchaseToken);
                        Thread.sleep(1000);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 獲取已經(jīng)內(nèi)購的商品
     *
     * @return
     */
    public List<Purchase> queryPurchasesInApp() {
        return queryPurchases(BillingClient.SkuType.INAPP);
    }

    /**
     * 獲取已經(jīng)訂閱的商品
     *
     * @return
     */
    public List<Purchase> queryPurchasesSubs() {
        return queryPurchases(BillingClient.SkuType.SUBS);
    }

    /**
     * 查詢最近的購買交易
     *
     * @param skuType
     * @return
     */
    private List<Purchase> queryPurchases(String skuType) {
        if (mBillingClient == null) {
            return null;
        }
        if (!mBillingClient.isReady()) {
            //如果斷開google連接,重新開始連接
            startConnection();
        } else {
            Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(skuType);

            if (purchasesResult != null) {
                if (purchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    List<Purchase> purchaseList = purchasesResult.getPurchasesList();
                  /*  if (isAutoConsumeAsync) {
                        if (purchaseList != null) {
                            for (Purchase purchase : purchaseList) {
                                if (skuType.equals(purchase.getSku())) {
                                    ConsumeParams.Builder consumeParams = ConsumeParams.newBuilder();
                                    consumeParams.setPurchaseToken(purchase.getPurchaseToken());
                            *//*        consumeParams
                                    consumeParams.setDeveloperPayload(purchase.getDeveloperPayload());*//*
                                    consumeAsync(consumeParams.build(), purchase);
                                }
                            }
                        }
                    }*/
                    return purchaseList;
                }
            }

        }
        return null;
    }

    /**
     * 獲取有效訂閱的數(shù)量
     *
     * @return -1查詢失敗忌怎,0沒有有效訂閱,>0具有有效的訂閱
     */
    public int getPurchasesSizeSubs() {
        List<Purchase> list = queryPurchasesSubs();
        if (list != null) {
            return list.size();
        }
        return -1;
    }

    /**
     * 通過sku獲取訂閱商品序號
     *
     * @param sku
     * @return
     */
    public int getSubsPositionBySku(String sku) {
        return getPositionBySku(sku, BillingClient.SkuType.SUBS);
    }

    /**
     * 通過sku獲取內(nèi)購商品序號
     *
     * @param sku
     * @return 成功返回需要 失敗返回-1
     */
    public int getInAppPositionBySku(String sku) {
        return getPositionBySku(sku, BillingClient.SkuType.INAPP);
    }

    private int getPositionBySku(String sku, String skuType) {

       /* if (skuType.equals(BillingClient.SkuType.INAPP)) {
            int i = 0;
            for (String s : inAppSKUS) {
                if (s.equals(sku)) {
                    return i;
                }
                i++;
            }
        } else if (skuType.equals(BillingClient.SkuType.SUBS)) {
            int i = 0;
            for (String s : subsSKUS) {
                if (s.equals(sku)) {
                    return i;
                }
                i++;
            }
        }*/
        return -1;
    }

    private void executeServiceRequest(final Runnable runnable) {
        if (startConnection()) {
            runnable.run();
        }
    }

    /**
     * 通過序號獲取訂閱sku
     *
     * @param position
     * @return
     */
    public String getSubsSkuByPosition(int position) {
       /* if (position >= 0 && position < subsSKUS.length) {
            return subsSKUS[position];
        } else {
            return null;
        }*/
        return null;
    }

    /**
     * 通過序號獲取內(nèi)購sku
     *
     * @param position
     * @return
     */
    public String getInAppSkuByPosition(int position) {
      /*  if (position >= 0 && position < inAppSKUS.length) {
            return inAppSKUS[position];
        } else {
            return null;
        }*/
        return null;
    }

    /**
     * 通過sku獲取商品類型(訂閱獲取內(nèi)購)
     *
     * @param sku
     * @return inapp內(nèi)購酪夷,subs訂閱
     */
    public String getSkuType(String sku) {
       /* if (Arrays.asList(inAppSKUS).contains(sku)) {
            return BillingClient.SkuType.INAPP;
        } else if (Arrays.asList(subsSKUS).contains(sku)) {
            return BillingClient.SkuType.SUBS;
        }*/
        return null;
    }

    /**
     * 檢測GooglePlay服務(wù)是否可用(需要導(dǎo)入包api "com.google.android.gms:play-services-location:11.8.0"榴啸,也可以不檢查,跳過這個(gè)代碼)
     *
     * @param context
     * @return
     */
    public static boolean isGooglePlayServicesAvailable(Context context) {
        GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
        if (googleApiAvailability != null) {
            int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context);
            return resultCode == ConnectionResult.SUCCESS;
        }
        return false;
        //return_img true;//不檢查直接跳過
    }

    public GoogleBillingUtil setOnQueryFinishedListener(OnQueryFinishedListener onQueryFinishedListener) {
        mOnQueryFinishedListener = onQueryFinishedListener;
        return mGoogleBillingUtil;
    }

    public GoogleBillingUtil setOnPurchaseFinishedListener(OnPurchaseFinishedListener onPurchaseFinishedListener) {
        mOnPurchaseFinishedListener = onPurchaseFinishedListener;
        return mGoogleBillingUtil;
    }

    public OnStartSetupFinishedListener getOnStartSetupFinishedListener() {
        return mOnStartSetupFinishedListener;
    }

    public GoogleBillingUtil setOnStartSetupFinishedListener(OnStartSetupFinishedListener onStartSetupFinishedListener) {
        mOnStartSetupFinishedListener = onStartSetupFinishedListener;
        return mGoogleBillingUtil;
    }

    public static OnConsumeResponseListener getmOnConsumeResponseListener() {
        return mOnConsumeResponseListener;
    }

    public GoogleBillingUtil setOnConsumeResponseListener(OnConsumeResponseListener onConsumeResponseListener) {
        mOnConsumeResponseListener = onConsumeResponseListener;
        return mGoogleBillingUtil;
    }

    /**
     * 本工具查詢回調(diào)接口
     */
    public interface OnQueryFinishedListener {
        //Inapp和sub都走這個(gè)接口查詢的時(shí)候一定要判斷skuType
        public void onQuerySuccess(String skuType, List<SkuDetails> list);

        public void onQueryFail(int responseCode);

        public void onQueryError();
    }

    /**
     * 本工具購買回調(diào)接口(內(nèi)購與訂閱都走這接口)
     */
    public interface OnPurchaseFinishedListener {

        public void onPurchaseSuccess(Purchase purchase);

        public void onPurchaseFail(int responseCode);

        public void onPurchaseError();

    }

    /**
     * google服務(wù)啟動接口
     */
    public interface OnStartSetupFinishedListener {
        public void onSetupSuccess();

        public void onSetupFail(int responseCode);

        public void onSetupError();
    }

    /**
     * 消耗回調(diào)監(jiān)聽器
     */
    public interface OnConsumeResponseListener {
        public void onConsumeSuccess(String purchaseToken);

        public void onConsumeFail(int responseCode);
    }

    public boolean isReady() {
        return mBillingClient != null && mBillingClient.isReady();
    }

    public boolean isAutoConsumeAsync() {
        return isAutoConsumeAsync;
    }

    public void setIsAutoConsumeAsync(boolean isAutoConsumeAsync) {
        this.isAutoConsumeAsync = isAutoConsumeAsync;
    }

    /**
     * 清除所有監(jiān)聽器晚岭,防止內(nèi)存泄漏
     * 如果有多個(gè)頁面使用了支付鸥印,需要確保上個(gè)頁面的cleanListener在下一個(gè)頁面的GoogleBillingUtil.getInstance()前使用。
     * 所以不建議放在onDestory里調(diào)用
     */
    public static void cleanListener() {
        mOnPurchaseFinishedListener = null;
        mOnQueryFinishedListener = null;
        mOnStartSetupFinishedListener = null;
        mOnConsumeResponseListener = null;
        if (builder != null) {
            builder.setListener(null);
        }
    }

    /**
     * 斷開連接google服務(wù)
     * 注意L贡ā?馑怠!一般情況不建議調(diào)用該方法片择,讓google保留連接是最好的選擇璃弄。
     */
    public static void endConnection() {
        //注意!9够亍!一般情況不建議調(diào)用該方法疏咐,讓google保留連接是最好的選擇纤掸。
        if (mBillingClient != null) {
            if (mBillingClient.isReady()) {
                mBillingClient.endConnection();
                mBillingClient = null;
            }
        }
    }

    private static void log(String msg) {
        if (IS_DEBUG) {
            Log.i(TAG, msg);
        }
    }

    /**
     * 查詢失敗的訂單
     * skuType :,BillingClient.SkuType.INAPP;  BillingClient.SkuType.SUBS
     * 0:PurchaseState.UNSPECIFIED_STATE:未知狀態(tài)
     * 1:PurchaseState.PURCHASED:付款完成
     * 2:PurchaseState.PENDING:購買正在等待付款完成。
     */
    public void queryFailOrder(Activity activity, String skuType) {
        MyToash.Log("pay", "-----查詢需要補(bǔ)貨的商品:");
        if (mBillingClient != null) {
            Purchase.PurchasesResult result = mBillingClient.queryPurchases(skuType);
            if (BillingClient.BillingResponseCode.OK == result.getResponseCode()) {
                for (Purchase purchase : result.getPurchasesList()) {
                    //PURCHASED --已購買
                    if (Purchase.PurchaseState.PURCHASED == purchase.getPurchaseState()) {
                        //MyToash.Log("pay", "----------需要補(bǔ)貨的商品:");
                        //調(diào)用google去消費(fèi)
                        getConsumeGoods(activity, purchase);
                    }
                }
            }
        }
    }

    /**
     * 公共消費(fèi)商品接口
     */
    public void getConsumeGoods(Activity activity, Purchase purchase) {
        if (purchase != null) {
            MyToash.Log("pay", "-----開始消耗商品:" + purchase.toString());
            //本地發(fā)送書券成功之后浑塞,調(diào)用消耗接口
            ConsumeParams.Builder consumeParams = ConsumeParams.newBuilder();
            consumeParams.setPurchaseToken(purchase.getPurchaseToken());
            //調(diào)用消耗商品方法
            consumeAsync(activity, consumeParams.build(), purchase);
        }
    }

    /**
     * 正常流程-調(diào)用本地接口發(fā)送書券和書幣
     */
    public void getHttpPay(Activity activity, Purchase purchase) {
        if (purchase != null) {
            String pId = purchase.getSkus().get(0);
            String purchaseToken = purchase.getPurchaseToken();
            String packageName = purchase.getPackageName();
            //保存本地單據(jù)
            saveLocalFaileOrder(activity, purchase);
            //調(diào)用本地發(fā)書券接口
            GoPay.httpPay(activity, pId, purchaseToken, "", packageName, new GooglePayActivity.UpPay() {
                @Override
                public void paySuccess(String success) {
                    MyToash.Log("pay", "--本地接口調(diào)用成功:" + success);
                    //WaitDialogUtils.dismissDialog();
                    ShareUitls.putString(activity, BaseKey.SP_BOOKID, "");
                    ShareUitls.putString(activity, "goods_id", null);
                    ShareUitls.putString(activity, "productId", null);
                    ShareUitls.putString(activity, "productToken", null);
                    //通知刷新頁面
                    EventBus.getDefault().post(new RefreshMine());
                    //成功提示
                    MyToash.ToashSuccess(activity, success);
                    //刪除本地的數(shù)據(jù)
                    deleteLocalFaileOrder(activity, purchaseToken);
                }

                @Override
                public void payFail(String msg) {
                    //本地接口調(diào)用失敗
                    MyToash.Log("pay", "----本地接口調(diào)用失斕统睢:" + msg);
                }
            });
        }
    }

    /**
     * 保存本地失敗的單據(jù)
     *
     * @param activity
     */
    private void saveLocalFaileOrder(Activity activity, Purchase purchase) {
        try {
            String pId = purchase.getSkus().get(0);
            String purchaseToken = purchase.getPurchaseToken();
            String packageName = purchase.getPackageName();
            //用戶id
            String uid = UserUtils.getUID(activity);
            List<String> list = new ArrayList<>();

            //獲取存儲的值
            String lastJson = MmkvUtils.decodeString(uid);
            if (!lastJson.isEmpty()) {
                //不是空,只有第一次請求失敗保存
                List<String> list2 = GsonUtil.GsonToList(lastJson, String.class);
                for (int i = 0; i < list2.size(); i++) {
                    String data = list2.get(i);
                    list.add(data);
                }
            }
            //沒有相同單據(jù)糊昙,保存
            HashMap map = new HashMap();
            map.put("pId", pId);
            map.put("purchaseToken", purchaseToken);
            map.put("packageName", packageName);
            list.add(GsonUtil.BeanToJson(map));

            String json = GsonUtil.BeanToJson(list);
            //保存上傳失敗的訂單
            MmkvUtils.encode(uid, json);
            MyToash.Log("pay", "----保存上傳失敗的訂單json:" + json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 刪除本地失敗的單據(jù)
     *
     * @param activity
     */
    private void deleteLocalFaileOrder(Activity activity, String purchaseToken) {
        try {
            //用戶id
            String uid = UserUtils.getUID(activity);
            String lastJson = MmkvUtils.decodeString(uid);
            if (!lastJson.isEmpty()) {
                MyToash.Log("pay", "----查詢準(zhǔn)備要刪除上傳失敗的訂單:" + lastJson);
                //不是空
                List<String> list = GsonUtil.GsonToList(lastJson, String.class);
                //是否有相等的下標(biāo)
                int index = -1;
                for (int i = 0; i < list.size(); i++) {
                    String data = list.get(i);
                    OrderFailBean bean = GsonUtil.GsonToBean(data, OrderFailBean.class);
                    String token = bean.getPurchaseToken();
                    if (purchaseToken.equals(token)) {
                        index = i;
                    }
                }
                //刪除數(shù)據(jù)
                if (index != -1) {
                    list.remove(index);
                    String json = GsonUtil.BeanToJson(list);
                    //保存數(shù)據(jù)
                    MmkvUtils.encode(uid, json);
                    MyToash.Log("pay", "----刪除上傳失敗的訂單:" + json);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查詢訂單失敗--調(diào)用本地接口發(fā)送書券和書幣
     */
    public void getLocalFailOrder(Activity activity) {
        try {
            //用戶id
            String uid = UserUtils.getUID(activity);
            List<String> list = new ArrayList<>();
            //獲取存儲的值
            String lastJson = MmkvUtils.decodeString(uid);
            MyToash.Log("pay", "---查詢失敗的訂單lastJson:" + lastJson);
            if (!lastJson.isEmpty()) {
                //不是空
                list = GsonUtil.GsonToList(lastJson, String.class);
                for (int i = 0; i < list.size(); i++) {
                    String data = list.get(i);
                    OrderFailBean bean = GsonUtil.GsonToBean(data, OrderFailBean.class);
                    String pId = bean.getpId();
                    String purchaseToken = bean.getPurchaseToken();
                    String packageName = bean.getPackageName();
                    //上傳失敗的單據(jù)
                    UploadLocalFailOrder(activity, uid, pId, packageName, purchaseToken);
                    Thread.sleep(1000);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 上傳本地單據(jù)
     *
     * @param pId
     * @param packageName
     * @param purchaseToken
     */
    private void UploadLocalFailOrder(Activity activity, String uid, String pId, String packageName, String purchaseToken) {
        try {
            //調(diào)用本地發(fā)書券接口
            GoPay.httpPay(activity, pId, purchaseToken, "", packageName, new GooglePayActivity.UpPay() {
                @Override
                public void paySuccess(String success) {
                    MyToash.Log("pay", "后臺上傳失敗訂單成功:" + success);
                    //WaitDialogUtils.dismissDialog();
                    ShareUitls.putString(activity, BaseKey.SP_BOOKID, "");
                    ShareUitls.putString(activity, "goods_id", null);
                    ShareUitls.putString(activity, "productId", null);
                    ShareUitls.putString(activity, "productToken", null);
                    //通知刷新頁面
                    EventBus.getDefault().post(new RefreshMine());
                    //刪除本地的數(shù)據(jù)
                    deleteLocalFaileOrder(activity, purchaseToken);
                    //移除消費(fèi)無響應(yīng)數(shù)據(jù)
                    deleteRecordLoaclConsumeGoods(activity, pId, purchaseToken, packageName);
                }

                @Override
                public void payFail(String msg) {
                    //本地接口調(diào)用失敗
                    try {
                        if (!TextUtils.isEmpty(msg)) {
                            int subIndex = msg.indexOf(",");
                            if (subIndex > 0) {
                                String code = msg.substring(0, subIndex);
                                String message = msg.substring(subIndex, msg.length());
                                if ("806".equals(code)) {
                                    //刪除本地的數(shù)據(jù)
                                    deleteLocalFaileOrder(activity, purchaseToken);
                                    //移除消費(fèi)無響應(yīng)數(shù)據(jù)
                                    deleteRecordLoaclConsumeGoods(activity, pId, purchaseToken, packageName);
                                }
                            }
                        }
                        MyToash.Log("pay", "后臺上傳失敗訂單失敗:" + msg);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 新-只刪除消費(fèi)商品無響應(yīng)的狀態(tài)
     *
     * @param activity
     * @param lastJson
     * @param pId
     * @param purchaseToken
     * @param packageName
     */
    private void deleteRecordLoaclConsumeGoods(Activity activity, String pId, String purchaseToken, String packageName) {
        try {
            String uid_key = UserUtils.getUID(activity) + CommonConstantUtils.CONSUME;
            List<String> list = new ArrayList<>();
            //獲取存儲的值
            String lastJson = MmkvUtils.decodeString(uid_key);
            //不是空
            if (!lastJson.isEmpty()) {
                int currentIndex = -1;
                List<String> list3 = GsonUtil.GsonToList(lastJson, String.class);
                for (int i = 0; i < list3.size(); i++) {
                    String data = list3.get(i);
                    list.add(data);
                    OrderFailBean bean = GsonUtil.GsonToBean(data, OrderFailBean.class);
                    String tokenKey = bean.getPurchaseToken() + CommonConstantUtils.CONSUME;
                    if ((tokenKey).equals(purchaseToken + CommonConstantUtils.CONSUME)) {
                        currentIndex = i;
                    }
                }
                if (currentIndex != -1) {
                    list.remove(currentIndex);
                    String json = GsonUtil.BeanToJson(list);
                    //保存上傳失敗的訂單
                    MmkvUtils.encode(uid_key, json);
                    MyToash.Log("pay", "--移除消耗的商品json:" + json);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 調(diào)用支付之前創(chuàng)建訂單
     * goods_id:商品ID
     * content_type:內(nèi)容類型 1小說
     */
    public void getCreateOrder(Activity activity, String goods_id, String content_type, CallCheckOrder CallCheckOrder) {
        ReaderParams params = new ReaderParams(activity);
        params.putExtraParams("goods_id", goods_id);
        params.putExtraParams("content_type", content_type);
        params.putExtraParams("book_id", ShareUitls.getString(activity, BaseKey.SP_BOOKID, ""));
        String json = params.generateParamsJson();
        //調(diào)用本地發(fā)書券接口
        //MyToash.Log("lyy", "--json:" + json);
        HttpUtils.getInstance().sendRequestRequestParams(activity, Api.mCreateOrderUrl, json, new HttpUtils.ResponseListener() {
                    @Override
                    public void onResponse(final String result) {
                        WaitDialogUtils.dismissDialog();
                        MyToash.Log("pay", "--創(chuàng)建訂單成功" + result);
                        if (CallCheckOrder != null) {
                            CallCheckOrder.call("success");
                        }
                    }

                    @Override
                    public void onErrorResponse(String error) {
                        WaitDialogUtils.dismissDialog();
                        MyToash.Log("pay", "--創(chuàng)建訂單失敗" + error);
                    }
                }
        );
    }

    public interface CallCheckOrder {
        void call(String result);
    }
}


參考鏈接:

1憔古、MmkvUtils 使用和初始化:

 //mmkv
implementation 'com.tencent:mmkv-static:1.2.7'
//application中mmkv初始化
MMKV.initialize(context)
MmkvUtils.getInstance()

//使用

//存儲
MmkvUtils.encode("key",value);
//獲取焰情,存什么類型,獲取的時(shí)候是什么類型
val value2 = MmkvUtils.decodeInt("key");

2验游、MmkvUtils 工具類:


package com.kana.crazytv.app.util;

import android.os.Parcelable;

import com.tencent.mmkv.MMKV;

import java.util.Collections;
import java.util.Set;

public class MmkvUtils {

    //--------封裝的方法---------
    private static MmkvUtils mInstance;
    private static MMKV mv;

    private MmkvUtils() {
        mv = MMKV.defaultMMKV();
    }

    /**
     * 初始化MMKV,只需要初始化一次,建議在Application中初始化
     *
     */
    public static MmkvUtils getInstance() {
        if (mInstance == null) {
            synchronized (MmkvUtils.class) {
                if (mInstance == null) {
                    mInstance = new MmkvUtils();
                }
            }
        }
        return mInstance;
    }

    /**
     * 保存數(shù)據(jù)的方法垒在,我們需要拿到保存數(shù)據(jù)的具體類型,然后根據(jù)類型調(diào)用不同的保存方法
     *
     * @param key
     * @param object
     */
    public static void encode(String key, Object object) {
        if (object instanceof String) {
            mv.encode(key, (String) object);
        } else if (object instanceof Integer) {
            mv.encode(key, (Integer) object);
        } else if (object instanceof Boolean) {
            mv.encode(key, (Boolean) object);
        } else if (object instanceof Float) {
            mv.encode(key, (Float) object);
        } else if (object instanceof Long) {
            mv.encode(key, (Long) object);
        } else if (object instanceof Double) {
            mv.encode(key, (Double) object);
        } else if (object instanceof byte[] ) {
            mv.encode(key, (byte[]) object);
        } else {
            mv.encode(key, object.toString());
        }
    }

    public static void encodeSet(String key, Set<String> sets) {
        mv.encode(key, sets);
    }

    public static void encodeParcelable(String key, Parcelable obj) {
        mv.encode(key, obj);
    }


    /**
     * 得到保存數(shù)據(jù)的方法,我們根據(jù)默認(rèn)值得到保存的數(shù)據(jù)的具體類型耘成,然后調(diào)用相對于的方法獲取值
     */
    public static Integer decodeInt(String key) {
        return mv.decodeInt(key, 0);
    }
    public static Double decodeDouble(String key) {
        return mv.decodeDouble(key, 0.00);
    }
    public static Long decodeLong(String key) {
        return mv.decodeLong(key, 0L);
    }
    public static Boolean decodeBoolean(String key) {
        return mv.decodeBool(key, false);
    }
    public static Float decodeFloat(String key) {
        return mv.decodeFloat(key, 0F);
    }
    public static byte[] decodeBytes(String key) {
        return mv.decodeBytes(key);
    }
    public static String decodeString(String key) {
        return mv.decodeString(key,"");
    }
    public static Set<String> decodeStringSet(String key) {
        return mv.decodeStringSet(key, Collections.<String>emptySet());
    }
    public static Parcelable decodeParcelable(String key) {
        return mv.decodeParcelable(key, null);
    }
    /**
     * 移除某個(gè)key對
     *
     * @param key
     */
    public static void removeKey(String key) {
        mv.removeValueForKey(key);
    }
    /**
     * 清除所有key
     */
    public static void clearAll() {
        mv.clearAll();
    }

    /**
     * 是否包含某個(gè)key
     */

    public static boolean containsKey(String key) {
        return mv.containsKey(key);
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市诵肛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌薛训,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件介袜,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸠珠,警方通過查閱死者的電腦和手機(jī)跳芳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門次乓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杏慰,你說我怎么就攤上這事轰胁。” “怎么了榛斯?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長王凑。 經(jīng)常有香客問我荤崇,道長,這世上最難降的妖魔是什么瓣戚? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任唬血,我火速辦了婚禮痹束,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脖捻。我一直安慰自己嗜浮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寨腔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乾蛤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機(jī)與錄音酪捡,去河邊找鬼。 笑死永罚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的哼御。 我是一名探鬼主播看靠,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼谤祖,長吁一口氣:“原來是場噩夢啊……” “哼粥喜!你這毒婦竟也來了旁舰?” 一聲冷哼從身側(cè)響起毯焕,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤坊罢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后活孩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體物遇,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年憾儒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了询兴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡起趾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出族扰,到底是詐尸還是另有隱情,我是刑警寧澤巩检,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布矩父,位于F島的核電站冒滩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏醋虏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宣吱。 院中可真熱鬧,春花似錦、人聲如沸扛点。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丸相。三九已至,卻和暖如春印颤,著一層夾襖步出監(jiān)牢的瞬間社牲,已是汗流浹背俏扩。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工努潘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留压怠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像瓦堵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子歌亲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容