Share
Embed
IAB x Redeem in Android
In-app Billing (IAB) 以及 Redeem code system 在 Android 上的實作與 solution 介紹.
Shaka Huang
November 07, 2012
Transcript
66 slides
-
Experience • was @ BenQisda • Mobile Software Engineer/Manager (UI)
• BREW / Android • now @ SmarTapper & Mocharabbit • .. coding (&) monkey Thursday, November 8, 12 -
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 -
Integration of IAB • Register Account • RTFM • Implement
• Profit Thursday, November 8, 12 -
Register • Register developer in Google Play • Register Google
Wallet for buyers • No more Google Adsense ! Thursday, November 8, 12 -
Request types • CHECK_BILLING_SUPPORTED • REQUEST_PURCHASE • GET_PURCHASE_INFORMATION • CONFIRM_NOTIFICATION
• RESTORE_TRANSATIONS Thursday, November 8, 12 -
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 -
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 -
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 -
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 -
Responses • Every request will receive 1 synchronous response •
Asynchronous response • 0 or N Thursday, November 8, 12 -
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 -
Asynchronous response • Broadcast intents • RESPONSE_CODE • IN_APP_NOTIFY •
PURCHASE_STATE_CHANGED Thursday, November 8, 12 -
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 -
Request types • CHECK_BILLING_SUPPORTED • REQUEST_PURCHASE • GET_PURCHASE_INFORMATION • CONFIRM_NOTIFICATION
• RESTORE_TRANSATIONS Thursday, November 8, 12 -
Check billing supported Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED"); Bundle response =
mService.sendBillingRequest(request); Thursday, November 8, 12 -
Check billing supported • Return: RESPONSE_CODE (Sync) • RESULT_OK /
RESULT_BILLING_UNAVAILABLE / RESULT_ERROR / RESULT_DEVELOPER_ERROR Thursday, November 8, 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 -
Request purchase • Return: RESPONSE_CODE, PURCHASE_INTENT, REQUEST_ID (Sync) • Return:
RESPONSE_CODE, IN_APP_NOTIFY (Async) Thursday, November 8, 12 -
Get purchase information Bundle request = makeRequestBundle("GET_PURCHASE_INFORMATION"); request.putLong(REQUEST_NONCE, mNonce); request.putStringArray(NOTIFY_IDS,
mNotifyIds); Bundle response = mService.sendBillingRequest(request); Thursday, November 8, 12 -
Get purchase information • Return: REQUEST_CODE, REQUEST_ID (Sync) • Return:
RESPONSE_CODE, PURCHASE_STATE_CHANGED Thursday, November 8, 12 -
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 -
Confirm notifications Bundle request = makeRequestBundle("CONFIRM_NOTIFICATIONS"); request.putStringArray(NOTIFY_IDS, mNotifyIds); Bundle response
= mService.sendBillingRequest(request); Thursday, November 8, 12 -
Confirm notifications • Return: RESPONSE_CODE, REQUEST_ID (Sync) • Return: RESPONSE_CODE
(Async) Thursday, November 8, 12 -
Unsolicited In-app notify • Two cases • Multiple devices •
Refunds Thursday, November 8, 12 -
Restore transactions Bundle request = makeRequestBundle("RESTORE_TRANSACTIONS"); request.putLong(REQUEST_NONCE, mNonce); Bundle response
= mService.sendBillingRequest(request); Thursday, November 8, 12 -
Restore transactions • Return: RESPONSE_CODE, REQUEST_ID (Sync) • Return: RESPONSE_CODE,
PURCHASE_STATE_CHANGED Thursday, November 8, 12 -
And .. • Don’t forget security ! • Signed purchased
info (JSON) • Nonce Thursday, November 8, 12 -
Android Billing Library • Full IAB implementation • Obfuscated purchases
database • Unit test https://github.com/robotmedia/AndroidBillingLibrary Thursday, November 8, 12 -
<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 -
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 -
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 -
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 -
Request purchase BillingController.requestPurchase(this, “<PRODUCT ID>”, true, /* confirm */ null
/* developer payload */ ); Thursday, November 8, 12 -
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 -
Tips • Use of proguard • Remove debug trace in
release build Thursday, November 8, 12 -
.. No official solution • BoomCodes.com • Request for early
access • Redeemco.de • https://github.com/thubalek/redeemco- de-rest-api Thursday, November 8, 12