Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,354 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "InAppPurchasesAndroid.h"
#include "InAppPurchasesModule.h"
#include <InAppPurchases/InAppPurchasesResponseBus.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/Android/JNI/JNI.h>
#include <AzCore/Android/Utils.h>
#include <AzCore/EBus/BusImpl.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/IO/FileIO.h>
namespace InAppPurchases
{
static bool IsFieldIdValid(jfieldID fid)
{
if (fid == NULL)
{
return false;
}
return true;
}
static PurchasedProductDetailsAndroid* ParseReceiptDetails(JNIEnv* env, jobjectArray jpurchasedProductDetails, int index)
{
jobject jpurchasedProduct = env->GetObjectArrayElement(jpurchasedProductDetails, index);
const int NUM_FIELDS_PURCHASED_PRODUCTS = 7;
jfieldID fid[NUM_FIELDS_PURCHASED_PRODUCTS];
jclass cls = env->GetObjectClass(jpurchasedProduct);
fid[0] = env->GetFieldID(cls, "m_productId", "Ljava/lang/String;");
fid[1] = env->GetFieldID(cls, "m_orderId", "Ljava/lang/String;");
fid[2] = env->GetFieldID(cls, "m_packageName", "Ljava/lang/String;");
fid[3] = env->GetFieldID(cls, "m_purchaseToken", "Ljava/lang/String;");
fid[4] = env->GetFieldID(cls, "m_signature", "Ljava/lang/String;");
fid[5] = env->GetFieldID(cls, "m_purchaseTime", "J");
fid[6] = env->GetFieldID(cls, "m_isAutoRenewing", "Z");
for (int i = 0; i < NUM_FIELDS_PURCHASED_PRODUCTS; i++)
{
if (!IsFieldIdValid(fid[i]))
{
AZ_TracePrintf("LumberyardInAppBilling", "Invaild FieldId in PurchasedProductDetails\n");
return nullptr;
}
}
PurchasedProductDetailsAndroid* purchasedProductDetails = new PurchasedProductDetailsAndroid();
purchasedProductDetails->SetProductId(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jpurchasedProduct, fid[0]))));
purchasedProductDetails->SetOrderId(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jpurchasedProduct, fid[1]))));
purchasedProductDetails->SetPackageName(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jpurchasedProduct, fid[2]))));
purchasedProductDetails->SetPurchaseToken(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jpurchasedProduct, fid[3]))));
purchasedProductDetails->SetPurchaseSignature(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jpurchasedProduct, fid[4]))));
purchasedProductDetails->SetPurchaseTime(env->GetLongField(jpurchasedProduct, fid[5]));
purchasedProductDetails->SetIsAutoRenewing(env->GetBooleanField(jpurchasedProduct, fid[6]));
return purchasedProductDetails;
}
void ProductInfoRetrieved(JNIEnv* env, jobject obj, jobjectArray jproductDetails)
{
int numProducts = env->GetArrayLength(jproductDetails);
InAppPurchasesInterface::GetInstance()->GetCache()->ClearCachedProductDetails();
const int NUM_FIELDS_PRODUCTS = 7;
jfieldID fid[NUM_FIELDS_PRODUCTS];
jclass cls;
if (numProducts > 0)
{
cls = env->GetObjectClass(env->GetObjectArrayElement(jproductDetails, 0));
fid[0] = env->GetFieldID(cls, "m_productId", "Ljava/lang/String;");
fid[1] = env->GetFieldID(cls, "m_type", "Ljava/lang/String;");
fid[2] = env->GetFieldID(cls, "m_price", "Ljava/lang/String;");
fid[3] = env->GetFieldID(cls, "m_currencyCode", "Ljava/lang/String;");
fid[4] = env->GetFieldID(cls, "m_title", "Ljava/lang/String;");
fid[5] = env->GetFieldID(cls, "m_description", "Ljava/lang/String;");
fid[6] = env->GetFieldID(cls, "m_priceMicro", "J");
}
for (int i = 0; i < NUM_FIELDS_PRODUCTS; i++)
{
if (!IsFieldIdValid(fid[i]))
{
AZ_TracePrintf("LumberyardInAppBilling", "Invaild FieldId in ProductDetails\n");
return;
}
}
for (int i = 0; i < numProducts; i++)
{
jobject jproduct = env->GetObjectArrayElement(jproductDetails, i);
ProductDetailsAndroid* productDetails = new ProductDetailsAndroid();
productDetails->SetProductId(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jproduct, fid[0]))));
productDetails->SetProductType(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jproduct, fid[1]))));
productDetails->SetProductPrice(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jproduct, fid[2]))));
productDetails->SetProductCurrencyCode(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jproduct, fid[3]))));
productDetails->SetProductTitle(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jproduct, fid[4]))));
productDetails->SetProductDescription(AZ::Android::JNI::ConvertJstringToString(static_cast<jstring>(env->GetObjectField(jproduct, fid[5]))));
productDetails->SetProductPriceMicro(env->GetLongField(jproduct, fid[6]));
InAppPurchasesInterface::GetInstance()->GetCache()->AddProductDetailsToCache(productDetails);
}
EBUS_EVENT(InAppPurchasesResponseBus, ProductInfoRetrieved, InAppPurchasesInterface::GetInstance()->GetCache()->GetCachedProductDetails());
}
void PurchasedProductsRetrieved(JNIEnv* env, jobject object, jobjectArray jpurchasedProductDetails)
{
InAppPurchasesInterface::GetInstance()->GetCache()->ClearCachedPurchasedProductDetails();
int numPurchasedProducts = env->GetArrayLength(jpurchasedProductDetails);
for (int i = 0; i < numPurchasedProducts; i++)
{
PurchasedProductDetailsAndroid* purchasedProduct = ParseReceiptDetails(env, jpurchasedProductDetails, i);
if (purchasedProduct != nullptr)
{
InAppPurchasesInterface::GetInstance()->GetCache()->AddPurchasedProductDetailsToCache(purchasedProduct);
}
}
EBUS_EVENT(InAppPurchasesResponseBus, PurchasedProductsRetrieved, InAppPurchasesInterface::GetInstance()->GetCache()->GetCachedPurchasedProductDetails());
}
void NewProductPurchased(JNIEnv* env, jobject object, jobjectArray jpurchaseReceipt)
{
PurchasedProductDetailsAndroid* purchasedProduct = ParseReceiptDetails(env, jpurchaseReceipt, 0);
if (purchasedProduct != nullptr)
{
InAppPurchasesInterface::GetInstance()->GetCache()->AddPurchasedProductDetailsToCache(purchasedProduct);
EBUS_EVENT(InAppPurchasesResponseBus, NewProductPurchased, purchasedProduct);
}
}
void PurchaseConsumed(JNIEnv* env, jobject object, jstring jpurchaseToken)
{
const char* purchaseToken = env->GetStringUTFChars(jpurchaseToken, nullptr);
EBUS_EVENT(InAppPurchasesResponseBus, PurchaseConsumed, AZStd::string(purchaseToken, env->GetStringUTFLength(jpurchaseToken)));
env->ReleaseStringUTFChars(jpurchaseToken, purchaseToken);
}
static JNINativeMethod methods[] = {
{ "nativeProductInfoRetrieved", "([Ljava/lang/Object;)V", (void*)ProductInfoRetrieved },
{ "nativePurchasedProductsRetrieved", "([Ljava/lang/Object;)V", (void*)PurchasedProductsRetrieved },
{ "nativeNewProductPurchased", "([Ljava/lang/Object;)V", (void*)NewProductPurchased },
{ "nativePurchaseConsumed", "(Ljava/lang/String;)V", (void*)PurchaseConsumed }
};
InAppPurchasesInterface* InAppPurchasesInterface::CreateInstance()
{
return new InAppPurchasesAndroid();
}
void InAppPurchasesAndroid::Initialize()
{
JNIEnv* env = AZ::Android::JNI::GetEnv();
jobject activityObject = AZ::Android::Utils::GetActivityRef();
jclass billingClass = AZ::Android::JNI::LoadClass("com/amazon/lumberyard/iap/LumberyardInAppBilling");
jmethodID mid = env->GetMethodID(billingClass, "<init>", "(Landroid/app/Activity;)V");
jobject billingInstance = env->NewObject(billingClass, mid, activityObject);
m_billingInstance = env->NewGlobalRef(billingInstance);
env->RegisterNatives(billingClass, methods, sizeof(methods) / sizeof(methods[0]));
mid = env->GetMethodID(billingClass, "IsKindleDevice", "()Z");
jboolean result = env->CallBooleanMethod(m_billingInstance, mid);
if (result)
{
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "Kindle Device Detected", "IAP currently unsupported on Kindle devices", false);
}
env->DeleteGlobalRef(billingClass);
env->DeleteLocalRef(billingInstance);
}
InAppPurchasesAndroid::~InAppPurchasesAndroid()
{
JNIEnv* env = AZ::Android::JNI::GetEnv();
jclass billingClass = env->GetObjectClass(m_billingInstance);
jmethodID mid = env->GetMethodID(billingClass, "UnbindService", "()V");
env->CallVoidMethod(m_billingInstance, mid);
env->DeleteLocalRef(billingClass);
env->DeleteGlobalRef(m_billingInstance);
}
void InAppPurchasesAndroid::QueryProductInfo(AZStd::vector<AZStd::string>& productIds) const
{
JNIEnv* env = AZ::Android::JNI::GetEnv();
jobjectArray jproductIds = static_cast<jobjectArray>(env->NewObjectArray(productIds.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")));
for (int i = 0; i < productIds.size(); i++)
{
env->SetObjectArrayElement(jproductIds, i, env->NewStringUTF(productIds[i].c_str()));
}
jclass billingClass = env->GetObjectClass(m_billingInstance);
jmethodID mid = env->GetMethodID(billingClass, "QueryProductInfo", "([Ljava/lang/String;)V");
env->CallVoidMethod(m_billingInstance, mid, jproductIds);
env->DeleteLocalRef(jproductIds);
env->DeleteLocalRef(billingClass);
}
void InAppPurchasesAndroid::QueryProductInfo() const
{
AZ::IO::FileIOBase* fileReader = AZ::IO::FileIOBase::GetInstance();
AZStd::string fileBuffer;
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::u64 fileSize = 0;
if (!fileReader->Open("@assets@/product_ids.json", AZ::IO::OpenMode::ModeRead, fileHandle))
{
AZ_TracePrintf("LumberyardInAppBilling", "Unable to open file product_ids.json\n");
return;
}
if ((!fileReader->Size(fileHandle, fileSize)) || (fileSize == 0))
{
AZ_TracePrintf("LumberyardInAppBilling", "Unable to read file product_ids.json - file truncated\n");
fileReader->Close(fileHandle);
return;
}
fileBuffer.resize(fileSize);
if (!fileReader->Read(fileHandle, fileBuffer.data(), fileSize, true))
{
fileBuffer.resize(0);
fileReader->Close(fileHandle);
AZ_TracePrintf("LumberyardInAppBilling", "Failed to read file product_ids.json\n");
return;
}
fileReader->Close(fileHandle);
rapidjson::Document document;
document.Parse(fileBuffer.data());
if (document.HasParseError())
{
const char* errorStr = rapidjson::GetParseError_En(document.GetParseError());
AZ_TracePrintf("LumberyardInAppBilling", "Failed to parse product_ids.json: %s\n", errorStr);
return;
}
const rapidjson::Value& productList = document["product_ids"];
const auto& end = productList.End();
AZStd::vector<AZStd::string> productIds;
for (auto it = productList.Begin(); it != end; it++)
{
const auto& elem = *it;
productIds.push_back(elem["id"].GetString());
}
QueryProductInfo(productIds);
}
void InAppPurchasesAndroid::PurchaseProduct(const AZStd::string& productId, const AZStd::string& developerPayload) const
{
JNIEnv* env = AZ::Android::JNI::GetEnv();
const AZStd::vector <AZStd::unique_ptr<ProductDetails const> >& cachedProductDetails = InAppPurchasesInterface::GetInstance()->GetCache()->GetCachedProductDetails();
AZStd::string productType = "";
for (int i = 0; i < cachedProductDetails.size(); i++)
{
const ProductDetailsAndroid* productDetails = azrtti_cast<const ProductDetailsAndroid*>(cachedProductDetails[i].get());
const AZStd::string& cachedProductId = productDetails->GetProductId();
if (cachedProductId.compare(productId) == 0)
{
productType = productDetails->GetProductType();
break;
}
}
if (productType.empty())
{
AZ_TracePrintf("LumberyardInAppBilling", "Failed to find product with id: %s", productId.c_str());
return;
}
jstring jproductId = env->NewStringUTF(productId.c_str());
jstring jdeveloperPayload = env->NewStringUTF(developerPayload.c_str());
jstring jproductType = env->NewStringUTF(productType.c_str());
jclass billingClass = env->GetObjectClass(m_billingInstance);
jmethodID mid = env->GetMethodID(billingClass, "PurchaseProduct", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
env->CallVoidMethod(m_billingInstance, mid, jproductId, jdeveloperPayload, jproductType);
env->DeleteLocalRef(jproductId);
env->DeleteLocalRef(jdeveloperPayload);
env->DeleteLocalRef(jproductType);
env->DeleteLocalRef(billingClass);
}
void InAppPurchasesAndroid::PurchaseProduct(const AZStd::string& productId) const
{
PurchaseProduct(productId, "");
}
void InAppPurchasesAndroid::QueryPurchasedProducts() const
{
JNIEnv* env = AZ::Android::JNI::GetEnv();
jclass billingClass = env->GetObjectClass(m_billingInstance);
jmethodID mid = env->GetMethodID(billingClass, "QueryPurchasedProducts", "()V");
env->CallVoidMethod(m_billingInstance, mid);
env->DeleteLocalRef(billingClass);
}
void InAppPurchasesAndroid::RestorePurchasedProducts() const
{
}
void InAppPurchasesAndroid::ConsumePurchase(const AZStd::string& purchaseToken) const
{
JNIEnv* env = AZ::Android::JNI::GetEnv();
jclass billingClass = env->GetObjectClass(m_billingInstance);
jmethodID mid = env->GetMethodID(billingClass, "ConsumePurchase", "(Ljava/lang/String;)V");
jstring jpurchaseToken = env->NewStringUTF(purchaseToken.c_str());
env->CallVoidMethod(m_billingInstance, mid, jpurchaseToken);
env->DeleteLocalRef(jpurchaseToken);
env->DeleteLocalRef(billingClass);
}
void InAppPurchasesAndroid::FinishTransaction(const AZStd::string& transactionId, bool downloadHostedContent) const
{
}
InAppPurchasesCache* InAppPurchasesAndroid::GetCache()
{
return &m_cache;
}
}
@@ -0,0 +1,64 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <InAppPurchases/InAppPurchasesInterface.h>
#include <jni.h>
namespace InAppPurchases
{
class InAppPurchasesAndroid
: public InAppPurchasesInterface
{
public:
void Initialize() override;
~InAppPurchasesAndroid();
void QueryProductInfo(AZStd::vector<AZStd::string>& productIds) const override;
void QueryProductInfo() const override;
void PurchaseProduct(const AZStd::string& productId, const AZStd::string& developerPayload) const override;
void PurchaseProduct(const AZStd::string& productId) const override;
void QueryPurchasedProducts() const override;
void RestorePurchasedProducts() const override;
void ConsumePurchase(const AZStd::string& purchaseToken) const override;
void FinishTransaction(const AZStd::string& transactionId, bool downloadHostedContent) const override;
InAppPurchasesCache* GetCache() override;
private:
jobject m_billingInstance;
};
class ProductDetailsAndroid
: public ProductDetails
{
public:
AZ_RTTI(ProductDetailsAndroid, "{59A14DA4-B224-4BBD-B43E-8C7BC2EEFEB5}", ProductDetails);
const AZStd::string& GetProductType() const { return m_productType; }
void SetProductType(const AZStd::string& productType) { m_productType = productType; }
protected:
AZStd::string m_productType;
};
}
@@ -0,0 +1,365 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.iap;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
public class LumberyardInAppBilling implements PurchasesUpdatedListener
{
public LumberyardInAppBilling(Activity activity)
{
m_activity = activity;
m_packageName = m_activity.getPackageName();
Resources resources = m_activity.getResources();
int stringId = resources.getIdentifier("public_key", "string", m_activity.getPackageName());
m_appPublicKey = resources.getString(stringId);
m_setupDone = false;
final LumberyardInAppBilling iapInstance = this;
if (!IsKindleDevice())
{
(new Thread(new Runnable()
{
public void run()
{
Looper.prepare();
m_billingClient = BillingClient.newBuilder(m_activity).setListener(iapInstance).build();
m_billingClient.startConnection(new BillingClientStateListener()
{
@Override
public void onBillingSetupFinished(int responseCode)
{
Log.d(s_tag, "Service connected");
if (!m_billingClient.isReady())
{
Log.d(s_tag, m_packageName + " IN_APP items not supported!");
return;
}
int subscriptionsResponseCode = m_billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS);
if (!VerifyResponseCode(subscriptionsResponseCode))
{
return;
}
subscriptionsResponseCode = m_billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS_UPDATE);
if (!VerifyResponseCode(subscriptionsResponseCode))
{
return;
}
m_setupDone = true;
}
@Override
public void onBillingServiceDisconnected()
{
Log.d(s_tag, "Service disconnected");
}
});
Looper.loop();
}
})).start();
}
Log.d(s_tag, "Instance created");
}
public static native void nativeProductInfoRetrieved(Object[] productDetails);
public static native void nativePurchasedProductsRetrieved(Object[] purchasedProductDetails);
public static native void nativeNewProductPurchased(Object[] purchaseReceipt);
public static native void nativePurchaseConsumed(String purchaseToken);
public void UnbindService()
{
if (!m_setupDone)
{
Log.e(s_tag, "Not initialized!");
return;
}
m_setupDone = false;
m_billingClient.endConnection();
m_billingClient = null;
}
public void QueryProductInfo(final String[] skuListArray)
{
if (!m_setupDone)
{
Log.e(s_tag, "Not initialized!");
return;
}
m_numResponses = 0;
final ArrayList<ProductDetails> responseList = new ArrayList<>();
List<String> skuList = Arrays.asList(skuListArray);
SkuDetailsResponseListener responseListener = new SkuDetailsResponseListener()
{
@Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList)
{
m_numResponses++;
if (!VerifyResponseCode(responseCode))
{
return;
}
for (SkuDetails skuDetails : skuDetailsList)
{
ProductDetails productDetails = new ProductDetails();
productDetails.m_productId = skuDetails.getSku();
productDetails.m_title = skuDetails.getTitle();
productDetails.m_description = skuDetails.getDescription();
productDetails.m_price = skuDetails.getPrice();
productDetails.m_currencyCode = skuDetails.getPriceCurrencyCode();
productDetails.m_type = skuDetails.getType();
productDetails.m_priceMicro = skuDetails.getPriceAmountMicros();
responseList.add(productDetails);
}
// Wait for responses for both subscriptions and regular products
if (m_numResponses == 2)
{
nativeProductInfoRetrieved(responseList.toArray());
}
}
};
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
m_billingClient.querySkuDetailsAsync(params.build(), responseListener);
params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS);
m_billingClient.querySkuDetailsAsync(params.build(), responseListener);
}
public void PurchaseProduct(String productSku, String developerPayload, String productType)
{
if (!m_setupDone)
{
Log.e(s_tag, "Not initialized!");
return;
}
BillingFlowParams flowParams = BillingFlowParams.newBuilder().setSku(productSku).setType(productType).build();
int responseCode = m_billingClient.launchBillingFlow(m_activity, flowParams);
if (!VerifyResponseCode(responseCode))
{
return;
}
Log.d(s_tag, "Purchase flow initiated.");
}
@Override
public void onPurchasesUpdated(int responseCode, List<Purchase> purchases)
{
if (!VerifyResponseCode(responseCode))
{
return;
}
ArrayList<PurchasedProductDetails> purchasedProducts = new ArrayList<>();
ParsePurchasedProducts(purchases, purchasedProducts);
nativeNewProductPurchased(purchasedProducts.toArray());
}
public void QueryPurchasedProducts()
{
if (!m_setupDone)
{
Log.e(s_tag, "Not initialized!");
return;
}
ArrayList<PurchasedProductDetails> purchasedProducts = new ArrayList<>();
if (!QueryPurchasedProductsBySkuType(BillingClient.SkuType.INAPP, purchasedProducts) || !QueryPurchasedProductsBySkuType(BillingClient.SkuType.SUBS, purchasedProducts))
{
return;
}
nativePurchasedProductsRetrieved(purchasedProducts.toArray());
}
public void ConsumePurchase(final String purchaseToken)
{
if (!m_setupDone)
{
Log.e(s_tag, "Not initialized!");
return;
}
ConsumeResponseListener listener = new ConsumeResponseListener()
{
@Override
public void onConsumeResponse(int responseCode, String purchaseToken)
{
if (!VerifyResponseCode(responseCode))
{
return;
}
nativePurchaseConsumed(purchaseToken);
}
};
m_billingClient.consumeAsync(purchaseToken, listener);
}
public boolean IsKindleDevice()
{
if (Build.MANUFACTURER.equals("Amazon") && Build.MODEL.contains("KF"))
{
Log.e(s_tag, "Kindle devices not currently supported");
return true;
}
return false;
}
private boolean QueryPurchasedProductsBySkuType(String skuType, ArrayList<PurchasedProductDetails> purchasedProducts)
{
Purchase.PurchasesResult purchasesResult = m_billingClient.queryPurchases(skuType);
if (!VerifyResponseCode(purchasesResult.getResponseCode()))
{
return false;
}
ParsePurchasedProducts(purchasesResult.getPurchasesList(), purchasedProducts);
return true;
}
private void ParsePurchasedProducts(List<Purchase> purchases, ArrayList<PurchasedProductDetails> purchasedProducts)
{
for (Purchase purchase : purchases)
{
PurchasedProductDetails purchasedProductDetails = new PurchasedProductDetails();
purchasedProductDetails.m_productId = purchase.getSku();
purchasedProductDetails.m_orderId = purchase.getOrderId();
purchasedProductDetails.m_packageName = purchase.getPackageName();
purchasedProductDetails.m_purchaseToken = purchase.getPurchaseToken();
purchasedProductDetails.m_signature = purchase.getSignature();
purchasedProductDetails.m_purchaseTime = purchase.getPurchaseTime();
purchasedProductDetails.m_isAutoRenewing = purchase.isAutoRenewing();
purchasedProducts.add(purchasedProductDetails);
}
}
private boolean VerifyResponseCode(int responseCode)
{
if (responseCode != BillingClient.BillingResponse.OK)
{
Log.d(s_tag, m_packageName + " returned error code " + BILLING_RESPONSE_RESULT_STRINGS[responseCode]);
return false;
}
return true;
}
public class ProductDetails
{
public String m_title;
public String m_description;
public String m_productId;
public String m_price;
public String m_currencyCode;
public String m_type;
public long m_priceMicro;
};
public class PurchasedProductDetails
{
public String m_productId;
public String m_orderId;
public String m_packageName;
public String m_purchaseToken;
public String m_signature;
public long m_purchaseTime;
public boolean m_isAutoRenewing;
};
private static final String[] BILLING_RESPONSE_RESULT_STRINGS = {
"Billing response result is ok",
"User cancelled the request",
"The requested service is unavailable",
"Billing is currently unavailable",
"The requested item is unavailable",
"Developer error",
"Error",
"The item being purchased is already owned by the user",
"The item is not owned by the user"
};
private static final String s_tag = "LumberyardInAppBilling";
private Activity m_activity;
private BillingClient m_billingClient;
private String m_appPublicKey;
private String m_packageName;
private boolean m_setupDone;
private int m_numResponses;
}
@@ -0,0 +1,10 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -0,0 +1,15 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
InAppPurchasesAndroid.h
InAppPurchasesAndroid.cpp
)