android in-app purchases 7 Аппсторов за полчаса Настя Каримова...
TRANSCRIPT
Android In-App Purchases7 Аппсторов за полчаса
Настя КаримоваРазработчик мобильных приложений
Топ приложений в Google Play
Рекламные баннеры
Покупки внутри приложений
5
контентфункциональностьсервисы
Можно продавать
6
сonsumable (заклинания, жизни, время) non-consumable (уровни, текст книги) subscriptions (подписки на контент)
Виды инаппов
7
Вот так выглядит инапп
com.game.SKU_THE_BEST_SWORD_EVER
8
9
<uses-permission android:name="com.android.vending.BILLING"/>
Permission
10
IabHelper helper = new IabHelper(context, "your_base_64_public_key");helper.startSetup(new IabHelper.OnIabSetupFinishedListener() { @Override public void onIabSetupFinished(IabResult result) { if (result.isSuccess()) { //query inventory } else { complain("Billing is not supported."); } } });
Настройка
11
helper.queryInventoryAsync(true, new IabHelper.QueryInventoryFinishedListener() { @Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) { if (result.isFailure()) { complain(String.format("queryInventory failed:%s", result)); } else { //consume if not consumed //update UI } }});
Запрашиваем инвентарь
12
final Purchase tipPurchase = inventory.getPurchase(IN_APP_SKU_TIP);if (tipPurchase != null) { helper.consumeAsync(tipPurchase, new IabHelper.OnConsumeFinishedListener() { @Override public void onConsumeFinished(Purchase purchase, IabResult result) { if (result.isSuccess()) { if (purchase.getSku().equals(IN_APP_SKU_TIP)) { //process it //update UI } } } });}
Выполняем consume
13
helper.launchPurchaseFlow(this, sku, REQUEST_CODE_PURCHASE_CODE, new IabHelper.OnIabPurchaseFinishedListener() { @Override public void onIabPurchaseFinished(IabResult result, Purchase info) { if (result.isSuccess() && info.getSku().equals(IN_APP_SKU_TIP)) {
//consume //update UI
} }});
// your code here@Overrideprotected void onActivityResult(int reqCode, int resCode, Intent data) { super.onActivityResult(requestCode, resultCode, data);
helper.handleActivityResult(requestCode, resultCode, data);}
Покупка
14
Аппсторы
15
16
<application>...<receiver android:name = "com.amazon.inapp.purchasing.ResponseReceiver" >
<intent-filter> <action android:name = "com.amazon.inapp.purchasing.NOTIFY" android:permission = "com.amazon.inapp.purchasing.Permission.NOTIFY" /></intent-filter></receiver>...</application>
В манифесте
17
public class MyPurchasingObserver extends BasePurchasingObserver {private static final String TAG = "IAPPurchasingObserver"; public MyPurchasingObserver(Activity iapActivity) { super(iapActivity);} public void onSdkAvailable(final boolean isSandboxMode) {}public void onGetUserIdResponse(final GetUserIdResponse response) {}public void onItemDataResponse(final ItemDataResponse response) {}public void onPurchaseResponse(final PurchaseResponse response) {}public void onPurchaseUpdatesResponse( final PurchaseUpdatesResponse response) {}}
Purchasing observer
18
public class IAPActivity extends Activity {... protected void onCreate(Bundle savedInstanceState) { ... PurchasingManager.registerObserver(new MyPurchasingObserver(this)); } ... }
Регистрируем observer
19
PurchasingManager.initiatePurchaseUpdatesRequest(getPersistedOffset());
public class MyPurchasingObserver extends BasePurchasingObserver {... public void onPurchaseUpdatesResponse( final PurchaseUpdatesResponse response) {
// No implementation required when dealing solely // with consumables.
}...}
Запрашиваем инвентарь
20
public class IAPActivity extends Activity { // ... private void initiatePurchase() { String requestId = PurchasingManager.initiatePurchaseRequest ("com.amazon.example.iap.consumable"); } // ...}
public class MyPurchasingObserver extends BasePurchasingObserver {... public void onPurchaseResponse(PurchaseResponse response) { final PurchaseRequestStatus status = response .getPurchaseRequestStatus(); if (status == PurchaseResponse.PurchaseRequestStatus.SUCCESSFUL) { } }...}
Процесс покупки
21
22
<!--all stores--><uses-permission android:name="android.permission.INTERNET"/><!--Google Play--><uses-permission android:name="com.android.vending.BILLING" /><!--Amazon--><uses-permission android:name="com.sec.android.iap.permission.BILLING" /><!--Samsung Apps--><uses-permission android:name="com.sec.android.iap.permission.BILLING" /><!--Open Store--><uses-permission android:name="org.onepf.openiab.permission.BILLING" /><!--T-Store and Fortumo--><uses-permission android:name="android.permission.RECEIVE_SMS"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.READ_PHONE_STATE" /><!--T-Store--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="com.tmoney.vending.INBILLING" /><permission android:name="com.tmoney.vending.INBILLING" /><!--Fortumo--><uses-permission android:name="android.permission.SEND_SMS" />
Permissions
23
Маппинг SKUOpenIabHelper.mapSku(SKU_PREMIUM, OpenIabHelper.NAME_AMAZON, "org.onepf.trivialdrive.amazon.premium");
OpenIabHelper.mapSku(SKU_PREMIUM, OpenIabHelper.NAME_TSTORE, "tstore_sku_premium");
OpenIabHelper.mapSku(SKU_PREMIUM, OpenIabHelper.NAME_SAMSUNG, "100000100696/000001003746");
OpenIabHelper.mapSku(SKU_PREMIUM, "com.yandex.store", "org.onepf.trivialdrive.premium");
OpenIabHelper.mapSku(SKU_GAS, OpenIabHelper.NAME_AMAZON, "org.onepf.trivialdrive.amazon.gas");
OpenIabHelper.mapSku(SKU_GAS, OpenIabHelper.NAME_SAMSUNG, "100000100696/000001003744");
OpenIabHelper.mapSku(SKU_GAS, "com.yandex.store", "org.onepf.trivialdrive.gas");
OpenIabHelper.mapSku(SKU_INFINITE_GAS, OpenIabHelper.NAME_AMAZON, "org.onepf.trivialdrive.amazon.infinite_gas");
OpenIabHelper.mapSku(SKU_INFINITE_GAS, OpenIabHelper.NAME_SAMSUNG, "100000100696/000001003747");
OpenIabHelper.mapSku(SKU_INFINITE_GAS, "com.yandex.store", "org.onepf.trivialdrive.infinite_gas");
24
OpenIabHelper.Options options = new OpenIabHelper.Options();Map<String, String> storeKeys = new HashMap<String, String>();storeKeys.put(OpenIabHelper.NAME_GOOGLE, googleBase64EncodedPublicKey);
/* storeKeys.put(OpenIabHelper.NAME_AMAZON, "Unavailable. Amazon doesn't support RSA verification. So this mapping is not needed"); */
/* storeKeys.put(OpenIabHelper.NAME_SAMSUNG,"Unavailable. SamsungApps doesn't support RSA verification. So this mapping is not needed"); */
storeKeys.put(OpenIabHelper.NAME_YANDEX, YANDEX_PUBLIC_KEY);
options.storeKeys = storeKeys; options.checkInventory = true; options.prefferedStoreNames = ... options.availableStores = ...
Опции
25
OpenIabHelper helper = new OpenIabHelper(context, options);helper.startSetup(new IabHelper.OnIabSetupFinishedListener() { @Override public void onIabSetupFinished(IabResult result) { if (result.isSuccess()) { //query inventory } else { complain("Billing is not supported."); } } });
Настройка
26
helper.queryInventoryAsync(true, new IabHelper.QueryInventoryFinishedListener() { @Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) { if (result.isFailure()) { complain(String.format("queryInventory failed: %s”, result)); } else { //consume if not consumed //update UI } }});
Запрашиваем инвентарь
27
final Purchase tipPurchase = inventory.getPurchase(IN_APP_SKU_TIP);
if (tipPurchase != null) {
helper.consumeAsync(tipPurchase, newIabHelper.OnConsumeFinishedListener() {
@Override public void onConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) { if (purchase.getSku().equals(IN_APP_SKU_TIP)) { //process it //update UI } } } });}
Выполняем consume
28
helper.launchPurchaseFlow(this, sku, REQUEST_CODE_PURCHASE_CODE, new IabHelper.OnIabPurchaseFinishedListener() { @Override public void onIabPurchaseFinished(IabResult result, Purchase info) {
if (result.isSuccess() && info.getSku().equals(IN_APP_SKU_TIP)) {
//consume //update UI
} }});
// your code here
@Overrideprotected void onActivityResult(int reqCode, int resCode, Intent data) { super.onActivityResult(requestCode, resultCode, data);
helper.handleActivityResult(requestCode, resultCode, data);}
Покупка
29
Верификация Server-2-Server
30
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) || TextUtils.isEmpty(signature)) { Log.e(TAG, "Purchase verification failed: missing data."); return false; } PublicKey key = Security.generatePublicKey(base64PublicKey); return Security.verify(key, signedData, signature); }
// Unique OpenIAB feature =)
Options opts = new Options();opts.verifyMode = Options.VERIFY_SKIP;
mHelper = new OpenIabHelper(context, opts);
Код верификации
31
One PlatformFoundation
www.onepf.org
github.com/onepf/OpenIABgithub.com/onepf/AppDF