Android 애플리케이션에서 인앱 결제를 구현하는 방법은 무엇입니까?
Android 앱에서 In-App Billing을 구현하는 것은 매우 복잡한 것입니다. 어떻게 할 수 있습니까? SDK의 샘플 앱 하나의 활동은 하나의 여러 활동, 하나의 여러 활동이있는 광산과 같은 애플리케이션의 경우에는 단순화됩니다.
글쎄, 내가 경험 한 것을 설명하려고 노력할 것입니다. 나는 나 자신을 이것에 대한 전문가라고 생각하지 않지만 며칠 동안 머리가 부러됩니다.
우선, 예제와 응용 프로그램의 워크 플로를 이해하는 데 매우 안타까웠습니다. 나는 간단한 예제로 시작하는 것이 더 낫다고 생각했지만 코드를 작은 조각으로 분리하는 것이 훨씬 어려우며 무엇이든 깨뜨리고 있는지 알지 못합니다. 내가 가지고있는 것과 그것을 사용하는 것입니다.
모든 구매가 단일 활동이 있습니다. Pro라고합니다.
먼저 공개 마켓 개발자 키로 보안 클래스의 base64EncodedPublicKey 변수를 업데이트해야합니다. 즉, 멋진 예외가 표시됩니다.
음, 내 활동을 BillingService에 다음과 같이 바인딩합니다.
public class Pro extends TrackedActivity implements OnItemClickListener {
private BillingService mBillingService;
private BillingPurchaseObserver mBillingPurchaseObserver;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pro);
//Do my stuff
mBillingService = new BillingService();
mBillingService.setContext(getApplicationContext());
mHandler = new Handler();
mBillingPurchaseObserver = new BillingPurchaseObserver(mHandler);
}
}
@Override
protected void onStart() {
//Register the observer to the service
super.onStart();
ResponseHandler.register(mBillingPurchaseObserver);
}
@Override
protected void onStop() {
//Unregister the observer since you dont need anymore
super.onStop();
ResponseHandler.unregister(mBillingPurchaseObserver);
}
@Override
protected void onDestroy() {
//Unbind the service
super.onDestroy();
mBillingService.unbind();
}
이렇게하면 모든 구매가이 서비스와 통신하여 JSON 요청을 시장에 보냅니다. 구매가 같은 순간에 이루어 졌다고 생각할 수도 있습니다. 요청을 보내면 몇 분 또는 몇 시간 후에 구매가 수 있습니다. 나는 주로 서버 및 신용 카드 승인이라고 생각합니다.
그런 다음 내 항목이있는 ListView는 AlertDialog를 포함하는 항목을 구매하도록 초대합니다. 해당 항목을 클릭하면 다음을 수행합니다.
private class BuyButton implements DialogInterface.OnClickListener {
private BillingItem item = null;
private String developerPayload;
public BuyButton(BillingItem item, String developerPayload) {
this.item = item;
this.developerPayload = developerPayload;
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (GeneralHelper.isOnline(getApplicationContext())){
//I track the buy here with GA SDK.
mBillingService.requestPurchase(this.item.getSku(), this.developerPayload);
} else {
Toast.makeText(getApplicationContext(), R.string.msg_not_online, Toast.LENGTH_SHORT).show();
}
}
}
알겠습니다. 마켓이 열리고 사용자가 구매를 완료하거나 취소하는 것을 볼 수 있습니다.
중요한 것은 시장이 모든 이벤트를 처리하는 PurChaseObserver입니다. (코드를 통해 의견을 참조하십시오).
private class BillingPurchaseObserver extends PurchaseObserver {
public BillingPurchaseObserver(Handler handler) {
super(Pro.this, handler);
}
@Override
public void onBillingSupported(boolean supported) {
if (supported) {
//Enable buy functions. Not required, but you can do stuff here. The market first checks if billing is supported. Maybe your country is not supported, for example.
} else {
Toast.makeText(getApplicationContext(), R.string.billing_not_supported, Toast.LENGTH_LONG).show();
}
}
@Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
//This is the method that is called when the buy is completed or refunded I believe.
// Here you can do something with the developerPayload. Its basically a Tag you can use to follow your transactions. i dont use it.
BillingItem item = BillingItem.getBySku(getApplicationContext(), itemId);
if (purchaseState == PurchaseState.PURCHASED) {
if (item != null){
//This is my own implementation that sets the item purchased in my database. BillingHelper is a class with methods I use to check if the user bought an option and update the UI. You should also check for refunded. You can see the Consts class to find what you need to check for.
boolean resu = item.makePurchased(getApplicationContext());
if (resu){
Toast.makeText(getApplicationContext(), R.string.billing_item_purchased, Toast.LENGTH_LONG).show();
}
}
}
}
private void trackPurchase(BillingItem item, long purchaseTime) {
//My code to track the purchase in GA
}
@Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode) {
//This is the callback that happens when you sent the request. It doesnt mean you bought something. Just that the Market received it.
if (responseCode == ResponseCode.RESULT_OK) {
Toast.makeText(getApplicationContext(), R.string.billing_item_request_sent, Toast.LENGTH_SHORT).show();
} else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
//The user canceled the item.
} else {
//If it got here, the Market had an unexpected problem.
}
}
@Override
public void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode) {
if (responseCode == ResponseCode.RESULT_OK) {
//Restore transactions should only be run once in the lifecycle of your application unless you reinstalled the app or wipe the data.
SharedPreferences.Editor edit = PreferencesHelper.getInstance().getDefaultSettings(getApplicationContext()).edit();
edit.putBoolean(Consts.DB_INITIALIZED, true);
edit.commit();
} else {
//Something went wrong
}
}
}
그리고 나는 당신이 다른 것을 편집 할 필요가 있다고 믿습니다. 나머지 코드는 "작동"합니다. 처음에는 'android.test.purchased'항목에서 샘플 SKU를 사용해 볼 수 있습니다. 지금까지 작동하지만 여전히 작동하지 않습니다. 이 경우 사용자가 기능을 유지하도록 허용하지만 수정하기 전에 완벽하게 작동하는지 확인하고 싶습니다.
당신과 다른 사람들에게 도움이되기를 바랍니다.
V3 : 빠른 시작을위한 튜토리얼이 있습니다. 그는 Google 예제 (Trivial Drive)의 도우미 클래스를 사용하고 있습니다. ... 첫 번째 "Hello Billing"으로 좋습니다 ..
Android 인앱 결제 v3 의 전체 예가 여기에 스크린 샷과 함께 단계별로 제공됩니다. 튜토리얼 : ServiceConnection 클래스를 사용하는 Android 인앱 결제 v3를 확인하세요.
도움이되기를 바랍니다.
자세한 내용은이 안내 : 버전 3 API에서 인앱 결제 구현을 참조 하세요.
프로젝트에 인앱 결제 라이브러리를 통합하기 위해 따라야 할 단계
AndroidManifest.xml 파일을 업데이트하십시오.
ServiceConnection을 만들고 IInAppBillingService에 바인딩합니다.
애플리케이션에서 IInAppBillingService로 인앱 결제 요청을 보냅니다.
Google Play의 인앱 결제 응답을 처리합니다.
AndroidManifest.xml 업데이트
<uses-permission android:name="com.android.vending.BILLING" />
Manifest.xml 파일에 권한 추가
프로젝트에 AIDL 파일 추가
애플리케이션을 빌드하십시오. 프로젝트의 / gen 디렉토리에 IInAppBillingService.java라는 생성 된 파일이 표시됩니다.
build.gradle 파일의 업데이트
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.inducesmile.androidinapppurchase"
minSdkVersion 14
targetSdkVersion 24
versionCode 2
versionName "1.1"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.intuit.sdp:sdp-android:1.0.3'
compile 'com.android.support:support-annotations:24.1.1'
compile 'org.jetbrains:annotations-java5:15.0'
}
InAppPurchaseActivity.java 및 activity_in_app_purchase.xml
여기서 앱 사용자에게 인앱 구매를 할 수있는 기회를 제공합니다. 레이아웃 파일에서 사용자에게 다른 액면가로 구매할 수있는 기회를 제공합니다.
InAppPurchaseActivity.java
참고 : 앱 충돌을 방지하려는 UI가 아닌 경우에서 getAllUserPurchase () 및 itemPurchaseAvailability () 메서드를 호출해야합니다.
public class InAppPurchaseActivity extends AppCompatActivity {
private static final String TAG = InAppPurchaseActivity.class.getSimpleName();
private IInAppBillingService mService;
private CustomSharedPreference customSharedPreference;
String[] productIds = new String[]{Helper.ITEM_ONE_ID, Helper.ITEM_TWO_ID, Helper.ITEM_THREE_ID};
private ImageView buyOneButton, buyTwoButton, buyThreeButton;
private static final char[] symbols = new char[36];
static {
for (int idx = 0; idx < 10; ++idx)
symbols[idx] = (char) ('0' + idx);
for (int idx = 10; idx < 36; ++idx)
symbols[idx] = (char) ('a' + idx - 10);
}
private String appPackageName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_app_purchase);
appPackageName = this.getPackageName();
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
customSharedPreference = new CustomSharedPreference(InAppPurchaseActivity.this);
buyOneButton = (ImageView)findViewById(R.id.buy_one);
buyOneButton.setVisibility(View.GONE);
assert buyOneButton != null;
buyOneButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isBillingSupported()){
Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support));
return;
}
purchaseItem(Helper.ITEM_ONE_ID);
}
});
buyTwoButton = (ImageView)findViewById(R.id.buy_two);
buyTwoButton.setVisibility(View.GONE);
assert buyTwoButton != null;
buyTwoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isBillingSupported()){
Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support));
return;
}
purchaseItem(Helper.ITEM_TWO_ID);
}
});
buyThreeButton = (ImageView)findViewById(R.id.buy_three);
buyThreeButton.setVisibility(View.GONE);
assert buyThreeButton != null;
buyThreeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isBillingSupported()){
Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support));
return;
}
purchaseItem(Helper.ITEM_THREE_ID);
}
});
}
ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
AvailablePurchaseAsyncTask mAsyncTask = new AvailablePurchaseAsyncTask(appPackageName);
mAsyncTask.execute();
}
};
private void purchaseItem(String sku){
String generatedPayload = getPayLoad();
customSharedPreference.setDeveloperPayLoad(generatedPayload);
try {
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", generatedPayload);
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
try {
startIntentSenderForResult(pendingIntent.getIntentSender(), Helper.RESPONSE_CODE, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Helper.RESPONSE_CODE) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject purchaseJsonObject = new JSONObject(purchaseData);
String sku = purchaseJsonObject.getString("productId");
String developerPayload = purchaseJsonObject.getString("developerPayload");
String purchaseToken = purchaseJsonObject.getString("purchaseToken");
//the developerPayload value is better stored in remote database but in this tutorial
//we will use a shared preference
for(int i = 0; i < productIds.length; i++){
if(productIds[i].equals(sku) && developerPayload.equals(customSharedPreference.getDeveloperPayload())){
customSharedPreference.setPurchaseToken(purchaseToken);
//access to private content
Intent contentIntent = new Intent(InAppPurchaseActivity.this, PrivateContentActivity.class);
startActivity(contentIntent);
}
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
}
}
private String getPayLoad(){
RandomString randomString = new RandomString(36);
String payload = randomString.nextString();
return payload;
}
public class RandomString {
private final Random random = new Random();
private final char[] buf;
public RandomString(int length) {
if (length < 1)
throw new IllegalArgumentException("length < 1: " + length);
buf = new char[length];
}
public String nextString() {
for (int idx = 0; idx < buf.length; ++idx)
buf[idx] = symbols[random.nextInt(symbols.length)];
return new String(buf);
}
}
public final class SessionIdentifierGenerator {
private SecureRandom random = new SecureRandom();
public String nextSessionId() {
return new BigInteger(130, random).toString(32);
}
}
private class AvailablePurchaseAsyncTask extends AsyncTask<Void, Void, Bundle> {
String packageName;
public AvailablePurchaseAsyncTask(String packageName){
this.packageName = packageName;
}
@Override
protected Bundle doInBackground(Void... voids) {
ArrayList<String> skuList = new ArrayList<String>();
skuList.add(Helper.ITEM_ONE_ID);
skuList.add(Helper.ITEM_TWO_ID);
skuList.add(Helper.ITEM_THREE_ID);
Bundle query = new Bundle();
query.putStringArrayList(Helper.ITEM_ID_LIST, skuList);
Bundle skuDetails = null;
try {
skuDetails = mService.getSkuDetails(3, packageName, "inapp", query);
} catch (RemoteException e) {
e.printStackTrace();
}
return skuDetails;
}
@Override
protected void onPostExecute(Bundle skuDetails) {
List<AvailablePurchase> canPurchase = new ArrayList<AvailablePurchase>();
int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
if(responseList != null){
for (String thisResponse : responseList) {
JSONObject object = null;
try {
object = new JSONObject(thisResponse);
String sku = object.getString("productId");
String price = object.getString("price");
canPurchase.add(new AvailablePurchase(sku, price));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
if(checkIfPurchaseIsAvailable(canPurchase, productIds[0])){
buyOneButton.setVisibility(View.VISIBLE);
}else{
buyOneButton.setVisibility(View.GONE);
}
if(checkIfPurchaseIsAvailable(canPurchase, productIds[1])){
buyTwoButton.setVisibility(View.VISIBLE);
}else{
buyTwoButton.setVisibility(View.GONE);
}
if(checkIfPurchaseIsAvailable(canPurchase, productIds[2])){
buyThreeButton.setVisibility(View.VISIBLE);
}else{
buyThreeButton.setVisibility(View.GONE);
}
}
}
@org.jetbrains.annotations.Contract("null, _ -> false")
private boolean checkIfPurchaseIsAvailable(List<AvailablePurchase> all, String productId){
if(all == null){ return false;}
for(int i = 0; i < all.size(); i++){
if(all.get(i).getSku().equals(productId)){
return true;
}
}
return false;
}
public boolean isBillingSupported(){
int response = 1;
try {
response = mService.isBillingSupported(3, getPackageName(), "inapp");
} catch (RemoteException e) {
e.printStackTrace();
}
if(response > 0){
return false;
}
return true;
}
public void consumePurchaseItem(String purchaseToken){
try {
int response = mService.consumePurchase(3, getPackageName(), purchaseToken);
if(response != 0){
return;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Bundle getAllUserPurchase(){
Bundle ownedItems = null;
try {
ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
} catch (RemoteException e) {
e.printStackTrace();
}
return ownedItems;
}
public List<UserPurchaseItems> extractAllUserPurchase(Bundle ownedItems){
List<UserPurchaseItems> mUserItems = new ArrayList<UserPurchaseItems>();
int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
if(purchaseDataList != null){
for (int i = 0; i < purchaseDataList.size(); ++i) {
String purchaseData = purchaseDataList.get(i);
assert signatureList != null;
String signature = signatureList.get(i);
assert ownedSkus != null;
String sku = ownedSkus.get(i);
UserPurchaseItems allItems = new UserPurchaseItems(sku, purchaseData, signature);
mUserItems.add(allItems);
}
}
}
return mUserItems;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
}
도우미 패키지 디렉터리 만들기
새 패키지 폴더를 만들고 이름을 도우미로 지정합니다. 패키지 내에서 새 Java 파일 Helper.java를 만듭니다.
Helper.java
public class Helper {
public static final String ITEM_ID_LIST = "ITEM_ID_LIST";
public static final String ITEM_ONE_ID = "productone";
public static final String ITEM_TWO_ID = "producttwo";
public static final String ITEM_THREE_ID = "productthree";
public static final int RESPONSE_CODE = 1001;
public static final String SHARED_PREF = "shared_pref";
public static final String DEVELOPER_PAYLOAD = "developer_payload";
public static final String PURCHASE_TOKEN = "purchase_token";
public static void displayMessage(Context context, String message){
Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
}
인앱 결제 구매 테스트
- Google+ 계정 만들기 (기본 계정 사용 안 함)
- 그룹 또는 커뮤니티에서 앱을 테스트 할 사용자를 추가하십시오.
인앱 구매 테스트 보관 수있는 오류
요청하신 항목은 구매할 수 없습니다.
솔루션- 유래의 AndreiBogdan에 따르면 ,
그의 튜토리얼에 대한 모든 카드는 Inducesmile 에 있습니다.
Android 개발자 블로그는 인앱 상품 판매에 대한 교육 과정도 권장합니다. 전체 구현을 확인하고 애플리케이션을 테스트하는 방법을 알아 보기이 안내를 확인하십시오 : 인앱 상품 판매
간편한 라이브러리를 사용하여 Google Play 및 Amazon Appstore에 게시하려는 경우 RoboBillingLibrary를 사용할 수 있습니다. 두 가지 세부 사항을 사용하기 쉬운 하나의 라이브러리로 추상화합니다. 자세한 지침은 Github 페이지에 있습니다.
좋습니다. 이것은 온라인에서 사용할 수있는 문서가 많지 않은 것 중 하나 모든 것을 단계별로 설명하기 위해 최선을 다할 것입니다. 내 블로그 게시물 (스크린 샷 포함)에 대한 자세한 버전은 여기 The Millibit에 있습니다. 더 이상 고민하지 않고
1 단계 : 권한이 가장 쉬운 단계입니다. manifest.xml 파일로 이동하여 태그 아래에 다음 행을 추가하십시오.
<uses-permission android:name="com.android.vending.BILLING" />
이렇게하면 앱에 인앱 결제에 액세스 할 수있는 권한이 부여됩니다. API 22 이상의 버전을 사용하는 경우이 권한이 실행에 부여되었는지 확인해야합니다.
2 단계 : Play Console 이제 앱을 Google Play Console에 업로드해야합니다. 우리는 아직 앱을 대중에게 공개하지 않고 있습니다. 베타 릴리스 섹션에 앱 내 구매를 테스트 할 수 있도록 업로드하고 있습니다. 이렇게해야하는 이유는 결제 프로세스가 실제로 작동하도록 Google에 일부 버전의 APK를 업로드해야합니다.
응용 프로그램 만들기
단계에 따라 앱 설정
앱 릴리스로 이동
베타로 이동
Android 스튜디오에서 앱 APK를 만들고 Play Console의 베타에 업로드합니다.
(출시하기 전에 스토어 등록 정보, 콘텐츠 등급, 가격 및 배포를 이미 작성했는지 확인하세요.)
- 매직 버튼 누르기 (게시!)
3 단계 : 프로젝트 설정 좋습니다.이 부분은 여러 파일을 복사하여 넣어야하는 부분입니다.
첫째, 잡아 이 파일을 다운로드하고 아래에 배치 src/main
그것은 다음 폴더에 자신을 구축 잡아해야 우리 이 전체 폴더 폴더의 유틸리티를하고 붙여 넣습니다 src/java folder.
다음 해결 오류로 프로젝트를 다시 빌드합니다. Util 폴더에는 다음 클래스가 포함되어 있습니다.
- IabBroadcastReceiver
- IabException
- IabHelper
- IabResult
- 목록
- 매수
- 보안
- SkuDetails
4 단계 : 제품 생성
관리되는 제품 생성
저장을 클릭하여 "가격 템플릿"을 만듭니다.
여기 에서이 제품의 가격을 선택합니다. 다른 국가에 대한 가격을 선택하거나 가격에서 모든 국가를 선택하기 만하면 자동으로 조정할 수 있습니다.
- 인앱 상품이 활성화되고 마지막으로 콘솔에서 올바른 애플리케이션과 연결 앱을 확인합니다.
마지막으로 제품 ID를 기록해. 다음 몇 단계 에서이 ID를 사용합니다.
- Base64EncodedString 가져 오기 오기
"서비스 및 API"로 이동하여 Base64EncodedString을 가져옵니다. 이를 복사하여 메모장에 넣어 넣어 액세스 할 수 있습니다. 관리자 누구와도 공유하지. 악의적 인 일을 할 수 있습니다.
5 단계 : 드디어! 코딩을 시작할 수 있습니다. 먼저 인앱 결제 라이브러리에 바인딩하고 사용자가 구매했거나 구매하지 않은 항목을 쿼리합니다. 그런 다음 사전 설정 한 제품을 구매합니다.
먼저 이전에 설정 한 모든 항목을 가져옵니다.
import util.*;
이제 mHelper라는 IabHelper 개체를 사용 하고이 모든 작업을 수행합니다.
base64EncodedPublicKey = ""; //PUT YOUR BASE64KEY HERE
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.enableDebugLogging(false); //set to false in real app
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem.
if (result.getResponse() == 3) {
new AlertDialog.Builder(MainActivity.this)
.setTitle("In app billing")
.setMessage("This device is not compatible with In App Billing, so" +
" you may not be able to buy the premium version on your phone. ")
.setPositiveButton("Okay", null)
.show();
}
Log.v(TAG, "Problem setting up In-app Billing: " + result);
} else {
Log.v(TAG, "YAY, in app billing set up! " + result);
try {
mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
}
});
좋아요, 여기서 무슨 일이 일어나고 있는지 분석하겠습니다. 기본적으로 "IabHelper"를 초기화하기 위해 "startSetup"을 호출합니다. 설정이 성공하면 사용자가 이미 가지고있는 구매 항목을 쿼리하고 응답을 저장합니다 mGotInventoryListener
. 다음 코드는 다음과 가변적입니다.
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
i = inventory;
if (result.isFailure()) {
// handle error here
Log.v(TAG, "failure in checking if user has purchases");
} else {
// does the user have the premium upgrade?
if (inventory.hasPurchase("premium_version")) {
premiumEditor.putBoolean("hasPremium", true);
premiumEditor.commit();
Log.v(TAG, "Has purchase, saving in storage");
} else {
premiumEditor.putBoolean("hasPremium", false);
premiumEditor.commit();
Log.v(TAG, "Doesn't have purchase, saving in storage");
}
}
}
};
위의 코드는 매우 자명합니다. 기본적으로 사용자가 이미 구매 한 항목 만 확인합니다. 이제 우리는 사용자가 이미 우리 제품을 구매했는지 여부를 우리 제품을 구매했는지 여부를 우리 제품 항목을 구매했는지 여부를 알았습니다! 이전에 제품을 구매 한 적이없는 구매 요청을 시작하겠습니다.
public void buyPremium() {
try {
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener
} catch (Exception e) {
e.printStackTrace();
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
new AlertDialog.Builder(MainActivity.this)
.setTitle("Error")
.setMessage("An error occurred in buying the premium version. Please try again.")
.setPositiveButton("Okay", null)
.show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
}
else
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.v(TAG, "purchase finished");
if (purchase != null) {
if (purchase.getSku().equals("premium_version")) {
Toast.makeText(MainActivity.this, "Purchase successful!", Toast.LENGTH_SHORT).show();
premiumEditor.putBoolean("hasPremium", true);
premiumEditor.commit();
}
} else {
return;
}
if (result.isFailure()) {
return;
}
}
};
여기서 우리는 다음과 같은 항목 (이전에 Play Console에서 한 ID로 생성)을 구매합니다.
mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener
mPurchaseFinishedListener
매개 변수에 전달한 것을 주목 하십시오. 리스너에게 돌아 오는 것을 의미합니다. 그런 다음 구매가 null인지 확인하고있는 경우 사용자가 구매 한 기능을 제공합니다.
청취자가 유출하지 마십시오! 앱이 파괴되면 반드시 파괴해야합니다.
@Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null)
try {
mHelper.dispose();
mHelper = null;
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
마지막으로 구매를 소비하고 다시 구매할 수 있습니다. 쉽게 수행 할 수 있습니다. 예를 들어 사용자가 가상 자동차 용 주유소를 구입 한 경우입니다. 동일한 제품을 다시 구매하며 두 번째 구매에 사용할 수 있습니다.
public void consume(){
//MAKING A QUERY TO GET AN ACCURATE INVENTORY
try {
mHelper.flagEndAsync(); //If any async is going, make sure we have it stop eventually
mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener
if(i.getPurchase("gas")==null){
Toast.makeText(this, "Already consumed!", Toast.LENGTH_SHORT).show();
}
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
Toast.makeText(this, "Error, try again", Toast.LENGTH_SHORT).show();
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
}
//ACTUALLY CONSUMING
try {
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
this.mHelper.consumeAsync(this.i.getPurchase("gas"), new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase paramAnonymousPurchase, IabResult paramAnonymousIabResult) {
//resell the gas to them
}
});
return;
} catch (IabHelper.IabAsyncInProgressException localIabAsyncInProgressException) {
localIabAsyncInProgressException.printStackTrace();
Toast.makeText(this, "ASYNC IN PROGRESS ALREADY!!!!" +localIabAsyncInProgressException, Toast.LENGTH_LONG).show();
Log.v("myTag", "ASYNC IN PROGRESS ALREADY!!!");
mHelper.flagEndAsync();
}
}
그게 다야! 이제 돈을 벌 수 있습니다. 정말 간단합니다!
다시 말씀 드리지만, 스크린 샷과 사진이 포함 된이 튜토리얼의 더 자세한 버전을 원한다면 여기 에서 원본 게시물을 방문 하십시오 . 더 궁금한 점이 있으면 댓글로 알려주세요.
'ProgramingTip' 카테고리의 다른 글
Brew Doctor-“경고 : / usr / local / include에서 추출되지 않은 헤더 파일을 발견 했습니까?”? (0) | 2020.11.02 |
---|---|
.NET 앱용 링커의 현황 (일명 "링크가 있습니다"2009 년판) (0) | 2020.11.02 |
jquery ajax 호출 -.fail 대. : 오류 (0) | 2020.11.02 |
간단한 CLI Java linter가 있습니까? (0) | 2020.11.02 |
Google Maps API v3 + jQuery UI 탭의 문제 (0) | 2020.11.02 |