Professional Documents
Culture Documents
0 Programming Guide
Revision History Version 1.0 2.0.1 2.0.2 Created Security-related content added Mode parameter added to getItemList method Description Author Date 13.04.15 13.06.04 13.06.12
Table of Contents
1. Samsung In App Purchase v2.0 ..................................................................................................................................... 4 1.1 Product Purchase ............................................................................................................................................................. 4 1.2 Product Type .................................................................................................................................................................... 5 2. Creating an IAP Link ....................................................................................................................................................... 6 2.1 Adding the IAP Library ................................................................................................................................................. 6 2.2 Adding Permissions ........................................................................................................................................................ 6 2.3 Installing IAP and Authorizing Samsung Account ................................................................................................. 7 2.4 Creating ServiceConnection and Binding IAPConnector ..................................................................................... 9 2.5 Processing the IAP Request and Response with IAPConnector ....................................................................... 10 init() method ...................................................................................................................................................... 10 getItemList() method ....................................................................................................................... 11 getItemsInbox() method ................................................................................................................. 11 Payment ........................................................................................................................................................... 12
3. IAP Reference ................................................................................................................................................................. 20 3.1 API Reference ............................................................................................................................................................... 20 init method ................................................................................................................................................ 20 getItemList method ............................................................................................................................ 21 getItemsInbox method ....................................................................................................................... 22 PaymentMethodListActivity ......................................................................................................... 22
Proceed with the Samsung account authorization process. Bind IAPConnector to enable the IAP API. Call the init() method start resetting IAP and receive the results as a bundle.
Call the getItemList() method to receive a list of purchasable products as a bundle. Select a product to purchase and call the startActivityForResult() method to call an activity (PaymentMethodListActivity) for selecting a payment method. Then, receive the results as a bundle in the onActivityResult() method. Call the getItemsInbox() method to receive a list of purchased products as a bundle. When the entire process ends, unbind IAPConnector to finish using IAP.
! If you do not register the permission, it will always fail in the next step, the Samsung account authorization process
If IAP is installed:
Test the validity of the installed IAP by comparing the signature hash codes of the IAP package. public class IAPUtils { private static final int IAP_SIGNATURE_HASHCODE = 0x7a7eaf4b; public static boolean isValidIAPPackage( Context _context ) { boolean result = true; try { Signature[] sigs = _context.getPackageManager().getPackageInfo( SamsungIAPHelper.IAP_PACKAGE_NAME, PackageManager.GET_SIGNATURES ).signatures; if( sigs[0].hashCode() != IAP_SIGNATURE_HASHCODE ) { result = false; } } catch( Exception e ) { e.printStackTrace(); result = false; } return result; } }
If the validity test of the installed IAP reveals that the IAP is not valid, a message dialog must be displayed as shown below, and the payment process must not proceed any further.
// If the installed IAP package is not valid:
.setTitle( R.string.in_app_purchase ) .setMessage( R.string.invalid_iap_package ) .setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); _activity.finish(); } } ).show(); } // ================================================================
If the installed IAP is valid, proceed with the Samsung account authorization process as below. You can move on to the next step if the account is successfully authorized.
// If the installed package is valid:
// ================================================================ else { ComponentName com = new ComponentName( "com.sec.android.iap", "com.sec.android.iap.activity.AccountActivity" ); Intent intent = new Intent(); intent.setComponent( com ); _activity.startActivityForResult( intent, 1001 ); } // ================================================================
Call the IAP AccountActivity to authorize the Samsung account. AccountActivity is used to authorize your Samsung account and reconfirm your password when you make a payment. When you call AccountActivity, the application uses the startActivityForResult method to call it, and IAP returns the Samsung account authorization results to the onActivityResult method in the application.
onActivityResult method
@Override protected void onActivityResult( int _requestCode, int _resultCode, Intent _ intent ) { if( _requestCode == 1001 ) { if( _resultCode == RESULT_OK ) { /* Login successful */ } } }
if( Build.VERSION.SDK_INT >= 12 ) { intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | FLAG_INCLUDE_STOPPED_PACKAGES ); } else { intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP ); } startActivity( intent );
Binding IAPConnector
To connect to IAP in applications, implement ServiceConnection in an Activity and bind IAPConnector. After creating ServiceConnection, use the overridden methods onServiceDisconnected and onServiceConnected to refer to IAPConnector instances.
IAPConnector mIAPConnector; ServiceConnection mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected( ComponentName name ) { mIAPConnector = null; } @Override public void onServiceConnected( ComponentName name, IBinder service ) { mIAPConnector = IAPConnector.Stub.asInterface( service ); } };
Bind using the bindService method that has the intent that is referred to for IAP service names and created ServiceConnection objects as parameters.
When you complete the process above, you can use mIAPConnector to communicate with IAP.
init() method
This must be called before you use any IAP services, including 'List of products', 'List of purchased products', and 'Payment'. After the Samsung account is successfully authorized, call the init() method to set basic required payment information. The init() method sets up the following basic information to use in IAP: IAP upgrade check, server URL, country code, shop ID, currency (currency unit) information, user ID, and email. Note: If you call the init() method on the main thread, an ANR (Application Not Responding) might occur. Create a separate thread to avoid the error. Bundle result = mIAPConnector.init( mMode ); if ( null != bundle ) { int statusCode = bundle.getInt( "STATUS_CODE" ); String errorString = bundle.getString( "ERROR_STRING" ); if ( statusCode == 0 ) {
/* Successful */
} }
If 'STATUS_CODE' is '0', the initial setup has successfully completed and you can use the services, including 'List of products', 'List of purchased products', and 'Payment'.
getItemList() method
This returns the List of purchasable products as a bundle. Note: If you call the getItemList() method on the main thread, an ANR (Application Not Responding) might occur. Create a separate thread to avoid the error. Bundle itemList = mIAPConnector.getItemList( mMode, getPackageName(), _itemGroupId, _startNum, _endNum, _itemType ); Information about whether the request was successful or not is saved in 'STATUS_CODE', the key value of the returned bundle. (If successful, '0'.)
getItemsInbox() method
This returns the List of purchased products as a bundle.
Note: If you call the getItemsInbox() method on the main thread, an ANR (Application Not Responding) might occur. Create a separate thread to avoid the error. Bundle itemsInboxList = mIAPConnector.getItemsInbox( mContext.getPackageNam e(), _itemGroupId, _startNum, _endNum, _startDate, _endDate ); Information about whether the request was successful or not is saved in 'STATUS_CODE', the key value of the returned bundle. (If successful, '0'.)
Payment
Make the process so that you can go to the IAP Payment Activity where you can pay for a product. Bundle bundle = new Bundle(); bundle.putString( "THIRD_PARTY_NAME", getPackageName() );
bundle.putString( "ITEM_GROUP_ID", _itemGroupId ); bundle.putString( "ITEM_ID", _itemId ); ComponentName com = new ComponentName( "com.sec.android.iap", "com.sec.android.iap.activity.PaymentMethodListActivity" ); Intent intent = new Intent( Intent.ACTION_MAIN ); intent.addCategory( Intent.CATEGORY_LAUNCHER ); intent.setComponent( com ); intent.putExtras( bundle ); startActivityForResult( intent, 1000 ); When you call the IAP Payment Details Activity, the application uses the startActivityForResult method to call it, and IAP returns the payment results in the applications onActivityResult method. onActivityResult method Data for the payment intents 'RESULT_OBJECT' key is returned as a JSON string as illustrated below. { "mItemDesc":"Learn about the books of the Bible##46;", "mCurrencyUnit":"", "mItemImageUrl":"http:\/\/img.samsungapps.com\/product\/2013\/0510\/000 000524646\/IconImage_20130510095823098_NEW_WEB_ICON.png", "mItemDownloadUrl":"", "mPaymentId":"ZPMTID20130603GBI0000376", "mItemPrice":1, "mVerifyUrl":"https:\/\/iap.samsungapps.com\/iap\/appsItemVerifyIAPRece ipt.as?protocolVersion=2.0", "mItemName":"Books", "mPurchaseDate":1370253642115, "mPurchaseId":"524822074a3a8671102cbe35bf033f59bcb6f9ec79a950ab5d83de67 b3ff92f8", "mReserved1":"", "mReserved2":"", "mItemId":"000000057508", "mItemPriceString":"1.00" } You can get a desired value by creating a JSON object as below. Additional values such as STATUS_CODE, THIRD_PARTY_NAME, ERROR_STRING and ITEM_ID are included in the bundle.
// treat result of IAPService // ======================================================================== ==== if( _requestCode == 1 ) { if( null == _intent ) { return; }
Bundle extras
= _intent.getExtras();
String itemId = ""; String thirdPartyName = ""; // payment success : 0 // payment cancelled : 1 // ===================================================================== === int statusCode = 1; // ===================================================================== === String errorString String purchaseData = ""; = "";
// ----------------------------------------------------------------------if( null != extras ) { thirdPartyName = extras.getString( "THIRD_PARTY_NAME" ); statusCode = extras.getInt( "STATUS_CODE" ); errorString = extras.getString( "ERROR_STRING" ); itemId = extras.getString( "ITEM_ID" ); purchaseData = extras.getString( "RESULT_OBJECT" ); } // ----------------------------------------------------------------------// 2) If there is no bundle information transferred from the IAP:
// ----------------------------------------------------------------------else { showResultDialog( getString( R.string.dlg_title_payment_error ), getString( R.string.msg_payment_was_not_processed_successfully ) ); } // ----------------------------------------------------------------------// 3) If payment is not cancelled:
// If payment was not cancelled // ----------------------------------------------------------------------if( RESULT_OK == _resultCode ) { try { JSONObject jObject = new JSONObject( purchaseData ); String paymentId = jObject.getString( "mPaymentId" ); String itemDesc = jObject.getString( "mItemDesc" ); String itemPrice = jObject.getString( "mItemPrice" ); String currencyUnit = jObject.getString( "mCurrencyUnit" ); String itemImageUrl = jObject.getString( "mItemImageUrl" ); String itemName = jObject.getString( "mItemName" ); String purchaseDate = jObject.getString( "mPurchaseDate" );
long timeInMillis = Long.parseLong( jObject.getString("mPurchaseD ate") ); String String String String itemId String String purchaseId = jObject.getString( "mPurchaseId" ); reserved1 = jObject.getString( "mReserved1" ); reserved2 = jObject.getString( "mReserved2" ); itemDownloadUrl = jObject.getString( "mItemDownloadUrl" ); = jObject.getString( "mItemId" ); itemPriceString = jObject.getString( "mItemPriceString" ); verifyUrl = jObject.getString( "mVerifyUrl" );
// --------------------------------------------------------------if( statusCode == 0 ) { String serverUrl = verifyUrl + "&purchaseID=" + purchaseId; new VerifyClientToServer( serverUrl, purchaseId, paymentId ).execute(); } // --------------------------------------------------------------b. If the payment result is failure:
// --------------------------------------------------------------else { showResultDialog( getString( R.string.dlg_title_payment_error ), "-itemId : " + itemId + "\n-thirdPartyName : " + thirdPartyName + "\n-statusCode : " + statusCode + "\n-errorString : " + errorString ); } // --------------------------------------------------------------} catch( JSONException e ) { e.printStackTrace(); } } // ----------------------------------------------------------------------// 4) If payment is canceled:
// If payment was cancelled // ----------------------------------------------------------------------else if( RESULT_CANCELED == _resultCode ) { showResultDialog( getString( R.string.dlg_title_payment_cancelled ), "-itemId : " + itemId + "\n-thirdPartyName : " + thirdPartyName + "\n-statusCode : " + statusCode ); } // ---------------------------------------------------------------------
In order to prevent problems caused by sniffing apps during payment result processing, use the mVerifyUrl and mPurchaseID values in the bundle that came as a result, to check if the payment is valid for the IAP server. For that purpose, VerifyClientToServer AsyncTask is run in the above example. Below is source code for VerifyClientToServer AsyncTask.
private class VerifyClientToServer extends AsyncTask<Void, Void, Boolean> { String mServerUrl = null; String mPurchaseId = null; String mPaymentId = null; VerificationVO mVerificationVO = null; public VerifyClientToServer ( String _strUrl, String _purchaseId, String _paymentId ) { mServerUrl = _strUrl; mPurchaseId = _purchaseId; mPaymentId = _paymentId; } @Override protected void onPreExecute() { super.onPreExecute();
// Cancel the task if the server URL, mPurchasedId and mPaymentId are empty.
// ================================================================ if( true == TextUtils.isEmpty( mServerUrl ) || true == TextUtils.isEmpty( mPurchaseId ) || true == TextUtils.isEmpty( mPaymentId) ) { this.cancel( true ); } // ================================================================ mProgressDialog = showProgressDialog( ItemList.this ); } @Override protected void onCancelled() { dismissProgressDialog( mProgressDialog ); super.onCancelled(); } @Override
protected Boolean doInBackground( Void... params ) { try { int retryCount = 0; String strResponse = null;
// For reliability, if an error occurs, retry up to 3 times.
// ============================================================= do { strResponse = getHttpGetData( mServerUrl, 10000, 10000 ); retryCount++; } while( retryCount < 3 && true == TextUtils.isEmpty( strResponse ) ); // ============================================================= if( strResponse == null || TextUtils.isEmpty( strResponse ) ) { return false; } else { mVerificationVO = new VerificationVO( strResponse );
// Only if Status Code is "true", and if PaymentId and // mPaymentId of mVerificationVO match, determine as success.
// ========================================================== if( mVerificationVO != null && true == "true".equals( mVerificationVO.getmStatus() ) && true == mPaymentId.equals( mVerificationVO.getmPaymentId() ) ) { return true; } // ========================================================== else { return false; } } } catch( Exception e ) { e.printStackTrace(); return false; } } @Override protected void onPostExecute( Boolean result ) { dismissProgressDialog( mProgressDialog ); if( true == result )
{ showResultDialog( getString( R.string.dlg_title_payment_success ), "-itemId : " + mVerificationVO.getmItemId() + "\n-paymentId : " + mVerificationVO.getmPaymentId() ); } else { showResultDialog( getString( R.string.dlg_title_payment_error ), getString( R.string.msg_invalid_purchase ) ); } } private String getHttpGetData ( final String _strUrl, final int _connTimeout, final int _readTimeout ) { String strResult = null; URLConnection con = null; HttpURLConnection httpConnection = null; BufferedInputStream bis = null; ByteArrayOutputStream buffer = null; try { URL url = new URL( _strUrl ); con = url.openConnection(); con.setConnectTimeout(10000); con.setReadTimeout(10000); httpConnection = (HttpURLConnection)con; httpConnection.setRequestMethod("GET"); httpConnection.connect(); int responseCode = httpConnection.getResponseCode(); if( responseCode == 200 ) { bis = new BufferedInputStream( httpConnection.getInputStream(), 4096 ); buffer = new ByteArrayOutputStream( 4096 ); byte [] bData = new byte[ 4096 ]; int nRead; while( ( nRead = bis.read( bData, 0, 4096 ) ) != -1 ) { buffer.write( bData, 0, nRead ); } buffer.flush(); strResult = buffer.toString(); }
} catch( Exception e ) { e.printStackTrace(); } finally { if( bis != null ) { try { bis.close(); } catch (Exception e) {} } if( buffer != null ) { try { buffer.close(); } catch (IOException e) {} } con = null; httpConnection = null; } return strResult; } }
In the VerifyClientToServer example above, strResponse is JSON formatted as shown below. { "itemId":"000000057507", "itemName":"Additional Game Modes", "itemDesc":"Unlock all game modes. Best value!", "purchaseDate":"2013-06-03 21:04:50", "paymentId":"ZPMTID20130603GBI0000384", "paymentAmount":"1.500", "status":"true" } You can receive strResponse from the server and extract a desired value, such as the creator of the verification class below. The example below is part of the VerificationVO source used as an object for saving task results in VerifyClientToServer AsyncTask.
public class VerificationVO { private String mItemId; private String mItemName; private String mItemDesc; private String mPurchaseDate; private String mPaymentId; private String mPaymentAmount; private String mStatus; public VerificationVO( String strJson ) { try { JSONObject jObject = new JSONObject( strJson );
= jObject.getString( "itemId" ); = jObject.getString( "itemName" ); = jObject.getString( "itemDesc" ); = jObject.getString( "purchaseDate" ); = jObject.getString( "paymentId" ); = jObject.getString( "paymentAmount" ); = jObject.getString( "status" );
For safer transactions, you can process tasks such as VerifyClientToServer in the above sample code through a 3rd-party server. If you deliver mVerifyUrl, mPurchaseId and mPaymentId from the client to the 3rd-party server, processing is possible on the server in the form shown in the above example.
3. IAP Reference
This chapter provides reference information to use IAP.
init method
init( int mode ) This method must be called before you use IAP services, including 'List of products', 'List of purchased products', and 'Payment Details'. It returns results as a bundle. - Parameter details
Parameter Type Value for IAP mode 0: Live (production) mode 1: Development mode (returned results are true every time) -1: Development mode (returned results are false every time) Description
mode
int
STATUS_CODE ERROR_STRING
Description Successful Failure while resetting IAP IAP upgrade required Error while running IAP
getItemList method
getItemList( String packageName, endNum, String itemType ) String itemGroupId, int startNum, int
This method returns the List of purchasable products as a bundle. - Parameter details
Parameter Type Value for IAP mode 0: Live (production) mode 1: Development mode (returned results are true every time) -1: Development mode (returned results are false every time) Package name of the application Group ID of the product Starting number of the list to be shown Ending number of the list to be shown Item type Consumable: 00 Non-consumable: 01 Subscription (short-term): 02 All: 10 Description
itemType
String
0 -1002 -1005
mItemId mItemName mItemPrice mCurrencyUnit mItemDesc mItemImageUrl mItemDownloadUrl mReserved1 mReserved2 mType
getItemsInbox method
getItemsInbox( String packageName, String itemGroupId, int startNum, int endNum, String startDate, String endDate ) method This method returns the List of purchased products as a bundle. - Parameter details
Parameter Type
Description Package name of the application Group ID of the product Starting number of the list to be shown Ending number of the list to be shown Start date of the purchase period (e.g. 20130422) End date of the purchase period (e.g. 20130430)
Description Result code (If not 0, error or failure) Message in case of an error or failure Purchased product list as a JSON string
IAP_ERROR_NONE IAP_ERROR_COMMON
0 -1002
mItemId mItemName mItemPrice mItemPriceString mItemDesc mCurrencyUnit mPurchaseDate mPaymentId mPurchaseId mItemImageUrl mItemDownloadUrl mReserved1 mReserved2 mType
Description Product ID Product name Product price Currency + product price Product description Currency Purchase date (in milliseconds) Payment ID Purchase ID Product image URL Product download URL Reserved 1 Reserved 2 Product type
PaymentMethodListActivity
This section explains the required bundle information and results that are delivered when the IAP payment feature is used.
Description Successful Payment canceled Error while running IAP The product is a non-consumable product and cannot be repurchased Called payment details without bundle information The payment results were not successful, but the list of purchased products needs to be checked because the product could have been purchased
mItemId mItemName mItemDesc mItemPrice mItemPriceString mCurrencyUnit mPaymentId mPurchaseDate mPurchaseId mReserved1 mReserved2 mItemImageUrl mItemDownloadUrl
Description Successful Payment canceled Failure while resetting IAP IAP upgrade required Error while running IAP The product is a non-consumable product and cannot be repurchased Called payment details without bundle information No product list requested The payment results were not successful, but the list of purchased products needs to be checked because