6월 3일의 ⁠#Android11: 베타 버전 출시 행사에 참여하세요.

Google Play 결제 라이브러리로 AIDL 이전 가이드

이 주제에서는 Android 인터페이스 정의 언어(AIDL)를 사용하는 Google Play 결제 통합에서 이전하는 방법을 설명합니다. AIDL을 사용하여 Google Play 결제에 액세스하는 기능은 지원이 중단되며 앞으로 모든 통합에서 Google Play 결제 라이브러리를 사용해야 합니다.

이전 단계

Google Play 결제 라이브러리 가져오기

먼저 Google Play 결제 라이브러리에 종속 항목을 추가합니다. Gradle을 사용한다면 앱의 build.gradle 파일에 다음을 추가할 수 있습니다.

dependencies {
        implementation 'com.android.billingclient:billing:2.2.0'
    }
    

이전 참조 코드에서 복사했을 수도 있는 IabHelper와 같은 '글루' 코드는 삭제할 수 있습니다. IabHelper에서 제공하는 기능은 이제 Google Play 결제 라이브러리의 일부입니다.

com.android.vending.BILLING 권한 삭제

Google Play 결제 라이브러리는 매니페스트에 com.android.vending.BILLING 권한을 삽입합니다. 이제 앱 매니페스트에 이 권한을 명시적으로 추가할 필요가 없습니다.

Google Play 결제에 연결

Google Play 결제 라이브러리의 BillingClient에서 연결 관리를 처리합니다. 이전하려면 앱을 다음과 같이 변경합니다.

다음 예는 이러한 변경사항을 적용하기 전과 후의 앱 모습을 보여줍니다.

mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
          ...
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
          ...
        }
    };

    Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
    serviceIntent.setPackage("com.android.vending");
    List<ResolveInfo> intentServices = mContext.getPackageManager()
        .queryIntentServices(serviceIntent, 0);
    if (intentServices != null && !intentServices.isEmpty()) {
        mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
    } else {
        // Handle errors.
        ...
    }

    ...

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        IabResult result;
        if (requestCode != mRequestCode || data == null) {
            // Handle errors.
            ...
        }

        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) {
            // Handle errors.
            ...
        }

        // Process successful purchase.
        ...

        return true;
    }
    

Kotlin

    class MyBillingImpl(private var billingClient: BillingClient) : PurchasesUpdatedListener {

        init {
            billingClient = BillingClient.newBuilder(activity).setListener(this).build()
            billingClient.startConnection(object : BillingClientStateListener {
                override fun onBillingSetupFinished(billingResult: BillingResult?) {
                    // Logic from ServiceConnection.onServiceConnected should be moved here.
                }

                override fun onBillingServiceDisconnected() {
                    // Logic from ServiceConnection.onServiceDisconnected should be moved here.
                }
            })
        }

        override fun onPurchasesUpdated(
            billingResult: BillingResult?,
            purchases: MutableList<Purchase>?
        ) {
            // Logic from onActivityResult should be moved here.
        }
    }
    

자바

    public class MyBillingImpl implements PurchasesUpdatedListener {
        private BillingClient billingClient;
        ...

        public void initialize() {
            billingClient = BillingClient.newBuilder(activity).setListener(this).build();
            billingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(BillingResult billingResult) {
                    // Logic from ServiceConnection.onServiceConnected should be moved here.
                }

                @Override
                public void onBillingServiceDisconnected() {
                    // Logic from ServiceConnection.onServiceDisconnected should be moved here.
                }
            });
        }

        @Override
        public void onPurchasesUpdated(
            @BillingResponse int responseCode, @Nullable List<Purchase> purchases) {
            // Logic from onActivityResult should be moved here.
        }
    }
    

구매하기

구매 대화상자를 시작하려면 다음을 실행합니다.

다음 예는 이러한 변경사항을 적용하기 전과 후의 앱 모습을 보여줍니다.

// Query Skus
    String skuToSell = "premium_upgrade";
    ArrayList<String> skus = new Arraylist<>();
    skus.add(skuToSell);
    Bundle querySkus = new Bundle();
    querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, skus);
    Bundle skuDetails = mService.getSkuDetails(3,
                                               mContext.getPackageName(),
                                               itemType,
                                               querySkus);

    if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) {
        // Handle errors.
        ...
    }

    // Launch Buy Flow
    Bundle buyIntentBundle = mService.getBuyIntent(3,
                                                   mContext.getPackageName(),
                                                   skuToSell,
                                                   "Inapp",
                                                   "");

    int response = getResponseCodeFromBundle(buyIntentBundle);
    if (response != BILLING_RESPONSE_RESULT_OK) {
        // Handle errors.
        ...
    }

    PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
    act.startIntentSenderForResult(pendingIntent.getIntentSender(),
                                   requestCode,
                                   new Intent(),
                                   Integer.valueOf(0),
                                   Integer.valueOf(0),
                                   Integer.valueOf(0));

    // Purchase is handled in onActivityResult illustrated in the previous section.
    

Kotlin

    val skuToSell = "premium_upgrade"
    val skuList = mutableListOf<String>()
    skuList.add(skuToSell)
    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
    billingClient.querySkuDetailsAsync(params.build(),
      object : SkuDetailsResponseListener {
        override fun onSkuDetailsResponse(
          billingResult: BillingResult?, skuDetailsList: MutableList<SkuDetails>?) {
            // Process the result.
            }
        })

    // SkuDetails object obtained above.
    val skuDetails = ...
    val purchaseParams = BillingFlowParams.newBuilder()
      .setSkuDetails(skuDetails)
       .build()

    billingClient.launchBillingFlow(activity, purchaseParams)

    // Purchase is handled in onPurchasesUpdated illustrated in the previous section
    

자바

    String skuToSell = "premium_upgrade";
    List<String> skuList = new ArrayList<> ();
    skuList.add(skuToSell);
    SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
    params.setSkusList(skuList).setType(SkuType.INAPP);
    billingClient.querySkuDetailsAsync(params.build(),
        new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(BillingResult billingResult,
                                             List<SkuDetails> skuDetailsList) {
                // Process the result.
                ...
            }
        });

    // SkuDetails object obtained above.
    SkuDetails skuDetails = ...;

    BillingFlowParams purchaseParams =
        BillingFlowParams.newBuilder()
                .setSkuDetails(skuDetails)
                .build();

    mBillingClient.launchBillingFlow(mActivity, purchaseParams);

    // Purchase is handled in onPurchasesUpdated illustrated in the previous section.
    

구매 사용

Google Play 결제 라이브러리를 사용하여 구매를 소비하는 방법은 다음과 같습니다.

다음 예는 이러한 변경사항을 적용하기 전과 후의 앱 모습을 보여줍니다.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        int responseCode = data.getIntExtra(RESPONSE_CODE);
        JSONObject purchaseData =
            new JSONObject(data.getStringExtra("INAPP_PURCHASE_DATA"));

        String token = purchaseData.get("purchaseToken");

        ...

        // Consume purchase
        int response = mService.consumePurchase(3, mContext.getPackageName(), token);
        if (response != BILLING_RESPONSE_RESULT_OK) {
            // Handle errors.
            ...
        }

        // Handle successful consumption.
    }
    

Kotlin

    class MyBillingImpl(private val billingClient: BillingClient) :
            ... , ConsumeResponseListener {

      fun consumePurchase(purchaseToken: String) {
        val consumeParams = ConsumeParams
          .newBuilder()
          .setPurchaseToken(purchaseToken)
          .build()
      }

      override fun onConsumeResponse(
        billingResult: BillingResult?,
        purchaseToken: String?) {
          // Handle consumption
        }
     }
    

자바

    public class MyBillingImpl implements ..., ConsumeResponseListener {
        private BillingClient billingClient;
        ...

        public void consumePurchase(String purchaseToken) {
            ConsumeParams consumeParams =
            ConsumeParams.newBuilder()
                    .setPurchaseToken(purchaseToken)
                    .build();
        }

        @Override
        void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            // Handle consumption.
            ...
        }
    }
    

구매 확인

Google Play 결제 라이브러리 버전 2.0부터는 앱에서 모든 구매를 소비하거나 확인해야 합니다.

3일 이내에 구매를 소비하거나 확인하지 않으면 Google에서 자동으로 구매를 취소하고 사용자에게 환불합니다. 자세한 내용은 구매 확인을 참조하세요.

앱 외부 구매 인식

앱 외부 구매 처리를 Google Play 결제 라이브러리로 이전하려면 다음 안내를 따르세요.

  • 앱의 onResume() 콜백에서 앱이 BillingClient.queryPurchases()를 호출하는지 확인합니다.
  • com.android.vending.billing.PURCHASES_UPDATED의 broadcast receiver를 삭제하고 이에 대응하는 콜백 코드를 PurchasesUpdatedListener로 이동합니다.

이전에는 Google Play 결제를 AIDL과 통합할 때 앱 외부에서 발생한 구매를 처리하기 위해 리스너를 등록하여 com.android.vending.billing.PURCHASES_UPDATED 인텐트를 수신해야 했습니다.

Google Play 결제 라이브러리를 사용하면 앱은 항상 앱이 실행되지 않는 동안 발생한 모든 구매가 인식된 것을 확인하는 첫 단계로 앱의 onResume() 콜백에서 queryPurchases()를 호출해야 합니다. 앱이 실행되는 동안 라이브러리는 자동으로 앱 외부 구매를 수신 대기하고 PurchasesUpdatedListener를 통해 알려줍니다.

대기 중인 거래 처리

Google Play 결제 라이브러리 버전 2.0부터 앱은 자격을 부여하기 전에 구매 후 추가 작업이 필요한 대기 중인 거래를 처리해야 합니다. 예를 들어, 사용자가 오프라인 상점에서 현금으로 인앱 상품을 구매하기로 선택할 수도 있습니다. 이는 거래가 앱 외부에서 완료된다는 것입니다. 이 시나리오에서는 사용자가 거래를 완료한 후에만 자격을 부여해야 합니다.

자세한 내용은 대기 중인 거래 지원을 참조하세요.

개발자 페이로드

개발자 페이로드는 과거에 사기를 예방하고 올바른 사용자에게 구매를 제공하는 등 다양한 목적으로 사용되었습니다. 이제 Google Play 결제 라이브러리에서 이러한 사용 사례를 지원하므로 Google Play 결제 라이브러리의 버전 2.2부터는 개발자 페이로드 지원이 중단됩니다. 자세한 내용은 개발자 페이로드를 참조하세요.

자세한 오류 메시지

Google Play 결제 라이브러리 버전 2.0부터 모든 오류에는 상응하는 디버깅 관련 메시지가 있습니다. 이러한 메시지는 BillingResult.getDebugMessage()를 호출하여 얻을 수 있습니다.