React-native-iap: ์ถœ์‹œ ํ›„ ๋งŽ์€ ๊ธฐ๊ธฐ์—์„œ Android ์ถฉ๋Œ

์— ๋งŒ๋“  2018๋…„ 11์›” 09์ผ  ยท  45์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: dooboolab/react-native-iap

react-native-iap ๋ฒ„์ „

"๋ฐ˜์‘ ๋„ค์ดํ‹ฐ๋ธŒ": "0.55.4"
"react-native-iap": "^ 2.3.2"

์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ํ”Œ๋žซํผ (IOS ๋˜๋Š” Android ๋˜๋Š” ๋‘˜ ๋‹ค?)

๊ธฐ๊ณ„์  ์ธ์กฐ ์ธ๊ฐ„

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™

์ œ๋ฐœ ๋„์™€์ฃผ์„ธ์š”.์ด ๋ฌธ์ œ๋ฅผ ์ฆ‰์‹œ ํ•ด๊ฒฐํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ 2 ์ฒœ๋ช…์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—

์‹ค์ œ ํ–‰๋™

๋งŽ์€ ์žฅ์น˜์—์„œ rniap๋กœ ์ธํ•ด ์•ฑ์ด ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค. Play Console์—์„œ์ด ์˜ค๋ฅ˜๋กœ ์ธํ•ด ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค.
java.lang.RuntimeException :

  1. com.facebook.react.bridge.CallbackImpl.invoke (CallbackImpl.java:28)
  2. com.facebook.react.bridge.PromiseImpl.resolve (PromiseImpl.java:30)
  3. com.dooboolab.RNIap.RNIapModule $ 4.run (RNIapModule.java:154)
  4. com.dooboolab.RNIap.RNIapModule $ 3.onBillingSetupFinished (RNIapModule.java:123)
  5. com.android.billingclient.api.BillingClientImpl $ BillingServiceConnection.onServiceConnected (BillingClientImpl.java:903)
  6. android.app.LoadedApk $ ServiceDispatcher.doConnected (LoadedApk.java:1264)
  7. android.app.LoadedApk $ ServiceDispatcher $ RunConnection.run (LoadedApk.java:1281)์—์„œ
  8. android.os.Handler.handleCallback (Handler.java:815)
  9. android.os.Handler.dispatchMessage์—์„œ (Handler.java:104)
  10. android.os.Looper.loop (Looper.java:207)
  11. android.app.ActivityThread.main (ActivityThread.java:5692)
  12. java.lang.reflect.Method.invoke (๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์†Œ๋“œ)
  13. com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:908)
  14. com.android.internal.os.ZygoteInit.main (ZygoteInit.java:769)

ํ…Œ์ŠคํŠธ ๋œ ํ™˜๊ฒฝ (์—๋ฎฌ๋ ˆ์ดํ„ฐ? ์‹ค์ œ ์žฅ์น˜?)

๋‹ค์Œ์€ Play Console์˜ ๋ณด๊ณ ์„œ์ž…๋‹ˆ๋‹ค.
https://ibb.co/gDxZQA

ํ–‰๋™์„ ์žฌํ˜„ํ•˜๋Š” ๋‹จ๊ณ„

๋ฌด์—‡์„ ํ•ด์•ผํ• ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด rniap์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
componentDidMount์—์„œ :
{
const ๊ฒฐ๊ณผ = ๋Œ€๊ธฐ RNIap.initConnection ();
} catch (err) {
console.log (err);
}
componentWillUnmount์—์„œ :
RNIap.endConnection ();

๐Ÿ› bug ๐Ÿค– android

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@hyochan ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, ์–ด์ œ ๋ณผ ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณง ๋ณผ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

@ Ilario17 ์•„๋งˆ๋„ ์–ด๋”˜๊ฐ€์— ๋…ผ๋ฆฌ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Œ์„ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ณต์ •ํ•˜๊ฒŒ

ํŽธ์ง‘ : ์•„, ๋ฌธ์ œ๋Š” onBillingSetupFinished ๊ฐ€ ๋‘ ๋ฒˆ ์ด์ƒ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ensureConnection ์™„๋ฃŒ๋˜๋ฉด (์ฆ‰, callback.run() ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ „๋‹ฌ ๋œ promise๋ฅผ ๊ฑฐ๋ถ€ ํ•  ๋•Œ) ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ œ๊ฑฐํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ ๊ณต์งœ ์—…๋ณด์— ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ์‰ฌ์šด ํ™๋ณด๊ฐ€๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค! ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ณง ๋„์ฐฉํ•˜๋„๋ก ๋…ธ๋ ฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค

๋ชจ๋“  45 ๋Œ“๊ธ€

์šฐ๋ฆฌ์˜ ์ตœ์‹  ๋ฒ„์ „ 2.3.19 ์™€ ์ปด๋ฐฑ์„ ์‹œ๋„ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

๋‚œ ๋ชปํ•ด. ์ฆ‰๊ฐ์ ์ธ ์ƒํ™ฉ ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ iap ํŒจํ‚ค์ง€๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ 3k ๋ช… ์ด์ƒ์˜ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ถฉ๋Œ์ด ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํ”„๋กœ์ ํŠธ์—์„œ ์‹œ๋„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”,์ด ๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์œผ๋ฉฐ ๋ฒ„์ „ 2.3.21์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

java.lang.RuntimeException :
com.facebook.react.bridge.CallbackImpl.invoke (CallbackImpl.java:28)
com.facebook.react.bridge.PromiseImpl.resolve (PromiseImpl.java:30)
com.dooboolab.RNIap.RNIapModule $ 4.run (RNIapModule.java:155)
com.dooboolab.RNIap.RNIapModule $ 3.onBillingSetupFinished (RNIapModule.java:124)
com.android.billingclient.api.BillingClientImpl $ BillingServiceConnection.onServiceConnected (BillingClientImpl.java:903)
android.app.LoadedApk $ ServiceDispatcher.doConnected (LoadedApk.java:1658)์—์„œ
android.app.LoadedApk $ ServiceDispatcher $ RunConnection.run (LoadedApk.java:1687)์—์„œ
android.os.Handler.handleCallback (Handler.java:789)
android.os.Handler.dispatchMessage (Handler.java:98)
android.os.Looper.loop (Looper.java:164)
android.app.ActivityThread.main (ActivityThread.java:6938)
java.lang.reflect.Method.invoke (๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์†Œ๋“œ)
com.android.internal.os.Zygote $ MethodAndArgsCaller.run (Zygote.java:327)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1374)

์ด๊ฒƒ์€ ๋‚ด componentDidMount์ž…๋‹ˆ๋‹ค.

           const itemSKus = Platform.select({
            ios: [
                ...
            ],
            android: [
                ...
            ]
        });

        try {
            const message = await RNIap.initConnection();
            //console.log(`message = ${message}`);
            const items = await RNIap.getProducts(itemSKus);
            //console.log(`items = ${items.length}`);
            //console.log(items);
            this.props.storeInAppPurchaseList(items);
        } catch (errorCode) {
            console.log(`error rniap = ${errorCode}`);
        }

@LinusU ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋ฅผ ๋„์™€ ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? callback.run() ์ด ๋ฆด๋ฆฌ์Šค์—์„œ ์ถฉ๋Œํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ensureConnection ์—์„œ callback ๊ฐ€ null์ธ์ง€ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? ํ . runtime exception ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ RuntimeException ์˜๋ฏธ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋” ์ด์ƒ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๊นŒ? @hyochan ์™œ callback ๊ฐ€ null ํ•˜์„ธ์š”? ๐Ÿค”

@LinusU ์˜๊ฒฌ์„ ๋ณด๋‚ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ํ˜„์žฌ ์ „ํ˜€ ๋ชจ๋ฅธ๋‹ค. ์•„๋ž˜์— ์ด์Šˆ ๋ผ์ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
image

RNIapModule.java 124 ํ–‰,

callback.run();

์ถ”๊ฐ€ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@LinusU ์ด๊ฒƒ์— ๋Œ€ํ•ด ์ „ํ˜€ ๋ชจ๋ฅธ๋‹ค๋ฉด ์ด์ „์— ์žˆ์—ˆ๋˜ ๊ณณ์œผ๋กœ ๋กค๋ฐฑํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ข‹์•„์š”, ์—ฌ๊ธฐ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค :

https://github.com/facebook/react-native/blob/370bcffba748e895ad8afa825bfef40bff859c95/ReactAndroid/src/main/java/com/facebook/react/bridge/CallbackImpl.java#L27 -L31

์–ด๋–ค ์ด์œ ๋กœ ์šฐ๋ฆฌ๋Š” ์ฝœ๋ฐฑ์„ ๋‘ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ ์‰ฝ๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

@LinusU ๋‹น์‹ ์ด ํŠธ๋ž™์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— PR ์„ ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

๊ทธ ๋ฉ”์†Œ๋“œ์— RuntimeException์ด์žˆ๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

if (!mInvoked) {
   mJSInstance.invokeCallback(mCallbackId, Arguments.fromJavaArgs(args));
   mInvoked = true;
}

์ด๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

@hyochan ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, ์–ด์ œ ๋ณผ ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณง ๋ณผ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

@ Ilario17 ์•„๋งˆ๋„ ์–ด๋”˜๊ฐ€์— ๋…ผ๋ฆฌ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Œ์„ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ณต์ •ํ•˜๊ฒŒ

ํŽธ์ง‘ : ์•„, ๋ฌธ์ œ๋Š” onBillingSetupFinished ๊ฐ€ ๋‘ ๋ฒˆ ์ด์ƒ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ensureConnection ์™„๋ฃŒ๋˜๋ฉด (์ฆ‰, callback.run() ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ „๋‹ฌ ๋œ promise๋ฅผ ๊ฑฐ๋ถ€ ํ•  ๋•Œ) ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ œ๊ฑฐํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ ๊ณต์งœ ์—…๋ณด์— ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ์‰ฌ์šด ํ™๋ณด๊ฐ€๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค! ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ณง ๋„์ฐฉํ•˜๋„๋ก ๋…ธ๋ ฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค

@LinusU ๊ทธ๋ ‡๋‹ค๋ฉด onBillingSetupFinished ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๊นŒ? billingClientStateListener ์ œ๊ฑฐํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? ๋ˆ„๊ตฐ๊ฐ€ ์šฐ๋ฆฌ์˜ ์ผ๋ถ€๊ฐ€๋˜๊ธฐ ์œ„ํ•ด ์ด๊ฒƒ์„ ์‹œ๋„ํ• ๊นŒ์š”?

๋น ๋ฅด๊ณ  ๋”๋Ÿฌ์šด ์†”๋ฃจ์…˜์„ ์›ํ•œ๋‹ค๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜์— ์ง€์—ญ ๋ณ€์ˆ˜ didCallCallback = false ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ callback.run() ๋’ค์— if (didCallCallback) { didCallCallback = true; callback.run() } callback.run() ํ†ตํ™”๋ฅผ ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค.

๋” ๊นจ๋—ํ•œ ํ•ด๊ฒฐ์ฑ…์€ ๋‹จ์ˆœํžˆ ์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ ํ›„ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋ก ์ทจ์†Œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Log.d(TAG, "billing client ready");
callback.run();
//deregister the listener here?

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฉ๊ธˆ 2.4.0-beta2 ์„ ์ถœ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹œ๋„ํ•˜์‹ญ์‹œ์˜ค.

@hyochan ๋‚˜๋Š”์ด ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ์‹œ๋„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค

๋ฒ ํƒ€ 2์— ํšŒ๊ท€ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์œผ๋ฏ€๋กœ 2.4.0-beta3์„ ์‹œ๋„ํ•˜์‹ญ์‹œ์˜ค.

@hyochan ์—…๋ฐ์ดํŠธ ํ›„์—๋„ ์˜ค๋ฅ˜๊ฐ€ ์—ฌ์ „ํžˆ ์žˆ์Šต๋‹ˆ๋‹ค.

@ Ilario17 2.4.0-beta5 ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ์ด๊ฒƒ์„ ๋‚ด ํŽธ์—์„œ ์žฌํ˜„ ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

@LinusU ๋งํ–ˆ๋“ฏ์ด @hyochan ์™œ ๊ทธ๋ƒฅ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ œ๊ฑฐ?

@ Ilario17 ๊ทธ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ ํ•ด๊ฒฐ์ฑ…์ธ์ง€ ๊ถ๊ธˆํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ onBillingServiceDisconnected ์—๊ฒŒ ์•Œ๋ ค์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๊ทธ๊ฒƒ์„ ์ œ๊ฑฐํ•˜๊ณ  ๋‹น์‹  ์ธก์—์„œ ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๊ด€๋ จ๋˜์–ด ์žˆ๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ 2.4.0-beta5์—์„œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์—๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์‰ฝ๊ฒŒ ์žฌํ˜„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
IAP๊ฐ€๋กœ๋“œ๋˜๋Š” ๋ถ€ํ’ˆ์„ ๋งˆ์šดํŠธํ•˜๊ณ  ๋งˆ์šดํŠธ ํ•ด์ œ ํ•œ ๋‹ค์Œ ๋‹ค์‹œ ๋งˆ์šดํŠธํ•˜๋ฉด ๋ถ์ด ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค.
ํŽธ์ง‘ : ๋‚˜๋Š”์ด ์ปค๋ฐ‹์œผ๋กœ ๋‹ค์šด ๊ทธ๋ ˆ์ด๋“œํ–ˆ๊ณ  ๋” ์ด์ƒ ์ถฉ๋Œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค https://github.com/dooboolab/react-native-iap/commit/2db337ac770a93508507ec046f31085ddbb346fb

Attempt to invoke virtual method 'void com.android.billingclient.api.BillingClient.querySkuDetailsAsync(com.android.billingclient.api.SkuDetailsParams, com.android.billingclient.api.SkuDetailsResponseListener)' on a null object reference
run
    RNIapModule.java:223
ensureConnection
    RNIapModule.java:113
getItemsByType
    RNIapModule.java:218
invoke
    Method.java
invoke
    JavaMethodWrapper.java:372
invoke
    JavaModuleWrapper.java:160
run
    NativeRunnable.java
handleCallback
    Handler.java:789
dispatchMessage
    Handler.java:98
dispatchMessage
    MessageQueueThreadHandler.java:29
loop
    Looper.java:164
run
    MessageQueueThreadImpl.java:192
run
    Thread.java:764

๋‘ ๋ฒˆ์งธ ๋งˆ์šดํŠธ (2.4.0-beta5)์—์„œ ์ถฉ๋Œ์„ ์ผ์œผํ‚ค๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

componentDidMount() {
    this.loadIAPProducts()
}

componentWillUnmount() {
    RNIap.endConnection();
}

loadIAPProducts = async () => {
    const itemSkus = ['XXXX_id']

    const result = await RNIap.initConnection();
    if(result) {
      try {
        const products = await RNIap.getProducts(itemSkus);
        if (products) {
          this.setState({ iapProducts: products })
        }
      } catch (err) {
        //console.log(err); // standardized err.code and err.message available
      }

      this.restoreIAPPurchases()
    }
  }

  restoreIAPPurchases = async () => {
    try {
      const purchases = await RNIap.getAvailablePurchases()
      if(purchases) {
        purchases.forEach(purchase => {
          if (purchase.productId === 'XXXX_id') {
            try {
              RNIap.consumePurchase(purchase.purchaseToken);
            } catch(err) {

            }
          }
        })
      }
    } catch (err) {
      //console.log(err)
    }
  }

@ rom1k ๊ธ€์Ž„ ์ด๊ฒƒ์€ ์•ˆ๋“œ๋กœ์ด๋“œ ์—๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์žฅ์น˜์—์„œ ์‹œ๋„ํ•˜์‹ญ์‹œ์˜ค.

@hyochan ๋ฒ„์ „ 2.4.0-

Android์—์„œ 2.4.0-beta3์˜ ๊ฒฝ์šฐ getProducts๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด "์ฝ”๋“œ๋กœ ๊ตฌ๋งค ์‹คํŒจ : 0 (OK)"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฒ ํƒ€ 4์™€ 5๋Š” ์‹ค์ œ๋กœ ์ถฉ๋Œ์„ ์ผ์œผ ํ‚ต๋‹ˆ๋‹ค. 2.3.5๋กœ ๋‹ค์šด ๊ทธ๋ ˆ์ด๋“œํ–ˆ๋Š”๋ฐ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€๋งŒ 2.4.0- ๋ฒ ํƒ€์—์„œ iOS ๋ฆฌ์Šค๋„ˆ ๋ฒ„๊ทธ ์ˆ˜์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

```
componentDidMount () {
this.getProductDetails ();
}

getProductDetails = async () => {
    try {
        await RNIap.initConnection();
        const details = await RNIap.getProducts(products);
        this.setState({
            title: details[0].title,
            description: details[0].description,
            price: details[0].localizedPrice,
            loading: false
        });
    } catch (err) {
        console.log(err.message)
    }
};

beta5์™€ ์ปด๋ฐฑ์„ ์‹œ๋„ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๊ทธ ๋ฒ„์ „์— ์‹ค์ˆ˜๊ฐ€์žˆ์–ด์„œ ๋นจ๋ฆฌ ๊ณ  ์ณค์–ด์š”

@hyochan @LinusU ๋ฒ„์ „ 2.4.0-

์ด ํ† ๋ก ๊ณผ ๊ด€๋ จ๋œ ๋ช‡ ๊ฐ€์ง€ ์ •๋ณด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

2.4.0-beta5์—์„œ ๋‹ค์Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Attempt to invoke virtual method void com.android.billingclient.api.BillingClient.querySkuDetailsAsync(...) on a null object reference

RNIapModule.java 223 ํ–‰์„ ๊ฐ€๋ฆฌ ํ‚ต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ mBillingClient ๊ฐ€ ์—ฌ๊ธฐ์„œ ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ null ์ž„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์žฌํ˜„ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋ฐ˜์‘ ๋„ค์ดํ‹ฐ๋ธŒ ํ˜ธ์ถœ ๊ตฌ์„ฑ ์š”์†Œ ๋งˆ์šดํŠธ RNIap.getProducts() ์— componentDidMount ๋ฐ RNIap.endConnection() ์— componentWillUnmount . ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งˆ์šดํŠธ ํ•ด์ œํ•˜๊ณ  ๋‹ค์‹œ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

onBillingServiceDisconnected() ์ฝœ๋ฐฑ์—์„œ false ๋กœ ์„ค์ •ํ•˜์—ฌ ๊ฒฐ์ œ ํด๋ผ์ด์–ธํŠธ ์„ค์ •์ด ์™„๋ฃŒ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ถ”์ ํ•˜๋Š” ๋ฐ clientReady ๋ณ€์ˆ˜๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ mBillingClient ์ž์ฒด๋Š” endConnection() ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ฆ‰์‹œ null๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ถˆ์ผ์น˜์ž…๋‹ˆ๋‹ค. mBillingClient ๊ฐ€ null ์ธ ๊ฒฝ์šฐ์—๋„ clientReady ์„ true ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด ์ ์–ด๋„ ๋‚ด ์‹ค์ˆ˜๋กœ ์ธํ•ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค. ๋ฌด์–ธ๊ฐ€๊ฐ€ onBillingServiceDisconnected() ํ˜ธ์ถœ์„ ๋ฐฉํ•ดํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š”๋ฐ›์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค "billing client disconnected" ๋„ˆ๋ฌด ์˜ˆ์ƒ๋Œ€๋กœ ๋งˆ์šดํŠธ ํ•ด์ œ์— ๋กœ๊ทธ ์บฃ์— onBillingServiceDisconnected() ์‹ค์ œ๋กœ ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ž ์žฌ์  ์ธ ์ˆ˜์ • : ์„ค์ • clientReady = false ๋‚ด์—์„œ endConnection() ๋ฐฉ๋ฒ• (์˜ˆ : ๋ผ์ธ 179 ์ดํ›„). ๋‚˜๋Š” ์ด๊ฒƒ์„ ์žฅ์น˜์—์„œ ํ…Œ์ŠคํŠธํ–ˆ์œผ๋ฉฐ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ธ์•ฑ ๊ตฌ๋งค ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ๋‹ค์‹œ ๋งˆ์šดํŠธํ•ด๋„ ์˜ค๋ฅ˜๋‚˜ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ mBillingClient ๋ฅผ null๋กœ ์„ค์ •

@ mitchellmcm27 ์žก์•„ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งค์šฐ ์œ ๋งํ•˜๋ฉฐ ๊ท€ํ•˜์—๊ฒŒ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ์˜ ๋ณต์ œ ์—†์ด๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•จ๊ป˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ๊ฐœ์„  ํ•  ์ƒ๊ฐ์ด ์žˆ์œผ์‹œ๋ฉด PR ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

์นœ์• ํ•˜๋Š”.

์ง€๊ธˆ๊นŒ์ง€ ์ˆ˜์ฒœ ๋ฒˆ์˜ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ํ†ตํ•ด ๋‚ด ์•ฑ์˜ ์ตœ์‹  ๋ฒ„์ „์—์„œ 2.4.0-beta6์„ ์ถœ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„์ง ์ถฉ๋Œ์ด ์—†์Šต๋‹ˆ๋‹ค ๐Ÿ‘

๋ฉ‹์ง€๋„ค์š”. ์ˆ˜์ • ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์ง€๊ธˆ์€์ด ๋ฌธ์ œ๋ฅผ ์ข…๋ฃŒํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ ๋™์ผํ•œ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋กœ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ–ˆ์ง€๋งŒ ์ด์ „๋ณด๋‹ค ํ›จ์”ฌ ์ ์Šต๋‹ˆ๋‹ค.

java.lang.RuntimeException: 
  at com.facebook.react.bridge.CallbackImpl.invoke (CallbackImpl.java:28)
  at com.facebook.react.bridge.PromiseImpl.resolve (PromiseImpl.java:30)
  at com.dooboolab.RNIap.RNIapModule$4.run (RNIapModule.java:155)
  at com.dooboolab.RNIap.RNIapModule$3.onBillingSetupFinished (RNIapModule.java:124)
  at com.android.billingclient.api.BillingClientImpl$BillingServiceConnection.onServiceConnected (BillingClientImpl.java:903)
  at android.app.LoadedApk$ServiceDispatcher.doConnected (LoadedApk.java:1658)
  at android.app.LoadedApk$ServiceDispatcher$RunConnection.run (LoadedApk.java:1687)
  at android.os.Handler.handleCallback (Handler.java:789)
  at android.os.Handler.dispatchMessage (Handler.java:98)
  at android.os.Looper.loop (Looper.java:164)
  at android.app.ActivityThread.main (ActivityThread.java:6944)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:327)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1374)

๋˜ํ•œ 2.4.0-beta6์—์„œ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ์Šคํƒ ์ถ”์ ์„ ์–ป์Šต๋‹ˆ๋‹ค.

์ด ๋ฒ„๊ทธ๋Š” ๋‹ค์‹œ ์—ด์–ด์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

schermata 2019-01-11 alle 10 51 20

24 ์‹œ๊ฐ„ ์ด๋‚ด์— 120 ๋ฒˆ ์ถฉ๋Œ.

์ตœ๊ทผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์œผ๋กœ ์ธํ•ด ์ž์ฒด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด mBillingClient ์ด null์ด ์•„๋‹ˆ์ง€๋งŒ ์•„์ง ์ค€๋น„๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ensureConnection ํ•จ์ˆ˜๊ฐ€ mBillingClient ๋ฅผ ๋ฎ์–ด ์”๋‹ˆ๋‹ค.

์•ˆ์ •๋œ ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑ ํ•  ์ˆ˜ ์žˆ๋„๋ก์ด ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์‹œ์ž‘ํ•œ ๋ฒ„์ „์„ ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋‚˜๋„ ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘๋˜์–ด์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์›๋ž˜ ๋ฌธ์ œ๋Š” ์ฝœ๋ฐฑ๊ณผ ๊ด€๋ จ์ด ์žˆ์—ˆ์œผ๋ฉฐ ์ด์— ๋Œ€ํ•œ ์ˆ˜์ •์œผ๋กœ "null object reference"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ํ›„์ž๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ (๋‚˜๋Š” ๋” ๋งŽ์€ ๋ฌธ์ œ๊ฐ€์žˆ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” @LinusU์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค), ์šฐ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ์›๋ž˜ ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

beta6์˜ ์ฝœ๋ฐฑ๊ณผ ๊ด€๋ จ๋œ ๋ช‡ ๊ฐ€์ง€ ์ถฉ๋Œ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋‹ค์‹œ ์—ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ PR ๋ฅผ ์ค„ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์–ธ์ œ ์†Œ๊ฐœ๋˜์—ˆ๋Š”์ง€ ์•„์‹ญ๋‹ˆ๊นŒ? ๋กค๋ฐฑํ•˜๋Š” ๊ฒƒ ์ด์ƒ์œผ๋กœ ๊ธฐ์ฉ๋‹ˆ๋‹ค.

Android์—์„œ 2.3.23๋ฟ๋งŒ ์•„๋‹ˆ๋ผ beta6์—๋„ ์ถฉ๋Œ์ด ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด [1] ์ฃผ์„์— ๋”ฐ๋ผ ์žฌ์ƒ์„ฑํ•˜๊ธฐ๊ฐ€ ์ƒ๋‹นํžˆ ์‰ฝ์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘ : ์ด์ œ ์ง‘์— ์žˆ์œผ๋ฏ€๋กœ beta6 ์šฉ Android ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋กœ ๋””๋ฒ„๊น…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋„์›€์ด๋˜๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค (์ ์–ด๋„ ๋‚ด ์•ฑ์—์„œ๋Š” getAvailablePurchases๋ฅผ ํ˜ธ์ถœ).

  1. "inapp"์— ๋Œ€ํ•œ getAvailableItemsByType์€ mBillingClient ์„ค์ •์—†์ด ์•„์ง ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
  2. ๊ฒฐ์ œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„ค์ •๋˜๊ณ  ๋ฆฌ์Šค๋„ˆ์˜ onBillingSetupFinished๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ์ฝœ๋ฐฑ (Promise)์ด ์‚ฌ์šฉ (ํ•ด๊ฒฐ)๋ฉ๋‹ˆ๋‹ค. ๋ฆฌ์Šค๋„ˆ๋Š” ์—ฌ์ „ํžˆ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  3. "subs"์— ๋Œ€ํ•œ getAvailableItemsByType์€ mBillingClient ์„ค์ •๊ณผ ํ•จ๊ป˜ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ ์ฝœ๋ฐฑ (Promise)์ด ์‚ฌ์šฉ (ํ•ด๊ฒฐ๋จ)๋ฉ๋‹ˆ๋‹ค.
  4. Play ์Šคํ† ์–ด ์„œ๋น„์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. BillingClientImpl์˜ onServiceDisconnected๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ํŒŒํŠธ 1์˜ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ ์„œ๋น„์Šค๊ฐ€ ์‹œ์ž‘๋˜๊ณ  1 ๋ถ€์˜ ๋ฆฌ์Šค๋„ˆ๊ฐ€ onBillingSetupFinished๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  1 ๋‹จ๊ณ„์˜ ์ฝœ๋ฐฑ (Promise)์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฉด ๋‹ค์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์€ ์ฒญ์ทจ์ž๋ฅผ ์ฃฝ์ด๋Š” ์‹คํ—˜์„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

[1]
https://github.com/googlesamples/android-play-billing/issues/45#issuecomment -324466519

# 379๋ฅผ ๋ณ‘ํ•ฉํ•˜๊ณ  beta8 ๋ฆด๋ฆฌ์Šคํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์„ ์‹œ๋„ํ•˜์‹ญ์‹œ์˜ค.

์ตœ์‹  ๋ฒ„์ „์—์„œ ์ˆ˜์ • ๋˜์—ˆ์Šต๋‹ˆ๊นŒ? ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๊นŒ?

@hyochan ์ด ๋ฌธ์ œ๋Š” ์—ฌ์ „ํžˆ 3.0.0-rc-2์— ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

java.lang.RuntimeException
com.dooboolab.RNIap.RNIapModule$3.onBillingSetupFinished

java.lang.RuntimeException: 
  at com.facebook.react.bridge.CallbackImpl.invoke (CallbackImpl.java:28)
  at com.facebook.react.bridge.PromiseImpl.resolve (PromiseImpl.java:30)
  at com.dooboolab.RNIap.RNIapModule$3.onBillingSetupFinished (RNIapModule.java:129)
  at com.android.billingclient.api.BillingClientImpl$BillingServiceConnection$1.run (BillingClientImpl.java:1518)
  at android.os.Handler.handleCallback (Handler.java:739)
  at android.os.Handler.dispatchMessage (Handler.java:95)
  at android.os.Looper.loop (Looper.java:148)
  at android.app.ActivityThread.main (ActivityThread.java:7325)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1230)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1120)

@fokoz 3.0.0-rc-4 ์‹œ๋„ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@hyochan try catch ํ•˜๋ฉด์ด ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. rc-4๊ฐ€์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค !

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰