华为In App Purchase沙盒测试,我已经购买过一次的商品无法购买

Huawei In App Purchase sandbox testing , I cannot purchase the item I have already purchased once

IAP沙盒测试中,消耗品和非消耗品购买一次后不可再次购买。导致无法显示支付页面。在 onActivityResult 中,IAP SDK returns code

ORDER_PRODUCT_OWNED 60051

这是普遍问题还是我做错了什么?

在沙盒环境中,您可以购买消耗品和非消耗品。购买后,您可以食用它们。之后可以再次购买,方便测试。

调用消费接口消费产品。

60051 表示用户已拥有该产品,因此未能购买该产品。并且@Disavowed 给出了解释和解决方案。 https://developer.huawei.com/consumer/en/doc/development/HMS-References/iap-ExceptionHandlingAndGeneralErrorCodes-v4

这是关于如何使用产品的代码。

/**
 * Consume the unconsumed purchase with type 0 after successfully delivering the product, then the Huawei payment server will update the order status and the user can purchase the product again.
 * @param inAppPurchaseData JSON string that contains purchase order details.
 */
private void consumeOwnedPurchase(final Context context, String inAppPurchaseData) {
    Log.i(TAG, "call consumeOwnedPurchase");
    IapClient mClient = Iap.getIapClient(context);
    Task<ConsumeOwnedPurchaseResult> task = mClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(inAppPurchaseData));
    task.addOnSuccessListener(new OnSuccessListener<ConsumeOwnedPurchaseResult>() {
        @Override
        public void onSuccess(ConsumeOwnedPurchaseResult result) {
            // Consume success
            Log.i(TAG, "consumeOwnedPurchase success");
            Toast.makeText(context, "Pay success, and the product has been delivered", Toast.LENGTH_SHORT).show();
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.e(TAG, e.getMessage());
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
            if (e instanceof IapApiException) {
                IapApiException apiException = (IapApiException)e;
                Status status = apiException.getStatus();
                int returnCode = apiException.getStatusCode();
                Log.e(TAG, "consumeOwnedPurchase fail,returnCode: " + returnCode);
            } else {
                // Other external errors
            }

        }
    });
}

/**
 * Create a ConsumeOwnedPurchaseReq instance.
 * @param purchaseData JSON string that contains purchase order details.
 * @return ConsumeOwnedPurchaseReq
 */
private ConsumeOwnedPurchaseReq createConsumeOwnedPurchaseReq(String purchaseData) {

    ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
    // Parse purchaseToken from InAppPurchaseData in JSON format.
    try {
        InAppPurchaseData inAppPurchaseData = new InAppPurchaseData(purchaseData);
        req.setPurchaseToken(inAppPurchaseData.getPurchaseToken());
    } catch (JSONException e) {
        Log.e(TAG, "createConsumeOwnedPurchaseReq JSONExeption");
    }

    return req;
}

错误代码60051表示用户购买产品失败,因为用户已经拥有该产品。

解决方法:使用obtainOwnedPurchasesAPI查询用户是否购买了商品

已购买的消耗品,发货后调用consumeOwnedPurchaseAPI消费商品。消费后,下次可以购买该产品。 如果产品是非消耗品或订阅产品,则无法再次购买该产品。

查询所有已购买的应用内商品信息,包括消耗品、非消耗品和自动续订订阅。

  • 如果返回耗材信息,则耗材 由于某些例外情况,可能无法交付。在这种情况下,您的应用 需要检查耗材是否送达。如果没有,应用程序 需要交付它们并调用 consumeOwnedPurchase API 来 消耗它们。
  • 如果返回非消耗品信息, 非消耗品不需要消耗
  • 如果信息 about subscriptions 被返回,所有现有的订阅 返回用户在应用中的关系。
  • 如果 purchaseState 是 0,这意味着产品是 PURCHASED.
  • 如果 purchaseState 是 1 那意味着产品是 CANCELED.
  • 如果 purchaseState 是 2,这意味着产品是 PURCHASED.

代码:

public void checkUserOwnedTheProduct(final Context context) {
        OwnedPurchasesReq ownedPurchasesReq = new OwnedPurchasesReq();
        ownedPurchasesReq.setPriceType(productType);
        Task<OwnedPurchasesResult> task = Iap.getIapClient(context).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);
                        boolean success = CipherUtil.doCheck(inAppPurchaseData, inAppSignature, Key.getPublicKey());
                        if (success) {
                            try {
                                InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseData);
                                int purchaseState = inAppPurchaseDataBean.getPurchaseState();                            

                            } catch (JSONException e) {
                            }
                        }
                    }
                } else {
                    if (productType == IapClient.PriceType.IN_APP_NONCONSUMABLE) {
                        ((HmsInAppPurchaseList) context).loadList();
                        return;
                    }
                }
            }
        }).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
                }
            }
        });
    }

有关更多信息,您可以访问以下链接: 应用内购买文章:

客户端: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201255229704010231&fid=0101187876626530001

服务器端: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201273064244120101&fid=0101187876626530001

应用内购买Github Link:

客户端: https://github.com/DTSE-India-Community/HMS-In-App-Purchase-Kit

服务器端: https://github.com/DTSE-India-Community/Huawei-In-App-Purchase-And-Push-Kit-Server_Side-Implementation