ChargeImpl.kt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. package cn.yyxx.eyuangame.core.impl.iab
  2. import android.app.Activity
  3. import android.text.TextUtils
  4. import cn.yyxx.eyuangame.base.entity.SdkChargeInfo
  5. import cn.yyxx.eyuangame.base.utils.Logger
  6. import cn.yyxx.eyuangame.core.entity.ResultInfo
  7. import cn.yyxx.eyuangame.core.impl.SdkBridgeImpl
  8. import cn.yyxx.eyuangame.core.internal.IImplCallback
  9. import cn.yyxx.eyuangame.core.internal.IRequestCallback
  10. import cn.yyxx.eyuangame.core.network.SdkRequest
  11. import cn.yyxx.eyuangame.core.utils.MMKVUtils
  12. import cn.yyxx.support.ResUtils
  13. import cn.yyxx.support.hawkeye.ToastUtils
  14. import com.android.billingclient.api.*
  15. import org.json.JSONException
  16. import org.json.JSONObject
  17. /**
  18. * @author #Suyghur.
  19. * Created on 2021/06/28
  20. */
  21. class ChargeImpl : InAppBilling() {
  22. //初始化IAB收银台客户端
  23. //连接谷歌商店
  24. private lateinit var implCallback: IImplCallback
  25. private lateinit var chargeInfo: SdkChargeInfo
  26. fun charge(activity: Activity, chargeInfo: SdkChargeInfo, callback: IImplCallback) {
  27. if (!checkGoogleApiAvailability(activity)) {
  28. ToastUtils.toastInfo(activity, "Your phone or Google account does not support In-app Billing")
  29. callback.onResult(-1, "谷歌iab支付服务不可用")
  30. return
  31. }
  32. this.implCallback = callback
  33. this.chargeInfo = chargeInfo
  34. showDialog(activity)
  35. // checkLocalNotifyFailedOrder(activity)
  36. getOrderId(activity)
  37. }
  38. override fun chargePurchasesUpdated(activity: Activity, purchase: Purchase) {
  39. Logger.d("chargePurchasesUpdated")
  40. notifyOrder2Backend(activity, chargeInfo.orderId, purchase, false)
  41. }
  42. override fun preRewardPurchasesUpdated(activity: Activity, purchase: Purchase) {
  43. }
  44. override fun queryRewardInfo(activity: Activity) {
  45. }
  46. /**
  47. * 查询未消耗订单
  48. */
  49. override fun queryChargeInfo(activity: Activity) {
  50. billingClient?.apply {
  51. val list = queryPurchases(BillingClient.SkuType.INAPP).purchasesList
  52. if (list.isNullOrEmpty()) {
  53. //正常发起支付流程
  54. querySkuDetails(activity)
  55. } else {
  56. Logger.e("存在未消耗订单,发起补单流程,size : ${list.size}")
  57. run breaking@{
  58. if (list.size > 0) {
  59. list.forEach { purchase ->
  60. if (purchase.sku == SdkBridgeImpl.initBean.rewardId) {
  61. Logger.e("存在阻塞的预注册奖励,停止支付流程")
  62. if (list.size == 1) {
  63. dismissDialog()
  64. disConnection()
  65. ToastUtils.toastInfo(activity, "In-app Billing has some error , please restart app and try again")
  66. implCallback.onResult(-1, "存在阻塞的预注册奖励,停止支付流程")
  67. }
  68. //跳出支付流程
  69. return@breaking
  70. } else {
  71. consumeCacheOrder(activity, purchase)
  72. }
  73. }
  74. } else {
  75. //发起正常支付流程
  76. querySkuDetails(activity)
  77. }
  78. }
  79. }
  80. // queryPurchasesAsync(BillingClient.SkuType.INAPP, object : PurchasesResponseListener {
  81. // override fun onQueryPurchasesResponse(billingResult: BillingResult, list: MutableList<Purchase>) {
  82. // logBillingResult("onQueryPurchasesResponse", billingResult)
  83. // if (list.isNullOrEmpty()) {
  84. // //正常发起支付流程
  85. // Logger.d("正常发起支付流程")
  86. // querySkuDetails(activity)
  87. // } else {
  88. // Logger.e("存在未消耗订单,发起补单流程,size : ${list.size}")
  89. // run breaking@{
  90. // list.forEach { purchase ->
  91. // purchase.skus.forEach {
  92. // if (it.equals(SdkBridgeImpl.initBean.rewardId)) {
  93. // Logger.e("存在阻塞的预注册奖励,停止支付流程")
  94. // if (list.size == 1) {
  95. // dismissDialog()
  96. // disConnection()
  97. // ToastUtils.toastInfo(activity, "In-app Billing has some error , please restart app and try again")
  98. // implCallback.onResult(-1, "存在阻塞的预注册奖励,停止支付流程")
  99. // }
  100. // //跳出支付流程
  101. // return@breaking
  102. // } else {
  103. // consumeCacheOrder(activity, purchase)
  104. // }
  105. // }
  106. // }
  107. // }
  108. // }
  109. // }
  110. // })
  111. }
  112. }
  113. override fun purchasesUpdatedFailed() {
  114. }
  115. override fun connectGooglePlayFailed() {
  116. }
  117. /**
  118. * 检查本地通知发货失败订单
  119. */
  120. private fun checkLocalNotifyFailedOrder(activity: Activity) {
  121. }
  122. /**
  123. * 获取订单号
  124. */
  125. private fun getOrderId(activity: Activity) {
  126. SdkRequest.instance.createOrder(activity, chargeInfo, object : IRequestCallback {
  127. override fun onResponse(resultInfo: ResultInfo) {
  128. if (resultInfo.code == 1 && !TextUtils.isEmpty(resultInfo.data)) {
  129. try {
  130. val jsonObject = JSONObject(resultInfo.data)
  131. chargeInfo.orderId = jsonObject.getString("order_id")
  132. Logger.d("order_id ---> ${chargeInfo.orderId}")
  133. //获取订单号成功,初始化IAB收银台客户端
  134. Logger.d("获取订单号成功,初始化IAB收银台客户端")
  135. initializeBillingClient(activity)
  136. connectGooglePlay(activity, false)
  137. } catch (e: JSONException) {
  138. e.printStackTrace()
  139. dismissDialog()
  140. val msg = if (TextUtils.isEmpty(resultInfo.msg)) {
  141. ResUtils.getResString(activity, "yyxx_charge_tv_error")
  142. } else {
  143. resultInfo.msg
  144. }
  145. ToastUtils.toastInfo(activity, msg)
  146. implCallback.onResult(-1, "获取订单异常")
  147. }
  148. } else {
  149. dismissDialog()
  150. val msg = if (TextUtils.isEmpty(resultInfo.msg)) {
  151. ResUtils.getResString(activity, "yyxx_charge_tv_error")
  152. } else {
  153. resultInfo.msg
  154. }
  155. ToastUtils.toastInfo(activity, msg)
  156. implCallback.onResult(-1, "获取订单异常")
  157. }
  158. }
  159. })
  160. }
  161. /**
  162. * 查询商品信息
  163. */
  164. private fun querySkuDetails(activity: Activity) {
  165. val skus = mutableListOf<String>()
  166. skus.add(chargeInfo.productId)
  167. val params = SkuDetailsParams.newBuilder().setType(BillingClient.SkuType.INAPP).setSkusList(skus).build()
  168. billingClient?.querySkuDetailsAsync(params) { billingResult, list ->
  169. logBillingResult("onSkuDetailsResponse", billingResult)
  170. dismissDialog()
  171. if (list.isNullOrEmpty()) {
  172. //查询商品信息失败
  173. Logger.e("查询商品信息失败")
  174. ToastUtils.toastInfo(activity, ResUtils.getResString(activity, "yyxx_charge_tv_error"))
  175. implCallback.onResult(-1, "查询商品信息失败")
  176. disConnection()
  177. } else {
  178. if (list.size == 1) {
  179. val skuDetails = list[0]
  180. Logger.d("product id : ${skuDetails.sku}")
  181. launchBillingFlow(activity, skuDetails)
  182. } else {
  183. ToastUtils.toastInfo(activity, ResUtils.getResString(activity, "yyxx_charge_tv_error"))
  184. implCallback.onResult(-1, "查询商品信息异常")
  185. disConnection()
  186. }
  187. }
  188. }
  189. }
  190. /**
  191. * 启动收银台
  192. *
  193. */
  194. private fun launchBillingFlow(activity: Activity, skuDetails: SkuDetails) {
  195. billingClient?.apply {
  196. if (isReady) {
  197. val flowParams = BillingFlowParams.newBuilder().setObfuscatedAccountId(chargeInfo.orderId).setSkuDetails(skuDetails).build()
  198. val billingResult = launchBillingFlow(activity, flowParams)
  199. logBillingResult("launchBillingFlow", billingResult)
  200. if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
  201. dismissDialog()
  202. disConnection()
  203. ToastUtils.toastInfo(activity, ResUtils.getResString(activity, "yyxx_charge_tv_error"))
  204. implCallback.onResult(-1, "启动谷歌收银台失败")
  205. }
  206. } else {
  207. dismissDialog()
  208. disConnection()
  209. ToastUtils.toastInfo(activity, ResUtils.getResString(activity, "yyxx_charge_tv_error"))
  210. implCallback.onResult(-1, "启动谷歌收银台失败")
  211. }
  212. }
  213. }
  214. /**
  215. * 通知服务端发货
  216. */
  217. private fun notifyOrder2Backend(activity: Activity, orderId: String, purchase: Purchase, isCache: Boolean = false) {
  218. SdkRequest.instance.notifyOrder(activity, orderId, purchase.originalJson, object : IRequestCallback {
  219. override fun onResponse(resultInfo: ResultInfo) {
  220. if (resultInfo.code == 1) {
  221. //消耗订单
  222. consumeAsync(activity, purchase, isCache)
  223. } else {
  224. //失败则缓存订单
  225. saveOrderInfo(purchase.orderId, orderId)
  226. dismissDialog()
  227. disConnection()
  228. val msg = if (TextUtils.isEmpty(resultInfo.msg)) {
  229. ResUtils.getResString(activity, "yyxx_charge_tv_error")
  230. } else {
  231. resultInfo.msg
  232. }
  233. ToastUtils.toastInfo(activity, msg)
  234. implCallback.onResult(-1, "发货失败")
  235. }
  236. }
  237. })
  238. }
  239. /**
  240. * 消耗缓存订单
  241. */
  242. private fun consumeCacheOrder(activity: Activity, purchase: Purchase) {
  243. //消耗完了再发起支付
  244. Logger.d("消耗缓存订单 : $purchase")
  245. var orderId = ""
  246. purchase.accountIdentifiers?.apply {
  247. orderId = if (!TextUtils.isEmpty(obfuscatedAccountId)) {
  248. obfuscatedAccountId!!
  249. } else {
  250. //TODO getLocalOrderId
  251. getOrderInfo(purchase.orderId)
  252. }
  253. }
  254. notifyOrder2Backend(activity, orderId, purchase, true)
  255. // consumeAsync(activity, purchase.purchaseToken, true)
  256. }
  257. /**
  258. * 消耗订单
  259. */
  260. private fun consumeAsync(activity: Activity, purchase: Purchase, isCache: Boolean = false) {
  261. val consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build()
  262. billingClient?.apply {
  263. if (isReady) {
  264. consumeAsync(consumeParams) { billingResult, token ->
  265. logBillingResult("onConsumeResponse", billingResult)
  266. dismissDialog()
  267. if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
  268. // saveOrder2Local()
  269. if (isCache) {
  270. querySkuDetails(activity)
  271. } else {
  272. implCallback.onResult(0, "支付成功")
  273. disConnection()
  274. }
  275. removeOrderInfo(purchase.orderId)
  276. } else {
  277. implCallback.onResult(-1, "消耗订单异常")
  278. disConnection()
  279. ToastUtils.toastInfo(activity, ResUtils.getResString(activity, "yyxx_charge_tv_error"))
  280. }
  281. }
  282. } else {
  283. dismissDialog()
  284. disConnection()
  285. implCallback.onResult(-1, "消耗订单异常")
  286. ToastUtils.toastInfo(activity, ResUtils.getResString(activity, "yyxx_charge_tv_error"))
  287. }
  288. }
  289. }
  290. private fun saveOrderInfo(googleOrderId: String, orderId: String) {
  291. try {
  292. MMKVUtils.instance.orderKV.encode(googleOrderId, orderId)
  293. } catch (e: JSONException) {
  294. e.printStackTrace()
  295. }
  296. }
  297. private fun getOrderInfo(googleOrderId: String): String {
  298. val info = MMKVUtils.instance.orderKV.decodeString(googleOrderId)
  299. Logger.d("getOrderInfo : $info")
  300. return if (TextUtils.isEmpty(info)) {
  301. ""
  302. } else {
  303. info!!
  304. }
  305. }
  306. private fun removeOrderInfo(googleOrderId: String) {
  307. MMKVUtils.instance.orderKV.removeValueForKey(googleOrderId)
  308. }
  309. companion object {
  310. val instance: ChargeImpl by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
  311. ChargeImpl()
  312. }
  313. }
  314. }