Problem setting up in app billing error checking for billing v3 support

Hello, I get the error mentioned in the title on Android. ios, however, works fine. system info OSX Mojave 10.14.15 Cordova 9.0.0 Android Platform: 8.0.0 Device:LG G3 // Android 5.1 Plugin Version:...

Hello,

I get the error mentioned in the title on Android. ios, however, works fine.

system info

OSX Mojave 10.14.15
Cordova 9.0.0
Android Platform: 8.0.0
Device:LG G3 // Android 5.1
Plugin Version: 8.1.1

Expected behavior

store.when(‘product’).updated(handler_function) fires and handler_function executes.
I have 2 non-consumables.

Observed behavior

The store.when(«product»).updated(function (p) { renderIAP(p); }) never fires, so my products never load. The error in the title seems to be the cause. Funny thing, if i try to call the refresh methodagain, the updated event fires but the product never becomes «loaded». so I still can’t see my products.

Here’s Android Studio’s log [THERE’S A SHORTER CHROME LOG BELOW]:
07-20 17:24:48.735 528-528/com.tareqdayya.sightreading D/CordovaWebViewImpl: onPageFinished(file:///android_asset/www/index.html)
07-20 17:24:49.567 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Num SKUs Found: 2
07-20 17:24:49.567 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Product SKU Added: clfalt012
07-20 17:24:49.567 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Product SKU Added: clf01bas
07-20 17:24:49.567 528-636/com.tareqdayya.sightreading D/CordovaPurchase: init start
07-20 17:24:49.568 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Creating IAB helper.
07-20 17:24:49.569 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Starting setup.
07-20 17:24:49.570 528-636/com.tareqdayya.sightreading D/IabHelper: Starting in-app billing setup.
07-20 17:24:49.574 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/js/script.js: Line 1703 : registerProducts
07-20 17:24:49.581 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1703)] «registerProducts», source: file:///android_asset/www/js/script.js (1703)
07-20 17:24:49.581 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘clfalt012 registered’
07-20 17:24:49.581 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘clfalt012 registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘Alto Clef registered’
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘Alto Clef registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘non consumable registered’
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘non consumable registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘registered’
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘clfalt012 updated’
07-20 17:24:49.582 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘clfalt012 updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘Alto Clef updated’
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘Alto Clef updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘non consumable updated’
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘non consumable updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘updated’
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘clf01bas registered’
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘clf01bas registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.583 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘Bass Clef registered’
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘Bass Clef registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘non consumable registered’
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘non consumable registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘registered’
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘registered'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘clf01bas updated’
07-20 17:24:49.584 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘clf01bas updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘Bass Clef updated’
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘Bass Clef updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘non consumable updated’
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘non consumable updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.queries !! ‘updated’
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.queries !! ‘updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: queries ++ ‘updated’
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: queries ++ ‘updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.585 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: queries ++ ‘Alto Clef approved’
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: queries ++ ‘Alto Clef approved'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: queries ++ ‘Bass Clef approved’
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: queries ++ ‘Bass Clef approved'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: queries ++ ‘Alto Clef updated’
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: queries ++ ‘Alto Clef updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: queries ++ ‘Bass Clef updated’
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: queries ++ ‘Bass Clef updated'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: store.trigger -> triggering action refreshed
07-20 17:24:49.586 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: store.trigger -> triggering action refreshed», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.587 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 1641 : [store.js] DEBUG: queries !! ‘refreshed’
07-20 17:24:49.587 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1641)] «[store.js] DEBUG: queries !! ‘refreshed'», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (1641)
07-20 17:24:49.587 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 2176 : InAppBilling[js]: setup ok
07-20 17:24:49.587 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(2176)] «InAppBilling[js]: setup ok», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (2176)
07-20 17:24:49.589 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 2176 : InAppBilling[js]: load [«clfalt012″,»clf01bas»]
07-20 17:24:49.590 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(2176)] «InAppBilling[js]: load [«clfalt012″,»clf01bas»]», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (2176)
07-20 17:24:49.594 528-636/com.tareqdayya.sightreading W/PluginManager: THREAD WARNING: exec() call to InAppBillingPlugin.init blocked the main thread for 23ms. Plugin should use CordovaInterface.getThreadPool().
07-20 17:24:49.617 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/js/script.js: Line 1476 : Uncaught TypeError: Cannot read property ‘openDatabase’ of undefined
07-20 17:24:49.617 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1476)] «Uncaught TypeError: Cannot read property ‘openDatabase’ of undefined», source: file:///android_asset/www/js/script.js (1476)
07-20 17:24:49.618 528-528/com.tareqdayya.sightreading D/IabHelper: Billing service connected.
07-20 17:24:49.619 528-528/com.tareqdayya.sightreading D/IabHelper: Checking for in-app billing 3 support.
07-20 17:24:49.693 528-528/com.tareqdayya.sightreading D/CordovaPurchase: Setup finished.
07-20 17:24:49.705 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/js/script.js: Line 1769 : ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)
07-20 17:24:49.705 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1769)] «ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)», source: file:///android_asset/www/js/script.js (1769)
07-20 17:24:52.447 528-569/com.tareqdayya.sightreading V/AudioTrack: ~AudioTrack, releasing session id from 528 on behalf of 528
07-20 17:24:54.714 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 2176 : InAppBilling[js]: setup ok
07-20 17:24:54.714 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(2176)] «InAppBilling[js]: setup ok», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (2176)
07-20 17:24:54.714 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js: Line 2176 : InAppBilling[js]: load [«clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»]
07-20 17:24:54.714 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(2176)] «InAppBilling[js]: load [«clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»]», source: file:///android_asset/www/plugins/cordova-plugin-purchase/www/store-android.js (2176)
07-20 17:24:54.715 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Num SKUs Found: 4
07-20 17:24:54.715 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Product SKU Added: clfalt012
07-20 17:24:54.715 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Product SKU Added: clf01bas
07-20 17:24:54.715 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Product SKU Added: clfalt012
07-20 17:24:54.715 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Product SKU Added: clf01bas
07-20 17:24:54.715 528-636/com.tareqdayya.sightreading D/CordovaPurchase: init start
07-20 17:24:54.716 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Creating IAB helper.
07-20 17:24:54.716 528-636/com.tareqdayya.sightreading D/CordovaPurchase: Starting setup.
07-20 17:24:54.716 528-636/com.tareqdayya.sightreading D/IabHelper: Starting in-app billing setup.
07-20 17:24:54.724 528-528/com.tareqdayya.sightreading D/IabHelper: Billing service connected.
07-20 17:24:54.724 528-528/com.tareqdayya.sightreading D/IabHelper: Checking for in-app billing 3 support.
07-20 17:24:54.731 528-528/com.tareqdayya.sightreading D/CordovaPurchase: Setup finished.
07-20 17:24:54.734 528-528/com.tareqdayya.sightreading D/SystemWebChromeClient: file:///android_asset/www/js/script.js: Line 1769 : ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)
07-20 17:24:54.734 528-528/com.tareqdayya.sightreading I/chromium: [INFO:CONSOLE(1769)] «ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)», source: file:///android_asset/www/js/script.js (1769)

Steps to reproduce

Here’s also the log from chrome at DEBUG verbosity; it’s easier to read:

SQLitePlugin.js:175 OPEN database: app.db
SQLitePlugin.js:179 OPEN database: app.db — OK
script.js:1476 DB Opened
script.js:1516 ADD: clefs with value of TT
script.js:1524 UPDATED: clefs with value of TT
script.js:1516 ADD: tempo with value of A
script.js:1524 UPDATED: tempo with value of A
script.js:1516 ADD: frthstLvl with value of 1
script.js:1516 ADD: level with value of 1
script.js:1538 GET: option frthstLvl, index 0 with value of: 1
script.js:1524 UPDATED: level with value of 1
script.js:1516 ADD: key with value of C
script.js:1524 UPDATED: key with value of C
script.js:1516 ADD: inappAA with value of 0
script.js:1538 GET: option inappAA, index 0 with value of: 0
script.js:1516 ADD: inappBB with value of 0
script.js:1538 GET: option inappBB, index 0 with value of: 0
script.js:1505 count in rows: 7
script.js:1702 registerProducts
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘clfalt012 registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘Alto Clef registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘non consumable registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘clfalt012 updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘Alto Clef updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘non consumable updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘clf01bas registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘Bass Clef registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘non consumable registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘registered’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘clf01bas updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘Bass Clef updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘non consumable updated’
store-android.js:1641 [store.js] DEBUG: store.queries !! ‘updated’
store-android.js:1641 [store.js] DEBUG: queries ++ ‘updated’
store-android.js:1641 [store.js] DEBUG: queries ++ ‘Alto Clef approved’
store-android.js:1641 [store.js] DEBUG: queries ++ ‘Bass Clef approved’
store-android.js:1641 [store.js] DEBUG: queries ++ ‘Alto Clef updated’
store-android.js:1641 [store.js] DEBUG: queries ++ ‘Bass Clef updated’
store-android.js:1641 [store.js] DEBUG: store.trigger -> triggering action refreshed
store-android.js:1641 [store.js] DEBUG: queries !! ‘refreshed’
store-android.js:2176 InAppBilling[js]: setup ok
store-android.js:2176 InAppBilling[js]: load [«clfalt012″,»clf01bas»]
script.js:1768 ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)
store-android.js:2176 InAppBilling[js]: setup ok
store-android.js:2176 InAppBilling[js]: load [«clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»]
script.js:1768 ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)
store-android.js:2176 InAppBilling[js]: setup ok
store-android.js:2176 InAppBilling[js]: load [«clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»]
script.js:1768 ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)
store-android.js:2176 InAppBilling[js]: setup ok
store-android.js:2176 InAppBilling[js]: load [«clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»]
script.js:1768 ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)
store-android.js:2176 InAppBilling[js]: setup ok
store-android.js:2176 InAppBilling[js]: load [«clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»,»clfalt012″,»clf01bas»]
script.js:1768 ERROR 6777001: Init failed — Problem setting up in-app billing: IabResult: Error checking for billing v3 support. (response: 3:Error)

Following is my code called from the device-ready handler. I’ve tried calling it from other places after the device ready event fires:

if (!window.store) {
        console.log('Store not available');
        return;
    }

    store.verbosity = store.DEBUG;   

    // Inform the store of your products
    store.register({
        id: FIRST_PRODUCT_ID,
        alias: 'Alto Clef',
        type: store.NON_CONSUMABLE
    });
    store.register({
        id: SECOND_PRODUCT_ID, 
        alias: 'Bass Clef',
        type: store.NON_CONSUMABLE
    });

    // When any product gets updated, refresh the HTML.
    store.when("product").updated(function (p) {
        renderIAP(p);
    });

    // when alto unlock is approved for purchase
    store.when("Alto Clef").approved(function (order) {
        console.log('You just unlocked the ALTO CLEF!');
        order.finish();
    });

    // when bass unlock is approved for purchase
    store.when("Bass Clef").approved(function (order) {
        console.log('You just unlocked the Bass CLEF!');
        order.finish();
    });

    // alto owned
    store.when("Alto Clef").updated(function (product) {
       // do something
    });

    // bass owned
    store.when("Bass Clef").updated(function (product) {
       // do something
    });

   
    store.ready(function () {
        console.log('Store Ready');
        // activate refresh button for restoring purchases
        $('#rfrshPrchs button').removeAttr('disabled').removeClass('disabled').click(() => {
            store.refresh();
        });
    });

    store.refresh();

    store.error(function(error) {
        console.log('ERROR ' + error.code + ': ' + error.message);
    });

    function renderIAP(prdct) {
        // array of labels
        let parts = prdct.id.split(".");

        // find element in DOM
        let elId = parts[parts.length - 1];
        let el = document.getElementById(elId + '-purchase');
        if (navigator.userAgent.includes('Android')) el = document.getElementById(elId.toUpperCase() + '-purchase');
        if (!el) return;
        // not loaded
        if (!prdct.loaded) {
            el.innerHTML = '<h5>Loading ...</h5>';
        } else if (!prdct.valid) {
            el.innerHTML = '<h5>' + prdct.alias + ' Invalid</h5>';
        } else if (prdct.valid) {
            // paragraph with Spinner for when button disappears after clicking it
            let html = "<p class='font-weight-bold col-8'>" + prdct.title + " - " + "<span class='font-weight-normal'>" + prdct.description + "</span>" + "</p>";
            html += `<span class="col-3 text-center"><img class="w-50" src="img/Spinner-1s-200px.svg" alt="loading"></span>`;
            if (prdct.canPurchase) {
                // buy button
                html = "<p class='font-weight-bold col-8'>" + prdct.title + " - " + "<span class='font-weight-normal'>" + prdct.description + "</span>" + "</p>"
                    + "<button class='btn btn-dark col-3 text-center' id='buy-" + prdct.id + "' data-productid='" + prdct.id + "' type='button'>" + prdct.price + "</button>";
            }
            // owned
            if (prdct.owned) {
                // owned tick
                html = "<p class='font-weight-bold col-8'>" + prdct.title + " - " + "<span class='font-weight-normal'>" + prdct.description + "</span>" + "</p>"
                    + `<span class="col-3 text-center">✓</span>`   // this is a tick
            }
            el.innerHTML = html;
            // buy btn listener
            if (prdct.canPurchase) {
                document.getElementById("buy-" + prdct.id).onclick = function () {
                    let pid = $(this).data('productid');
                    store.order(pid);
                };
            }
        }
    }

Я использую код InApp V3 для покупок в моем приложении, я получаю эту ошибку BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE и error value :3, когда учетная запись Google недоступна на устройстве. Я хочу знать, есть ли другие возможности получить эту ошибку, потому что, когда я получаю эту ошибку, мне нужно показать пользователю всплывающее окно с некоторыми данными. Если это вызвано тем, что учетная запись Google недоступна на устройстве, я покажу диалоговое окно со связанным текстом. это код, который я использую

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
    public void onIabSetupFinished(IabResult result) {              
        if (!result.isSuccess()) {
                // error here               
            return;
        }
    }
});

Это ошибка Error checking for billing v3 support. (response: 3:Billing Unavailable)

8 ответов

Лучший ответ

Как мы видим непосредственно в код настройки IabHElper образца, предоставленного Google, ошибка означает:

«Платежная служба недоступна на устройстве».

Как вы можете прочитать здесь, эта ошибка означает

Billing API version is not supported for the type requested

Это Справочник по биллингу в приложении (IAB версии 3), поэтому ошибка означает, что IAB v3 не установлен на устройстве.

Фактически это означает, что у пользователя есть учетная запись Google и, вероятно, также служба биллинга в приложении, но у нее нет последней версии. Это происходит на старых устройствах, где пользователь никогда ничего не обновляет. Раньше это были устройства, на которых вы могли видеть старое приложение Market вместо приложения Play.

Таким образом, ошибка, которую вы должны показать пользователю, и тест, который вы должны выполнить, заключается не в том, есть ли на устройстве учетная запись Google, а в том, что на нем установлены и должным образом обновлены службы Google Play.

Если вы ищете код во всех библиотечных SDK и вспомогательных классах, предоставляемых Google, единственное место, где мы можем найти это именно в вызываемой вами функции: startSetup из IabHelper класс

Intent serviceIntent = new Intent(
                "com.android.vending.billing.InAppBillingService.BIND");
        if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0)
                .isEmpty()) {
            // service available to handle that Intent
            mContext.bindService(serviceIntent, mServiceConn,
                    Context.BIND_AUTO_CREATE);
        } else {
            // no service available to handle that Intent
            mServiceConn=null;
            if (listener != null) {
                listener.onIabSetupFinished(new IabResult(
                        BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
                        "Billing service unavailable on device."));
            }
        }

Это означает, что приложению не удалось подключиться к службе на устройстве, поскольку диспетчер пакетов даже не знает об этом . Это единственный вариант, который может вызвать эту ошибку. И что это значит, что он не смог подключиться к услуге? Это означает одно из следующих:

  • На устройстве не установлена ​​служба.
  • У него старая версия, поскольку мы знаем, что в последних версиях игрового магазина используется IAB v3.

Итак, ваша ошибка может означать только одно из них, что для вас означает, что вы должны показать пользователю сообщение типа «У вас не установлены службы Google Play или вам необходимо обновить их». И нет других возможностей получить эту ошибку.

Но, если вы хотите, чтобы пользователям было проще, вы можете сказать, что им нужно обновить приложение Google Play до последней версии . И это заставит все работать как шарм.


38

Null
16 Апр 2021 в 11:42

Эта ошибка также возникает после того, как пользователь удаляет свою учетную запись Google с устройства.


17

pareshgoel
30 Июл 2014 в 14:22

Войдите в Play Маркет с любой учетной записью.

После входа в систему биллинг работает даже на Android Studio Emulator.


8

Babken Vardanyan
7 Апр 2018 в 09:58

Основная вероятность вашей проблемы BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE заключается в том, что в некоторых странах покупка в приложении по-прежнему не разрешена, например в Сербии и многих других странах.

Таким образом, любой пользователь из страны, где Google Play не поддерживает покупку в приложении, получит BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE.

Это будет меньше шансов, что у пользователя не установлена ​​обновленная служба Google Play на телефоне, поэтому не беспокойтесь об этом случае.


7

N Sharma
22 Янв 2014 в 10:52

Попробуйте эти хаки

Удалите кеш, данные и обновления для приложения для воспроизведения, перезапустите приложение для воспроизведения снова, оно должно работать! или попробуйте установить последние обновления (если они исправлены)

FYI :

Только имейте в виду:

  • IABv3 встроен в сервисы Google Play, поэтому требуется обновление до APK Play Services.

  • Версия V2 была встроена в сам клиент магазина Play. Проблема с кешем / лицензионным соглашением не имеет прямого отношения к IAB, это связано с настройкой Play Сервисы (и, надеюсь, не нужны большинству пользователей, часть кеша в любом случае).

  • Обидно быть в этот переходный период, когда у Play Services есть быть обновленным, прежде чем ваше приложение сможет использовать это в своих интересах, но я думаю большинство людей согласятся, что это лучше, чем ждать ОС Обновить.’

См. Эту ссылку: отчет об ошибке, сообщение в Google+


2

LOG_TAG
21 Янв 2014 в 12:20

Иногда, если вы не аутентифицировали свое устройство с помощью учетной записи Google, может появиться эта ошибка.


1

User2364902
7 Дек 2016 в 07:21

Для тех, кто все еще сталкивается с этой проблемой, в большинстве случаев iab не поддерживается в вашей стране, как сказал Уильямс. Вы можете использовать VPN, чтобы заставить его работать.


1

Drilon Blakqori
4 Апр 2017 в 00:33

Если вы находитесь в Тонге (как я) или в любой другой стране, где вы не можете покупать приложения в Google Play, вы должны использовать VPN для проверки биллинга. В противном случае вы получите эту ошибку.


0

John T
1 Июл 2019 в 08:44

Я использую код InApp V3 для покупок в приложении в моем приложении, я получаю эту ошибку BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE и error value :3 когда учетная запись Google недоступна в устройстве. Я хочу знать, есть ли другие возможности получить эту ошибку, потому что, когда я получаю эту ошибку, мне нужно показать всплывающее окно пользователю с некоторыми данными. Если это вызывает из-за учетной записи Google, недоступной на устройстве, я покажу Диалог со связанным текстом. это код, который я использую

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
    public void onIabSetupFinished(IabResult result) {              
        if (!result.isSuccess()) {
                // error here               
            return;
        }
    }
});

это ошибка Error checking for billing v3 support. (response: 3:Billing Unavailable)

7 ответов


как мы можем видеть непосредственно в код установки из iabhelper образца, предоставленного google, ошибка означает:

» служба выставления счетов недоступна на устройстве.»

как вы можете прочитать здесь, что значит

Billing API version is not supported for the type requested

это Справочник по выставлению счетов в приложении (IAB версии 3), поэтому ошибка означает, что IAB v3 не установлен на устройстве.

на самом деле это означает, что пользователь имеет учетная запись google, а также, вероятно, в приложении биллинговой службы, но он не имеет последнюю версию. Это происходит в старых устройствах, и там, где пользователь никогда ничего не обновляет, он использует устройства, где вы можете увидеть старое приложение Market вместо приложения Play.

таким образом, ошибка, которую вы должны показать пользователю, и тест, который вы должны выполнить, не является, если устройство имеет Google acount, но если у него установлены службы google play и правильно усовершенствованный.

обновление:

если вы ищете код во всех библиотеках SDK и вспомогательных классах, предоставляемых google, единственное место, где мы можем найти это именно в функции, которую вы вызываете:startSetup на IabHelper класс

Intent serviceIntent = new Intent(
                "com.android.vending.billing.InAppBillingService.BIND");
        if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0)
                .isEmpty()) {
            // service available to handle that Intent
            mContext.bindService(serviceIntent, mServiceConn,
                    Context.BIND_AUTO_CREATE);
        } else {
            // no service available to handle that Intent
            mServiceConn=null;
            if (listener != null) {
                listener.onIabSetupFinished(new IabResult(
                        BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
                        "Billing service unavailable on device."));
            }
        }

Это означает, что приложение не может подключиться к службе в устройства, так как менеджер пакетов даже не знаю, что это. Это единственный вариант, который может вызвать эту ошибку. И что это значит? не удалось подключиться к сервису? Это означает одно из следующих:

  • на устройстве не установлена служба.
  • он имеет старую версию, так как мы знаем, что последние версии play store, использует IAB v3.

таким образом, ваша ошибка может означать только одно из этого, что для вас означает, что вы должны показать mesagge пользователю, как «у вас нет Google play services установлен, или вы должны обновить его». И нет других возможностей или получения эта ошибка.

но, если вы хотите сделать его проще для пользователей, вы можете сказать, что им нужно обновить приложение Google Play до последней версии. И тогда все будет работать как по маслу.


эта ошибка также появляется после того, как пользователь удаляет свою учетную запись google с устройства.


в основном возможность вашего вопроса BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE Это то, что в некоторых странах все еще в покупке приложения не разрешено, как Сербия и многие страны.

Так любой пользователь из страны, где Google Play не поддерживает в покупке приложения, то вы получите BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE.

это было бы меньше шансов, что пользователь не обновил сервис Google Play, установленный на телефоне, поэтому не беспокойтесь об этом случае.


попробуйте это хаки

удалить кэш, данные и обновления для приложения play, перезапустить приложение play снова он должен работать! или попробуйте с последними обновлениями (если он исправлен)

FYI:

Только имейте в виду:

  • IABv3 встроен в сервисы Google Play, поэтому требуется обновление
    игра услуги apk.

  • V2 был встроен в сам клиент Play store. Проблема кэша/EULA
    не в частности, связанные с IAB, это связано с настройкой игры
    Сервисы (и, надеюсь, не нужны для большинства пользователей, часть кэша
    в любом случае.)

  • это облом, чтобы быть в этот переходный период, когда Play Services имеет
    чтобы быть обновленным, прежде чем ваше приложение сможет воспользоваться этим, но я думаю
    большинство людей согласятся, что это лучше, чем ждать ОС
    модернизировать.’

посмотреть этот код: сообщить об ошибке, G+ пост


когда-нибудь, если вы не прошли проверку подлинности устройства с учетной записью Google, может появиться эта ошибка.


войдите в Play Store с любой учетной записью.

после входа в биллинг работает даже на эмуляторе Android Studio.

1

автор: Babken Vardanyan


для тех, кто все еще сталкивается с этой проблемой, в большинстве случаев iab не поддерживается в вашей стране, как сказал Уильямс. Вы можете использовать VPN, чтобы заставить его работать.


【Ссылки】

https://blog.csdn.net/liuweiballack/article/details/50331909

https://blog.csdn.net/change_from_now/article/details/36668017

I. Обзор

При использовании In-app Billing наше приложение получает доступ к In-app Billing service через соответствующий API. Приложение не взаимодействует напрямую с сервером Google Play. Вместо этого оно отправляет запрос на расчет в Google Play и получает ответ от Google Play посредством межпроцессного взаимодействия (IPC).

Перед использованием In-app Billing необходимо обратить внимание на следующие моменты:

  • Может использоваться только для продажи цифрового контента, а не физических товаров
  • После покупки приложение не может быть возвращено Google
  • Play не обеспечивает доставку контента, разработчикам необходимо самостоятельно доставлять цифровой контент, приобретенный в приложении.
  • Одно приложение не может покупать продукты, опубликованные другим приложением

В настоящее время используется In-app Billing версии 3, которая должна работать на Android 2.2 или выше и требует, чтобы на устройстве была установлена ​​последняя версия магазина Google Play. Третье издание поддерживает два типа продуктов: управляемый продукт и подписку.

Продукты для размещенных приложений: Товары, информация о владении которыми отслеживается и управляется Google Play. После покупки управляемых товаров необходимо отправить запрос на потребление в Google Play для потребления, прежде чем пользователи смогут снова их приобрести. Чтобы
подписка: Позволяет разработчикам продавать контент, услуги или функции пользователям в приложении посредством ежемесячного или годового выставления счетов, при этом подписки не могут быть использованы.

2. Процесс покупки в Google Play

Когда начинается покупка, приложение должно отправить запрос на оплату соответствующего продукта внутри приложения. Затем Google Play обработает все детали оформления заказа для этой транзакции, включая запрос и проверку способов оплаты и обработку финансовых транзакций. Чтобы
Когда процесс оформления заказа будет завершен, Google Play отправит данные о покупке приложения, такие как номер заказа, дату и время заказа, а также уплаченную цену. Приложению не нужно иметь дело с какими-либо финансовыми транзакциями, за эти вопросы несет полную ответственность Google Play.

Конкретный процесс выглядит следующим образом:

  1. Приложение отправляет запрос isBillingSupported в Google Play, чтобы определить, поддерживается ли целевая версия используемого в настоящее время API биллинга в приложении;
  2. При запуске или когда пользователь входит в систему, запросите Google Play, чтобы определить, какими продуктами владеет пользователь, и отправьте запрос getPurchases;
  3. Сообщите пользователю, доступен ли продукт для покупки, и отправьте запрос getSkuDetails;
  4. Предложите пользователю приобрести и отправьте запрос getBuyIntent.

3. Потребляйте управляемые продукты, продаваемые через приложение.

После продажи продукта внутри приложения он считается «принадлежащим». Контент для продажи в приложениях в состоянии «принадлежит» больше нельзя покупать в Google Play. Прежде чем Google Play снова сделает их доступными для покупки, необходимо отправить запрос на потребление. Потребление продуктов в приложении вернет их в статус «не принадлежит» и удалит данные о предыдущих покупках. Чтобы
Запрос на потребление осуществляется путем отправки consumerPurchase.

Подготовка перед доступом

1. Создайте приложение в консоли разработчика Google Play.

  1. Зарегистрируйте учетную запись разработчика, следуйте инструкциям, чтобы привязать кредитную карту и заплатить 25 долларов;
  2. Создайте новое приложение и введите название приложения;
  3. Щелкните «Подготовить список магазинов» на рисунке выше и введите основную информацию о приложении, как это предлагается. Информация должна быть заполнена полностью, иначе приложение не может быть опубликовано в обычном режиме;
  4. В Сервисах и API найдите лицензионный ключ и запишите его.

2. Добавьте файлы библиотеки биллинга в приложении (например, Android Studio).

  1. Найдите файл IInAppBillingService.aidl в yourSDKpath / extras / google / market_billing /, создайте папку aidl в src / main, а затем создайте пакет com.android.vending.billing, как показано на рисунке.
  2. Нажмите, чтобы выполнить перестройку, и увидите файл IInAppBillingService.java в позиции, показанной на рисунке ниже, то есть импорт успешен.
  3. Скопируйте весь пакет утилит из market_billing / samples / TrivialDrive в проект.

В-третьих, объявите разрешения

Объявите разрешения в AndroidManifest.xml:

<uses-permission android:name="com.android.vending.BILLING" />

В-четвертых, инициализируйте соединение с Google Play

1. Инициализируйте IabHelper.

IabHelper mHelper;

@Override
public void onCreate(Bundle savedInstanceState) {
    // ...
    String base64EncodedPublicKey;

    // compute your public key and store it in base64EncodedPublicKey
    mHelper = new IabHelper(this, base64EncodedPublicKey);
}

2. Вызовите метод startSetup.

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
@Override
public void onIabSetupFinished(IabResult result) {
    if (!result.isSuccess()) {
        Log.e(TAG, "Problem setting up In-app Billing: " + result);
    }

    if (mHelper == null) {
        return;
    }
}
});

3. Отключите действие onDestory ()

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mHelper != null) {
        mHelper.dispose();
    }
    mHelper = null;
}

Создавайте покупки в приложении

1. Загрузите APK и опубликуйте

Войдите в панель APK и загрузите файл APK с подписью. Файл можно загрузить в любой из следующих разделов: ПРОИЗВОДСТВО, БЕТА-ТЕСТИРОВАНИЕ, АЛЬФА-ТЕСТИРОВАНИЕ; различия между ними показаны в Приложении 1. В следующем процессе в качестве примера используется БЕТА-тест.

  1. Зайдите на страницу БЕТА-ТЕСТИРОВАНИЯ и загрузите APK-файл с подписью;
  2. Войдите на страницу рейтинга контента, заполните анкету с рейтингом контента, завершите оценку, примите оценку на основе результатов или заполните заново;
  3. Перейдите на страницу «Цены и распространение», выберите платное или бесплатное приложение, а затем выберите страну и регион, где будет выпущен продукт;
  4. Убедитесь, что все четыре содержимого, как показано на рисунке, отображаются в виде зеленой галочки, после чего вы можете опубликовать приложение;
  5. Нажмите Опубликовать приложение, чтобы опубликовать приложение. Если кнопка неактивна, вы можете нажать Почему я не могу опубликовать? Проверьте контент, который нужно добавить, подождите несколько часов после выпуска приложения. Вы можете просмотреть статус публикации приложения на любой странице. Изображение слева показывает, что оно публикуется, а изображение справа показывает, что оно было успешно опубликовано. Чтобы
      

2. Выберите метод тестирования (например, БЕТА-тест).

После выпуска приложения выберите метод тестирования. Существует три метода тестирования (подробности см. В приложении). В качестве примера мы рассмотрим закрытое бета-тестирование.

  1. Войдите в консоль разработчика Google Play.
  2. Выберите соответствующее приложение, нажмите APK в левом меню.
  3. Выберите Бета-тест и введите «Закрытый тест».
  4. Создайте список или выберите существующий список
  5. Отправьте ссылку в URL-адресе согласия тестеру
  6. Тестировщикам необходимо нажать на тестовое соединение и выбрать присоединиться к тесту.

Примечание. Вы можете выбрать, требуется ли для тестовой учетной записи реальный платеж во время тестирования. Например, когда приложение проходит крупномасштабное тестирование, пользователь должен иметь возможность выполнять реальные платежные операции, а внутренние тестировщики могут выполнять тестирование платежей, но реальной оплаты не требуется. Вы можете настроить внутреннюю тестовую учетную запись на серверную часть Google Play. Чтобы
Конфигурация следующая:

Введите номер счета на картинке ниже

В-третьих, создайте продукт

  1. Нажмите «Товары для продажи» в меню слева и выберите «Добавить новый продукт».
  2. Версия 3 для биллинга через приложение поддерживает продукты «Управляемый продукт» и «Подписка».
  3. При настройке идентификатора продукта убедитесь, что он уникален. Идентификатор продукта также называется SKU. Идентификатор продукта необходимо использовать при запросе и покупке продуктов.
  4. Завершив с информацией о продукте, установите его статус на активный

Реализация контента, связанного с клиентом

Чтобы узнать о конкретном содержании реализации клиента, обратитесь к официальной демонстрации, вот только краткий пример (пример кода не является полным, см. Демонстрацию).

1. Запросить подробную информацию о продукте.

Через идентификатор продукта, добавляемый при создании продукта, сведения о продукте могут быть запрошены на стороне клиента и вызвать queryInventoryAsync (boolean, List, QueryInventoryFinishedListener)

  • логическое значение: следует ли возвращать сведения о продукте (здесь должно быть установлено значение true)
  • Список: идентификаторы продуктов (артикулы) для запроса
  • QueryInventoryFinishedListener: отслеживание результатов запроса

Если запрос выполнен успешно, возвращенный результат инкапсулируется в объект Inventory.

Пример кода:

List<String> productNameList = new ArrayList<>();
productNameList.add(PRODUCT_ONE);
productNameList.add(PRODUCT_TWO);
mHelper.queryInventoryAsync(true, productNameList, new IabHelper.QueryInventoryFinishedListener() {
    @Override
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        String price = inventory.getSkuDetails(PRODUCT_ONE).getPrice();
    }
});

Во-вторых, покупка управляемых товаров

Идентификатор продукта также требуется при покупке товаров, вызовите launchPurchaseFlow (Activity, String, int, OnIabPurchaseFinishedListener, String)

  • Activity: вызовите активность launchPurchaseFlow
  • Строка: идентификатор продукта, убедитесь, что это идентификатор, а не название продукта.
  • Int: значение кода запроса, которое может быть любым положительным целым числом, Google Play вернет значение в onActivityResult действия вместе с ответом на покупку.
  • OnIabPurchaseFinishedListener: слушатель завершения запроса на покупку
  • String: Тег String, единственный признак запроса на покупку, может быть пустым. Если вы укажете строковое значение, Google вернет эту строку вместе с ответом о покупке. В реальных проектах лучше всего использовать строку, случайно сгенерированную сервером

Если покупка прошла успешно, возвращенный результат инкапсулируется в объект Purchase. Чтобы
Продукты типа «Управляемый продукт» можно приобретать повторно, но перед повторной покупкой необходимо отправить запрос на потребление продукта в Google Play, в противном случае покупка будет невозможна.

Пример кода:

mHelper.launchPurchaseFlow(this, productId, REQUEST_CODE, new IabHelper.OnIabPurchaseFinishedListener() {
            @Override
            public void onIabPurchaseFinished(IabResult result, Purchase info) {

            }
        }, "XXXXXXX");

3. Приобретайте контент по подписке.

При покупке контента по подписке метод вызова отличается от метода покупки управляемых продуктов. Если вы принудительно вызовете тот же метод, вы получите сообщение об ошибке, возвращаемое Google Play при подтверждении покупки. Чтобы
Вызовите launchSubscriptionPurchaseFlow (Activity, String, int, OnIabPurchaseFinishedListener, String), описание параметра относится к содержимому в 2.

Пример кода:

mHelper.launchSubscriptionPurchaseFlow(this, productId, REQUEST_CODE, new IabHelper.OnIabPurchaseFinishedListener() {
    @Override
    public void onIabPurchaseFinished(IabResult result, Purchase info) {

}
}, "purchase subscription");

В-четвертых, запросите купленные товары

Чтобы запросить продукты, приобретенные пользователем, вызовите queryInventoryAsync (QueryInventoryFinishedListener)
Примечание. При запросе о приобретенных продуктах обнаруживаются продукты, которые были приобретены пользователями, которые в настоящее время вошли в Google Play.

Пример кода:

mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
    @Override
    public void onQueryInventoryFinished(IabResult result, Inventory inv) {

    }
});

V. Потребление управляемых товаров

После того, как продукт приобретен, он будет считаться «принадлежащим» статусом, и продукт с этим статусом не может быть снова приобретен тем же пользователем. Следовательно, необходимо отправить запрос потребления в Google Play в соответствии с запросом и вызвать consumerAsync (Purchase, OnConsumeFinishedListener)

  • Покупка: предмет для употребления.
  • OnConsumeFinishedListener: отслеживать после завершения потребления

Пример кода:

mHelper.consumeAsync(purchase, new IabHelper.OnConsumeFinishedListener() {
        @Override
        public void onConsumeFinished(Purchase purchase, IabResult result) {

        }
    });

Примечания к испытаниям

Из-за сложной конфигурации во время тестирования проекта, если вы столкнетесь с проблемой «приложение не может быть куплено», проверьте, выполнены ли следующие пункты:

  • Обязательно загрузите файл APK с подписью;
  • Убедитесь, что на устройстве установлен подписанный APK, а не отладочная версия;
  • Убедитесь, что к тесту добавлены тестеры;
  • Убедитесь, что учетная запись, используемая устройством, является тестовой.
  • Убедитесь, что для тестовой учетной записи активирована тестовая ссылка (проще всего игнорировать);
  • Убедитесь, что номер версии и название версии приложения на устройстве соответствуют APK, загруженному в фоновом режиме разработчика Google;
  • Убедитесь, что статус продукта Активен.

Справочные документы:

http://developer.android.com/intl/zh-cn/google/play/billing/index.html 

http://developer.android.com/intl/zh-cn/training/in-app-billing/index.html

Вышеупомянутое является лишь кратким введением, ниже приводится подробное объяснение.

(1)Google Play Service

В загруженном пакете сервиса Google Play будет проект библиотеки.

/**  
 * Check the device to make sure it has the Google Play Services APK.If  
 * it doesn't, display a dialog that allows users to download the APK from  
 * the Google Play Store or enable it in the device's system settings  
 */  
private boolean checkPlayServices()  
{  
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);  
    if(resultCode != ConnectionResult.SUCCESS)  
    {  
        if(GooglePlayServicesUtil.isUserRecoverableError(resultCode))  
        {  
            GooglePlayServicesUtil.getErrorDialog(resultCode, this,  
                    PLAY_SERVICES_RESOLUTION_REQUEST).show();  
        }  
        else  
        {  
            Log.i(TAG, "This device is not supported");  
            finish();  
        }  
        return false;  
    }  
    return true;  
}  

Если служба Google на текущем устройстве недоступна, появится всплывающее окно с инструкциями по настройке и установке. Если это устройство не поддерживает это, нет необходимости проверять, доступен ли Google биллинг. Еще одна вещь: сервис Google Play может многое. Если вы считаете, что просто использовать вышеуказанные функции слишком просто, вы можете подумать о добавлении автоматических обновлений в приложение. Когда вы загружаете новую версию программы в Google Play, Google Play поможет. Вы предлагаете пользователю обновить программу. Еще один интересный момент: если вы представите этот проект библиотеки, вы можете добавить GCM (Google Cloud Messaging), который является функцией отправки сообщений. Конечно, это более хлопотно. Если вам интересно, вы можете добавить его.

(2) Просмотрите имя пакета

Имя пакета Google Play — «com.android.vending», а имя пакета службы Google Play, работающей на устройстве, — «com.google.android.gms». Имя пакета на устройстве может быть отслежено при запуске программы. , Если таких двух вещей нет, посоветуйте пользователям установить.

Метод имени пакета Traverse

//Check Google Play  
protected boolean isHaveGooglePlay(Context context, String packageName)  
{  
    //Get PackageManager  
    final PackageManager packageManager = context.getPackageManager();  
  
    //Get The All Install App Package Name  
    List<PackageInfo> pInfo = packageManager.getInstalledPackages(0);  
      
    //Create Name List  
    List<String> pName = new ArrayList<String>();  
      
    //Add Package Name into Name List  
    if(pInfo != null){  
        for(int i=0; i<pInfo.size(); i++){  
            String pn = pInfo.get(i).packageName;  
            pName.add(pn);  
              
            //Log.v("Package Name", "PackAgeName: = " + pn);  
        }  
    }  
      
    //Check   
    return pName.contains(packageName);  
}  

Подскажите способ установки

Uri uri = Uri.parse ("market: // details? Id =" + "Имя пакета устанавливаемой программы");  
            Intent it = new Intent(Intent.ACTION_VIEW, uri);   
            startActivity(it);  

Вышеупомянутый метод откроет магазин приложений на вашем телефоне и найдет программу для установки.

Тем не менее, по-прежнему рекомендуется использовать Google Play Service для обнаружения, похоже, что второй тип, даже если некоторые пользователи установили Google Play (например, внутренние пользователи), Google Play Service не поддерживается.

3. Добавьте код (наконец, добавьте код оплаты)

Скопируйте весь код util из примеров, загруженных в предыдущей статье, в свой проект, вы можете создать новый пакет и поместить его внутрь.


Позвольте мне объяснить. Фактически, код этого примера все еще хорош. В соответствии с мировым кодом и идеей его использования, просто используйте его напрямую! Конечно, если вы чувствуете, что код написан плохо или не подходит для вашего проекта, вы можете сами написать соответствующий код в соответствии с документацией. Конечно, в документации написано, что для предотвращения взлома вашей игры другими лучше всего изменить имена переменных и методов внутри, ведь код здесь может увидеть кто угодно. Мой подход состоит в том, чтобы скопировать его здесь, но я только что изменил IabHelper.java, потому что это ключ ко всему платежу, а все остальные вспомогательные и их можно игнорировать.

Скопировав сюда код и импортировав все импортированные товары, вы можете начать писать собственный платеж в соответствии с кодом в примерах. Для автономных игр необходимо учитывать это преобразование кода, а также локальную проверку и шифрование. Для онлайн-игр это просто. Потому что я на самом деле не знаком с java , Так что автономное шифрование, базовая проверка и путаница введены не будут. Далее в основном речь идет об онлайн-играх.

(1)IabHelper.java

Это ключевой код для оплаты. Были написаны методы выставления счетов, запроса продукта, покупки продукта, обратного вызова продукта, проверки продукта и обратного вызова. Вы можете напрямую обратиться к примерам.

01. Настройте биллинг

Привязка ServiceConnection кIInAppBillingService. Функции идеальны, включая обратные вызовы для успеха и неудачи, а также различные исключения. После проверки того, является ли устройство службой Google Play в процессе запуска вашей программы, вы можете создать новый IabHelper для вызова этого метода и выполнить соответствующую обработку в соответствии с различными обратными вызовами.

/**  
     * Starts the setup process. This will start up the setup process asynchronously.  
     * You will be notified through the listener when the setup process is complete.  
     * This method is safe to call from a UI thread.  
     *  
     * @param listener The listener to notify when the setup process is complete.  
     */  
    public void startSetup(final OnIabSetupFinishedListener listener) {  
        // If already set up, can't do it again.  
        checkNotDisposed();  
        if (mSetupDone) throw new IllegalStateException("IAB helper is already set up.");  
  
        // Connection to IAB service  
        logDebug("Starting in-app billing setup.");  
        mServiceConn = new ServiceConnection() {  
            @Override  
            public void onServiceDisconnected(ComponentName name) {  
                logDebug("Billing service disconnected.");  
                mService = null;  
            }  
  
            @Override  
            public void onServiceConnected(ComponentName name, IBinder service) {  
                if (mDisposed) return;  
                logDebug("Billing service connected.");  
                mService = IInAppBillingService.Stub.asInterface(service);  
                String packageName = mContext.getPackageName();  
                try {  
                    logDebug("Checking for in-app billing 3 support.");  
  
                    // check for in-app billing v3 support  
                    int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP);  
                    if (response != BILLING_RESPONSE_RESULT_OK) {  
                        if (listener != null) listener.onIabSetupFinished(new IabResult(response,  
                                "Error checking for billing v3 support."));  
  
                        // if in-app purchases aren't supported, neither are subscriptions.  
                        mSubscriptionsSupported = false;  
                        return;  
                    }  
                    logDebug("In-app billing version 3 supported for " + packageName);  
  
                    // check for v3 subscriptions support  
                    response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS);  
                    if (response == BILLING_RESPONSE_RESULT_OK) {  
                        logDebug("Subscriptions AVAILABLE.");  
                        mSubscriptionsSupported = true;  
                    }  
                    else {  
                        logDebug("Subscriptions NOT AVAILABLE. Response: " + response);  
                    }  
  
                    mSetupDone = true;  
                }  
                catch (RemoteException e) {  
                    if (listener != null) {  
                        listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,  
                                                    "RemoteException while setting up in-app billing."));  
                    }  
                    e.printStackTrace();  
                    return;  
                }  
  
                if (listener != null) {  
                    listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));  
                }  
            }  
        };  
  
        Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");  
        serviceIntent.setPackage("com.android.vending");  
        if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0).isEmpty()) {  
            // service available to handle that Intent  
            mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);  
        }  
        else {  
            // no service available to handle that Intent  
            if (listener != null) {  
                listener.onIabSetupFinished(  
                        new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,  
                        "Billing service unavailable on device."));  
            }  
        }  
    }  

код в простом

// Create the helper, passing it our context and the public key to verify signatures with  
        Log.d(TAG, "Creating IAB helper.");  
        mHelper = new IabHelper(this, base64EncodedPublicKey);  
  
  
        // enable debug logging (for a production application, you should set this to false).  
        mHelper.enableDebugLogging(true);  
  
  
        // Start setup. This is asynchronous and the specified listener  
        // will be called once setup completes.  
        Log.d(TAG, "Starting setup.");  
        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {  
            public void onIabSetupFinished(IabResult result) {  
                Log.d(TAG, "Setup finished.");  
  
  
                if (!result.isSuccess()) {  
                    // Oh noes, there was a problem.  
                    complain("Problem setting up in-app billing: " + result);  
                    return;  
                }  
  
  
                // Have we been disposed of in the meantime? If so, quit.  
                if (mHelper == null) return;  
  
  
                // IAB is fully set up. Now, let's get an inventory of stuff we own.  
                Log.d(TAG, "Setup successful. Querying inventory.");  
                mHelper.queryInventoryAsync(mGotInventoryListener);  
            }  
        });  
    }  

02. Проверить товары

В конце метода настройки есть один

mHelper.queryInventoryAsync(mGotInventoryListener);  

Он используется для запроса продуктов, которыми вы в настоящее время владеете. Код обратного вызова выглядит следующим образом

// Listener that's called when we finish querying the items and subscriptions we own  
    IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {  
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {  
            Log.d(TAG, "Query inventory finished.");  
  
            // Have we been disposed of in the meantime? If so, quit.  
            if (mHelper == null) return;  
  
            // Is it a failure?  
            if (result.isFailure()) {  
                complain("Failed to query inventory: " + result);  
                return;  
            }  
  
            Log.d(TAG, "Query inventory was successful.");  
  
            /*  
             * Check for items we own. Notice that for each purchase, we check  
             * the developer payload to see if it's correct! See  
             * verifyDeveloperPayload().  
             */  
  
            // Do we have the premium upgrade?  
            Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);  
            mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));  
            Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));  
  
            // Do we have the infinite gas plan?  
            Purchase infiniteGasPurchase = inventory.getPurchase(SKU_INFINITE_GAS);  
            mSubscribedToInfiniteGas = (infiniteGasPurchase != null &&  
                    verifyDeveloperPayload(infiniteGasPurchase));  
            Log.d(TAG, "User " + (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE")  
                        + " infinite gas subscription.");  
            if (mSubscribedToInfiniteGas) mTank = TANK_MAX;  
  
            // Check for gas delivery -- if we own gas, we should fill up the tank immediately  
            Purchase gasPurchase = inventory.getPurchase(SKU_GAS);  
            if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {  
                Log.d(TAG, "We have gas. Consuming it.");  
                mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);  
                return;  
            }  
  
            updateUi();  
            setWaitScreen(false);  
            Log.d(TAG, "Initial inventory query finished; enabling main UI.");  
        }  
    };  

Поскольку наши покупки в приложении в настоящее время можно выкупить повторно, поэтому после успешного запроса о продуктах, которые мы уже купили, мы выполнили операцию потребления продуктов. Используемый код здесь

// Check for gas delivery -- if we own gas, we should fill up the tank immediately  
Purchase gasPurchase = inventory.getPurchase(SKU_GAS);  
if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {  
    Log.d(TAG, "We have gas. Consuming it.");  
    mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);  
    return;  
}  

Прежде чем говорить о расходе, сначала объясните причину вышеуказанной операции. Как упоминалось в продукте для покупки в приложении, если вы устанавливаете повторяемый продукт, вам необходимо активно потреблять продукт после успешной покупки продукта, и вы можете приобрести его снова только после того, как потребление будет успешным. Некоторые люди могут подумать, что этот параметр не подходит. Мой продукт изначально подлежит повторной покупке. Почему я должен уведомлять Google Play, чтобы использовать продукт после успешной покупки? (Может быть, сам продукт бесполезен. Это просто название )? Я лично считаю, что этот параметр может помешать пользователям размещать повторные заказы на покупку, а во-вторых, он может гарантировать, что каждый заказ клиента уникален. С двумя указанными выше пунктами можно хорошо справиться с пропущенными заказами. Итак, после того, как приведенный выше код успешно настроил биллинг, первая операция — запросить товары, которыми вы владеете, что является обработкой отсутствующего заказа. Поскольку процесс оплаты на самом деле является вашим приложением ——> программа Google Play (через службу Google Play) ——> сервер Google ——-> программа Google Play (через службу Google Play) ——> Ваше приложение. Такой интерактивный процесс также требует поддержки сети, поэтому каждая платежная операция не гарантирует 100% успеха. Это приведет к пропущенным заказам, то есть платеж пользователя будет успешным, но когда Google Play уведомит ваше приложение о результате платежа, Он по какой-то причине отключен, поэтому ваша программа не знает, успешна ли операция оплаты, поэтому, когда вы войдете в игру в следующий раз, вы должны проверить, есть ли какие-либо предметы, которые были куплены, но не были потреблены. Если они есть, они будут потреблены , А затем отправьте товар пользователю. Поскольку пользователь не может снова приобрести этот продукт до того, как он будет использован, один пользователь будет соответствовать только одному пропущенному заказу, и повторных пропущенных заказов не будет. Вся эта информация хранится на сервере Google, просто измените код запроса напрямую в коде.

02. Расходные материалы.

Расходные материалы появятся в двух местах. Один из отсутствующих заказов, упомянутых в запросе продукта, а второй — потребление после каждой успешной покупки. Также существует обратный вызов для потребительских товаров, а именно:

// Called when consumption is complete  
   IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {  
       public void onConsumeFinished(Purchase purchase, IabResult result) {  
           Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);  
  
           // if we were disposed of in the meantime, quit.  
           if (mHelper == null) return;  
  
           // We know this is the "gas" sku because it's the only one we consume,  
           // so we don't check which sku was consumed. If you have more than one  
           // sku, you probably should check...  
           if (result.isSuccess()) {  
               // successfully consumed, so we apply the effects of the item in our  
               // game world's logic, which in our case means filling the gas tank a bit  
               Log.d(TAG, "Consumption successful. Provisioning.");  
               mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;  
               saveData();  
               alert("You filled 1/4 tank. Your tank is now " + String.valueOf(mTank) + "/4 full!");  
           }  
           else {  
               complain("Error while consuming: " + result);  
           }  
           updateUi();  
           setWaitScreen(false);  
           Log.d(TAG, "End consumption flow.");  
       }  
   };  

Код относительно прост, просто внесите в него небольшие изменения для собственной игровой логики.

03. Покупка товаров.

По степени важности покупка товаров должна быть на первом месте, но если следить за процессом оплаты, покупка товара не первая, поэтому давайте проследим процесс оплаты.

/**  
    * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,  
    * which will involve bringing up the Google Play screen. The calling activity will be paused while  
    * the user interacts with Google Play, and the result will be delivered via the activity's  
    * {@link android.app.Activity#onActivityResult} method, at which point you must call  
    * this object's {@link #handleActivityResult} method to continue the purchase flow. This method  
    * MUST be called from the UI thread of the Activity.  
    *  
    * @param act The calling activity.  
    * @param sku The sku of the item to purchase.  
    * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS)  
    * @param requestCode A request code (to differentiate from other responses --  
    *     as in {@link android.app.Activity#startActivityForResult}).  
    * @param listener The listener to notify when the purchase process finishes  
    * @param extraData Extra data (developer payload), which will be returned with the purchase data  
    *     when the purchase completes. This extra data will be permanently bound to that purchase  
    *     and will always be returned when the purchase is queried.  
    */  
   public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode,  
                       OnIabPurchaseFinishedListener listener, String extraData) {  
       checkNotDisposed();  
       checkSetupDone("launchPurchaseFlow");  
       flagStartAsync("launchPurchaseFlow");  
       IabResult result;  
  
       if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {  
           IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE,  
                   "Subscriptions are not available.");  
           flagEndAsync();  
           if (listener != null) listener.onIabPurchaseFinished(r, null);  
           return;  
       }  
  
       try {  
           logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);  
           Bundle buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType, extraData);  
           int response = getResponseCodeFromBundle(buyIntentBundle);  
           if (response != BILLING_RESPONSE_RESULT_OK) {  
               logError("Unable to buy item, Error response: " + getResponseDesc(response));  
               flagEndAsync();  
               result = new IabResult(response, "Unable to buy item");  
               if (listener != null) listener.onIabPurchaseFinished(result, null);  
               return;  
           }  
  
           PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);  
           logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);  
           mRequestCode = requestCode;  
           mPurchaseListener = listener;  
           mPurchasingItemType = itemType;  
           act.startIntentSenderForResult(pendingIntent.getIntentSender(),  
                                          requestCode, new Intent(),  
                                          Integer.valueOf(0), Integer.valueOf(0),  
                                          Integer.valueOf(0));  
       }  
       catch (SendIntentException e) {  
           logError("SendIntentException while launching purchase flow for sku " + sku);  
           e.printStackTrace();  
           flagEndAsync();  
  
           result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.");  
           if (listener != null) listener.onIabPurchaseFinished(result, null);  
       }  
       catch (RemoteException e) {  
           logError("RemoteException while launching purchase flow for sku " + sku);  
           e.printStackTrace();  
           flagEndAsync();  
  
           result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow");  
           if (listener != null) listener.onIabPurchaseFinished(result, null);  
       }  
   }  

Вышеупомянутый код оплаты и покупки в IabHelper включает в себя обработку повторной покупки типов продуктов и разовой покупки типов продуктов. Основной код — это пробный блок

try {  
    logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);  
    Bundle buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType, extraData);  
    int response = getResponseCodeFromBundle(buyIntentBundle);  
    if (response != BILLING_RESPONSE_RESULT_OK) {  
        logError("Unable to buy item, Error response: " + getResponseDesc(response));  
        flagEndAsync();  
        result = new IabResult(response, "Unable to buy item");  
        if (listener != null) listener.onIabPurchaseFinished(result, null);  
        return;  
    }  
  
    PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);  
    logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);  
    mRequestCode = requestCode;  
    mPurchaseListener = listener;  
    mPurchasingItemType = itemType;  
    act.startIntentSenderForResult(pendingIntent.getIntentSender(),  
                                   requestCode, new Intent(),  
                                   Integer.valueOf(0), Integer.valueOf(0),  
                                   Integer.valueOf(0));  
}  

1. Вызовите метод getBuyIntent в In-app Billing, который передаст несколько параметров: первый параметр 3 представляет версию используемого в настоящее время платежного API, второй параметр — имя вашего пакета, а третий параметр — вы. Идентификатор продукта для покупки в приложении, четвертый параметр — это тип этой покупки, inapp и subs, мы используем первый, второй — тип, который можно купить только один раз, а пятый параметр — это заказ. число. Следует упомянуть только третий и пятый параметры.

Третий параметр, идентификатор продукта, — это имя продукта для покупки в приложении, который вы задали на сервере разработчика Google. Название каждого товара должно быть уникальным. Рекомендуется использовать комбинацию названия продукта, подчеркивания и цены, например «crystal_0.99», чтобы вы могли знать, что цена этого продукта составляет 0,99 доллара США, а продукт является кристальным.

Пятый параметр — это порядковый номер. Если существует локальный платежный сервер, этот номер заказа может быть сгенерирован платежным сервером и затем передан клиенту.Таким образом, локальный сервер также может записывать информацию о заказе для облегчения будущих запросов и операций. В формате номера заказа рекомендуется использовать метку времени, а также название продукта и цену, чтобы информацию о заказе также можно было легко увидеть. Этот номер заказа будет передан в Google, и Google передаст его вам в том виде, в каком он есть после успешной покупки, поэтому вы также можете добавить к нему информацию о ярлыке для сравнения. жестяная банкаИспользуется для хранения данных, которые разработчики хотят хранить

Во-вторых, после успешного выполнения getBuyIntent в возвращенном пакете будет одинBILLING_RESPONSE_RESULT_OKКод возврата, что означает успех. Затем используйте этот пакет для получения PendingIntent, как показано в приведенном выше коде.

Три, сделайте платеж

act.startIntentSenderForResult(pendingIntent.getIntentSender(),  
                               requestCode, new Intent(),  
                               Integer.valueOf(0), Integer.valueOf(0),  
                               Integer.valueOf(0));  

Этот метод является методом в Activity. После вызова этого метода выполняется обратный вызов для получения результата. За исключением первого параметра PengdingIntent, остальные могут быть записаны произвольно в соответствии с типом параметра.

В-четвертых, оплата завершена

/**  
    * Handles an activity result that's part of the purchase flow in in-app billing. If you  
    * are calling {@link #launchPurchaseFlow}, then you must call this method from your  
    * Activity's {@link [email protected]} method. This method  
    * MUST be called from the UI thread of the Activity.  
    *  
    * @param requestCode The requestCode as you received it.  
    * @param resultCode The resultCode as you received it.  
    * @param data The data (Intent) as you received it.  
    * @return Returns true if the result was related to a purchase flow and was handled;  
    *     false if the result was not related to a purchase, in which case you should  
    *     handle it normally.  
    */  
   public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {  
       IabResult result;  
       if (requestCode != mRequestCode) return false;  
  
       checkNotDisposed();  
       checkSetupDone("handleActivityResult");  
  
       // end of async purchase operation that started on launchPurchaseFlow  
       flagEndAsync();  
  
       if (data == null) {  
           logError("Null data in IAB activity result.");  
           result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result");  
           if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);  
           return true;  
       }  
  
       int responseCode = getResponseCodeFromIntent(data);  
       String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);  
       String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);  
  
       if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) {  
           logDebug("Successful resultcode from purchase activity.");  
           logDebug("Purchase data: " + purchaseData);  
           logDebug("Data signature: " + dataSignature);  
           logDebug("Extras: " + data.getExtras());  
           logDebug("Expected item type: " + mPurchasingItemType);  
  
           if (purchaseData == null || dataSignature == null) {  
               logError("BUG: either purchaseData or dataSignature is null.");  
               logDebug("Extras: " + data.getExtras().toString());  
               result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature");  
               if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);  
               return true;  
           }  
  
           Purchase purchase = null;  
           try {  
               purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature);  
               String sku = purchase.getSku();  
  
               // Verify signature  
               if (!Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) {  
                   logError("Purchase signature verification FAILED for sku " + sku);  
                   result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku);  
                   if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase);  
                   return true;  
               }  
               logDebug("Purchase signature successfully verified.");  
           }  
           catch (JSONException e) {  
               logError("Failed to parse purchase data.");  
               e.printStackTrace();  
               result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data.");  
               if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);  
               return true;  
           }  
  
           if (mPurchaseListener != null) {  
               mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase);  
           }  
       }  
       else if (resultCode == Activity.RESULT_OK) {  
           // result code was OK, but in-app billing response was not OK.  
           logDebug("Result code was OK but in-app billing response was not OK: " + getResponseDesc(responseCode));  
           if (mPurchaseListener != null) {  
               result = new IabResult(responseCode, "Problem purchashing item.");  
               mPurchaseListener.onIabPurchaseFinished(result, null);  
           }  
       }  
       else if (resultCode == Activity.RESULT_CANCELED) {  
           logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));  
           result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.");  
           if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);  
       }  
       else {  
           logError("Purchase failed. Result code: " + Integer.toString(resultCode)  
                   + ". Response: " + getResponseDesc(responseCode));  
           result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response.");  
           if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);  
       }  
       return true;  
   }  
  
   public Inventory queryInventory(boolean querySkuDetails, List<String> moreSkus) throws IabException {  
       return queryInventory(querySkuDetails, moreSkus, null);  
   }  

После того, как будет возвращен результат платежа, будет вызван вышеуказанный метод.При сбое платежа и ошибке код написан очень четко, и вы можете справиться с этим самостоятельно. Что касается вышеупомянутого метода, давайте кратко поговорим о процессе и посмотрим, откуда этот метод вызывается. Сначала перейдите к MainActivity в Sample, чтобы найти метод onActivityResult

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);  
    if (mHelper == null) return;  
  
    // Pass on the activity result to the helper for handling  
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {  
        // not handled, so handle it ourselves (here's where you'd  
        // perform any handling of activity results not related to in-app  
        // billing...  
        super.onActivityResult(requestCode, resultCode, data);  
    }  
    else {  
        Log.d(TAG, "onActivityResult handled by IABUtil.");  
    }  
}  

Этот метод будет вызываться, когда платеж будет завершен и ваша программа вернется на передний план. Вы можете увидеть в этом методе

!mHelper.handleActivityResult(requestCode, resultCode, data)  

Здесь вызывается метод handleActivityResult в IabHelper. Тогда в этом методе вы увидите место, где вызывается PurchseListener

if (mPurchaseListener != null) {  
                mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase);  
            }  

На этом этапе вы можете узнать, откуда вызывается OnIabPurchaseFinishedListener. Затем вы можете увидеть этот код в методе handleActivityResult

// Verify signature  
               if (!Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) {  
                   logError("Purchase signature verification FAILED for sku " + sku);  
                   result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku);  
                   if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase);  
                   return true;  
               }  
               logDebug("Purchase signature successfully verified."); 

Некоторые детские туфли тоже спрашивают. После моей покупки Google Play сообщает, что покупка прошла успешно, но в OnIabPurchaseFinishedListener все равно не удается. Сообщение об ошибке

Purchase signature verification FAILED for sku xxx


Это сообщение об ошибке выводится отсюда.Что касается того, почему эта ошибка возникает, это потому, что локальная проверка в образце не удалась (фактически, покупка была успешной). Это чаще встречается из-за использования зарезервированного Google ID теста:

android.test.purchased

Поскольку аутентификация не выполняется в Security.java

public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {  
     if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||  
             TextUtils.isEmpty(signature)) {  
         Log.e(TAG, "Purchase verification failed: missing data.");  
         return false;  
     }  
  
     PublicKey key = Security.generatePublicKey(base64PublicKey);  
     return Security.verify(key, signedData, signature);  
 }  

Или, если base64PublicKey пуст, или если подпись пуста, проверка завершится неудачно.

Решение:

1. Используйте локальную аутентификацию. Просто взгляните на методы в разделе «Безопасность», а затем внимательно проверьте, что пошло не так.

2. Используйте аутентификацию сервера. Просто измените метод handleActivityResult f в IabHelper и больше не используйте локальную безопасность для проверки. См. Ниже, как работать.

В образце есть еще одна яма безопасности, то есть после того, как вы успешно приобрели продукт, но он не был использован, при следующем входе в игру для проверки будет сообщено об ошибке.

Failed to query inventory: IabResult: Error refreshing inventory (querying owned items). (response: -1003:Purchase signature verification failed)  

Причина этой ошибки в том, что метод verifyPurchase в Security также запускается при запросе. Отслеживание источника и пошаговый поиск кода будут в IabHelper.

int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException {... ...}  

Найдите Security в этом методе и вызовите verifyPurchase.

Если это сервер для проверки, просто закомментируйте проверку, как показано на рисунке выше.Если это локальная проверка, найдите метод verifyPurchase в разделе «Безопасность», чтобы узнать, что пошло не так и где переданное значение пусто.

Внимание

Комментируя код подтверждения, не забудьте вызвать две строки кода, которые сгенерировали покупку, иначе вы не вернете запрошенный продукт при запросе. Если вы внимательно посмотрите на пример кода, вы обнаружите, что многие прослушиватели обратного вызова на самом деле вызываются из IabHelper. Две строчки кода выглядят следующим образом:

Purchase purchase = new Purchase(itemType, purchaseData, signature);  
                  
                inv.addPurchase(purchase); 

Создайте покупку на основе запрошенных данных, а затем добавьте эту покупку в инвентарь, чтобы вы могли вызвать ее, когда запрос будет успешным.

Метод inventory.getPurchase используется для получения продуктов, которые были куплены, но не потреблены.

Теперь остановимся на проверке результата после успешной оплаты. В приведенном выше методе из данных результата платежа будут получены два json-данных.

int responseCode = getResponseCodeFromIntent(data);  
String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);  
String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);  

Это PurchaseData и dataSignature. Для подтверждения платежа необходимы эти два параметра и publicKey.Метод проверки в примере написан на Security.java. Написано три метода для завершения проверки результата платежа.

Для игр с локальным платежным сервером эта операция может быть перенесена на сервер, и клиенту нужно только передать ему PurchaData и dataSignature на платежный сервер. Затем платежный сервер передает результат проверки клиенту, а затем обрабатывает успех и неудачу.После успеха операция потребления товаров. Я лично считаю, что для игр, в которых нет платежного сервера, сложно обеспечить безопасность локальных операций. Однако существует также риск для сервера проверить результат платежа, но риск невелик.

/**  
    * Verifies that the data was signed with the given signature, and returns  
    * the verified purchase. The data is in JSON format and signed  
    * with a private key. The data also contains the {@link PurchaseState}  
    * and product ID of the purchase.  
    * @param base64PublicKey the base64-encoded public key to use for verifying.  
    * @param signedData the signed JSON string (signed, not encrypted)  
    * @param signature the signature for the data, signed with the private key  
    */  
   public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {  
       if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||  
               TextUtils.isEmpty(signature)) {  
           Log.e(TAG, "Purchase verification failed: missing data.");  
           return false;  
       }  
  
       PublicKey key = Security.generatePublicKey(base64PublicKey);  
       return Security.verify(key, signedData, signature);  
   }  
  
   /**  
    * Generates a PublicKey instance from a string containing the  
    * Base64-encoded public key.  
    *  
    * @param encodedPublicKey Base64-encoded public key  
    * @throws IllegalArgumentException if encodedPublicKey is invalid  
    */  
   public static PublicKey generatePublicKey(String encodedPublicKey) {  
       try {  
           byte[] decodedKey = Base64.decode(encodedPublicKey);  
           KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);  
           return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));  
       } catch (NoSuchAlgorithmException e) {  
           throw new RuntimeException(e);  
       } catch (InvalidKeySpecException e) {  
           Log.e(TAG, "Invalid key specification.");  
           throw new IllegalArgumentException(e);  
       } catch (Base64DecoderException e) {  
           Log.e(TAG, "Base64 decoding failed.");  
           throw new IllegalArgumentException(e);  
       }  
   }  
  
   /**  
    * Verifies that the signature from the server matches the computed  
    * signature on the data.  Returns true if the data is correctly signed.  
    *  
    * @param publicKey public key associated with the developer account  
    * @param signedData signed data from server  
    * @param signature server signature  
    * @return true if the data and signature match  
    */  
   public static boolean verify(PublicKey publicKey, String signedData, String signature) {  
       Signature sig;  
       try {  
           sig = Signature.getInstance(SIGNATURE_ALGORITHM);  
           sig.initVerify(publicKey);  
           sig.update(signedData.getBytes());  
           if (!sig.verify(Base64.decode(signature))) {  
               Log.e(TAG, "Signature verification failed.");  
               return false;  
           }  
           return true;  
       } catch (NoSuchAlgorithmException e) {  
           Log.e(TAG, "NoSuchAlgorithmException.");  
       } catch (InvalidKeyException e) {  
           Log.e(TAG, "Invalid key specification.");  
       } catch (SignatureException e) {  
           Log.e(TAG, "Signature exception.");  
       } catch (Base64DecoderException e) {  
           Log.e(TAG, "Base64 decoding failed.");  
       }  
       return false;  
   }  

PublicKey: base64EncodedPublicKey

Этот PublicKey используется для проверки результата платежа, поэтому это определенно ключ, который не должен быть известен другим людям.Этот ключ размещается на сервере оплаты и не хранится локально. Если вы скопируете код из Sample для использования, вы обнаружите, что он будет передан в конструктор, когда новый IabHelper

Фактически, Sample использует этот PublicKey только для локальной проверки и не имеет ничего общего с инициализацией. Если внимательно посмотреть на исходный код IabHelper, вы увидите, что этот PublicKey не сохраняется локально. Когда используется новый IabHelper, вы можете просто передать строку или построить IabHelper. Измените метод и больше не передавайте это значение.

Код в примерах очень интересный, подскажите улыбку?

Для автономной игры найдите способ сохранить где-нибудь этот ключ и добавить секрет или что-то в этом роде, лучше не записывать его прямо в код. (Фактически, для автономных игр, если у вас нет собственного сервера для проверки результата платежа, независимо от того, как вы используете его локально, его легко взломать. Если игра популярна, рекомендуется написать платежный сервер для проверки результата платежа)

/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY  
 * (that you got from the Google Play developer console). This is not your  
 * developer public key, it's the *app-specific* public key.  
 *  
 * Instead of just storing the entire literal string here embedded in the  
 * program,  construct the key at runtime from pieces or  
 * use bit manipulation (for example, XOR with some other string) to hide  
 * the actual key.  The key itself is not secret information, but we don't  
 * want to make it easy for an attacker to replace the public key with one  
 * of their own and then fake messages from the server.  
 */  
String base64EncodedPublicKey = "CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE";  
  
// Some sanity checks to see if the developer (that's you!) really followed the  
// instructions to run this sample (don't put these checks on your app!)  
if (base64EncodedPublicKey.contains("CONSTRUCT_YOUR")) {  
    throw new RuntimeException("Please put your app's public key in MainActivity.java. See README.");  
}  
if (getPackageName().startsWith("com.example")) {  
    throw new RuntimeException("Please change the sample's package name! See README.");  
}  

Дополнение к проверке локального сервера:

Что касается проверки результатов платежа, то, помимо использования publicKey для проверки подписи, локальный сервер также может запросить подтверждение результата платежа в бэкэнде Google. Для этого требуется интерактивная связь между локальным сервером и сервером Google. Вы можете обратиться к этому документу.

Справочный адрес:

https://developer.android.google.cn/google/play/developer-api.html#publishing_api_overview

Однако для отечественных разработчиков, когда Google все чаще блокируется, определенно возникнут препятствия для связи с серверами Google, потому что из-за препятствий для связи ваша проверка не удастся, поэтому эта функция является необязательной, и те, кто заинтересован, могут Добавить.

Дополнение 1:

Если вы используете код примеров напрямую, следует отметить несколько моментов. Во-первых, измените сообщение об ошибке, чтобы оно было удобным для пользователя. Поскольку подсказки об ошибках в примерах предназначены в основном для разработчиков, подсказки очень подробны, но пользователю они не нужны. Вам нужно только сообщить пользователю об успехе, неудаче и простой причине неудачи. Во-вторых, отключите печать информации при выпуске официальной версии. В-третьих, измените имя класса и имя переменной.

Дополнение 2:

Если вы столкнулись с ошибкой во время тестовой оплаты, вы можете с ней справиться.

1. Текущее приложение не поддерживает покупку этого продукта: убедитесь, что название пакета и подпись на вашем мобильном телефоне соответствуют тем, которые были загружены в фоновом режиме. p.s. После загрузки в фоновый режим APK вступит в силу.

2. Купленного продукта не существует: убедитесь, что название продукта в вашем коде совпадает с названием продукта на сервере. Если они совпадают, вам может потребоваться подождать час или два перед тестированием. Проблемы с серверной частью Google.

3. Он загружается в течение долгого времени, и, наконец, выдается неизвестная ошибка: «Не беспокойтесь об этом, проблема с фоном Google, мы проверим еще раз позже».

Наконец-то отечественные разработчики обязательно протестируют под vpn! ! ! !

На обороте написано:

Выше приведен код добавленного Google In-app Billing, по сути, это просто для разговора о примерах. , Поэтому рекомендуется ознакомиться с официальными документами и образцами, где вы узнаете больше.

1. В последнее время у некоторых детских ботинок спрашивают, как исправить ошибку непроверенного аккаунта при тестировании оплаты. Я не сталкивался с этой ошибкой при тестировании. Лично может быть так, что ваше приложение не прошло проверку Google. Загрузка фона займет полчаса или час. Конечно, если причина не в этом, пожалуйста, отправьте личное сообщение детским ботинкам, которые решили эту проблему. Я добавлю его в блог, чтобы он мог помочь другим. Заранее спасибо.
2. В последнее время занимаюсь единством, поэтому в CSDN хожу редко (давно не обновляю блог, стыдно и стыдно). Оставьте электронное письмо, если у вас есть какие-либо вопросы, вы можете отправить мне электронное письмо[email protected] Учись и добивайся прогресса вместе!

ОколоAuthentication is required. You need to sign into your Google Account.Решение.

1. Этот вопрос задавался детской обувью и раньше, то есть, когда при проверке платежей выясняется, что учетная запись не аутентифицирована, вам необходимо войти в учетную запись Google. Известное на данный момент решение — использовать его на мобильном телефоне.Тестовая учетная запись (в предыдущей статье, как настроить тестовую учетную запись), не используйте обычную учетную запись Google.

P.S. Добавьте используемую тестовую учетную запись, если это учетная запись Gmail, но не используйте свою учетную запись разработчика, то есть не входите в учетную запись разработчика на тестовой машине для тестирования, помните, помните. Иначе появится

«Невозможно купить желаемый товар«ошибка!

2. Столкнулся при тестировании на android 5.0

java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.android.vending.billing.InAppBillingService.BIND } 

Судя по реакции детской обуви, ситуация такая. . . Прошло ранее 5.0Intent перевод bindService()Использование явного намерения в этом методе — рекомендуемый уровень, но он является обязательным после версии 5.0.

Решение:

1. Обновите In-app Billing.

Используйте новейшие библиотеки биллинга. Я видел, что версия биллинга выросла до 5. Я, наверное, читал, а новых публичных методов нет.

2. Переход на более раннюю версию targetSdkVersion

Если targetSdkVersion использует API 21 уровня 5.0, уменьшите targetSdkVersion до 19 (соответствующая версия — 4.4.2). Также не забудьте изменить файл атрибутов проектацель в project.properties.

В последнее время многие дети спрашивают о фоне загрузки APK, установленной на альфа- или бета-версию, и продукт настроен. Я тоже ждал час, а некоторые ждали целый день. . . Подождите, я все готов, но я все еще не могу купить QAQ, когда тестирую и покупаю продукты ~ Позже я узнал, что, хотя продукты, которые они загружали в серверную часть Google, были установлены на альфа или бета, они все еще были в статусе черновика, и Он не находится в выпущенном состоянии, поэтому его нельзя протестировать. Причина этомуВ основном потому, что информация APP в фоновом режиме Google не заполнена полностью. После загрузки вашей собственной программы в фоновый режим вам необходимо заполнить соответствующую информацию. Хотя это тест, не думайте, что просто написание имени приложения — это конец. Вам необходимо заполнить полную информацию о приложении, прежде чем вы сможете опубликовать его, даже если это не официальная версия. Кроме того, продукт также должен иметь статус деблокирования, и продукт в оттиске также будет иметь статус черновика.

Поэтому загрузите приложение в фоновый режим Google, подождав некоторое время, не забудьте проверить статус вашего приложения, чтобы убедиться, что это статус черновика. В правом верхнем углу фона также отображается подсказка «Почему нельзя опубликовать это приложение?«, нажмите и посмотрите, вы тоже кое-что получите.

Не забывай быть осторожным ~

int responseCode = getResponseCodeFromIntent(data);  
String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);  
String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);  

Понравилась статья? Поделить с друзьями:
  • Proactive command rejected by me мегафон как исправить
  • Pro tools 12 ошибка 6117 как исправить
  • Pro info failed to get pmt info как исправить
  • Pro dialog plus ошибки
  • Pro cs 80r ошибка ec