update:更新SDK 修改

This commit is contained in:
2026-05-21 14:45:27 +08:00
parent dc622282f2
commit f06074d9cc
7 changed files with 252 additions and 79 deletions

View File

@@ -5,7 +5,7 @@ public class ChannelEntry {
public static final String CHANNEL = "Flexion"; public static final String CHANNEL = "Flexion";
public static SDKManager.IChannelSDKAdapter createSDKAdapter() { public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
return new FlexionSDKService(); return new FlexionSDKAdapter();
} }

View File

@@ -1,6 +1,8 @@
package com.unity3d.player; package com.unity3d.player;
import android.app.Activity; import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.flexionmobile.ddpx.listener.ConnectionStateListener; import com.flexionmobile.ddpx.listener.ConnectionStateListener;
@@ -30,17 +32,38 @@ import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
public class FlexionSDKService implements public class FlexionSDKAdapter implements
SDKManager.IChannelSDKAdapter, SDKManager.IChannelSDKAdapter,
SDKManager.IPaymentAdapter, SDKManager.IPaymentAdapter,
SDKManager.IProductQueryAdapter, SDKManager.IProductQueryAdapter,
SDKManager.IPendingPurchaseAdapter { SDKManager.IPendingPurchaseAdapter,
private static final String TAG = "FlexionSDKService"; SDKManager.IBillingLifecycleAdapter {
private static final String TAG = "FlexionSDKAdapter";
private static BillingService billingService; private static BillingService billingService;
private static boolean billingConnected = false; private static boolean billingConnected = false;
private static boolean billingConnecting = false;
private static Activity billingActivity;
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static final List<String> pendingPurchases = new ArrayList<>(); private static final List<String> pendingPurchases = new ArrayList<>();
private static final List<PendingBillingOperation> pendingBillingOperations = new ArrayList<>();
private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread")); private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread"));
private interface BillingReadyOperation {
void run(BillingService service);
}
private static class PendingBillingOperation {
final String operation;
final BillingReadyOperation onReady;
final Runnable onFail;
PendingBillingOperation(String operation, BillingReadyOperation onReady, Runnable onFail) {
this.operation = operation;
this.onReady = onReady;
this.onFail = onFail;
}
}
private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> { private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> {
Log.w(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode() Log.w(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode()
+ " hasPurchase=" + (purchase != null)); + " hasPurchase=" + (purchase != null));
@@ -100,12 +123,7 @@ public class FlexionSDKService implements
public void pay(Activity activity, String productId, String productPrice, public void pay(Activity activity, String productId, String productPrice,
String productName, String productCount, String prodorderId, String productName, String productCount, String prodorderId,
String appInfo) { String appInfo) {
Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected); Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingConnected=" + billingConnected);
if (billingService == null || !billingConnected || !billingService.isReady()) {
Log.e(TAG, "[PAY-FLOW] billing not ready");
sendPayCallback(1, "billing_not_ready", null, null, null, null);
return;
}
String developerPayload = ""; String developerPayload = "";
try { try {
byte[] decoded = Base64.getDecoder().decode(appInfo); byte[] decoded = Base64.getDecoder().decode(appInfo);
@@ -127,31 +145,32 @@ public class FlexionSDKService implements
Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage()); Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage());
} }
String finalPayload = developerPayload; String finalPayload = developerPayload;
billingExecutor.execute(() -> { ensureBillingReady(activity, "pay", bs ->
try { billingExecutor.execute(() -> {
BillingService bs = billingService; try {
if (bs == null || !bs.isReady()) { if (bs == null || !bs.isReady()) {
Log.e(TAG, "[PAY-FLOW] billingService became null or not ready"); Log.e(TAG, "[PAY-FLOW] billingService became null or not ready");
sendPayCallback(1, "billing_not_ready", null, null, null, null); sendPayCallback(1, "billing_not_ready", null, null, null, null);
return; return;
} }
Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload); Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload);
BillingResult result = bs.launchBillingFlow(activity, BillingResult result = bs.launchBillingFlow(activity,
new BillingFlowParams(productId, "inapp", finalPayload)); new BillingFlowParams(productId, "inapp", finalPayload));
Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode() Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode()
+ " debugMsg=" + result.getDebugMessage()); + " debugMsg=" + result.getDebugMessage());
if (result.getResponseCode() != 2000) { if (result.getResponseCode() != 2000) {
Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode() Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode()
+ " debugMsg=" + result.getDebugMessage()); + " debugMsg=" + result.getDebugMessage());
sendPayCallback(1, "billing_flow_error_" + result.getResponseCode() sendPayCallback(1, "billing_flow_error_" + result.getResponseCode()
+ "_" + result.getDebugMessage(), null, null, null, null); + "_" + result.getDebugMessage(), null, null, null, null);
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName() Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName()
+ " msg=" + e.getMessage()); + " msg=" + e.getMessage());
sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null, null); sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null, null);
} }
}); }),
() -> sendPayCallback(1, "billing_not_ready", null, null, null, null));
} }
@Override @Override
@@ -162,36 +181,119 @@ public class FlexionSDKService implements
@Override @Override
public void queryProducts(String productIds) { public void queryProducts(String productIds) {
Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected); Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected);
if (billingService == null || !billingConnected) {
sendExtensionError("billing_not_ready");
return;
}
List<String> ids = Arrays.asList(productIds.split(",")); List<String> ids = Arrays.asList(productIds.split(","));
Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config"); Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config");
queryProductDetails(ids); ensureBillingReady(null, "queryProducts",
bs -> queryProductDetails(bs, ids),
() -> sendExtensionError("billing_not_ready"));
} }
private static void initBillingService(Activity activity) { private static void initBillingService(Activity activity) {
Log.i(TAG, "[PAY-FLOW] initBillingService"); Log.i(TAG, "[PAY-FLOW] initBillingService");
billingService = FLX.createBillingService(activity, purchasesListener); ensureBillingReady(activity, "initBillingService",
billingService.startConnection(activity, new ConnectionStateListener() { bs -> queryPendingPurchases(),
@Override () -> Log.w(TAG, "[PAY-FLOW] initBillingService failed: billing not ready"));
public void onBillingSetupFinished(BillingResult result) { }
Log.i(TAG, "[PAY-FLOW] onBillingSetupFinished: code=" + result.getResponseCode());
if (result.getResponseCode() == BillingResults.ResultCode.CONNECTION_SUCCESS_CODE) {
billingConnected = true;
queryPendingPurchases();
}
}
@Override private static void ensureBillingReady(Activity activity, String operation,
public void onBillingServiceDisconnected() { BillingReadyOperation onReady, Runnable onFail) {
Log.w(TAG, "[PAY-FLOW] billing service disconnected"); ensureBillingReady(activity, operation, onReady, onFail, false);
}
private static void ensureBillingReady(Activity activity, String operation,
BillingReadyOperation onReady, Runnable onFail,
boolean forceReconnect) {
if (activity != null) {
billingActivity = activity;
}
Activity targetActivity = activity != null ? activity : billingActivity;
if (targetActivity == null) {
Log.e(TAG, "[PAY-FLOW] ensureBillingReady failed: activity is null operation=" + operation);
if (onFail != null) onFail.run();
return;
}
targetActivity.runOnUiThread(() -> {
try {
if (billingService == null) {
Log.i(TAG, "[PAY-FLOW] create BillingService operation=" + operation);
billingService = FLX.createBillingService(targetActivity, purchasesListener);
}
boolean ready = billingService != null && billingService.isReady();
Log.i(TAG, "[PAY-FLOW] ensureBillingReady operation=" + operation
+ " connected=" + billingConnected
+ " ready=" + ready
+ " connecting=" + billingConnecting
+ " forceReconnect=" + forceReconnect);
if (ready && !forceReconnect) {
billingConnected = true;
if (onReady != null) onReady.run(billingService);
return;
}
pendingBillingOperations.add(new PendingBillingOperation(operation, onReady, onFail));
if (billingConnecting) {
Log.i(TAG, "[PAY-FLOW] billing connection already in progress, queued operation=" + operation);
return;
}
billingConnecting = true;
Log.i(TAG, "[PAY-FLOW] billing reconnect start operation=" + operation);
billingService.startConnection(targetActivity, new ConnectionStateListener() {
@Override
public void onBillingSetupFinished(BillingResult result) {
int code = result.getResponseCode();
Log.i(TAG, "[PAY-FLOW] onBillingSetupFinished: code=" + code);
billingConnecting = false;
billingConnected = code == BillingResults.ResultCode.CONNECTION_SUCCESS_CODE;
drainBillingOperations(billingConnected);
if (billingConnected) {
queryPendingPurchases();
}
}
@Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "[PAY-FLOW] billing service disconnected");
billingConnected = false;
}
});
mainHandler.postDelayed(() -> {
if (!billingConnecting) return;
boolean timeoutReady = billingService != null && billingService.isReady();
Log.w(TAG, "[PAY-FLOW] billing reconnect timeout operation=" + operation
+ " ready=" + timeoutReady);
billingConnecting = false;
billingConnected = timeoutReady;
drainBillingOperations(timeoutReady);
}, 10000);
} catch (Exception e) {
Log.e(TAG, "[PAY-FLOW] ensureBillingReady exception operation=" + operation
+ " msg=" + e.getMessage());
billingConnecting = false;
billingConnected = false; billingConnected = false;
drainBillingOperations(false);
} }
}); });
} }
private static void drainBillingOperations(boolean success) {
List<PendingBillingOperation> operations = new ArrayList<>(pendingBillingOperations);
pendingBillingOperations.clear();
Log.i(TAG, "[PAY-FLOW] drainBillingOperations success=" + success
+ " count=" + operations.size());
for (PendingBillingOperation operation : operations) {
if (success) {
if (operation.onReady != null) operation.onReady.run(billingService);
} else {
Log.w(TAG, "[PAY-FLOW] billing operation failed operation=" + operation.operation);
if (operation.onFail != null) operation.onFail.run();
}
}
}
private static void handlePurchase(Purchase purchase) { private static void handlePurchase(Purchase purchase) {
Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState() Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState()
+ " orderId=" + purchase.getOrderId() + " orderId=" + purchase.getOrderId()
@@ -208,17 +310,15 @@ public class FlexionSDKService implements
private static void queryPendingPurchases() { private static void queryPendingPurchases() {
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService
+ " billingConnected=" + billingConnected); + " billingConnected=" + billingConnected);
if (billingService == null || !billingConnected) { ensureBillingReady(null, "queryPendingPurchases",
Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected"); bs -> bs.queryPurchasesAsync(
return; new QueryPurchasesParams("inapp"),
} (result, purchases) -> {
billingService.queryPurchasesAsync( Log.i(TAG, "[PAY-FLOW] queryPendingPurchases result: code=" + result.getResponseCode()
new QueryPurchasesParams("inapp"), + " count=" + (purchases == null ? 0 : purchases.size()));
(result, purchases) -> { if (purchases != null) for (Purchase p : purchases) cachePendingPurchase(p);
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases result: code=" + result.getResponseCode() }),
+ " count=" + (purchases == null ? 0 : purchases.size())); () -> Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected"));
if (purchases != null) for (Purchase p : purchases) cachePendingPurchase(p);
});
} }
private static void cachePendingPurchase(Purchase purchase) { private static void cachePendingPurchase(Purchase purchase) {
@@ -272,10 +372,33 @@ public class FlexionSDKService implements
} }
} }
private static void queryProductDetails(List<String> productIds) { @Override
billingService.queryProductDetailsAsync( public void refreshBilling() {
Log.i(TAG, "[PAY-FLOW] refreshBilling requested");
billingConnected = false;
ensureBillingReady(null, "refreshBilling",
bs -> Log.i(TAG, "[PAY-FLOW] refreshBilling ready"),
() -> Log.w(TAG, "[PAY-FLOW] refreshBilling failed"),
true);
}
private static void queryProductDetails(BillingService service, List<String> productIds) {
final boolean[] completed = {false};
mainHandler.postDelayed(() -> {
if (completed[0]) return;
completed[0] = true;
Log.w(TAG, "[PAY-FLOW] queryProductDetails timeout count=" + productIds.size());
sendExtensionError("flexion_query_timeout");
}, 30000);
service.queryProductDetailsAsync(
new ProductDetailsParams("inapp", productIds), new ProductDetailsParams("inapp", productIds),
(billingResult, productDetails) -> { (billingResult, productDetails) -> {
if (completed[0]) {
Log.w(TAG, "[PAY-FLOW] queryProductDetails callback ignored after timeout");
return;
}
completed[0] = true;
try { try {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
if (billingResult.getResponseCode() == BillingResults.ResultCode.QUERY_PRODUCT_DETAILS_SUCCESS_CODE if (billingResult.getResponseCode() == BillingResults.ResultCode.QUERY_PRODUCT_DETAILS_SUCCESS_CODE
@@ -338,15 +461,17 @@ public class FlexionSDKService implements
} }
public static void consumePurchase(String token) { public static void consumePurchase(String token) {
if (billingService == null || token == null || token.isEmpty()) { if (token == null || token.isEmpty()) {
Log.w(TAG, "[PAY-FLOW] consumePurchase: billingService or token is null"); Log.w(TAG, "[PAY-FLOW] consumePurchase: token is null");
return; return;
} }
Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token); Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token);
billingService.consumeAsync( ensureBillingReady(null, "consumePurchase",
new ConsumeParams(token), bs -> bs.consumeAsync(
r -> Log.i(TAG, "[PAY-FLOW] consume result: " + new ConsumeParams(token),
(r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); r -> Log.i(TAG, "[PAY-FLOW] consume result: " +
(r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))),
() -> Log.w(TAG, "[PAY-FLOW] consumePurchase skipped: billing not connected"));
} }
public static void cancelPurchase() { public static void cancelPurchase() {

View File

@@ -61,6 +61,8 @@ public class ChannelBuildTool
File.Delete(oldJava); File.Delete(oldJava);
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKService.java")) foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKService.java"))
File.Delete(oldJava); File.Delete(oldJava);
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKAdapter.java"))
File.Delete(oldJava);
var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java"); var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java");
if (File.Exists(oldConfig)) File.Delete(oldConfig); if (File.Exists(oldConfig)) File.Delete(oldConfig);

View File

@@ -76,6 +76,10 @@ public class SDKManager {
String getPendingPurchases(); String getPendingPurchases();
} }
public interface IBillingLifecycleAdapter {
void refreshBilling();
}
private static IChannelSDKAdapter sdkAdapter; private static IChannelSDKAdapter sdkAdapter;
public static void queryProductsByIDs(String productIds) { public static void queryProductsByIDs(String productIds) {
@@ -100,9 +104,16 @@ public class SDKManager {
return "[]"; return "[]";
} }
public static void RefreshBillingService() {
Log.i(TAG, "RefreshBillingService");
if (sdkAdapter instanceof IBillingLifecycleAdapter) {
((IBillingLifecycleAdapter) sdkAdapter).refreshBilling();
}
}
public static void CancelBillingFlow() { public static void CancelBillingFlow() {
Log.w(TAG, "CancelBillingFlow"); Log.w(TAG, "CancelBillingFlow");
// FlexionSDKService.cancelPurchase(); // FlexionSDKAdapter.cancelPurchase();
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------

View File

@@ -322,6 +322,9 @@ namespace tysdk
result.isSuccess = true; result.isSuccess = true;
result.userId = _accountInfo.userId; result.userId = _accountInfo.userId;
SetUserInfo(); SetUserInfo();
#if UNITY_ANDROID
UnityBridgeFunc.RefreshBillingService();
#endif
} }
TYSDKCallbackManager.Instance.TryTriggerCallback(result); TYSDKCallbackManager.Instance.TryTriggerCallback(result);
@@ -414,6 +417,9 @@ namespace tysdk
{ {
_accountInfo.AddLinkedAccount(accoutType); _accountInfo.AddLinkedAccount(accoutType);
_accountInfo.Save(); _accountInfo.Save();
#if UNITY_ANDROID
UnityBridgeFunc.RefreshBillingService();
#endif
} }
return result; return result;
} }

View File

@@ -160,10 +160,18 @@ namespace tysdk
var result = new ProductListInfo(); var result = new ProductListInfo();
return result; return result;
#elif UNITY_ANDROID || UNITY_IOS #elif UNITY_ANDROID || UNITY_IOS
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(); var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(TimeSpan.FromSeconds(45));
UnityBridgeFunc.GetSKUList(); UnityBridgeFunc.GetSKUList();
var result = await task; try
return result; {
var result = await task;
return result;
}
catch (TaskCanceledException e)
{
Debug.LogWarning($"[TYSdkFacade] GetSKUList timeout: {e.Message}");
return new ProductListInfo() { code = 1, msg = "sku_callback_timeout" };
}
#endif #endif
} }
@@ -176,10 +184,18 @@ namespace tysdk
await Task.Yield(); await Task.Yield();
return new ProductListInfo(); return new ProductListInfo();
#elif UNITY_ANDROID #elif UNITY_ANDROID
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(); var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(TimeSpan.FromSeconds(45));
UnityBridgeFunc.GetSKUListByIapIds(productIds); UnityBridgeFunc.GetSKUListByIapIds(productIds);
var result = await task; try
return result; {
var result = await task;
return result;
}
catch (TaskCanceledException e)
{
Debug.LogWarning($"[TYSdkFacade] GetSKUListByIapIds timeout: {e.Message}");
return new ProductListInfo() { code = 1, msg = "sku_callback_timeout" };
}
#else #else
return new ProductListInfo() { code = -1, msg = "unsupported platform" }; return new ProductListInfo() { code = -1, msg = "unsupported platform" };
#endif #endif

View File

@@ -48,6 +48,8 @@ namespace tysdk
public static string GetPendingPurchases() => "[]"; public static string GetPendingPurchases() => "[]";
public static void RefreshBillingService() { }
//打点 //打点
public static void SetGaUserInfo(string userId){} public static void SetGaUserInfo(string userId){}
public static void SetGaCommonInfo(string SetGaCommonInfo){} public static void SetGaCommonInfo(string SetGaCommonInfo){}
@@ -224,6 +226,15 @@ namespace tysdk
} }
} }
public static void RefreshBillingService()
{
UnityEngine.Debug.Log("UnityBridgeFunc.RefreshBillingService");
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("RefreshBillingService");
}
}
public static void CancelBillingFlow() public static void CancelBillingFlow()
{ {
UnityEngine.Debug.Log("UnityBridgeFunc.CancelBillingFlow"); UnityEngine.Debug.Log("UnityBridgeFunc.CancelBillingFlow");
@@ -470,6 +481,8 @@ namespace tysdk
public static string GetPendingPurchases() => "[]"; public static string GetPendingPurchases() => "[]";
public static void RefreshBillingService() { }
#endif #endif
} }
} }