# 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 Code | Value | Description |
|---|---|---|
RESULT_OK | 0 | Indicates 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_CANCELED | 1 | Indicates that the user pressed the back button on the checkout page instead of buying the item. |
RESULT_SERVICE_UNAVAILABLE | 2 | Indicates that the network connection is down. |
RESULT_BILLING_UNAVAILABLE | 3 | Indicates 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_UNAVAILABLE | 4 | Indicates 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_ERROR | 5 | Indicates 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_ERROR | 6 | Indicates 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.
| Intent | Extra | Description |
|---|---|---|
com.android.vending.billing.RESPONSE_CODE | request_id | A long 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_CODE | response_code | An int representing the actual Google Play server response code. |
com.android.vending.billing.IN_APP_NOTIFY | notification_id | A String 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_CHANGED | inapp_signed_data | A String 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_CHANGED | inapp_signature | A String 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()를 호출하고 다루는 페이지이다.
댓글 없음:
댓글 쓰기