2012년 5월 10일 목요일

[In-app Billing_1]In-app Billing 구현 2


# In-App Billing을 구현해 보자.

- 아래는 전반적인 구현 순서이다.


1. in-app billing 샘플 애플리케이션을 다운 받는다.

2. IMarketBillingService.aidl 파일을 당신의 프로젝트에 추가한다.

3. Manifest.xml을 업데이트한다.

4. Service를 만들어 MarketBillingService 와 묶는다. 이는 구글 플레이 요청과 응답을 받기 위함이다.

5.  broadcast intents와 관련된 응답을 처리하기 위한 BroadcastReceiver를 만든다.

6.  in-app billing이 지원하는 애플리케이션 코드를 수정한다.



-  전 게시물에서 서비스를 만들어 다루는 법(4번)을 설명을 드렸습니다. 오늘은 BroadcastReceiver를 만드는 것으로 시작하겠습니다.


@ Downloading the Sample Application

- 구글 플레이는 사용자 애플리케이션에 비동기적으로 결제 정보를 보냅니다. 이를 받기 위해서는 BroadcastReceiver가 있어야 합니다. 이 Receiver 다루는 응답은 아래와 같습니다.

  • com.android.vending.billing.RESPONSE_CODE :  요청이 잘 들어 갔는지에 대한 응답이다. 요청이 완료되었다는 의미는 아니다. 단순히 전달이 잘되었는지만 알려준다.
Table 1. Summary of response codes returned by Google Play.
Response CodeValueDescription
RESULT_OK0Indicates that the request was sent to the server successfully. When this code is returned in response to aCHECK_BILLING_SUPPORTED request, indicates that billing is supported.
RESULT_USER_CANCELED1Indicates that the user pressed the back button on the checkout page instead of buying the item.
RESULT_SERVICE_UNAVAILABLE2Indicates that the network connection is down.
RESULT_BILLING_UNAVAILABLE3Indicates that in-app billing is not available because the API_VERSION that you specified is not recognized by the Google Play application or the user is ineligible for in-app billing (for example, the user resides in a country that prohibits in-app purchases).
RESULT_ITEM_UNAVAILABLE4Indicates that Google Play cannot find the requested item in the application's product list. This can happen if the product ID is misspelled in your REQUEST_PURCHASE request or if an item is unpublished in the application's product list.
RESULT_DEVELOPER_ERROR5Indicates that an application is trying to make an in-app billing request but the application has not declared the com.android.vending.BILLING permission in its manifest. Can also indicate that an application is not properly signed, or that you sent a malformed request, such as a request with missing Bundle keys or a request that uses an unrecognized request type.
RESULT_ERROR6Indicates an unexpected server error. For example, this error is triggered if you try to purchase an item from yourself, which is not allowed by Google Checkout.

  • com.android.vending.billing.IN_APP_NOTIFY : 요청이 응답되었는지 알려 준다. 이때 notification_id를 포함하는데 이것을 이용해 GET_PURCHASE_INFORMATION 요청을 보내면 아래 응답에서 세부적인 정보를 알수 있다.
  • com.android.vending.billing.PURCHASE_STATE_CHANGED : 요청에 대한 실제적인 응답 정보를 담고 있다. 결제가 성공했는지 실패했는지, 환불되었는지에 대한 정보를 담고 있다. 
Table 1. Description of broadcast intent extras that are sent in response to billing requests.
IntentExtraDescription
com.android.vending.billing.RESPONSE_CODErequest_idlong representing a request ID. A request ID identifies a specific billing request and is returned by Google Play at the time a request is made.
com.android.vending.billing.RESPONSE_CODEresponse_codeAn int representing the actual Google Play server response code.
com.android.vending.billing.IN_APP_NOTIFYnotification_idString representing the notification ID for a given purchase state change. Google Play notifies you when there is a purchase state change and the notification includes a unique notification ID. To get the details of the purchase state change, you send the notification ID with theGET_PURCHASE_INFORMATION request.
com.android.vending.billing.PURCHASE_STATE_CHANGEDinapp_signed_dataString representing the signed JSON string. The JSON string contains information about the billing transaction, such as order number, amount, and the item that was purchased or refunded.
com.android.vending.billing.PURCHASE_STATE_CHANGEDinapp_signatureString representing the signature of the JSON string.

 - 아래의 샘플 코드는 Broadcast intent 응답을 어떻게 받아서 Extra 정보를 추출하는지 보여준다.

public class BillingReceiver extends BroadcastReceiver {

  private static final String TAG = "BillingReceiver";

  // Intent actions that we receive in the BillingReceiver from Google Play.
  // These are defined by Google Play and cannot be changed.
  // The sample application defines these in the Consts.java file.
  public static final String ACTION_NOTIFY = "com.android.vending.billing.IN_APP_NOTIFY";
  public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE";
  public static final String ACTION_PURCHASE_STATE_CHANGED =
    "com.android.vending.billing.PURCHASE_STATE_CHANGED";

  // The intent extras that are passed in an intent from Google Play.
  // These are defined by Google Play and cannot be changed.
  // The sample application defines these in the Consts.java file.
  public static final String NOTIFICATION_ID = "notification_id";
  public static final String INAPP_SIGNED_DATA = "inapp_signed_data";
  public static final String INAPP_SIGNATURE = "inapp_signature";
  public static final String INAPP_REQUEST_ID = "request_id";
  public static final String INAPP_RESPONSE_CODE = "response_code";


  @Override
  public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (ACTION_PURCHASE_STATE_CHANGED.equals(action)) {
      String signedData = intent.getStringExtra(INAPP_SIGNED_DATA);
      String signature = intent.getStringExtra(INAPP_SIGNATURE);
      // Do something with the signedData and the signature.
    } else if (ACTION_NOTIFY.equals(action)) {
      String notifyId = intent.getStringExtra(NOTIFICATION_ID);
      // Do something with the notifyId.
    } else if (ACTION_RESPONSE_CODE.equals(action)) {
      long requestId = intent.getLongExtra(INAPP_REQUEST_ID, -1);
      int responseCodeIndex = intent.getIntExtra(INAPP_RESPONSE_CODE,
        ResponseCode.RESULT_ERROR.ordinal());
      // Do something with the requestId and the responseCodeIndex.
    } else {
      Log.w(TAG, "unexpected action: " + action);
    }
  }
  // Perform other processing here, such as forwarding intent messages to your local service.
}

@ Verifying Signatures and Nonces

- 인앱 결제는 두가지 메커니즘으로 결제를 식별한다. nonces와 signatures이다. nonces는 암호화된 보안 번호이다. 사용자 애플리케이션에서 생성해서 GET_PURCHASE_INFORMATION 과 RESTORE_TRANSACTIONS 요청에 함께 보낸다. 이 nonces는 PURCHASE_STATE_CHANGED broadcast intent응답에 함께 와서 요청에 대한 응답임을 식별한다. 그리고 PURCHASE_STATE_CHANGED 응답에는 세부 결제 정보와 signature를 가지고 있다. 이는 결제를 식별하는 또 하나의 인자이다.


  private static final SecureRandom RANDOM = new SecureRandom();
  private static HashSet<Long> sKnownNonces = new HashSet<Long>();

  public static long generateNonce() {
    long nonce = RANDOM.nextLong();
    sKnownNonces.add(nonce);
    return nonce;
  }

  public static void removeNonce(long nonce) {
    sKnownNonces.remove(nonce);
  }

  public static boolean isNonceKnown(long nonce) {
    return sKnownNonces.contains(nonce);
  }

  - 사용자 애플리케이션은 PURCHASE_STATE_CHANGED에 포함되어 있는 signature를 통해 거래를 식별할 수 있다. 이때 구글 public key가 있어야 한다. 이 키는 퍼블리셔 계정 사이트에서 얻을 수 있다.


Figure 2. The Licensing and In-app Billing panel of your account's Edit Profile page lets you see your public key.
# 구글 공개키를 그대로 String타입에 넣어서 코딩 하지 말것을 권한다. 이는 해커나 크래커의 타겟이 될수 있다.


@ Modifying Your Application Code


  * Creating a storage mechanism for storing purchase information

 - 결제를 성공하고 결제 내역을 저장해야 한다. 샘플 애플리케이션의 경우 내부에 저장하지만 이것은 보안적으로 좋지 않다. 서비스 서버를 운영한다면 거기에 저장하는 것이 좋다.

# 만약 결제 내역을 내부에 저장 한다면 암호화를 해서 넣어야 한다. "unmanaged" 결제의 경우 반드시 결제 내역을 저장해서 관리를 해야 한다. 즉, RESTORE_TRANSACTIONS 으로도 결제가 회복되지 않으므로 unmanaged 결제는 백업을 꼭 해야 한다.

  * Creating a user interface for selecting items


- 결제를 위한 UI페이지를 만들어야 한다. 이 페이지는 sendBillingRequest()를 호출하고 다루는 페이지이다.

















댓글 없음:

댓글 쓰기