Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
1. What are In-App Payments on the ONE Store?
One app marketplace in South Korea has more payments in Korea than Google Play. Fewer iphones, after all, it’s Samsung.
2. Development Environment Configuration It is suggested that the development environment required by IAP SDK for Android apps is as follows:
- Android 4.0 and later (API version 14 and above)
- Java SDK 1.6 version
- Android Studio 2.0 or later
3. Prepare
- 3-1. Configure the application ID
- 3-2. Fill in the bank information
- 3-3. In-app product registration
- 3-3.1 Individual registration of in-app goods
- 3-3.2 In-app product batch registration
- 3-4. Configure the authentication key
- 3-5. Download the sample application
- 3-6. Added in-app Payment Library
- 3-7. Set up the Android Manifest file
- 3-8. Install the ONE Store app
4. In-app payment
Use the SDK to implement in-app payments
4-1. Initiate a login request for ONE Store
To call launchLoginFlowAsync, please log into the ONE Store first.
The requestCode passed as an argument is used to later validate the data returned to onActivityResult.
/* * launchLoginFlowAsync API (login) callback listener for purchasecent */
PurchaseClient.LoginFlowListener mLoginFlowListener = new PurchaseClient.LoginFlowListener() {
@Override
public void onSuccess(a) {
Log.d(TAG, "launchLoginFlowAsync onSuccess");
// Developers should write their own solutions after successful login.
}
@Override
public void onError(IapResult result) {
Log.e(TAG, "launchLoginFlowAsync onError, " + result.toString());
}
@Override
public void onErrorRemoteException(a) {
Log.e(TAG, LaunchLoginFlowAsync onError cannot connect to ONE Store service);
}
@Override
public void onErrorSecurityException(a) {
Log.e(TAG, LaunchLoginFlowAsync onError, request payment under application status exception);
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, LaunchLoginFlowAsync onError, need to update ONE Store client); }};int IAP_API_VERSION = 5;
int LOGIN_REQUEST_CODE = 2000; // Request code returned to onActivityResult
mPurchaseClient.launchLoginFlowAsync(IAP_API_VERSION, "Call the Activity".this, LOGIN_REQUEST_CODE, mLoginFlowListener)
Copy the code
4-2. In-app payment initialization and connection
When using the In-App Payment SDK, you should initialize, create an PurchaseClient object, and perform the purchase method. You first enter the Context information and signing key value for the current Activity when you create your PurchaseClient object. After the object is created, perform the Connect connection. During this process, the SDK connects with the in-app payment service to initiate actions for the various parameters set for the purchase.
/* * The Connect API callback listener for PurchaseClient * returns the results of binding success or failure and whether to update the ONE Store service. * /
PurchaseClient.ServiceConnectionListener mServiceConnectionListener = new PurchaseClient.ServiceConnectionListener() {
@Override
public void onConnected(a) {
Log.d(TAG, "Service connected");
}
@Override
public void onDisconnected(a) {
Log.d(TAG, "Service disconnected");
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, "Connect onError, need to update ONE Store client");
PurchaseClient.launchUpdateOrInstallFlow(this); }};@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// PurchaseClient initialization -- The public key is passed as a parameter to validate context and Signature.
mPurchaseClient = new PurchaseClient(this, AppSecurity.getPublicKey());
// Request a binding with the ONE Store service to enable in-app payments.
mPurchaseClient.connect(mServiceConnectionListener);
}
Copy the code
Please pay attention to the connection not installed ONE store client or ONE store client version does not support application pay inside V17 version of the situation, will call ServiceConnectionListener onErrorNeedUpdateException of (). Appeared the error, call to install or update the ONE store client method, namely PurchaseClient. LaunchUpdateOrInstallFlow.
When exiting the Activity, enter the code to cancel PurchaseClient in the onDestroy method.
@Override
protected void onDestroy(a) {
super.onDestroy();
if (mPurchaseClient == null) {
Log.d(TAG, "PurchaseClient is not initialized");
return;
}
// Interrupt service with PurchaseClient when closing the application.
mPurchaseClient.terminate();
}
Copy the code
4-3. Check whether the IP address is supported
Before using the in-app payment method, developers should call the corresponding method to confirm whether in-app payment can be enabled.
If the SDK methods use the API provided by AIDL, return success and failure results in the form of callbacks.
For failure to return will provide using SDK developers must deal with the three big mistake (onErrorRemoteException onErrorSecurityException onErrorNeedUpdateException) and common errors (onError). The IapResult returned to the onError listener has a return code and an Enum that describes the return code. The developer should handle the error according to the development solution.
/* * Callback listener of isBillingSupportedAsync (query supported or not) */ for PurchaseClient
PurchaseClient.BillingSupportedListener mBillingSupportedListener = new PurchaseClient.BillingSupportedListener() {
@Override
public void onSuccess(a) {
Log.d(TAG, "isBillingSupportedAsync onSuccess");
}
@Override
public void onError(IapResult result) {
Log.e(TAG, "isBillingSupportedAsync onError, " + result.toString());
}
@Override
public void onErrorRemoteException(a) {
Log.e(TAG, "IsBillingSupportedAsync onError, cannot connect to ONE Store service");
}
@Override
public void onErrorSecurityException(a) {
Log.e(TAG, "IsBillingSupportedAsync onError, request payment under application state exception");
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, "IsBillingSupportedAsync onError, need to update ONE Store client"); }};// ONE Store in-app payment API version
int IAP_API_VERSION = 5;
mPurchaseClient.isBillingSupportedAsync(IAP_API_VERSION, mBillingSupportedListener);
Copy the code
4-4. Query product information
The developer uses the queryProductAsync method as an argument in the ArrayList input to put the ID of the item in the application that it wants to get information on and calls it, returning the result to the registered listener. Product ID refers to the product ID defined by the developer when registering the product in the Developer Center. The product information is returned to the onSuccess listener as ProductDetail.
/* * queryProductsAsync API (Commodity Information Query) callback listener for PurchaseClient */
PurchaseClient.QueryProductsListener mQueryProductsListener = new PurchaseClient.QueryProductsListener() {
@Override
public void onSuccess(List<ProductDetail> productDetails) {
Log.d(TAG, "queryProductsAsync onSuccess, " + productDetails.toString());
}
@Override
public void onErrorRemoteException(a) {
Log.e(TAG, QueryProductsAsync onError, unable to connect to ONE Store service);
}
@Override
public void onErrorSecurityException(a) {
Log.e(TAG, "QueryProductsAsync onError, request payment if application status is abnormal");
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, QueryProductsAsync onError, need to update ONE Store client);
}
@Override
public void onError(IapResult result) {
Log.e(TAG, "queryProductsAsync onError, "+ result.toString()); }};int IAP_API_VERSION = 5;
String productType = IapEnum.ProductType.IN_APP.getType(); // "inapp"
ArrayList<String> productCodes = new ArrayList<>();
productCodes.add("p5000");
productCodes.add("p10000");
mPurchaseClient.queryProductsAsync(IAP_API_VERSION, productCodes, productType, mQueryProductsListener);
Copy the code
4-5. Initiate a purchase request
Call the launchPurchaseFlowAsync method to perform the purchase. When calling the method, enter the in-app item ID, item name, item category, and launchPurchaseFlowAsync (up to 100 bytes), as the developer chooses, to confirm correctness of the data and additional data upon successful payment, The requestCode passed as a parameter is used to confirm the data returned to onActivityResult.
On a successful purchase the result is returned to the onSuccess listener, in the PurchaseData specification of the SDK. Based on the results received, the developer verifies the correctness of the data and additional data via developerPayload, which is verified by signature information.
Managed goods, which provide goods to users by setting up goods consumption processing. ONE Store offers users various promotional activities such as sending coupons and cashback. The developer can allow or control the user to participate in the promotion through gameUserId and promotionApplicable parameters when making a purchase request. The developer selects the unique identifier of the app and whether to participate in the campaign and passes it to the ONE Store, which processes the user’s campaign offers based on this value.
Note: The gameUserId, protectionApplicable parameters must be applied to ONE Store beforehand. In general, this value should not be sent.
In addition, the gameUserId parameter should be sent to a hash single value so that there are no privacy protection issues when sending value information beforehand.
/* * launchPurchaseFlowAsync API (purchase) callback listener for PurchaseClient */
PurchaseClient.PurchaseFlowListener mPurchaseFlowListener = new PurchaseClient.PurchaseFlowListener() {
@Override
public void onSuccess(PurchaseData purchaseData) {
Log.d(TAG, "launchPurchaseFlowAsync onSuccess, " + purchaseData.toString());
// Check developer payload after the purchase succeeds.
if(! isValidPayload(purchaseData.getDeveloperPayload())) { Log.d(TAG,"launchPurchaseFlowAsync onSuccess, Payload is not valid.");
return;
}
// Check the signature after successful purchase.
boolean validPurchase = AppSecurity.isValidPurchase(purchaseData.getPurchaseData(), purchaseData.getSignature());
if (validPurchase) {
if (product5000.equals(purchaseData.getProductId())) {{
// Managed goods (inapp) are consumed after successful purchase.consumeItem(purchaseData); }}else {
Log.d(TAG, "launchPurchaseFlowAsync onSuccess, Signature is not valid.");
return; }}@Override
public void onError(IapResult result) {
Log.e(TAG, "launchPurchaseFlowAsync onError, " + result.toString());
}
@Override
public void onErrorRemoteException(a) {
Log.e(TAG, "LaunchPurchaseFlowAsync onError, unable to connect to ONE Store service");
}
@Override
public void onErrorSecurityException(a) {
Log.e(TAG, "LaunchPurchaseFlowAsync onError, Request payment under Application Status Exception");
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, "LaunchPurchaseFlowAsync onError, ONE Store client needs to be updated"); }};int IAP_API_VERSION = 5;
int PURCHASE_REQUEST_CODE = 1000; // Return the request code of onActivityResult
String product5000 = "p5000"; // The ID of the requested item
String productName = ""; // "" displays the product name registered by the Developer center
String productType = IapEnum.ProductType.IN_APP.getType(); // "inapp"
String devPayload = AppSecurity.generatePayload();
String gameUserId = ""; / / the default ""
boolean promotionApplicable = false;
mPurchaseClient.launchPurchaseFlowAsync(IAP_API_VERSION, "Call the Activity".this, PURCHASE_REQUEST_CODE, product5000, productName, productType, devPayload, gameUserId, promotionApplicable, mPurchaseFlowListener);
Copy the code
The Purchase information returned to the listener on a successful payment refers to “In-app Payment Reference – getPurchaseIntent() initiates a Purchase request.”
The result of the payment is returned to onActivityResult of the Activity that called launchPurchaseFlowAsync, where the handlePurchaseData method must be added for the SDK to process the purchase result. When the handlePurchaseData method is started, false (failed) is returned if the PurchaseFlowListener entered as an argument to the request purchase is NULL. Processing results of success or error are returned via PurchaseFlowListener.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG, "onActivityResult resultCode " + resultCode);
switch (requestCode) {
case PURCHASE_REQUEST_CODE:
/* * The intent data received on a call to the launchPurchaseFlowAsync API is returned by handlePurchaseData resolution. * Return results after parsing are returned from PurchaseFlowListener when invoked from launchPurchaseFlowAsync. * /
if (resultCode == Activity.RESULT_OK) {
if (mPurchaseClient.handlePurchaseData(data) == false) {
Log.e(TAG, "onActivityResult handlePurchaseData false ");
// listener is null}}else {
Log.e(TAG, "onActivityResult user canceled");
// user canceled , do nothing..
}
break;
default:}}Copy the code
4-6. Consumption of goods
In the case of managed goods (INAPP), the purchased goods cannot be purchased again if they are not consumed. After a user purchases a product, his purchase information is hosted to ONE Store. When the product is consumed, ONE Store immediately takes back the user’s permission to purchase the product. That is to say, if the managed goods have been purchased but not consumed, they can be used as permanent goods; if the goods are consumed immediately after purchase, they can be used as consumable goods; if the purchased goods are consumed beyond a certain period, they can be used as time-limited goods. To make an item consumption request, the purchase information is returned to launchPurchaseFlowAsync or queryPurchasesAsync, passed and invoked as an argument to the consumeAsync method.
/* * consumeAsync API (Commodity Consumption) callback listener for purchasecent */
PurchaseClient.ConsumeListener mConsumeListener = new PurchaseClient.ConsumeListener() {
@Override
public void onSuccess(PurchaseData purchaseData) {
Log.d(TAG, "consumeAsync onSuccess, " + purchaseData.toString());
// After successful consumption of goods, follow the successful purchase plan prepared by each developer.
}
@Override
public void onErrorRemoteException(a) {
Log.e(TAG, "ConsumeAsync onError, cannot connect to ONE Store service");
}
@Override
public void onErrorSecurityException(a) {
Log.e(TAG, "ConsumeAsync onError, request payment under application status exception");
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, "ConsumeAsync onError, need to update ONE Store client");
}
@Override
public void onError(IapResult result) {
Log.e(TAG, "consumeAsync onError, "+ result.toString()); }};int IAP_API_VERSION = 5;
PurchaseData purchaseData; // Query purchase records and PurchaseData received after the purchase was requested
mPurchaseClient.consumeAsync(IAP_API_VERSION, purchaseData, mConsumeListener);
Copy the code
4-7. Query purchase records
Call the queryPurchasesAsync method to get the managed goods purchased but not consumed by the user (inApp) and the monthly auto-payment goods subscribed by the user (Auto). The SDK verifies the signature to verify that the purchase information data is forged. If the signature verification fails, the value of iAPresult. IAP_ERROR_SIGNATURE_VERIFICATION defined in “IapResult” is returned to the onError listener. An error indicates that the purchase information data may be forged, and it is necessary to confirm whether there is an abusing attack.
Developers query purchase records to obtain managed goods (INApp), which can be provided to users by setting consumption of goods.
/* * queryPurchasesAsync API (Query purchase record) callback listener for PurchaseClient */
PurchaseClient.QueryPurchaseListener mQueryPurchaseListener = new PurchaseClient.QueryPurchaseListener() {
@Override
public void onSuccess(List<PurchaseData> purchaseDataList, String productType) {
Log.d(TAG, "queryPurchasesAsync onSuccess, " + purchaseDataList.toString());
if (IapEnum.ProductType.IN_APP.getType().equalsIgnoreCase(productType)) {
// If it is a managed item (inapp) obtained after querying the purchase record, verify the signature first, and consume the item after success.
} else if (IapEnum.ProductType.AUTO.getType().equalsIgnoreCase(productType)) {
// If it is the monthly automatic payment product (auto) obtained after querying the purchase record, verify the signature first, and write the scheme according to the developer's application processing requirements after success.}}@Override
public void onErrorRemoteException(a) {
Log.e(TAG, "QueryPurchasesAsync onError, unable to connect to ONE Store service");
}
@Override
public void onErrorSecurityException(a) {
Log.e(TAG, "QueryPurchasesAsync onError, request payment under application status exception");
}
@Override
public void onErrorNeedUpdateException(a) {
Log.e(TAG, "QueryPurchasesAsync onError, need to update ONE Store client");
}
@Override
public void onError(IapResult result) {
Log.e(TAG, "queryPurchasesAsync onError, "+ result.toString()); }};int IAP_API_VERSION = 5;
String productType = IapEnum.ProductType.IN_APP.getType(); // "inapp"
mPurchaseClient.queryPurchasesAsync(IAP_API_VERSION, productType, mQueryPurchaseListener);
Copy the code
Mobile terminal consumption failed, resulting in order loss
1. Background order query
2. Click Consume in onestore background for manual consumption, and then manually fill the order for the player
This version is V5, onestore has been updated to v6 to access the latest version, please go to:
The ONE Store accesses the portal.
The test need to know
In-app payment testing process
Important points
-
Add a Google Email or Facebook account for testing (sandbox version).
-
In the official version, the test account must be bound to a Korean mobile phone number to be tested.
-
You can find a Korean partner to test for you, and the refund can be solved offline or in the background of the application.