Upgrade to Pro — share decks privately, control downloads, hide ads and more …

IAB x Redeem in Android

Shaka Huang
November 07, 2012

IAB x Redeem in Android

In-app Billing (IAB) 以及 Redeem code system 在 Android 上的實作與 solution 介紹.

Shaka Huang

November 07, 2012
Tweet

More Decks by Shaka Huang

Other Decks in Programming

Transcript

  1. Experience • was @ BenQisda • Mobile Software Engineer/Manager (UI)

    • BREW / Android • now @ SmarTapper & Mocharabbit • .. coding (&) monkey Thursday, November 8, 12
  2. Overview • Google Play service • 30% of the sale

    price • API level 4+ (1.6+) • Types • In-app product (Managed/unmanaged) • Subscription Thursday, November 8, 12
  3. Register • Register developer in Google Play • Register Google

    Wallet for buyers • No more Google Adsense ! Thursday, November 8, 12
  4. Requests • MarketBillingService interface • (IMarketBillingService.aidl) • Requests sent by

    single IPC method (sendBillingRequest()) of the interface • Request parameters are sent as Bundle Thursday, November 8, 12
  5. Binding try { boolean bindResult = mContext.bindService( new Intent("com.android.vending.billing.MarketBillingService.BIND"), this,

    Context.BIND_AUTO_CREATE); if (bindResult) { Log.i(TAG, "Service bind successful."); } else { Log.e(TAG, "Could not bind to the MarketBillingService."); } } catch (SecurityException e) { Log.e(TAG, "Security exception: " + e); } public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "MarketBillingService connected."); mService = IMarketBillingService.Stub.asInterface(service); } Thursday, November 8, 12
  6. protected Bundle makeRequestBundle(String method) { Bundle request = new Bundle();

    request.putString(BILLING_REQUEST, method); request.putInt(API_VERSION, 1); request.putString(PACKAGE_NAME, getPackageName()); return request; } Request bundle Thursday, November 8, 12
  7. Make request Bundle request = makeRequestBundle("REQUEST_PURCHASE"); request.putString(ITEM_ID, mProductId); // Request

    is for a standard in-app product request.putString(ITEM_TYPE, "inapp"); // Note that the developer payload is optional. if (mDeveloperPayload != null) { request.putString(DEVELOPER_PAYLOAD, mDeveloperPayload); } Bundle response = mService.sendBillingRequest(request); Thursday, November 8, 12
  8. Responses • Every request will receive 1 synchronous response •

    Asynchronous response • 0 or N Thursday, November 8, 12
  9. Synchronous response • RESPONSE_CODE • status of request • PURCHASE_INTENT

    • PendingIntent to launch checkout UI • REQUEST_ID • unique request identifier for the request Thursday, November 8, 12
  10. Receiving asynchronous responses @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); } } Thursday, November 8, 12
  11. Check billing supported • Return: RESPONSE_CODE (Sync) • RESULT_OK /

    RESULT_BILLING_UNAVAILABLE / RESULT_ERROR / RESULT_DEVELOPER_ERROR Thursday, November 8, 12
  12. Request purchase Bundle request = makeRequestBundle("REQUEST_PURCHASE"); request.putString(ITEM_ID, mProductId); // Request

    is for a standard in-app product request.putString(ITEM_TYPE, "inapp"); // Note that the developer payload is optional. if (mDeveloperPayload != null) { request.putString(DEVELOPER_PAYLOAD, mDeveloperPayload); } Bundle response = mService.sendBillingRequest(request); Thursday, November 8, 12
  13. Request purchase • Return: RESPONSE_CODE, PURCHASE_INTENT, REQUEST_ID (Sync) • Return:

    RESPONSE_CODE, IN_APP_NOTIFY (Async) Thursday, November 8, 12
  14. Get purchase information • Return: REQUEST_CODE, REQUEST_ID (Sync) • Return:

    RESPONSE_CODE, PURCHASE_STATE_CHANGED Thursday, November 8, 12
  15. JSON of return info { "nonce" : 1836535032137741465, "orders" :

    [{ "notificationId" : "android.test.purchased", "orderId" : "transactionId.android.test.purchased", "packageName" : "com.example.dungeons", "productId" : "android.test.purchased", "developerPayload" : "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ", "purchaseTime" : 1290114783411, "purchaseState" : 0, "purchaseToken" : "rojeslcdyyiapnqcynkjyyjh" }] } Thursday, November 8, 12
  16. And .. • Don’t forget security ! • Signed purchased

    info (JSON) • Nonce Thursday, November 8, 12
  17. Android Billing Library • Full IAB implementation • Obfuscated purchases

    database • Unit test https://github.com/robotmedia/AndroidBillingLibrary Thursday, November 8, 12
  18. <manifest> <uses-permission android:name="com.android.vending.BILLING" /> <service android:name="net.robotmedia.billing.BillingService" /> <receiver android:name="net.robotmedia.billing.BillingReceiver"> <intent-filter>

    <action android:name="com.android.vending.billing.IN_APP_NOTIFY" /> <action android:name="com.android.vending.billing.RESPONSE_CODE" /> <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED " /> </intent-filter> </receiver> </manifest> AndroidManifest.xml Thursday, November 8, 12
  19. Set configuration @Override public void onCreate() { super.onCreate(); ! BillingController.setDebug(isDebug(this));

    ! BillingController.setConfiguration(new BillingController.IConfiguration() { ! ! public byte[] getObfuscationSalt() { ! ! ! return new byte[] {-127, -96, -88, 77, 127, 115, 41, -90, -116, -41, 66, -53, 122, -110, 1, 73, 57, 110, 48, -116}; ! ! } ! ! public String getPublicKey() { ! ! ! return “<YOUR PUBLIC KEY>”; ! } }); } Thursday, November 8, 12
  20. mBillingObserver = new AbstractBillingObserver(this) { public void onBillingChecked(boolean supported) {

    } public void onPurchaseStateChanged(String itemId, PurchaseState state) { } public void onRequestPurchaseResponse(String itemId, ResponseCode response) { } public void onSubscriptionChecked(boolean supported) { } }; Create observer Thursday, November 8, 12
  21. Check billing support @Override public void onCreate(Bundle savedInstanceState) { //

    ... BillingController.registerObserver(mBillingObserver); BillingController.checkBillingSupported(this); // ... } protected void onBillingChecked(boolean supported) { if (supported) { // continue } else { showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID); } } Thursday, November 8, 12
  22. Restore transactions private void restoreTransactions() { if (!mBillingObserver.isTransactionsRestored()) { BillingController.restoreTransactions(this);

    Toast.makeText(this, R.string.restoring_transactions, Toast.LENGTH_LONG).show(); } } protected void onBillingChecked(boolean supported) { if (supported) { restoreTransactions(); } ... } Thursday, November 8, 12
  23. Tips • Use of proguard • Remove debug trace in

    release build Thursday, November 8, 12
  24. .. No official solution • BoomCodes.com • Request for early

    access • Redeemco.de • https://github.com/thubalek/redeemco- de-rest-api Thursday, November 8, 12