2 Commits

Author SHA1 Message Date
4d3eae1c88 update:更新补单逻辑 2026-06-02 19:50:44 +08:00
f06074d9cc update:更新SDK 修改 2026-05-21 14:45:27 +08:00
12 changed files with 316 additions and 157 deletions

View File

@@ -54,13 +54,13 @@
追加 ${applicationId}Flexion 包名)作为后缀后,与 GP 版 authority 不同, 追加 ${applicationId}Flexion 包名)作为后缀后,与 GP 版 authority 不同,
两个渠道包可同时安装在同一设备上,不会触发 INSTALL_FAILED_CONFLICTING_PROVIDER。 两个渠道包可同时安装在同一设备上,不会触发 INSTALL_FAILED_CONFLICTING_PROVIDER。
参考Flexion 官方文档《Unique package name - follow-on requirements》 参考Flexion 官方文档《Unique package name - follow-on requirements》
-->
<provider <provider
android:name="com.facebook.FacebookContentProvider" android:name="com.facebook.FacebookContentProvider"
android:authorities="com.facebook.app.FacebookContentProvider162066986963808.${applicationId}" android:authorities="com.facebook.app.FacebookContentProvider162066986963808.${applicationId}"
android:exported="true" android:exported="true"
tools:replace="android:authorities" /> tools:replace="android:authorities" />
-->
</application> </application>
<queries> <queries>
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" /> <provider android:authorities="com.facebook.katana.provider.PlatformProvider" />

View File

@@ -1,11 +1,11 @@
package com.unity3d.player; package com.unity3d.player;
public class ChannelEntry { 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();
} }
@@ -14,7 +14,7 @@ public class ChannelEntry {
public static String normalizeSkuList(String msg) { public static String normalizeSkuList(String msg) {
return msg; return msg;
} }
//public SDKExtBuilder; //public SDKExtBuilder;
} }

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

@@ -34,7 +34,7 @@ dependencies {
// Google Play In-App Review // Google Play In-App Review
implementation 'com.google.android.play:review:2.0.1' implementation 'com.google.android.play:review:2.0.1'
// Google Play Billing — tuyoosdk AAR references BillingManager during init // Google Play Billing — tuyoosdk AAR references BillingManager during init
implementation 'com.android.billingclient:billing:6.0.1' implementation 'com.android.billingclient:billing:7.0.0'
implementation 'net.aihelp:android-aihelp-aar:5.3.+' implementation 'net.aihelp:android-aihelp-aar:5.3.+'
implementation 'com.google.android.gms:play-services-auth:20.2.0' implementation 'com.google.android.gms:play-services-auth:20.2.0'
@@ -52,7 +52,7 @@ dependencies {
implementation 'com.miui.referrer:homereferrer:1.0.0.6' implementation 'com.miui.referrer:homereferrer:1.0.0.6'
implementation platform('com.google.firebase:firebase-bom:32.7.4') implementation platform('com.google.firebase:firebase-bom:34.6.0')
implementation 'com.google.firebase:firebase-analytics' implementation 'com.google.firebase:firebase-analytics'
implementation('com.facebook.android:facebook-android-sdk:16.2.0') implementation('com.facebook.android:facebook-android-sdk:16.2.0')
@@ -78,6 +78,7 @@ dependencies {
implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8 implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6 implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6
implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5 implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5
implementation 'com.tapjoy:tapjoy-android-unitybridge:14.5.0' // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:10
// Android Resolver Dependencies End // Android Resolver Dependencies End
**DEPS**} **DEPS**}

View File

@@ -28,7 +28,10 @@ dependencyResolutionManagement {
} }
// Android Resolver Repos Start // Android Resolver Repos Start
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/") def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven {
url "https://sdk.tapjoy.com/" // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:9
}
maven { maven {
url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8 url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
} }

View File

@@ -55,31 +55,17 @@ public class BuildTool
BuildiOS(bparams); BuildiOS(bparams);
} }
[MenuItem("Tools/Build Android")] [MenuItem("Tools/Build Android")]
public static void BuildAndroid() public static void BuildAndroid()
{ {
ChannelBuildTool.Build_GooglePlay();
BuildParams bparams = new BuildParams() }
{
outPath = "../Bin/tysdkTest.apk" [MenuItem("Tools/Build Android With Debug")]
}; public static void BuildAndroidWithDebug()
{
BuildAndroid(bparams); ChannelBuildTool.Build_GooglePlay_Debug();
} }
[MenuItem("Tools/Build Android With Debug")]
public static void BuildAndroidWithDebug()
{
BuildParams bparams = new BuildParams()
{
outPath = "../Bin/tysdkTest_debug.apk",
};
var buildOptions = BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection;
BuildAndroid(bparams, buildOptions);
}
public static void CIBuildAndroid() public static void CIBuildAndroid()
{ {

View File

@@ -7,6 +7,8 @@ public class ChannelBuildTool
{ {
public static readonly string[] SupportedChannels = { "GooglePlay", "Rustore", "Flexion" }; public static readonly string[] SupportedChannels = { "GooglePlay", "Rustore", "Flexion" };
private const string DefaultChannel = "GooglePlay";
private const string DefaultAndroidBundleIdentifier = "com.arkgame.ft";
private const string ExtraCfgRoot = "../ExtraPluginCfgs/Android"; private const string ExtraCfgRoot = "../ExtraPluginCfgs/Android";
private const string TysdkPlugins = "Packages/tysdk/Plugins/Android"; private const string TysdkPlugins = "Packages/tysdk/Plugins/Android";
private const string TysdkFiles = "Packages/tysdk/Files"; private const string TysdkFiles = "Packages/tysdk/Files";
@@ -61,6 +63,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);
@@ -111,35 +115,51 @@ public class ChannelBuildTool
// ==================== Build ==================== // ==================== Build ====================
private static void RestoreDefaultChannel()
{
Debug.Log($"[ChannelBuild] Restore default channel -> {DefaultChannel}");
SwitchChannel(DefaultChannel);
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, DefaultAndroidBundleIdentifier);
Debug.Log($"[ChannelBuild] Restore bundle identifier -> {DefaultAndroidBundleIdentifier}");
AssetDatabase.SaveAssets();
}
private static void BuildAndroidForChannel(string channel, bool debug) private static void BuildAndroidForChannel(string channel, bool debug)
{ {
SwitchChannel(channel); try
string fileName = debug ? $"tysdkTest_{channel}_debug.apk" : $"tysdkTest_{channel}.apk";
string outPath = $"../Bin/{fileName}";
var buildOptions = debug
? BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection
: BuildOptions.None;
var buildPlayerOptions = new BuildPlayerOptions
{ {
scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(), SwitchChannel(channel);
locationPathName = outPath,
target = BuildTarget.Android,
options = buildOptions,
};
Debug.Log($"[ChannelBuild] Building {channel} → {outPath}"); string fileName = debug ? $"tysdkTest_{channel}_debug.apk" : $"tysdkTest_{channel}.apk";
var buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions); string outPath = $"../Bin/{fileName}";
var buildOptions = debug
? BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection
: BuildOptions.None;
if (buildReport?.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded) var buildPlayerOptions = new BuildPlayerOptions
{ {
Debug.Log($"[ChannelBuild] Success: {Path.GetFullPath(outPath)}"); scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(),
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); locationPathName = outPath,
target = BuildTarget.Android,
options = buildOptions,
};
Debug.Log($"[ChannelBuild] Building {channel} → {outPath}");
var buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);
if (buildReport?.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
{
Debug.Log($"[ChannelBuild] Success: {Path.GetFullPath(outPath)}");
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
}
else
{
Debug.LogError($"[ChannelBuild] Failed: {buildReport?.summary}");
}
} }
else finally
{ {
Debug.LogError($"[ChannelBuild] Failed: {buildReport?.summary}"); RestoreDefaultChannel();
} }
} }
@@ -157,29 +177,7 @@ public class ChannelBuildTool
public static void Switch_Flexion() => SwitchChannel("Flexion"); public static void Switch_Flexion() => SwitchChannel("Flexion");
// ==================== Menu: Build ==================== // ==================== Menu: Build ====================
[MenuItem("Tools/Build Android (Current Channel)")]
public static void Build_CurrentChannel()
{
if (string.IsNullOrEmpty(currentChannel))
{
Debug.LogError("[ChannelBuild] No channel selected. Use Tools/Switch Channel first.");
return;
}
BuildAndroidForChannel(currentChannel, false);
}
[MenuItem("Tools/Build Android (Current Channel) Debug")]
public static void Build_CurrentChannel_Debug()
{
if (string.IsNullOrEmpty(currentChannel))
{
Debug.LogError("[ChannelBuild] No channel selected. Use Tools/Switch Channel first.");
return;
}
BuildAndroidForChannel(currentChannel, true);
}
[MenuItem("Tools/Build Android (Channel)/GooglePlay")] [MenuItem("Tools/Build Android (Channel)/GooglePlay")]
public static void Build_GooglePlay() => BuildAndroidForChannel("GooglePlay", false); public static void Build_GooglePlay() => BuildAndroidForChannel("GooglePlay", false);

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
} }
} }