{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Hey, guys! I'm still working on my mobile multiplayer survival game. In my article titled Build a Game That Features Local In-App Purchases , I shared my experience of configuring in-app product information in the language and currency of the country or region where the user's account is located, which streamlines the purchase journey for users and boosts monetization.
Some new challenges have arisen, though. When an in-app product is configured, I need to test its purchase process before it can be brought online. Hence, I need a virtual purchase environment that doesn't actually charge me real money. Sandbox testing it is.
Aside from this, network latency or abnormal process termination can sometimes cause data of the app and the in-app purchases server to be out of synchronization. In this case, my app won't deliver the virtual products users have just purchased. This same issue can be pretty tricky for many developers and operations personnel as we don't want to see a dreaded 1 star on the "About this app" screen of our app on app stores or users venting their anger about our apps on tech forums. Of course my app lets users request a refund by filing a ticket to start the process, but guess how they feel about the extra time they have to put into this?
So I wondered how to implement sandbox testing and ensure a successful product delivery for my app. That's where HMS Core In-App Purchases (IAP) comes to the rescue. I integrated its SDK to do the trick. Let's see how it works.
Sandbox TestingSandbox testing of IAP supports end-to-end testing without real payments for joint debugging.
Preparing for Sandbox TestingI added a test account by going to Users and permissions > Sandbox > Test accounts. The test account needs to be a registered HUAWEI ID and will take effect between 30 minutes and an hour after it has been added.
As the app package I want to test hasn't been released in AppGallery Connect, its versionCode should exceed 0. For an app package once released in AppGallery Connect, the versionCode should be greater than that of the released one.
If you fail to access the sandbox when trying out the function, use the IapClient.isSandboxActivated (for Android) or HMSIAP.isSandboxActivated API (for HarmonyOS) in your app for troubleshooting.
Testing Non-Subscription PaymentsI signed in with the test account and installed the app to be tested on my phone. When a request was initiated to purchase a one-time product (stealth skill card), IAP detected that I was a test user, so it skipped the payment step and displayed a message indicating that the payment was successful.
It was impressively smooth. The purchase process in the sandbox testing environment accurately reflected what would happen in reality. I noticed that the purchaseType field on the receipt generated in IAP had a value of 0, indicating that the purchase was a sandbox test record.
Let's try out a non-consumable product — the chance to unlock a special game character. In the sandbox testing environment, I purchased it and consumed it, and then I could purchase this character again.
Sandbox testing for a one-time product on a phone
Testing Subscription RenewalThe purchase process of subscriptions is similar to that of one-time products but subscriptions have more details to consider, such as the subscription renewal result (success or failure) and subscription period. Test subscriptions renew much faster than actual subscriptions. For example, the actual subscription period is 1 week, while the test subscription renews every 3 minutes.
Sandbox testing for a subscription on a phone
Sandbox testing helps me test new products before I launch them in my app.
Consumable Product RedeliveryWhen a user purchased a consumable such as a holiday costume, my app would call an API to consume it. However, if an exception occurred, the app would fail to determine whether the payment was successful, so the purchased product might not be delivered as expected.
Note: A non-consumable or subscription will not experience such a delivery failure because they don't need to be consumed.
I turned to IAP to implement consumable redelivery. The process is as follows.
Consumable Redelivery Process
Here's my development process.
1. Call obtainOwnedPurchases to obtain the purchase data of the consumable that has been purchased but not delivered. Specify priceType as 0 in OwnedPurchasesReq.
If this API is successfully called, IAP will return an OwnedPurchasesResult object, which contains the purchase data and signature data of all products purchased but not delivered. Use the public key allocated by AppGallery Connect to verify the signature.
The data of each purchase is a character string in JSON format and contains the parameters listed in InAppPurchaseData. Parse the purchaseState field from the InAppPurchaseData character string. If purchaseState of a purchase is 0, the purchase is successful. Deliver the required product for this purchase again.
Code:
// Construct an OwnedPurchasesReq object.
OwnedPurchasesReq ownedPurchasesReq = new OwnedPurchasesReq();
// priceType: 0: consumable; 1: non-consumable; 2: subscription
ownedPurchasesReq.setPriceType(0);
// Obtain the Activity object that calls the API.
final Activity activity = getActivity();
// Call the obtainOwnedPurchases API to obtain the order information about all consumable products that have been purchased but not delivered.
Task<OwnedPurchasesResult> task = Iap.getIapClient(activity).obtainOwnedPurchases(ownedPurchasesReq);
task.addOnSuccessListener(new OnSuccessListener<OwnedPurchasesResult>() {
@Override
public void onSuccess(OwnedPurchasesResult result) {
// Obtain the execution result if the request is successful.
if (result != null && result.getInAppPurchaseDataList() != null) {
for (int i = 0; i < result.getInAppPurchaseDataList().size(); i++) {
String inAppPurchaseData = result.getInAppPurchaseDataList().get(i);
String inAppSignature = result.getInAppSignature().get(i);
// Use the IAP public key to verify the signature of inAppPurchaseData.
// Check the purchase status of each product if the verification is successful. When the payment has been made, deliver the required product. After a successful delivery, consume the product.
try {
InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseData);
int purchaseState = inAppPurchaseDataBean.getPurchaseState();
} catch (JSONException e) {
}
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
Status status = apiException.getStatus();
int returnCode = apiException.getStatusCode();
} else {
// Other external errors.
}
2. Call the consumeOwnedPurchase API to consume a delivered product.
Conduct a delivery confirmation for all products queried through the obtainOwnedPurchases API. If a product is already delivered, call the consumeOwnedPurchase API to consume the product and instruct the IAP server to update the delivery status. After the consumption is complete, the server resets the product status to available for purchase. Then the product can be purchased again.
[HEADING=1]Conclusion[/HEADING]
A 1-star app rating is an unwelcome sight for any developer. For game developers in particular, one of the major barriers to their app achieving a 5-star rating is a failed virtual product delivery.
I integrated HMS Core In-App Purchases into my mobile game to implement the consumable redelivery function, so now my users can smoothly make in-app purchases. Furthermore, when I need to launch a new skill card in the game, I can perform tests without having to fork out real money thanks to the kit.
I hope this practice helps you guys tackle similar challenges. If you have any other tips about game development that you'd like to share, please leave a comment.
}
});
Related
Subscriptions are mostly used for video, music, and reading apps. Once a user has purchased a subscription, it can be automatically renewed on a periodic basis.
The graph below shows the process for purchasing a subscription.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
This guide tells you how to integrate HUAWEI In-App Purchases' (IAP) product management function so you can manage subscriptions in your apps. It will also provide solutions for problems that can occur during the integration.
Key development steps:
1. Create a subscription in the product management system (PMS) and add it to your app. You can do this by referring to Adding a Product.
Notes:
(1) Before you create a subscription, you need to create a subscription group to manage subscriptions of the same type.
(2) A new subscription in PMS will be automatically allocated to a default group, so you cannot choose a specific group for it. The subscription also cannot be deleted or re-created. Fixing such problems takes a great deal of effort, especially when the product, whose ID stays the same, is released on multiple platforms.
(3) When you add a subscription, its status will be set to Invalid by default. You need to activate it before you can start selling it. You can present invalid subscriptions in your app, but users will be unable to purchase them.
2. How the Code Works
a. Check whether the country or region of the signed-in HUAWEI ID is supported by HUAWEI IAP.
Code:
/**
* Check whether the country or region of the signed-in HUAWEI ID is included in the countries or regions
* supported by HUAWEI IAP.
* @param activity indicates the activity object that initiates a request.
*/
public static void isBillingSupported(final Activity activity) {
// Get the Activity instance that calls this API.
Task<IsEnvReadyResult> task = Iap.getIapClient(activity).isEnvReady();
task.addOnSuccessListener(new OnSuccessListener<IsEnvReadyResult>() {
@Override
public void onSuccess(IsEnvReadyResult result) {
// Obtain the execution result.
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
Status status = apiException.getStatus();
if (status.getStatusCode() == OrderStatusCode.ORDER_HWID_NOT_LOGIN) {
// Not signed in.
if (status.hasResolution()) {
try {
// 3333 is an int constant defined by the developer.
status.startResolutionForResult(activity, 3333);
} catch (IntentSender.SendIntentException exp) {
}
}
} else if (status.getStatusCode() == OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED) {
// The current country or region is not supported by HUAWEI IAP.
}
}
}
});
}
Notes:
(1) If a user tries to purchase a subscription in your app without first signing in to their HUAWEI ID, an error code will be reported to this API. Set the API to display the sign-in page upon receiving this code.
(2) If a user tries to purchase a subscription of your app from a location where HUAWEI IAP payment is not supported, an error code will be reported to this API.
(3) It is recommended that the context of the main activity be used, because this allows you to directly display the sign-in page and process subsequent services after sign-in.
It is best to call this API before the subscription list is displayed to users.
b. Obtain in-app product details configured in AppGallery Connect.
Code:
//**
* Obtain in-app product details configured in AppGallery Connect.
* @param productIds ID list of products to be queried.
* Each product ID must exist and be unique in the current app.
* @param type In-app product type.
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
*/
private void queryProducts(final Activity activity) {
// Pass in the productId list of products to be queried.
List<String> productIdList = new ArrayList<>();
// The product ID is the same as that set by a developer when configuring product information
// in AppGallery Connect.
productIdList.add("SubProduct1001");
ProductInfoReq req = new ProductInfoReq();
// priceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.setPriceType(IapClient.PriceType.IN_APP_SUBSCRIPTION);
req.setProductIds(productIdList);
// Call the obtainProductInfo API.
Task<ProductInfoResult> task = Iap.getIapClient(activity).obtainProductInfo(req);
task.addOnSuccessListener(new OnSuccessListener<ProductInfoResult>() {
@Override
public void onSuccess(ProductInfoResult result) {
// Obtain the result.
List<ProductInfo> productList = result.getProductInfoList();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
int returnCode = apiException.getStatusCode();
} else {
// Other external errors.
}
}
});
}
When calling this API, errors can occur if:
(1) The product type parameter does not match the product. For example, the value 0 (consumable) is passed to the parameter type for a subscription.
(2) More than 200 products are imported in one batch.
(3) The product ID is different from the one configured in the PMS.
(4) The product has not been activated.
c. Create orders for in-app products in the PMS.
Code:
/**
* Create orders for in-app products in the PMS.
*
* @param productId ID of the in-app product to be paid.
* The in-app product ID is the product ID you set during in-app product
* configuration in AppGallery Connect.
* @param type In-app product type.
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
*/
private void buy(final Activity activity, String productId, int type) {
// Construct a PurchaseIntentReq object.
PurchaseIntentReq req = new PurchaseIntentReq();
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
req.setProductId("productId");
// In-app product type contains:
// priceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
// type:IapClient.PriceType.IN_APP_SUBSCRIPTION
req.setPriceType(type);
req.setDeveloperPayload("developer define info");
// Call the createPurchaseIntent API.
Task<PurchaseIntentResult> task = Iap.getIapClient(activity).createPurchaseIntent(req);
task.addOnSuccessListener(new OnSuccessListener<PurchaseIntentResult>() {
@Override
public void onSuccess(PurchaseIntentResult result) {
// Obtain the payment result.
Status status = result.getStatus();
if (status.hasResolution()) {
try {
// 6666 is an int constant defined by the developer.
status.startResolutionForResult(activity,status.startResolutionForResult(activity, Constants.REQ_CODE_BUY));
} catch (IntentSender.SendIntentException exp) {
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
Status status = apiException.getStatus();
int returnCode = apiException.getStatusCode();
} else {
// Other external errors.
}
}
});
}
When calling this API, errors can occur if:
(1) The product is still invalid after it has been purchased.
(2) The payment page does not display. This can happen if the parameter or context is incorrect. We recommend you use the context of the main activity, because this will enable your app to provide services after the user has made a successful payment.
(3) The user has to sign the payment agreement because they are purchasing a subscription for the first time.
d. HUAWEI IAP returns the payment result to your app through the main activity's onActivityResult callback.
Code:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Constants.REQ_CODE_LOGIN || requestCode == Constants.REQ_CODE_BUYWITHPRICE_CONTINUE) {
int returnCode = IapClientHelper.parseRespCodeFromIntent(data);
Log.i(TAG,"onActivityResult, returnCode: " + returnCode);
if (returnCode == OrderStatusCode.ORDER_STATE_SUCCESS) {
// If success is returned, you can call the buyWithPrice API again.
//buyWithPrice(productId);
} else if(returnCode == OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED){
Log.e(TAG,"This is unavailable in your country/region." );
} else {
Log.e(TAG,"user cancel login" );
}
return;
}
if (requestCode == Constants.REQ_CODE_BUY) {
if (data == null) {
Log.e("onActivityResult", "data is null");
return;
}
PurchaseResultInfo purchaseResultInfo = Iap.getIapClient(this).parsePurchaseResultInfoFromIntent(data);
switch(purchaseResultInfo.getReturnCode()) {
case OrderStatusCode.ORDER_STATE_CANCEL:
// The user cancels payment.
break;
case OrderStatusCode.ORDER_STATE_FAILED:
case OrderStatusCode.ORDER_PRODUCT_OWNED:
// Check if there exists undelivered products.
break;
case OrderStatusCode.ORDER_STATE_SUCCESS:
// The payment is successful.
String inAppPurchaseData = purchaseResultInfo.getInAppPurchaseData();
String inAppPurchaseDataSignature = purchaseResultInfo.getInAppDataSignature();
// Use the public key of your app to verify the signature.
// If the signature is correct, you can deliver your products.
// If the user purchased a consumable product, call the consumeOwnedPurchase
// API to consume it after successfully delivering the product.
break;
default:
break;
}
}
}
Notes:
(1) This API is used for payment results like payment cancellations, abnormal purchasing activity, payment successes, or when the user tries to purchase a product they have already purchased.
(2) If a user tries to purchase a subscription without first signing in to their HUAWEI ID, set this API to resume processing after they have signed in.
(3) If a user tries to purchase a subscription without having first signed the payment agreement, set this API to enable your app to continue the purchasing process after the user has signed the agreement.
e. Once a user has successfully purchased a subscription, verify the purchase on your app to check that the subscription is valid and provides its service.
Code:
/**
* Deliver products for the user .
*/
private void deliverProduct(final String inAppPurchaseDataStr, final String inAppPurchaseDataSignature) {
if (TextUtils.isEmpty(inAppPurchaseDataStr) || TextUtils.isEmpty(inAppPurchaseDataSignature)) {
Utils.showMessage(this, "purchase data is error");
return;
}
if (CipherUtil.doCheck(inAppPurchaseDataStr, inAppPurchaseDataSignature, Key.getPublicKey())) {
try {
InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseDataStr);
if(inAppPurchaseDataBean.isSubValid())
{
// Provide the user with the service.
}
} catch (JSONException e) {
Log.e(TAG, "delivery:" + e.getMessage());
}
} else {
Log.e(TAG, "delivery:" + getString(R.string.verify_signature_fail));
}
}
Notes:
(1) You need to generate the public key used for verifying signatures by going to Project Setting > In-App Purchases > Settings in AppGallery Connect.
(2) After a successful verification, for services (such as video app memberships) that are only available when they have been renewed in time, if InApppurchaseData.subIsvalid is true, you need to keep providing the services for the user.
(3) If possible, store the delivered products' purchaseToken on your app server. This can then be used as a credential when a user switches between subscriptions or accounts.
f. Redelivery. This mechanism reduces unsuccessful deliveries.
Generally, if an exception (such as a network error or process failure) occurs after the subscription has been paid for, your app will be unable to detect whether the payment was successful. This means the purchased product may not be delivered as expected.
However, with HUAWEI IAP, you can redeliver products.
Code:
/**
* Query information about all subscribed in-app products, including consumables, non-consumables,
* and auto-renewable subscriptions.
* If consumables are returned, the system needs to deliver them and calls the consumeOwnedPurchase
* API to consume the products.
* If non-consumables are returned, the in-app products do not need to be consumed.
* If subscriptions are returned, all existing subscription relationships of the user under the
* app are returned.
* @param mClient IapClient instance to call the obtainOwnedPurchases API.
* @param type In-app product type.
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
*/
public static void obtainOwnedPurchases(IapClient mClient, final int type) {
Log.i(TAG, "call obtainOwnedPurchases");
String continuationToken = null;
// Construct a OwnedPurchasesReq object.
OwnedPurchasesReq ownedPurchasesReq = new OwnedPurchasesReq();
// In-app product type contains:
// priceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
ownedPurchasesReq.setPriceType(2);
// Call the obtainOwnedPurchases API.
// Get the Activity instance that calls this API.
Task<OwnedPurchasesResult> task = mClient.obtainOwnedPurchases(ownedPurchasesReq);
task.addOnSuccessListener(new OnSuccessListener<OwnedPurchasesResult>() {
@Override
public void onSuccess(OwnedPurchasesResult result) {
// Obtain the execution result.
if (result != null && result.getInAppPurchaseDataList() != null) {
for (int i = 0; i < result.getInAppPurchaseDataList().size(); i++) {
String inAppPurchaseData = result.getInAppPurchaseDataList().get(i);
String InAppSignature = result.getInAppSignature().get(i);
// Use the payment public key to verify the signature of the inAppPurchaseData.
deliverProduct(inAppPurchaseData,InAppSignature);
// The verification is successful.
try {
InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseData);
int purchaseState = inAppPurchaseDataBean.getPurchaseState();
} catch (JSONException e) {
}
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
Status status = apiException.getStatus();
int returnCode = apiException.getStatusCode();
} else {
// Other external errors.
}
}
});
}
Notes:
(1) This mechanism works by either querying the IAP server for information about all valid, in-app product subscriptions, and then reporting this to your app server and providing services, or by providing services directly from the client.
(2) For services (such as video app memberships) that are only available when they have been renewed in time, if InApppurchaseData.subIsvalid is true, you need to keep providing the services for the user. For other custom services (such as magazine subscriptions), if you require the subscription renewal data for previous periods, you can query the receipts for all subscriptions through IapClient.obtainOwnedPurchaseRecord. The receipt format is the same as it is in InAppPurchaseData.
(3) If InApppurchaseData.purchaseState is set to 0, the subscription is in purchased state. The time segment from InApppurchaseData.purchaseTime to InApppurchaseData.expirationDate indicates a subscription's validity period.
(4) If a subscription is in purchased state and its validity period matches your app server's valid provisioning period, your app needs to provide the relevant services. For all subscriptions, your app determines whether to provide services in this way.
3. Key subscription event callback from the Huawei IAP Server
(1) Before you request callback, you need to sign in to AppGallery Connect and go to Project Setting > In-App Purchases > Settings to generate a public key and app server callback address.
(2) Phases of the subscription process:
Promotional period: The user enjoys a subscription for free or at a promotional price. This period will begin when the user successfully subscribes.
Renewal:
First renewal: The system renews the subscription at the standard price. During this period, the subscription is valid and the user is entitled to the service provided by the subscription.
Second and subsequent renewals: For each of these renewals, HUAWEI IAP renews the subscription by periodically deducting fees from the user's account. It obtains the fee and product status 10 days before the subscription expires. HUAWEI IAP will attempt to deduct fees from the user's account 24 hours before the subscription renews. If this fails, the lapse period will start. During this period, HUAWEI IAP will try to deduct fees once a day. It will stop trying when this period ends.
Expiring: The subscription is valid and services are available. However, because the user has canceled the subscription, no fee will be deducted for the next renewal.
Expired: The subscription has expired and services are no longer available. There are several possible causes for this: HUAWEI IAP was unable to deduct a fee for the last renewal; the user has canceled the subscription; the user has not agreed to a price increase; the fee deducted account is abnormal.
To-be-effective: The subscription has not taken effect, and the user cannot use the service provided by the subscription. This period occurs when a user switches between subscriptions in the same subscription group. For example, a user may have switched to a yearly subscription from a monthly one which is still valid, in which case the yearly subscription would be in the to-be-effective period.
Suspended: Users can decide on a suspension plan for a subscription which is under renewal on the subscription management screen of HMS Core (APK). Once this plan is completed, the subscription renewal will be suspended, but the suspension will not take effect and users can still use the services provided by the subscription. Once the subscription validity period has ended, the subscription renewal will be suspended, and the user will no longer be able to use the services. Then, after the suspension period ends, the subscription will be automatically renewed. If the renewal is successful, the subscription can be used. If the renewal fails, the subscription will expire. Users can adjust suspension plans at any time, and the maximum suspension period is three months. Users can cancel the suspension plan at any time to revert to the auto-renewal status and enjoy the services immediately.
Grace period (coming soon): If you enable a grace period for your app, it will activate when HUAWEI IAP is unable to deduct a fee at the end of the subscription validity period. During the grace period, the user can still use the subscription while the HUAWEI IAP server attempts to deduct fees. If the fee deduction is successful within the grace period, the renewal will go ahead as planned. If the fee deduction is successful once the grace period has passed, the renewal will be interrupted. Then, the renewal start time will be the actual fee deduction time.
Lapse period: If a subscription is not renewed (for example, the user proactively cancels a renewal) or the renewal fails after the grace period has passed, a subscription enters the lapse period. For a subscription in the lapse period, there is a 30-day retention period when services are unavailable but a user can still resume the subscription. During this period, the subscription can be considered as having expired.
Key subscription event callback returned to your app server:
POST /subscription/notify/address
Content-Type: application/json; charset=UTF-8
Accept: application/json
Content-Length: 2754
Code:
{
"statusUpdateNotification": "{\"environment\":\"Sandbox\",\"notificationType\":7,\"subscriptionId\":\"1581789719266.D40972AC.3089\",\"orderId\":\"1581789719266.148748E7.3089\",\"latestReceipt\":\"00000173741056a37eef310dff9c6a86fec57efafe318ae478e52d9c4261994d64c8f6fc8ea1abbdx5347.5.3089\",\"latestReceiptInfo\":\"{\\\"autoRenewing\\\":true,\\\"subIsvalid\\\":true,\\\"orderId\\\":\\\"1581789719266.148748E7.3089\\\",\\\"lastOrderId\\\":\\\"1581790584090.DDB1EE93.5610\\\",\\\"packageName\\\":\\\"com.example.hwinappdemo\\\",\\\"applicationId\\\":123456,\\\"productId\\\":\\\"monthly_subscription2\\\",\\\"kind\\\":2,\\\"productName\\\":\\\"monthly_subscription2\\\",\\\"productGroup\\\":\\\"185F2E6A04BB4299B82C2525D3D68EBE\\\",\\\"purchaseTime\\\":1582791622434,\\\"oriPurchaseTime\\\":1582790122434,\\\"purchaseState\\\":0,\\\"developerPayload\\\":\\\"test\\\",\\\"purchaseToken\\\":\\\"00230173741056a37eef310dff9c6a86fec57efafe318ae478e52d9c4261994d64c8f6fc8ea1abbdx5347.5.3089\\\",\\\"purchaseType\\\":0,\\\"currency\\\":\\\"TRY\\\",\\\"price\\\":2,\\\"country\\\":\\\"TR\\\",\\\"subscriptionId\\\":\\\"1581789719266.D40972AC.3089\\\",\\\"quantity\\\":1,\\\"daysLasted\\\":26,\\\"numOfPeriods\\\":6,\\\"numOfDiscount\\\":0,\\\"expirationDate\\\":1582791922434,\\\"retryFlag\\\":1,\\\"introductoryFlag\\\":0,\\\"trialFlag\\\":0,\\\"renewStatus\\\":1,\\\"cancelledSubKeepDays\\\":30,\\\"payOrderId\\\":\\\"SandBox_1581789719266.148748E7.3089\\\",\\\"payType\\\":\\\"0\\\",\\\"acknowledged\\\":1,\\\"cancelWay\\\":0,\\\"cancellationTime\\\":1582789943392}\",\"latestReceiptInfoSignature\":\"S94effXhWisNXNE+lFBPv5DHGixYDY+3aI8fNDA+/wGQA+gZEx76Vilf0OqmW7zWorenMp98ra98nO7zJwmoZIF7R8/ZEd0V7dWiMOU+iMkhfDpfjAmtkraj5tjTYJR+QIhrQQWIxXg1AhqmGBuTyBdy0TVrfsVoKfCnh5oFZ5H7UB7jZA3KJtIHqsziAFdPEP67oE9kf4eHNsYAw/xSl4wTPXgsRJINu6SmCWcUzZ2UNMN/zWVgiqYlc9CGu6jParOfYeEnUCspdyZrsdWkmh2IB3pXOV8JxVsinzkklPwjotU21Oja46mjW9E+WSEomSjk9dop/iA0SjbzuH8pSogu0UyeO9FelFf/ZsaVQZLLjCHBK6pQ3SuHD3amqRB0GP3lGKdD1z5N7N5rO8ngS+mmlPi1gZwj+FiXdScVbmX6fOpvP+PFhyOginZp84EenKYx9Z9asXH+cJxmw9usN95cL7gAFhk+pu19Ng0Ro+t0+DPuj+cTOzyhVy7FTcDM\",\"autoRenewStatus\":1,\"productId\":\"monthly_subscription2\",\"applicationId\":\"123456\"}",
"notifycationSignature": "lD8NiUCrH+TK7gWBJB6XP3ZG9L2/TZvAGPIvRetAus9ZrKLjIzeagGjfi9rW9f/wEctIrp2gtDZ+k9xEyFT9apAqEdp629pUmufKe8jWlj6ZrHxqABcBQ2+ZFcDQH2u1Vm8tsEsk0ApQ4Z/nY2GCpifU/L6ITqT/Gn5hEBm7aCBEzOzg+SOBlCxqMOehusgPQTss32Y+E7KAPFy3SzH7o4/mMWOBoqvhZQrs/t9hlS8oamnsm3MmT3fNkoV8W/H7ckZhA62bfryjO8B/RVmz69x4CbvuIXj5Uo459aaIX6wE/5gPEqnnj1QrzYd10arNk6u0/nPUoKllm1NJ56qH/i9SLX+fRVnRnhvM/6t01IXcfxOg5Nx4pT2R/UkOEdcZs7lRrnWnVY7VsMQVZAkxbPAe1eSyzhGnW7s0mr1VfdMmr56eQ3zxNeQ2KXzeVQkBCb4EiY0MB2swFMzsWlo/OMBrUjG7ojflq0Shww45bSOBNO0nlz9B/keFj17fUKFt"
}
Case study:
In this example, a social media app integrates HUAWEI IAP. Below, you can see the kinds of major problems which may arise during the integration process, when they might occur, and how they can be solved.
1. Is there a redelivery mechanism available for subscriptions?
Scenario: The redelivery mechanism should take effect after a successful payment, but sometimes the purchased product is not delivered as expected because an exception occurs. For example, your app may fail to receive or correctly handle notifications about key subscription events, or the process may be interrupted by an unexpected app exit caused by a program error.
Solution: When your app restarts, set it to call obtainOwnedPurchases(OwnedPurchasesReq ownedPurchasesReq) to query information about all the auto-renewal subscriptions the user has purchased, and return the results. This information can be obtained from InAppPurchaseData and returned to your app server. It ensures your app can provide users with uninterrupted services. The redelivery mechanism can be implemented for all subscriptions using this kind of API.
2. When does the parameter subscriptionId change? Will it change when a user switches between subscriptions, or when a user resumes a subscription?
Scenario: This parameter is used to provide service credentials to users whom you want to be able to enjoy your service.
Solution: The subscription ID of a product remains the same when the subscription renewal is normal.
Currently, the subscription ID only changes in the following two scenarios:
l A user purchases a new product.
l A user resumes a subscription after the grace period. If this happens, the subscription ID changes because the user's previous subscription has become invalid. However, if a user proactively resumes a subscription before it enters the lapse period, the subscription ID will not change.
This is also true of the subscription's purchaseToken.
3. What notifications about key subscription events indicate the subscription is valid?
Scenario: Your app needs to be able to deliver a product to the user based on the notification types it receives.
Solution:
(1) For details about the notification types, refer to the Table above.
(2) The app will provide users with the services they are entitled once it has checked that InApppurchaseData.subIsvalid is set to true and the period from InApppurchaseData.purchaseTime to InApppurchaseData.expirationDate indicates that the subscription is valid.
4. What should I do with the subscriptions when my app supports multiple accounts for the same user?
Scenario 1: Your app has its own account system. For example, a user has two accounts, A and B. If this user purchases a monthly subscription for A, will this subscription be available for B?
Solution: HUAWEI IAP only distinguishes between HUAWEI IDs, and not user accounts registered to your app. Therefore, the purchased subscription is shared by all accounts as long as they are related to the same HUAWEI ID. In this scenario, we recommend that the user still be entitled to the service when signing in with B, up until the subscription they purchased for A has expired. Since the subscription ID stays the same, you can build an agreement mechanism according to your needs.
Scenario 2: A user purchases a monthly subscription for account A and then a yearly subscription for account B. Which of these subscriptions should my app provide them with?
Solution: In this scenario, if the user switches between subscriptions using HUAWEI IAP's subscription management feature, you can use setDeveloperPayLoad("xxxx") to set user tags and provide account B with the service when the yearly subscription becomes valid. However, when the user switches between subscriptions using the subscription switching function, your app cannot tell which account switches the subscription. Therefore, your app will provide the yearly subscription to account A, which the user used first to purchase a subscription in your app.
Scenario 3: What happens when a user disables switching between accounts A and B, cancels a subscription of A, and resumes the subscription for B?
Solution: The subscription's service will be provided to whichever user account is bound to the subscription ID. In this scenario, since this ID remains unchanged, the service could be provided to either A or B. To avoid this issue, your app server needs to unbind the subscription from A and provide the service to B.
Very interesting and helpful
Hmm
Very useful post.
These posts have shown the first few steps of developing a Unity game:
Unity Editor Installation and APK Packaging
How Can I Use the HUAWEI Game Service Demo Provided by Unity
Initializing a Huawei Game and Implementing HUAWEI ID Sign-in Using Unity
I tried to upload the packaged APK file to AppGallery Connect. Let’s see how it went.
Based on Unity documents, I first completed my game information in UDP, and uploaded its demo package. But the upload failed and UdpSdkNotDetectedError was displayed.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
I asked Unity technical support for help, and they told me to also integrate the UDP SDK.
The UDP SDK is used for in-app purchases. You can learn more about it from this document:
https://docs.unity3d.com/Packages/[email protected]/manual/index.html
Integration of the UDP SDK
According to the Unity documents, there are three ways to integrate the UDP SDK:
Using Unity IAP (Unity IAP adds another layer to the UDP APIs.)
UDP is included in Unity IAP from version 1.22.0 to 1.23.5. If you use the Unity IAP package (1.22.0–1.23.5), do not install the UDP separately.
Using the UDP package
Using the UDP package and Unity IAP
The package of the Unity IAP 2.0.0 and later does not contain the UDP package. You need to download them both.
Latest version of Unity Asset Store (2020–11–24)
UDP version: 2.0.0
Unity IAP version: 2.2.2
Unity official documentation:
https://docs.unity3d.com/Packages/[email protected]/manual/index.html
https://distribute.dashboard.unity.com/udp/guideDoc/HUAWEI
Importing a UDP Package
If it’s your first time integrating the UDP SDK, just download the latest version of UDP package from Unity Asset Store.
Here’s the address:
https://assetstore.unity.com/packages/add-ons/services/billing/unity-distribution-portal-138507
Import the resource to Unity Editor.
If you see Unity Distribution Portal here, the resource has been imported.
Associating a Unity Project with a UDP Client ID
Document: https://docs.unity3d.com/Packages/[email protected]/manual/games-with-iap.html
Before you integrate Unity IAP, you need to associate your Unity project with a UDP client ID. Follow the instructions below.
Integrating the Unity IAP API.
Reference:
https://docs.unity3d.com/Packages/[email protected]/manual/games-with-iap.html
Before You Start
If you’re still confused by now, don’t worry, check the sample code provided by Unity. You can view it here after you downloaded the UDP package.
Integrating and Testing the StoreService.Initialize API
Integration
According to Unity, this API must be called before your game is released to other app stores using UDP.
I will continue with my earlier integrations, and here’s a few tips for you:
1. Add a button. Right-click HeaderPanel and choose UI > Button. Rename it Initialize.
2. Write code for the button.
Key points:
1) Add a namespace for UDP.
2) Declare the Initialize button, the callback listener of the API, and the boolean value.
C#:
private static InitListener m_InitListener=new InitListener();
// IAP initialized flag
private static bool m_Initialized;
private static Button initialize_button;
3) Capture the Initialize button in initUI().
C#:
initialize_button= GameObject.Find(“Initialize”).GetComponent<Button>();
4) Add the following code to private void initListeners():
C#:
initialize_button.onClick.AddListener(() =>
{
Show("starting initialize");
StoreService.Initialize(m_InitListener);
Show("initialize finished");
});
5) If you want the StoreService.Initialize API to be automatically called at app launch, add the highlighted code here:
C#:
void Start()
{
initUI();
initListeners();
// Call the Appinit method in your game code
appInit();
// Call the Initialize method in your game code
initialize();
}
private void initialize()
{
m_Initialized = false;
Debug.Log("Init button is clicked.");
Show("Initializing");
StoreService.Initialize(m_InitListener);
}
6) Edit the callback processing logic.
C#:
public class InitListener : IInitListener
{
public void OnInitialized(UserInfo userInfo)
{
Debug.Log("[Game]On Initialized suceeded");
Show("Initialize succeeded");
m_Initialized = true;
}
public void OnInitializeFailed(string message)
{
Debug.Log("[Game]OnInitializeFailed: " + message);
Show("Initialize Failed: " + message);
}
}
Packaging and Testing
The following information indicates that the API is called successfully.
Adding a Sandbox Test Account
If the following message is displayed, you need to add a sandbox account to check whether the IAP API is integrated successfully before app release.
Check this document:
https://docs.unity3d.com/Packages/[email protected]/manual/creating-a-game-on-udp.html
Make sure to click SAVE after adding the account.
The following information indicates that the API is called successfully.
Integrating and Testing the StoreService.QueryInventory API
API Functions
This API is used to query HUAWEI IAP for any missing transactions.
For consumables, if a product is purchased but not delivered, this API returns the product ID. Which means, it returns the consumable with a payment exception.
For non-consumables, this API returns all non-consumables that have been purchased by the player.
Integration
Similar to the integration of StoreService.Initialize.
Packaging and Testing
When calling this API, you can pass the product ID you want to query. If you pass one, you’ll see the corresponding information here.
After launching the app, you’ll see the following screen:
Creating a Product
To better test the IAP API, you can create a few products. Check this document:
https://docs.unity3d.com/Packages/c...#managing-in-app-purchases-on-the-udp-console
Here, I created two products.
Note: Currently, Unity IAP does not support subscriptions for games released on HUAWEI AppGallery.
Integrating and Testing the StoreService.Purchase API
Integration
Similar to the integration of StoreService.Initialize.
Packaging and TestingNon-consumables
StoreService.Purchase(“non_consumable_product01”, “payload”, m_PurchaseListener);
Here, non_consumable_product01 is a non-consumable I created in UDP.
After a successfull purchase is successful, call the API again. The following message is displayed.
Call StoreService.QueryInventory(productIds,m_PurchaseListener);.
Define productIds as follows:
List<string> productIds = new List<string> { “consumable_product01”, “non_consumable_product01” };
Now, you can see the information of the two products I created and the non-consumable I just purchased.
Consumables
After a consumable is purchased, it will be delivered and consumed.
If a consumable fails to be delivered after being purchased, you can call the StoreService.QueryInventory API to query its information, which is similar to HUAWEI IAP.
1) The consumable is not consumed after purchase.
Call this API:
StoreService.Purchase("non_consumable_product01", "payload", m_PurchaseListener);
A message is displayed, indicating that the product needs to be consumed before another purchase.
2) StoreService.QueryInventory(productIds,m_PurchaseListener);
Define productIds as follows:
C#:
List<string> productIds = new List<string> { "consumable_product01", "non_consumable_product01" };
The consumable is included in the purchased products that are returned.
3) Call this API to consume the product:
C#:
StoreService.ConsumePurchase(PurchaseInfo,IPurchaseListener)
According to the official document, PurchaseInfo is returned using the onPurchase method. You can obtain this method from the callback of StoreService.QueryInventory or StoreService.Purchase.
Here, I called the StoreService.QueryInventory API, obtained the consumable, and consumed it:
C#:
StoreService.ConsumePurchase(inventory.GetPurchaseInfo("consumable_product01"), this);
The following information indicates that the product is consumed.
Note:
After the consumption, if you call the StoreService.QueryInventory API again, you’ll see that there’s no consumable left. The query and redeliver process is complete.
Server APIs involved in Unity IAP
According to Unity, if the server APIs fail to receive the callback, no matter what the payment result is, from an app store, payment failure will be returned by UDP. Unity also provides some server APIs for you to integrate.
I’m not going to show you the demo test. You can try it on your own.
Payment Callback API
If you have your own game server, you can receive successful payment notifications sent by UDP.
For details, check this document:
https://docs.unity3d.com/Packages/c...ames-with-iap.html#game-client-implementation
Configure the callback URL here.
Order Query API
You can also send an HTTP GET request for order details and verify its information.
For details, check this document:
https://docs.unity3d.com/Packages/c...ames-with-iap.html#server-side-implementation
Very useful if you use unity.
Rebis said:
Very useful if you use unity.
Click to expand...
Click to collapse
Thank you for your liking appreciation.
What Does a Delivery Failure Mean?
A delivery failure refers to a situation where a user does not receive the item they have purchased.
A user purchases a virtual product or service within an app, but the user does not receive the purchased item due to an error (such as network exception or process termination) occurring in data synchronization between the app and the in-app purchases server.
Delivery failures can be damaging to both users and developers, and may lead to your app receiving a one-star rating, or users directly turning away from it. This may in turn have a knock-on effect on potential users, driving them away from your app before even giving it a chance. Clearly, this is something that needs to be avoided.
Common Way of Handling a Delivery Failure
The user claims that they have paid but did not receive the purchased item, and subsequently requests a refund. This will undoubtedly break the user's trust in your app.
Integrating HUAWEI IAP to Eliminate Delivery Failures
With HUAWEI IAP, you can completely eliminate the chance of a missing delivery occurring.
Your app calls the APIs of HUAWEI IAP to query order information from the HUAWEI IAP server. The server will return orders that are paid but not consumed to your app, which will then redeliver the products based on the query result, and tell the server to update the order status.
You can complete integration by following these instructions:
HUAWEI IAP: Purchase Process
For a purchased consumable, your app will call the consumption API to consume the product. If the API call fails, product delivery will fail. A non-consumable product or subscription service will not experience a delivery failure.
A typical consumable purchasing process:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
1. A user initiates a purchase request from your app, which your app then sends to HMS Core (APK).
2. Request a delivery. Verify the signature of purchase data before delivering the requested product. 3. Deliver the product and send the purchase token to your app server. This token can be used to obtain the product delivery status so that you can determine whether to redeliver a product if its consumption fails due to delivery failure.
3. After a product is successfully delivered, call the consumeOwnedPurchaseAPI to consume the product and send a notification to the Huawei IAP server to update its delivery status. purchaseToken is passed in the API call request, and once consumption is complete, the Huawei IAP server resets the product status to available for purchase. The product can then be purchased again.
Besides the consumeOwnedPurchase API provided by the IAP client, your app can also use the API provided by the IAP server for product consumption. For details, please refer to Confirming the Purchase for the Order Service.
How HUAWEI IAP redelivers a product:
With HUAWEI IAP you can redeliver a consumable when a delivery failure occurs because of data synchronization errors (caused by network exception, process termination, or others) between your app and the IAP server. The following figure illustrates the redelivery process.
Your app needs to trigger a redelivery in the following scenarios:
The app launches.
Result code -1 (OrderStatusCode. ORDER_STATE_FAILED) is returned for a purchase request.
Result code 60051 (OrderStatusCode. ORDER_PRODUCT_OWNED) is returned for a purchase request.
Implement the redelivery function as follows:
1. Use obtainOwnedPurchases to obtain the purchase information about the purchased but undelivered consumable. Specify priceType as 0 in OwnedPurchasesReq.
If this API is successfully called, HUAWEI IAP will return an OwnedPurchasesResult object, which contains the purchase data and signature data of all purchased products that have not being delivered. Use the public key allocated by AppGallery Connect to verify the signature. For more information about the verification method, please refer to Verifying the Signature in the Returned Result.
Each purchase’s data is a character string in JSON format that contains the parameters listed in InAppPurchaseData. You need to parse the purchaseState field from the InAppPurchaseData character string. If purchaseState of a purchase is 0, the purchase is successful and delivery will be performed.
Code:
// Construct an OwnedPurchasesReq object.
OwnedPurchasesReq ownedPurchasesReq = new OwnedPurchasesReq();
// priceType: 0: consumable; 1: non-consumable; 2: subscription
ownedPurchasesReq.setPriceType(0);
// Obtain the Activity object that calls the API.
final Activity activity = getActivity();
// Call the obtainOwnedPurchases API to obtain the order information about all consumable products that have been purchased but not delivered.
Task<OwnedPurchasesResult> task = Iap.getIapClient(activity).obtainOwnedPurchases(ownedPurchasesReq);
task.addOnSuccessListener(new OnSuccessListener<OwnedPurchasesResult>() {
@Override
public void onSuccess(OwnedPurchasesResult result) {
// Obtain the execution result if the request is successful.
if (result != null && result.getInAppPurchaseDataList() != null) {
for (int i = 0; i < result.getInAppPurchaseDataList().size(); i++) {
String inAppPurchaseData = result.getInAppPurchaseDataList().get(i);
String inAppSignature = result.getInAppSignature().get(i);
// Use the IAP public key to verify the signature of inAppPurchaseData.
// Check the purchase status of each product if the verification is successful. When the payment has been made, deliver the required product. After a successful delivery, consume the product.
try {
InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseData);
int purchaseState = inAppPurchaseDataBean.getPurchaseState();
} catch (JSONException e) {
}
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
Status status = apiException.getStatus();
int returnCode = apiException.getStatusCode();
} else {
// Other external errors.
}
}
});
2. Call the consumeOwnedPurchase API to consume a delivered product.
Perform a delivery confirmation for all products queried through the obtainOwnedPurchases API. If a product is already delivered, call the consumeOwnedPurchase API to consume the product and instruct the Huawei IAP server to update the delivery status. Following consumption, the Huawei IAP server resets the product status to available for purchase, allowing the product to be purchased again.
For more details, please click:
HUAWEI IAP official website
HUAWEI IAP Development Guide
HUAWEI HMS Core Community
To learn more, please visit:
HUAWEI Developers official website
Development Guide
Reddit to join developer discussions
GitHub or Gitee to download the demo and sample code
Stack Overflow to solve integration problems
Follow our official account for the latest HMS Core-related news and updates.
Original Source
View attachment Featured image.jpg
As the start of the new academic year approaches, many college students will be leaving their parents to start college life. However, a lack of experience makes it easy for college students to become victims of electronic fraud such as phone scams.
The start of the new academic year is often a period of time that sees an uptick in phone scams, especially those targeting college students. Some scammers trick students to download and register an account on malicious financial apps embedded with viruses and Trojan horses or ones that imitate legitimate apps. With such malicious apps installed on students' phones, scammers are able to steal students' sensitive data, such as bank card numbers and passwords. Some scammers trick students, by offering them small gifts or coupons, to scan QR codes which then direct them to pages that ask users to enter their personal information, such as their phone number and address. Once a student has done this, they will receive a large number of fraudulent calls and junk SMS messages from then on. If the students scan QR codes linking to phishing websites, their personal data may be leaked and sold for malicious purposes. Some scammers even lie about offering students scholarships or grants, in order to trick them into visiting phishing websites and entering their bank account numbers and passwords, causing significant financial losses to such students.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
To deal with the ever-changing tricks of fraudsters, an app needs to detect phishing websites, malicious apps, and other risks and remind users to be on the lookout for such risks with in-app tips, in order to keep users and their data safe. So, is there a one-stop service that can enhance app security from multiple dimensions? Fortunately, HMS Core Safety Detect can help developers quickly build security capabilities into their apps, and help vulnerable user groups such as college students safeguard their information and property.
The AppsCheck API in Safety Detect allows your app to obtain a list of malicious apps installed on a user's device. The API can identify 99% of malicious apps and detect unknown threats based on app behavior. Your app can then use this information to determine whether to restrict users from performing in-app payments and other sensitive operations.
The URLCheck API in Safety Detect checks whether an in-app URL is malicious. If the URL is determined to be malicious, the app can warn the user of the risk or block the URL.
Safety Detect also provides capabilities to check system integrity and detect fake users, helping developers quickly improve their app security. The integration process is straightforward, which I'll describe below.
Demo
Integration ProcedurePreparationsYou can follow the instructions here to prepare for the integration.
Using the AppsCheck APIYou can directly call getMaliciousAppsList of SafetyDetectClient to obtain a list of malicious apps. The sample code is as follows:
Code:
private void invokeGetMaliciousApps() {
SafetyDetectClient appsCheckClient = SafetyDetect.getClient(MainActivity.this);
Task task = appsCheckClient.getMaliciousAppsList();
task.addOnSuccessListener(new OnSuccessListener<MaliciousAppsListResp>() {
@Override
public void onSuccess(MaliciousAppsListResp maliciousAppsListResp) {
// Indicates that communication with the service was successful.
// Use resp.getMaliciousApps() to obtain a list of malicious apps.
List<MaliciousAppsData> appsDataList = maliciousAppsListResp.getMaliciousAppsList();
// Indicates that the list of malicious apps was successfully obtained.
if(maliciousAppsListResp.getRtnCode() == CommonCode.OK) {
if (appsDataList.isEmpty()) {
// Indicates that no known malicious apps were detected.
Log.i(TAG, "There are no known potentially malicious apps installed.");
} else {
Log.i(TAG, "Potentially malicious apps are installed!");
for (MaliciousAppsData maliciousApp : appsDataList) {
Log.i(TAG, "Information about a malicious app:");
// Use getApkPackageName() to obtain the APK name of the malicious app.
Log.i(TAG, "APK: " + maliciousApp.getApkPackageName());
// Use getApkSha256() to obtain the APK SHA-256 of the malicious app.
Log.i(TAG, "SHA-256: " + maliciousApp.getApkSha256());
// Use getApkCategory() to obtain the category of the malicious app.
// Categories are defined in AppsCheckConstants.
Log.i(TAG, "Category: " + maliciousApp.getApkCategory());
}
}
}else{
Log.e(TAG,"getMaliciousAppsList failed: "+maliciousAppsListResp.getErrorReason());
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// An error occurred during communication with the service.
if (e instanceof ApiException) {
// An error with the HMS API contains some
// additional details.
ApiException apiException = (ApiException) e;
// You can retrieve the status code using the apiException.getStatusCode() method.
Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage());
} else {
// A different, unknown type of error occurred.
Log.e(TAG, "ERROR: " + e.getMessage());
}
}
});
}
Using the URLCheck API1. Initialize the URLCheck API.
Before using the URLCheck API, you must call the initUrlCheck method to initialize the API. The sample code is as follows:
Code:
SafetyDetectClient client = SafetyDetect.getClient(getActivity());
client.initUrlCheck();
2. Request a URL check.
You can pass target threat types to the URLCheck API as parameters. The constants in the UrlCheckThreat class include the current supported threat types.
Code:
public class UrlCheckThreat {
// URLs of this type are marked as URLs of pages containing potentially malicious apps (such as home page tampering URLs, Trojan-infected URLs, and malicious app download URLs).
public static final int MALWARE = 1;
// URLs of this type are marked as phishing and spoofing URLs.
public static final int PHISHING = 3;
}
a. Initiate a URL check request.
The URL to be checked contains the protocol, host, and path but does not contain the query parameter. The sample code is as follows:
Code:
String url = "https://developer.huawei.com/consumer/cn/";
SafetyDetect.getClient(this).urlCheck(url, appId, UrlCheckThreat.MALWARE, UrlCheckThreat.PHISHING).addOnSuccessListener(this, new OnSuccessListener<UrlCheckResponse >(){
@Override
public void onSuccess(UrlCheckResponse urlResponse) {
if (urlResponse.getUrlCheckResponse().isEmpty()) {
// No threat exists.
} else {
// Threats exist.
}
}
}).addOnFailureListener(this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// An error occurred during communication with the service.
if (e instanceof ApiException) {
// HMS Core (APK) error code and corresponding error description.
ApiException apiException = (ApiException) e;
Log.d(TAG, "Error: " + CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()));
// Note: If the status code is SafetyDetectStatusCode.CHECK_WITHOUT_INIT,
// you did not call the initUrlCheck() method or you have initiated a URL check request before the call is completed.
// If an internal error occurs during the initialization, you need to call the initUrlCheck() method again to initialize the API.
} else {
// An unknown exception occurred.
Log.d(TAG, "Error: " + e.getMessage());
}
}
});
b. Call the getUrlCheckResponse method of the returned UrlCheckResponse object to obtain the URL check result.
The result contains List<UrlCheckThreat>, which includes the detected URL threat type. If the list is empty, no threat is detected. Otherwise, you can call getUrlCheckResult in UrlCheckThreat to obtain the specific threat code. The sample code is as follows:
Code:
final EditText testRes = getActivity().findViewById(R.id.fg_call_urlResult);
List<UrlCheckThreat> list = urlCheckResponse.getUrlCheckResponse();
if (list.isEmpty()) {
testRes.setText("ok");
}
else{
for (UrlCheckThreat threat : list) {
int type = threat.getUrlCheckResult();
}
}
3. Close the URL check session.
If your app does not need to call the URLCheck API anymore or will not need to for a while, you can call the shutdownUrlCheck method to close the URL check session and release relevant resources.
Code:
SafetyDetect.getClient(this).shutdownUrlCheck();
ConclusionElectronic fraud such as phone scams are constantly evolving and becoming more and more difficult to prevent, bringing great challenges to both developers and users. To combat such risks, developers must utilize technical means to identify phishing websites, malicious apps, and other risks, in order to safeguard users' personal information and property.
In this article, I demonstrated how HMS Core Safety Detect can be used to effectively combat electronic fraud. The whole integration process is straightforward and cost-efficient, and is a quick and effective way to build comprehensive security capabilities into an app.
In the mobile Internet era, people are increasingly using mobile apps for a variety of different purposes, such as buying products online, hailing taxis, and much more. When using such an app, a user usually needs to manually enter their address for package delivery or search for an appropriate pick-up and drop-off location when they hail a taxi, which can be inconvenient.
To improve user experience, many apps nowadays allow users to select a point on the map and then use the selected point as the location, for example, for package delivery or getting on or off a taxi. Each location has a longitude-latitude coordinate that pinpoints its position precisely on the map. However, longitude-latitude coordinates are simply a string of numbers and provide little information to the average user. It would therefore be useful if there was a tool which an app can use to convert longitude-latitude coordinates into human-readable addresses.
Fortunately, the reverse geocoding function in HMS Core Location Kit can obtain the nearest address to a selected point on the map based on the longitude and latitude of the point. Reverse geocoding is the process of converting a location as described by geographic coordinates (longitude and latitude) to a human-readable address or place name, which is much more useful information for users. It permits the identification of nearby street addresses, places, and subdivisions such as neighborhoods, counties, states, and countries.
Generally, the reverse geocoding function can be used to obtain the nearest address to the current location of a device, show the address or place name when a user taps on the map, find the address of a geographic location, and more. For example, with reverse geocoding, an e-commerce app can show users the detailed address of a selected point on the map in the app; a ride-hailing or takeout delivery app can show the detailed address of a point that a user selects by dragging the map in the app or tapping the point on the map in the app, so that the user can select the address as the pick-up address or takeout delivery address; and an express delivery app can utilize reverse geocoding to show the locations of delivery vehicles based on the passed longitude-latitude coordinates, and intuitively display delivery points and delivery routes to users.
Bolstered by a powerful address parsing capability, the reverse geocoding function in this kit can display addresses of locations in accordance with local address formats with an accuracy as high as 90%. In addition, it supports 79 languages and boasts a parsing latency as low as 200 milliseconds.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
DemoThe file below is a demo of the reverse geocoding function in this kit.
Preparations
Before getting started with the development, you will need to make the following preparations:
Register as a Huawei developer and complete identity verification on the HUAWEI Developers website. You can click here to find out the detailed registration and identity verification procedure.
Create a project and then create an app in the project in AppGallery Connect. Before doing so, you must have a Huawei developer account and complete identity verification.
Generate a signing certificate fingerprint and configure it in AppGallery Connect. The signing certificate fingerprint is used to verify the authenticity of an app. Before releasing an app, you must generate a signing certificate fingerprint locally based on the signing certificate and configure it in AppGallery Connect.
Integrate the Location SDK into your app. If you are using Android Studio, you can integrate the SDK via the Maven repository.
Here, I won't be describing how to generate and configure a signing certificate fingerprint and integrate the SDK. You can click here to learn about the detailed procedure.
Development Procedure
After making relevant preparations, you can perform the steps below to use the reverse geocoding service in your app. Before using the service, ensure that you have installed HMS Core (APK) on your device.
1. Create a geocoding service client.
In order to call geocoding APIs, you first need to create a GeocoderService instance in the onClick() method of GeocoderActivity in your project. The sample code is as follows:
Code:
Locale locale = new Locale("zh", "CN");
GeocoderService geocoderService = LocationServices.getGeocoderService(GeocoderActivity.this, locale);
2. Obtain the reverse geocoding information.
To empower your app to obtain the reverse geocoding information, you need to call the getFromLocation() method of the GeocoderService object in your app. This method will return a List<HWLocation> object containing the location information based on the set GetFromLocationRequest object.
a. Set reverse geocoding request parameters.
There are three request parameters in the GetFromLocationRequest object, which indicate the latitude, longitude, and maximum number of returned results respectively. The sample code is as follows:
Code:
// Parameter 1: latitude
// Parameter 2: longitude
// Parameter 3: maximum number of returned results
// Pass valid longitude-latitude coordinates. If the coordinates are invalid, no geographical information will be returned. Outside China, pass longitude-latitude coordinates located outside China and ensure that the coordinates are correct.
GetFromLocationRequest getFromLocationRequest = new GetFromLocationRequest(39.985071, 116.501717, 5);
b. Call the getFromLocation() method to obtain reverse geocoding information.
The obtained reverse geocoding information will be returned in a List<HWLocation> object. You can add listeners using the addOnSuccessListener() and addOnFailureListener() methods, and obtain the task execution result using the onSuccess() and onFailure() methods.
The sample code is as follows:
Code:
private void getReverseGeocoding() {
// Initialize the GeocoderService object.
if (geocoderService == null) {
geocoderService = new GeocoderService(this, new Locale("zh", "CN"));
}
geocoderService.getFromLocation(getFromLocationRequest)
.addOnSuccessListener(new OnSuccessListener<List<HWLocation>>() {
@Override
public void onSuccess(List<HWLocation> hwLocation) {
// TODO: Define callback for API call success.
if (null != hwLocation && hwLocation.size() > 0) {
Log.d(TAG, "hwLocation data set quantity: " + hwLocation.size());
Log.d(TAG, "CountryName: " + hwLocation.get(0).getCountryName());
Log.d(TAG, "City: " + hwLocation.get(0).getCity());
Log.d(TAG, "Street: " + hwLocation.get(0).getStreet());
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// TODO: Define callback for API call failure.
}
});
}
Congratulations, your app is now able to use the reverse geocoding function to obtain the address of a location based on its longitude and latitude.
Conclusion
The quick development and popularization of the mobile Internet has caused many changes to our daily lives. One such change is that more and more people are using mobile apps on a daily basis, for example, to buy daily necessities or hail a taxi. These tasks traditionally require users to manually enter the delivery address or pick-up and drop-off location addresses. Manually entering such addresses is inconvenient and prone to mistakes.
To solve this issue, many apps allow users to select a point on the in-app map as the delivery address or the address for getting on or off a taxi. However, the point on the map is usually expressed as a set of longitude-latitude coordinates, which most users will find hard to understand.
As described in this post, my app resolves this issue using the reverse geocoding function, which is proven a very effective way for obtaining human-readable addresses based on longitude-latitude coordinates. If you are looking for a solution to such issues, have a try to find out if this is what your app needs.