Skip to content

Commit

Permalink
Merge pull request #52 from defold/Issue-51-update-billing-library-to…
Browse files Browse the repository at this point in the history
…-version-5

Updated the Android billing library to version 5
  • Loading branch information
britzl authored Sep 8, 2022
2 parents 0a10ff0 + 3d3433e commit 40648f4
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 92 deletions.
49 changes: 47 additions & 2 deletions extension-iap/api/iap.script_api
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
type: string
desc: Facebook only. [icon:facebook] Optional custom unique request id to
set for this transaction. The id becomes attached to the payment within the Graph API.
- name: token
type: string
desc: [icon:googleplay] Which subscription offer to use when buying a subscription. The token can be retrieved from
the subscriptions table returned when calling iap.list()

examples:
- desc: |-
Expand Down Expand Up @@ -128,15 +132,56 @@
- name: price
type: number
desc: The price of the product.
[icon:googleplay]: Used only for in-app products

- name: price_string
type: string
desc: The price of the product, as a formatted string (amount and currency symbol).
[icon:googleplay]: Used only for in-app products

- name: currency_code
type: string
desc: The currency code. On Google Play, this reflects the merchant's locale, instead of the user's.
[icon:ios] [icon:googleplay] [icon:facebook]
desc: [icon:ios] [icon:googleplay] [icon:facebook] The currency code.
[icon:googleplay]: The merchant's locale, instead of the user's
[icon:googleplay]: Used only for in-app products

- name: subscriptions
type: table
desc: [icon:googleplay] List of subscription offers.
Each offer contains a token and a list of price and billing options.
See https://developer.android.com/reference/com/android/billingclient/api/ProductDetails.PricingPhase
members:
- name: token
type: string
desc: The token associated with the pricing phases for the subscription.

- name: pricing
type: table
desc: The pricing phases for the subscription.
members:
- name: price_string
type: string
desc: Formatted price for the payment cycle, including currency sign.

- name: price
type: number
desc: Price of the payment cycle in micro-units.

- name: currency_code
type: string
desc: ISO 4217 currency code

- name: billing_period
type: string
desc: Billing period of the payment cycle, specified in ISO 8601 format

- name: billing_cycle_count
type: number
desc: Number of cycles for which the billing period is applied.

- name: recurrence_mode
type: string
desc: FINITE, INFINITE or NONE

- name: error
type: table
Expand Down
2 changes: 1 addition & 1 deletion extension-iap/manifests/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
dependencies {
implementation 'com.android.billingclient:billing:3.0.0'
implementation 'com.android.billingclient:billing:5.0.0'
}
44 changes: 27 additions & 17 deletions extension-iap/src/iap_android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ static IAP g_IAP;

static int IAP_ProcessPendingTransactions(lua_State* L)
{
DM_LUA_STACK_CHECK(L, 0);

dmAndroid::ThreadAttacher threadAttacher;
JNIEnv* env = threadAttacher.GetEnv();
env->CallVoidMethod(g_IAP.m_IAP, g_IAP.m_ProcessPendingConsumables, g_IAP.m_IAPJNI);
Expand All @@ -49,11 +51,11 @@ static int IAP_ProcessPendingTransactions(lua_State* L)

static int IAP_List(lua_State* L)
{
int top = lua_gettop(L);
DM_LUA_STACK_CHECK(L, 0);

char* buf = IAP_List_CreateBuffer(L);
if( buf == 0 )
{
assert(top == lua_gettop(L));
return 0;
}

Expand All @@ -68,36 +70,46 @@ static int IAP_List(lua_State* L)
env->DeleteLocalRef(products);

free(buf);
assert(top == lua_gettop(L));
return 0;
}

static int IAP_Buy(lua_State* L)
{
int top = lua_gettop(L);
DM_LUA_STACK_CHECK(L, 0);

int top = lua_gettop(L);
const char* id = luaL_checkstring(L, 1);
const char* token = "";

if (top >= 2 && lua_istable(L, 2)) {
luaL_checktype(L, 2, LUA_TTABLE);
lua_pushvalue(L, 2);
lua_getfield(L, -1, "token");
token = lua_isnil(L, -1) ? "" : luaL_checkstring(L, -1);
lua_pop(L, 2);
}

dmAndroid::ThreadAttacher threadAttacher;
JNIEnv* env = threadAttacher.GetEnv();
jstring ids = env->NewStringUTF(id);
env->CallVoidMethod(g_IAP.m_IAP, g_IAP.m_Buy, ids, g_IAP.m_IAPJNI);
jstring tokens = env->NewStringUTF(token);
env->CallVoidMethod(g_IAP.m_IAP, g_IAP.m_Buy, ids, tokens, g_IAP.m_IAPJNI);
env->DeleteLocalRef(ids);
env->DeleteLocalRef(tokens);

assert(top == lua_gettop(L));
return 0;
}

static int IAP_Finish(lua_State* L)
{
DM_LUA_STACK_CHECK(L, 0);

if(g_IAP.m_autoFinishTransactions)
{
dmLogWarning("Calling iap.finish when autofinish transactions is enabled. Ignored.");
return 0;
}

int top = lua_gettop(L);

luaL_checktype(L, 1, LUA_TTABLE);

lua_getfield(L, -1, "state");
Expand All @@ -107,7 +119,6 @@ static int IAP_Finish(lua_State* L)
{
dmLogError("Invalid transaction state (must be iap.TRANS_STATE_PURCHASED).");
lua_pop(L, 1);
assert(top == lua_gettop(L));
return 0;
}
}
Expand All @@ -130,13 +141,12 @@ static int IAP_Finish(lua_State* L)
env->DeleteLocalRef(receiptUTF);
}

assert(top == lua_gettop(L));
return 0;
}

static int IAP_Acknowledge(lua_State* L)
{
int top = lua_gettop(L);
DM_LUA_STACK_CHECK(L, 0);

luaL_checktype(L, 1, LUA_TTABLE);

Expand All @@ -147,7 +157,6 @@ static int IAP_Acknowledge(lua_State* L)
{
dmLogError("Invalid transaction state (must be iap.TRANS_STATE_PURCHASED).");
lua_pop(L, 1);
assert(top == lua_gettop(L));
return 0;
}
}
Expand All @@ -170,28 +179,27 @@ static int IAP_Acknowledge(lua_State* L)
env->DeleteLocalRef(receiptUTF);
}

assert(top == lua_gettop(L));
return 0;
}

static int IAP_Restore(lua_State* L)
{
// TODO: Missing callback here for completion/error
// See iap_ios.mm
DM_LUA_STACK_CHECK(L, 1);

int top = lua_gettop(L);
dmAndroid::ThreadAttacher threadAttacher;
JNIEnv* env = threadAttacher.GetEnv();
env->CallVoidMethod(g_IAP.m_IAP, g_IAP.m_Restore, g_IAP.m_IAPJNI);

assert(top == lua_gettop(L));

lua_pushboolean(L, 1);
return 1;
}

static int IAP_SetListener(lua_State* L)
{
DM_LUA_STACK_CHECK(L, 0);

IAP* iap = &g_IAP;

bool had_previous = iap->m_Listener != 0;
Expand All @@ -212,6 +220,8 @@ static int IAP_SetListener(lua_State* L)

static int IAP_GetProviderId(lua_State* L)
{
DM_LUA_STACK_CHECK(L, 1);

lua_pushinteger(L, g_IAP.m_ProviderId);
return 1;
}
Expand Down Expand Up @@ -409,7 +419,7 @@ static dmExtension::Result InitializeIAP(dmExtension::Params* params)
jclass iap_jni_class = dmAndroid::LoadClass(env, "com.defold.iap.IapJNI");

g_IAP.m_List = env->GetMethodID(iap_class, "listItems", "(Ljava/lang/String;Lcom/defold/iap/IListProductsListener;J)V");
g_IAP.m_Buy = env->GetMethodID(iap_class, "buy", "(Ljava/lang/String;Lcom/defold/iap/IPurchaseListener;)V");
g_IAP.m_Buy = env->GetMethodID(iap_class, "buy", "(Ljava/lang/String;Ljava/lang/String;Lcom/defold/iap/IPurchaseListener;)V");
g_IAP.m_Restore = env->GetMethodID(iap_class, "restore", "(Lcom/defold/iap/IPurchaseListener;)V");
g_IAP.m_Stop = env->GetMethodID(iap_class, "stop", "()V");
g_IAP.m_ProcessPendingConsumables = env->GetMethodID(iap_class, "processPendingConsumables", "(Lcom/defold/iap/IPurchaseListener;)V");
Expand Down
2 changes: 1 addition & 1 deletion extension-iap/src/iap_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct DM_ALIGNED(16) IAPCommand
// Used for storing eventual callback info (if needed)
dmScript::LuaCallbackInfo* m_Callback;

// THe actual command payload
// The actual command payload
int32_t m_Command;
int32_t m_ResponseCode;
void* m_Data;
Expand Down
2 changes: 1 addition & 1 deletion extension-iap/src/java/com/defold/iap/IapAmazon.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void listItems(final String skus, final IListProductsListener listener, f
}
}

public void buy(final String product, final IPurchaseListener listener) {
public void buy(final String product, final String token, final IPurchaseListener listener) {
synchronized (purchaseListeners) {
RequestId req = PurchasingService.purchase(product);
if (req != null) {
Expand Down
Loading

0 comments on commit 40648f4

Please sign in to comment.