안드로이드

[Android] 인앱 결제(inapp billing) 순서

코딩하는후운 2022. 10. 20. 14:11
반응형

Android 인앱 결제(inapp billing) 순서

1. 구글 인앱 빌링 라이브러리 설치 및 임포트
2. aidl, util부분 가져온다.
3. 매니페트스트 수정
	-<uses-permission android:name="com.android.vending.BILLING" />
	-V2에 비해 AndroidManifest.xml이 쉬워졌습니다. 위와같이 빌링 관련된 권한만 추가해주면 됩니다.
4. 인앱 빌링 초기화 및 인벤토리 요청
	static final int RC_REQUEST = 1001	//IabHelper용 콜백 결과를 구분하기 위한 상수
	private IabHelper mHelper;
	
	/*
		strPublicKey는
		구글 개발자 콘솔 -> 출시 앱 선택 -> 서비스 및 API -> 
		라이선스 및 인앱 결제 
		라이선스 키를 사용하여 앱의 무단 배포를 방지할 수 있습니다. 이 키를 사용하여 인앱 결제로 구매한 항목을 확인할 수도 있습니다. 라이선스에 대해 자세히 알아보기 
		이 애플리케이션용 라이선스 키 
		바이너리에 포함하려는 Base64 인코딩 RSA 공개 키입니다. 공백을 삭제해 주세요. 
		**BASE64 키 
	*/
	
	
	public void InAppInit_U(String strPublicKey, boolean bDebug)
	{
		mHelper = new IabHelper(this, strPublicKey);		
	}
	
	if(bDebug == true)
	{
		mHelper.enableDebugLogging(true, "IAB");                                                                   
	}

5.초기화 성공 후 onIabSetupFinished에서 구매 후 소비되지 않은 아이템들이 있는지 확인을 위해 queryInventoryAsync를 실행
	mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
	{
		@Override
		public void onIabSetupFinished(IabResult result)
		{
			boolean bInit = result.isSuccess();
			
			if(bInit == true)
			{
				mHelper.queryInventoryAsync(mGotInventoryListener);
			}
			
			
		}
	}




6. 구매목록 쿼리 요청 후 콜백된 onQueryInventoryFinished에서 결과에 따라 처리하면 되는데 일단 인벤토리에 있는 것중
아이템 타입이 인앱인 것 즉, 소모성 아이템인 것을 모두 가져와 consumeAsync를 통해 구글에 소비 요청을 합니다.
이때 바로 위처럼 하면
List<String> inappList = inventory.getAllOwnedSkus(IabHelper.ITEM_TYPE_INAPP);
위 부분에서 에러가 발생하는데요, getAllOwnedSkus가 public가 아니라서 그렇습니다. Inventory.java 의 71라인에 있는 부분에 public를 추가해줍니다.


IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() 
{
	public void onQueryInventoryFinished(IabResult result, Inventory inventory)
	{
		if(result.isFailure())
		{
		
		}
		
		List<String> inappList = inventory.getAllOwnedSkus(IabHelper.ITEM_TYPE_INAPP);
		
		for(String inappSku : inappList)
		{
			Purchase purchase = inventory.getPurchase(inappSku);
			mHelper.consumeAsync(putchase, mConsumeFinishedListener);
		}
	}
}



7. 아이템 구매 및 소비

//아이템 구매는 V2와 마찬가지로 아이템 id만 있으면 요청할 수 있습니다. RC_REQUEST는 위에서 추가했던 상수
public void inAppBuyItem(final String strItemnId)
{
	runOnUiThread(new Runnable()
	{
		@Override
		public void run()		
		{
			String payload = "";
			mHelper.launchPurchaseFlow(액티비티, strItemId, RC_REQUEST, mPurchaseFinishedListener, payload);
		}
	});
}


8.구매요청 완료

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener()
{
	public void onIabPurchaseFinished(IabResult result, Purchase purchase)
	{
		if(purchase != null)
		{
			if(!verifyDeveloperPayload(purchase))
			{
				//Error purchasing
			
			}
			
			//구매완료후 콜백처리부분
			//V2에서는 구매완료후 소비를 바로 하지 않아도 되었는데 V3에서는 바로 소비를 하도록 강제해 중복구매를 방지
			mHelper.consumeAsync(purchase, mConsumeFinishedListener);
		}
		else
		{
			//구매실패에대한 처리
		}
	}
}


9. 인증강화 부분인듯

boolean verifyDeveloperPayload(Purchase p)
{
	String payload = p.getDeveloperPayload();

	//인증 강화 부분인듯한데 샘플그대로 true	
	return true;
}


10. 소비 요청에 대한 콜백

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener()
{
	public void onConsumeFinished(Purchase purchase, IabResult result)
	{
		SendConsumeResult(purchase, result);
	}
};

protected void SendConsumeResult(Purchase purchase, IabResult result)
{
	JSONObject jsonObj = new JSONObject();
	
	try
	{
		jsonObj.put("Result", result.getResponse());
		if(purchase !=null)
		{
			jsonObj.put("OrderId", purchase.getOrderId());
			jsonObj.put("Sku", purchase.getSku());
			jsonObj.put("purchaseData", purchase.getOriginalJson());
			jsonObj.put("signature", purchase.getSignature());
		}		
	}
	catch(JSONException e)
	{
		e.printStackTrace();
	}	
	
}



11. 인앱 구매 처리 부분
//strPurchaseData와 strSignature를 서버에 보내 영수증 확인을 처리하도록 한다.


public void InAppButItem(String strItemId)
{
	curActivity.Call("InAppBuyItem_U", strItemId);
}

// IAB Helper error codes
public static final int IABHELPER_ERROR_BASE = -1000;
public static final int IABHELPER_REMOTE_EXCEPTION = -1001;
public static final int IABHELPER_BAD_RESPONSE = -1002;
public static final int IABHELPER_VERIFICATION_FAILED = -1003;
public static final int IABHELPER_SEND_INTENT_FAILED = -1004;
public static final int IABHELPER_USER_CANCELLED = -1005;
public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006;
public static final int IABHELPER_MISSING_TOKEN = -1007;
public static final int IABHELPER_UNKNOWN_ERROR = -1008;
public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009;
public static final int IABHELPER_INVALID_CONSUMPTION = -1010;

private void InAppBuyItemResult_J(String strResult)
{
	int iResult = System.Conver.ToInt32(strResult);
	switch(iResult)
	{
		case -1005:
			//InAppBuyItem Cancel
			break;
		default:
			//InAppBuyItem Failed
			break;
	}
}


// Billing response codes
public static final int BILLING_RESPONSE_RESULT_OK = 0;
public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4;
public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5;
public static final int BILLING_RESPONSE_RESULT_ERROR = 6;
public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8;

private void InAppConsumeResult_J(String strResult)
{
	JsonData jData = JSonMapper.ToObject(strResult);
	
	int iREsult = System.Convert.ToInt32(jData["result"].ToString());
	switch(iResult)
	{
		case 0:
			String strOrderId = jData["OrderId"].ToString();
			String strSku = jData["Sku"].ToString();
			String strPurchaseData = jData["purchaseData"].ToString();
			String strSignature = jData["signature"].ToString();
			//성공
			break;
		default:
			//실패
		break;
	}
}


12. 후처리
@Override
public void onDestroy() {
	super.onDestroy();

	// very important:
	Log.d(LOG_TAG, "Destroying helper.");
	if (mHelper != null) mHelper.dispose();
	mHelper = null;
}

//구매요청후 액티비티가 다시 돌아올 때 onActivityResult에서 RC_REQUEST를 가지고 인앱 빌링 처리 후인지 구분합니다.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
	
	if(requestCode == RC_REQUEST) {
	   // 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(LOG_TAG, "onActivityResult handled by IABUtil.");
	   }
	}
}



13. 구글마켓에 apk파일 올리기
14. 인앱상품을 등록 (인앱상품을 등록하려면 구글 계정에 판매자계정이 등록 되어있어야한다.)
http://theeye.pe.kr/archives/2130

관리되는 제품, 구독

기본적으로 모든 관리되는 제품은 구매가 성공적으로 이루어졌을 때 보유(Owned) 상태가 됩니다. 이 상태에서는 Google Play를 통해 같은 상품을 중복 구매할 수 없게 됩니다.
이 보유상태의 제품을 consumePurchase() 를 호출하여 소진(Consume)하여 다시한번 비보유(Unowned) 상태로 되돌릴 수 있습니다.
이 소진행위는 Google Play로 하여금 다시 해당 상품을 구매 가능한 상태로 되돌리며 이전의 구매 정보를 파기하게 됩니다.
소진이 불가능한 상품이라는 의미는 똑같은 상품을 두번 이상 구매할 수 없는것을 의미합니다. 소진이 가능한 상품의 경우 똑같은 상품을 계속해서 반복 구매하는 것이 가능합니다. 


구독의 경우에는 다음과 같이 한달 또는 일년 단위로 자동 연장되는 결제 방식을 의미합니다. 
음원 서비스들에서 주로 볼 수 있는 상품의 모습이라고 생각됩니다. 
구독의 취소는 [Google 월렛의 내 구독 정보]에서 취소할 수 있습니다.


14-1. 판매자계정 등록 
세금정보
사업자유형
1.일반과세
2.간이과세
3.개인

사업자등록번호 : 

대표자명 : 

비지니스 법률 정보
도/시 : 
시/구/군 : 
주소 : 
주소입력란 : 

상호 : 

전화번호 : 

웹사이트 : 

판매하는 제품 및 서비스 : 

고객 서비스 이메일 : 

신용카드 명세서 이름 : (영어로)

***입력후 제출




15. 사용할때 에러사항
구매버튼 눌러도 반응이없을때 
- 1003: Purchase signature verification failed
: 이 경우에 base64키값이 달랐었다. 

결제를 시도해도 에러 메시지뜸
Error: Error purchasing: labResult: Unable to buy item(response: 7:Item Already Owned)
: 이 경우에 m_app.m_iabHelper.consumeAsync(purchases, mConsumeMultiFinishedListener); 를 호출해주어 아이템을 소비한다.

요청하신 항목은 구매할 수 없습니다.
: 이 경우에는 구글에 올라간 버전이 테스트한 버전보다 낮았었다. 업데이트 시켜준다.

 

 

참조 :

http://westwoodforever.blogspot.kr/2013/10/unity3d-integration-google-in-app.html

반응형