diff --git a/.gitignore b/.gitignore index ca10b67..783ed2a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ sdk-intergration/.vscode sdk-intergration/' sdk-intergration/Assets/.claude/settings.json sdk-intergration/.idea -.worktrees/* \ No newline at end of file +.worktrees/* +.docs/* +.claude/* + diff --git a/ExtraPluginCfgs/Android/flexion/ChannelHelpers.java b/ExtraPluginCfgs/Android/flexion/ChannelEntry.java similarity index 70% rename from ExtraPluginCfgs/Android/flexion/ChannelHelpers.java rename to ExtraPluginCfgs/Android/flexion/ChannelEntry.java index 0111209..d7eb824 100644 --- a/ExtraPluginCfgs/Android/flexion/ChannelHelpers.java +++ b/ExtraPluginCfgs/Android/flexion/ChannelEntry.java @@ -1,11 +1,11 @@ package com.unity3d.player; -public class ChannelHelpers { - +public class ChannelEntry { + public static final String CHANNEL = "Flexion"; - static void register() { - SDKManager.registerHelper(new FlexionSDKHelper()); + public static SDKManager.IChannelSDKAdapter createSDKAdapter() { + return new FlexionSDKService(); } @@ -14,7 +14,7 @@ public class ChannelHelpers { public static String normalizeSkuList(String msg) { return msg; } - - + + //public SDKExtBuilder; } diff --git a/ExtraPluginCfgs/Android/flexion/FlexionSDKHelper.java b/ExtraPluginCfgs/Android/flexion/FlexionSDKService.java similarity index 69% rename from ExtraPluginCfgs/Android/flexion/FlexionSDKHelper.java rename to ExtraPluginCfgs/Android/flexion/FlexionSDKService.java index 06efbf4..73bee1e 100644 --- a/ExtraPluginCfgs/Android/flexion/FlexionSDKHelper.java +++ b/ExtraPluginCfgs/Android/flexion/FlexionSDKService.java @@ -17,35 +17,41 @@ import com.flexionmobile.ddpx.service.BillingService; import com.flexionmobile.fdk.FLX; import com.tuyoo.gamesdk.api.SDKAPI; -import com.tuyoo.gamesdk.api.SDKCallBack; import com.tuyoo.gamesdk.model.InitParam; -import com.tuyoo.gamesdk.util.SDKLog; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; -public class FlexionSDKHelper implements SDKManager.ISDKHelper { - private static final String TAG = "FlexionSDKHelper"; +public class FlexionSDKService implements + SDKManager.IChannelSDKAdapter, + SDKManager.IPaymentAdapter, + SDKManager.IProductQueryAdapter, + SDKManager.IPendingPurchaseAdapter { + private static final String TAG = "FlexionSDKService"; private static BillingService billingService; private static boolean billingConnected = false; + private static final List pendingPurchases = new ArrayList<>(); + private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread")); private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> { - - - Log.i(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode() + Log.w(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode() + " hasPurchase=" + (purchase != null)); if (billingResult.getResponseCode() == BillingResults.ResultCode.PURCHASE_SUCCESS_CODE) { if (purchase != null) handlePurchase(purchase); } else if (billingResult.getResponseCode() == BillingResults.ResultCode.PURCHASE_USER_CANCELLED_CODE) { Log.i(TAG, "[PAY-FLOW] user cancelled"); - sendPayCallback(1, "user_cancelled", null, null, null); + sendPayCallback(1, "user_cancelled", null, null, null, null); } else { Log.e(TAG, "[PAY-FLOW] purchase failed: code=" + billingResult.getResponseCode()); - sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null); + sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null, null); } }; @@ -95,9 +101,9 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { String productName, String productCount, String prodorderId, String appInfo) { Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected); - if (billingService == null || !billingConnected) { + if (billingService == null || !billingConnected || !billingService.isReady()) { Log.e(TAG, "[PAY-FLOW] billing not ready"); - sendPayCallback(1, "billing_not_ready", null, null, null); + sendPayCallback(1, "billing_not_ready", null, null, null, null); return; } String developerPayload = ""; @@ -105,97 +111,66 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { byte[] decoded = Base64.getDecoder().decode(appInfo); JSONObject json = new JSONObject(new String(decoded, "UTF-8")); JSONObject payload = new JSONObject(); + String customData = json.optString("customData", ""); + payload.put("orderId", prodorderId); payload.put("userId", json.optString("userId", "")); payload.put("pfid", json.optString("pfid", "")); - payload.put("customData", json.optString("customData", "")); + if (!customData.isEmpty()) { + payload.put("customDataB64", Base64.getEncoder().encodeToString(customData.getBytes("UTF-8"))); + } payload.put("prodPrice", productPrice); payload.put("prodCount", productCount); developerPayload = payload.toString(); + Log.i(TAG, "[PAY-CUSTOMDATA] customData length=" + customData.length() + + " developerPayload length=" + developerPayload.length()); } catch (Exception e) { Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage()); } String finalPayload = developerPayload; - activity.runOnUiThread(() -> { - Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload); - Log.i(TAG, "[PAY-FLOW] billingService=" + billingService + " billingConnected=" + billingConnected); + billingExecutor.execute(() -> { try { - BillingResult result = billingService.launchBillingFlow(activity, + BillingService bs = billingService; + if (bs == null || !bs.isReady()) { + Log.e(TAG, "[PAY-FLOW] billingService became null or not ready"); + sendPayCallback(1, "billing_not_ready", null, null, null, null); + return; + } + Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload); + BillingResult result = bs.launchBillingFlow(activity, new BillingFlowParams(productId, "inapp", finalPayload)); Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode() + " debugMsg=" + result.getDebugMessage()); - if (result.getResponseCode() == 2001) { - Log.i(TAG, "[PAY-FLOW] service not ready, retry after 3s..."); - new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> { - try { - BillingResult retryResult = billingService.launchBillingFlow(activity, - new BillingFlowParams(productId, "inapp", finalPayload)); - Log.i(TAG, "[PAY-FLOW] launchBillingFlow retry result: code=" + retryResult.getResponseCode() - + " debugMsg=" + retryResult.getDebugMessage()); - if (retryResult.getResponseCode() != 2000) { - sendPayCallback(1, "billing_flow_error_" + retryResult.getResponseCode() - + "_" + retryResult.getDebugMessage(), null, null, null); - } - } catch (Exception ex) { - sendPayCallback(1, "launch_billing_failed: " + ex.getMessage(), null, null, null); - } - }, 3000); - return; - } if (result.getResponseCode() != 2000) { Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode() + " debugMsg=" + result.getDebugMessage()); sendPayCallback(1, "billing_flow_error_" + result.getResponseCode() - + "_" + result.getDebugMessage(), null, null, null); + + "_" + result.getDebugMessage(), null, null, null, null); } } catch (Exception e) { Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName() + " msg=" + e.getMessage()); - sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null); + sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null, null); } }); } @Override public void queryProducts() { - Log.i(TAG, "queryProducts called, billingConnected=" + billingConnected); + // Flexion 渠道不通过 thirdExtend 获取商品列表,由 queryProducts(String) 替代 + } + + @Override + public void queryProducts(String productIds) { + Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected); if (billingService == null || !billingConnected) { - Log.w(TAG, "queryProducts: billing not ready"); sendExtensionError("billing_not_ready"); return; } - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("action", "ACTION_GET_SKU_DETAILS_LIST"); - } catch (JSONException e) { - throw new RuntimeException(e); - } - SDKAPI.getIns().thirdExtend(jsonObject.toString(), new SDKCallBack.Extend() { - @Override - public void callback(int code, String msg) { - Log.i(TAG, "TuYoo thirdExtend callback: code=" + code + " msg=" + msg); - if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS && msg != null) { - try { - JSONArray products = new JSONArray(msg); - List productIds = new java.util.ArrayList<>(); - for (int i = 0; i < products.length(); i++) { - JSONObject p = products.getJSONObject(i); - String pid = p.optString("productId", ""); - if (!pid.isEmpty()) productIds.add(pid); - } - Log.i(TAG, "queryProductDetails with " + productIds.size() + " items: " + productIds); - queryProductDetails(productIds); - } catch (JSONException e) { - sendExtensionError("parse_error: " + e.getMessage()); - } - } else { - sendExtensionError("tuyoo_query_failed: " + msg); - } - } - }); + List ids = Arrays.asList(productIds.split(",")); + Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config"); + queryProductDetails(ids); } - // ---- Billing Infrastructure ---- - private static void initBillingService(Activity activity) { Log.i(TAG, "[PAY-FLOW] initBillingService"); billingService = FLX.createBillingService(activity, purchasesListener); @@ -208,6 +183,7 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { queryPendingPurchases(); } } + @Override public void onBillingServiceDisconnected() { Log.w(TAG, "[PAY-FLOW] billing service disconnected"); @@ -218,29 +194,84 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { private static void handlePurchase(Purchase purchase) { Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState() + + " orderId=" + purchase.getOrderId() + " token=" + purchase.getToken()); if (purchase.getPurchaseState() == 0) { - // 支付成功 → 回传数据给 Unity 做服务端验签,验签通过后由 C# 调 ConsumePurchase Log.i(TAG, "[PAY-FLOW] purchase success, sending to Unity for verification"); - sendPayCallback(0, "success", + sendPayCallback(0, "success", purchase.getOrderId(), purchase.getPurchaseJson(), purchase.getSignature(), purchase.getToken()); - // [旧逻辑] 直接消费,跳过验签 - // Log.i(TAG, "[PAY-FLOW] auto-consuming pending purchase: " + purchase.getToken()); - // consumePurchase(purchase.getToken()); } else if (purchase.getPurchaseState() == 2) { Log.i(TAG, "[PAY-FLOW] purchase state=2 (pending), skip"); } } private static void queryPendingPurchases() { - if (billingService == null || !billingConnected) return; + Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService + + " billingConnected=" + billingConnected); + if (billingService == null || !billingConnected) { + Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected"); + return; + } billingService.queryPurchasesAsync( new QueryPurchasesParams("inapp"), (result, purchases) -> { - if (purchases != null) for (Purchase p : purchases) handlePurchase(p); + Log.i(TAG, "[PAY-FLOW] queryPendingPurchases result: code=" + result.getResponseCode() + + " count=" + (purchases == null ? 0 : purchases.size())); + if (purchases != null) for (Purchase p : purchases) cachePendingPurchase(p); }); } + private static void cachePendingPurchase(Purchase purchase) { + if (purchase == null) { + Log.w(TAG, "[PAY-FLOW] cachePendingPurchase skipped: purchase is null"); + return; + } + Log.i(TAG, "[PAY-FLOW] cachePendingPurchase state=" + purchase.getPurchaseState() + + " orderId=" + purchase.getOrderId() + " token=" + purchase.getToken()); + if (purchase.getPurchaseState() != 0) return; + try { + JSONObject result = new JSONObject(); + if (purchase.getOrderId() != null) result.put("orderId", purchase.getOrderId()); + if (purchase.getPurchaseJson() != null) result.put("purchaseJson", purchase.getPurchaseJson()); + if (purchase.getSignature() != null) result.put("signature", purchase.getSignature()); + if (purchase.getToken() != null) result.put("purchaseToken", purchase.getToken()); + + synchronized (pendingPurchases) { + String token = purchase.getToken(); + for (String item : pendingPurchases) { + JSONObject cached = new JSONObject(item); + if (token != null && token.equals(cached.optString("purchaseToken", null))) { + Log.i(TAG, "[PAY-FLOW] pending purchase already cached token=" + token); + return; + } + } + pendingPurchases.add(result.toString()); + } + Log.i(TAG, "[PAY-FLOW] cached pending purchase: orderId=" + purchase.getOrderId() + + " cachedCount=" + pendingPurchases.size()); + } catch (JSONException e) { + Log.e(TAG, "[PAY-FLOW] cachePendingPurchase json error: " + e.getMessage()); + } + } + + @Override + public String getPendingPurchases() { + synchronized (pendingPurchases) { + Log.i(TAG, "[PAY-FLOW] getPendingPurchases start cachedCount=" + pendingPurchases.size()); + JSONArray result = new JSONArray(); + for (String item : pendingPurchases) { + try { + result.put(new JSONObject(item)); + } catch (JSONException e) { + Log.e(TAG, "[PAY-FLOW] getPendingPurchases json error: " + e.getMessage()); + } + } + pendingPurchases.clear(); + Log.i(TAG, "[PAY-FLOW] getPendingPurchases count=" + result.length()); + return result.toString(); + } + } + private static void queryProductDetails(List productIds) { billingService.queryProductDetailsAsync( new ProductDetailsParams("inapp", productIds), @@ -278,17 +309,17 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { }); } - // ---- Callbacks to Unity ---- - - private static void sendPayCallback(int code, String errStr, + private static void sendPayCallback(int code, String errStr, String storeOrderId, String purchaseJson, String signature, String purchaseToken) { Log.i(TAG, "[PAY-FLOW] sendPayCallback: code=" + code + " errStr=" + errStr + + " storeOrderId=" + storeOrderId + " hasPurchaseJson=" + (purchaseJson != null) + " hasSignature=" + (signature != null) + " hasToken=" + (purchaseToken != null)); try { JSONObject result = new JSONObject(); result.put("code", code); result.put("errStr", errStr != null ? errStr : ""); + if (storeOrderId != null) result.put("orderId", storeOrderId); if (purchaseJson != null) result.put("purchaseJson", purchaseJson); if (signature != null) result.put("signature", signature); if (purchaseToken != null) result.put("purchaseToken", purchaseToken); @@ -318,6 +349,10 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { (r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); } + public static void cancelPurchase() { + Log.i(TAG, "[PAY-FLOW] cancelPurchase: TODO - dismiss billing dialog"); + } + private static void sendExtensionError(String errStr) { try { JSONObject result = new JSONObject(); diff --git a/ExtraPluginCfgs/Android/flexion/mainTemplate.gradle b/ExtraPluginCfgs/Android/flexion/mainTemplate.gradle index 0505686..d457d1d 100644 --- a/ExtraPluginCfgs/Android/flexion/mainTemplate.gradle +++ b/ExtraPluginCfgs/Android/flexion/mainTemplate.gradle @@ -16,13 +16,6 @@ android { namespace "com.unity3d.player" } -// Flexion 渠道专用:排除不需要的 AppsFlyer 模块 -// - purchase-connector: 基于 Google Play Billing,Flexion 不走 GP 计费,没用还增大包 -// 即使 Unity Android Resolver 下次又把 implementation 行写回来,这里的 exclude 也兜底排除 -configurations.all { - exclude group: 'com.appsflyer', module: 'purchase-connector' -} - dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) @@ -82,9 +75,7 @@ dependencies { implementation 'com.applovin.mediation:vungle-adapter:7.4.2.2' // Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml:4 implementation 'com.applovin:applovin-sdk:13.5.1' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:4 implementation 'com.appsflyer:af-android-sdk:6.17.3' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:5 - // Flexion: purchase-connector removed — Flexion does not use Google Play Billing. - // Also covered by top-level `configurations.all { exclude ... }` as a safety net. - // 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.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5 // Android Resolver Dependencies End diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java b/ExtraPluginCfgs/Android/googleplay/ChannelEntry.java similarity index 57% rename from sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java rename to ExtraPluginCfgs/Android/googleplay/ChannelEntry.java index b62fba6..3c61fc0 100644 --- a/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java +++ b/ExtraPluginCfgs/Android/googleplay/ChannelEntry.java @@ -1,10 +1,10 @@ package com.unity3d.player; -public class ChannelHelpers { +public class ChannelEntry { public static final String CHANNEL = "GooglePlay"; - static void register() { - // GooglePlay uses default behavior, no additional SDKHelper + public static SDKManager.IChannelSDKAdapter createSDKAdapter() { + return null; } public static String normalizeSkuList(String msg) { diff --git a/ExtraPluginCfgs/Android/googleplay/mainTemplate.gradle b/ExtraPluginCfgs/Android/googleplay/mainTemplate.gradle index fa895c0..814c5c6 100644 --- a/ExtraPluginCfgs/Android/googleplay/mainTemplate.gradle +++ b/ExtraPluginCfgs/Android/googleplay/mainTemplate.gradle @@ -72,7 +72,6 @@ dependencies { 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.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 **DEPS**} diff --git a/ExtraPluginCfgs/Android/googleplay/settingsTemplate.gradle b/ExtraPluginCfgs/Android/googleplay/settingsTemplate.gradle index d25174e..73133c1 100644 --- a/ExtraPluginCfgs/Android/googleplay/settingsTemplate.gradle +++ b/ExtraPluginCfgs/Android/googleplay/settingsTemplate.gradle @@ -18,9 +18,6 @@ dependencyResolutionManagement { mavenCentral() // Android Resolver Repos Start def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/") - maven { - url "https://sdk.tapjoy.com/" // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:9 - } maven { url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8 } diff --git a/ExtraPluginCfgs/Android/rustore/ChannelHelpers.java b/ExtraPluginCfgs/Android/rustore/ChannelEntry.java similarity index 86% rename from ExtraPluginCfgs/Android/rustore/ChannelHelpers.java rename to ExtraPluginCfgs/Android/rustore/ChannelEntry.java index 231322c..6d9e42b 100644 --- a/ExtraPluginCfgs/Android/rustore/ChannelHelpers.java +++ b/ExtraPluginCfgs/Android/rustore/ChannelEntry.java @@ -6,11 +6,11 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -public class ChannelHelpers { +public class ChannelEntry { public static final String CHANNEL = "Rustore"; - static void register() { - // Rustore uses default behavior, no additional SDKHelper + public static SDKManager.IChannelSDKAdapter createSDKAdapter() { + return null; } public static String normalizeSkuList(String msg) { @@ -37,7 +37,7 @@ public class ChannelHelpers { } return products.toString(); } catch (JSONException e) { - Log.e("ChannelHelpers", "normalizeSkuList error: " + e.getMessage()); + Log.e("ChannelEntry", "normalizeSkuList error: " + e.getMessage()); return msg; } } diff --git a/ExtraPluginCfgs/Android/rustore/baseProjectTemplate.gradle b/ExtraPluginCfgs/Android/rustore/baseProjectTemplate.gradle new file mode 100644 index 0000000..332c297 --- /dev/null +++ b/ExtraPluginCfgs/Android/rustore/baseProjectTemplate.gradle @@ -0,0 +1,14 @@ +plugins { + // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity + // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html + // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle + // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false + id 'com.google.gms.google-services' version '4.3.3' apply false + **BUILD_SCRIPT_DEPS** +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/docs/.claude/settings.local.json b/docs/.claude/settings.local.json new file mode 100644 index 0000000..0a37a01 --- /dev/null +++ b/docs/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "WebSearch" + ] + } +} diff --git a/sdk-intergration/Assets/Editor/ChannelBuildTool.cs b/sdk-intergration/Assets/Editor/ChannelBuildTool.cs index 988665f..98aab8a 100644 --- a/sdk-intergration/Assets/Editor/ChannelBuildTool.cs +++ b/sdk-intergration/Assets/Editor/ChannelBuildTool.cs @@ -42,6 +42,8 @@ public class ChannelBuildTool { foreach (var oldAar in Directory.GetFiles(TysdkPlugins, "tuyoosdk_*.aar")) File.Delete(oldAar); + foreach (var oldJar in Directory.GetFiles(TysdkPlugins, "tuyoosdk_*.jar")) + File.Delete(oldJar); foreach (var aar in Directory.GetFiles(srcAarDir, "*.aar")) { var dest = Path.Combine(TysdkPlugins, Path.GetFileName(aar)); @@ -53,8 +55,12 @@ public class ChannelBuildTool // 2. Clean up previous channel Java files foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "ChannelHelpers.java")) File.Delete(oldJava); + foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "ChannelEntry.java")) + File.Delete(oldJava); foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKHelper.java")) File.Delete(oldJava); + foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKService.java")) + File.Delete(oldJava); var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java"); if (File.Exists(oldConfig)) File.Delete(oldConfig); diff --git a/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle b/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle index fa895c0..814c5c6 100644 --- a/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle +++ b/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle @@ -72,7 +72,6 @@ dependencies { 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.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 **DEPS**} diff --git a/sdk-intergration/Assets/Plugins/Android/settingsTemplate.gradle b/sdk-intergration/Assets/Plugins/Android/settingsTemplate.gradle index d25174e..73133c1 100644 --- a/sdk-intergration/Assets/Plugins/Android/settingsTemplate.gradle +++ b/sdk-intergration/Assets/Plugins/Android/settingsTemplate.gradle @@ -18,9 +18,6 @@ dependencyResolutionManagement { mavenCentral() // Android Resolver Repos Start def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/") - maven { - url "https://sdk.tapjoy.com/" // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:9 - } maven { url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8 } diff --git a/sdk-intergration/Assets/Scripts/SDKTest.cs b/sdk-intergration/Assets/Scripts/SDKTest.cs index eee3ee0..3790369 100644 --- a/sdk-intergration/Assets/Scripts/SDKTest.cs +++ b/sdk-intergration/Assets/Scripts/SDKTest.cs @@ -82,10 +82,10 @@ public class SDKTest : MonoBehaviour //====================== Test Server =========================== //=============================================================================== // - _InitBtn.onClick.AddListener(Init); - TYSdkFacade.OnInitComplete += (success, msg) => - { - _messageTxt.text = success ? $"Init OK: {msg}" : $"Init Failed: {msg}"; + _InitBtn.onClick.AddListener(Init); + TYSdkFacade.OnInitComplete += (success, msg) => + { + _messageTxt.text = success ? $"Init OK: {msg}" : $"Init Failed: {msg}"; }; _loginGuestBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwGuest)); _loginGoogleBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwGoogle)); @@ -143,7 +143,7 @@ public class SDKTest : MonoBehaviour private void UpdateChannelTitle() { #if UNITY_ANDROID - using (var cls = new AndroidJavaClass("com.unity3d.player.ChannelHelpers")) + using (var cls = new AndroidJavaClass("com.unity3d.player.ChannelEntry")) { string channel = cls.GetStatic("CHANNEL"); Debug.Log($"[SDKTest] Channel from Java: {channel}"); @@ -237,11 +237,22 @@ public class SDKTest : MonoBehaviour var purchaseInfo = new JObject(); purchaseInfo["pfid"] = "TestPlayer"; purchaseInfo["customData"] = "{\"test\":\"test\"}"; + // purchaseInfo["customData"] ="{\"BuyType\":{\"type\":1,\"ID\":100502,\"key\":null,\"endDay\":0,\"extraPurcheData\":null,\"IsHide\":false},\"IAPID\":302,\"ItemData\":[{\"id\":1005,\"curCount\":22728,\"count\":260}],\"ShowType\":8,\"CONTENT_ID\":\"com.arkgame.ft.pack0499\",\"PRICE\":4.99,\"CURRENCY\":\"USD\",\"ProdPrice\":4.99,\"extraPurcheData\":{\"purchase_count\":9,\"trade_name\":\"商店-钻石直充4.99\",\"trade_type\":\"商店-钻石直充\",\"usd_price\":4.99,\"iap_id\":302,\"drop_id\":1005,\"trade_group\":\"商店\",\"item_1005\":260},\"orderId\":null,\"time\":\"5/20/2026 3:59:07 AM\"}"; purchaseInfo["userId"] = TYSdkFacade.TYAccountInfo.strUserId; - PaymentInfo payResult = await TYSdkFacade.Instance.Pay(sku, 1, sku.ProdPrice, purchaseInfo); - _messageTxt.text = "\n" + $"Success : {payResult.isSuccess}"; - _messageTxt.text += "\n" + $"message : {payResult.msg}"; + PaymentInfo payResult = await TYSdkFacade.Instance.Pay(sku, 1, sku.ProdPrice, purchaseInfo); + _messageTxt.text = "\n" + $"Success : {payResult.isSuccess}"; + _messageTxt.text += "\n" + $"message : {payResult.msg}"; + _messageTxt.text += "\n" + $"orderId : {payResult.orderId}"; + _messageTxt.text += "\n" + $"storeOrderId : {payResult.storeOrderId}"; + var consumeToken = payResult.Receipt?.PurchaseToken ?? payResult.purchaseToken; + _messageTxt.text += "\n" + $"hasToken : {!string.IsNullOrEmpty(consumeToken)}"; + if (!string.IsNullOrEmpty(consumeToken)) + { + Debug.Log($"[SDKTest] Consume purchase token after pay result token={consumeToken}"); + UnityBridgeFunc.ConsumePurchase(consumeToken); + _messageTxt.text += "\nconsume requested"; + } } SKUDetail sku = null; diff --git a/ExtraPluginCfgs/Android/googleplay/ChannelHelpers.java b/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelEntry.java similarity index 57% rename from ExtraPluginCfgs/Android/googleplay/ChannelHelpers.java rename to sdk-intergration/Packages/tysdk/Plugins/Android/ChannelEntry.java index b62fba6..3c61fc0 100644 --- a/ExtraPluginCfgs/Android/googleplay/ChannelHelpers.java +++ b/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelEntry.java @@ -1,10 +1,10 @@ package com.unity3d.player; -public class ChannelHelpers { +public class ChannelEntry { public static final String CHANNEL = "GooglePlay"; - static void register() { - // GooglePlay uses default behavior, no additional SDKHelper + public static SDKManager.IChannelSDKAdapter createSDKAdapter() { + return null; } public static String normalizeSkuList(String msg) { diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java.meta b/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelEntry.java.meta similarity index 93% rename from sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java.meta rename to sdk-intergration/Packages/tysdk/Plugins/Android/ChannelEntry.java.meta index 11a6de8..d87e5c0 100644 --- a/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java.meta +++ b/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelEntry.java.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 576805d2d0194634b8d235fd6837195e +guid: eac9aa9954e93c54fbafd3974ca30a5b PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java b/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java index 25e5cbe..8996aca 100644 --- a/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java +++ b/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java @@ -34,12 +34,10 @@ import com.tuyoo.gamesdk.model.InitParam; import com.tuyoo.gamesdk.pay.model.PayType; import com.tuyoo.gamesdk.util.SDKLog; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -57,57 +55,74 @@ public class SDKManager { private static SnsInfo tmpSnsInfo; private static String channel = "GooglePlay"; - // Channel helper - public interface ISDKHelper { + // Channel SDK adapter + public interface IChannelSDKAdapter { void init(Activity activity); + } + + public interface IPaymentAdapter { void pay(Activity activity, String productId, String productPrice, String productName, String productCount, String prodorderId, String appInfo); - void queryProducts(); void consume(String token); } - private static ISDKHelper sdkHelper; + public interface IProductQueryAdapter { + void queryProducts(); + void queryProducts(String productIds); + } - public static void registerHelper(ISDKHelper helper) { - sdkHelper = helper; + public interface IPendingPurchaseAdapter { + String getPendingPurchases(); + } + + private static IChannelSDKAdapter sdkAdapter; + + public static void queryProductsByIDs(String productIds) { + if (sdkAdapter instanceof IProductQueryAdapter) { + ((IProductQueryAdapter) sdkAdapter).queryProducts(productIds); + return; + } + Log.e(TAG, "queryProductsByIDs: no product query adapter"); } public static void ConsumePurchase(String token) { Log.i(TAG, "ConsumePurchase: token=" + token); - if (sdkHelper != null) { - sdkHelper.consume(token); + if (sdkAdapter instanceof IPaymentAdapter) { + ((IPaymentAdapter) sdkAdapter).consume(token); } } + public static String GetPendingPurchases() { + if (sdkAdapter instanceof IPendingPurchaseAdapter) { + return ((IPendingPurchaseAdapter) sdkAdapter).getPendingPurchases(); + } + return "[]"; + } + + public static void CancelBillingFlow() { + Log.w(TAG, "CancelBillingFlow"); +// FlexionSDKService.cancelPurchase(); + } + // ------------------------------------------------------------------------- // Init — with channel helper support // ------------------------------------------------------------------------- public static void InitSDK(Activity activity, String ch) { - channel = ChannelHelpers.CHANNEL; - ChannelHelpers.register(); - -// var builder = SDKManager.ExtendBuilder() -// .WithChannel(SDKManagerExt.CHANNEL) -// .WithInit(SDKManagerExt.GetInit()) -// .WithPay(SDKManagerExt.GetPay()) -// .WithQuery(SDKManagerExt.GetQuery()) -// .Builder() - - - + channel = ChannelEntry.CHANNEL; + sdkAdapter = ChannelEntry.createSDKAdapter(); + Log.i(TAG, "[CHANNEL-VERIFY] Java InitSDK channel=" + channel + " fromCSharp=" + ch - + " helper=" + (sdkHelper != null ? sdkHelper.getClass().getSimpleName() : "default")); + + " adapter=" + (sdkAdapter != null ? sdkAdapter.getClass().getSimpleName() : "default")); // Sync channel to C# ChannelConfig UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, "SetChannelFromNative", channel); - -// build.init(activity) - if (sdkHelper != null) { - sdkHelper.init(activity); + + if (sdkAdapter != null) { + sdkAdapter.init(activity); } else { initDefault(activity); } @@ -394,7 +409,7 @@ public class SDKManager { // ------------------------------------------------------------------------- public static void UnityKnow(String productId, String productName, String productCount, - String prodorderId, String appInfo) { + String prodorderId, String appInfo) { SDKLog.i("UnityKnow : " + productId); HashMap extraInfo = new HashMap(); extraInfo.put("appInfo", appInfo); @@ -405,8 +420,8 @@ public class SDKManager { public static void UnityKnowNew(String productId, String productPrice, String productName, String productCount, String prodorderId, String appInfo, String ptype) { - if (sdkHelper != null) { - sdkHelper.pay(UnityPlayer.currentActivity, productId, productPrice, productName, productCount, prodorderId, appInfo); + if (sdkAdapter instanceof IPaymentAdapter) { + ((IPaymentAdapter) sdkAdapter).pay(UnityPlayer.currentActivity, productId, productPrice, productName, productCount, prodorderId, appInfo); return; } PayEventData.PayData payData = new PayEventData.PayData(); @@ -430,9 +445,9 @@ public class SDKManager { // ------------------------------------------------------------------------- public static void thirdExtend() { - Log.e(TAG, "thirdExtend called, sdkHelper=" + (sdkHelper != null ? sdkHelper.getClass().getSimpleName() : "null")); - if (sdkHelper != null) { - sdkHelper.queryProducts(); + Log.e(TAG, "thirdExtend called, sdkAdapter=" + (sdkAdapter != null ? sdkAdapter.getClass().getSimpleName() : "null")); + if (sdkAdapter instanceof IProductQueryAdapter) { + ((IProductQueryAdapter) sdkAdapter).queryProducts(); return; } defaultThirdExtend(); @@ -454,7 +469,7 @@ public class SDKManager { JSONObject result = new JSONObject(); result.put("code", code); if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS) { - result.put("respObj", ChannelHelpers.normalizeSkuList(msg)); + result.put("respObj", ChannelEntry.normalizeSkuList(msg)); } else { Log.e(TAG, "thirdExtend failed: " + msg); result.put("thirdExtend_errStr", msg); @@ -520,7 +535,7 @@ public class SDKManager { case 2: GetGameGa().track(EventType.COIN, eventstr, paramsBuilder); break; case 3: GetGameGa().track(EventType.PAY, eventstr, paramsBuilder); break; case 4: GetGameGa().track(EventType.GAME, eventstr, paramsBuilder); break; - case 5: GetGameGa().track(EventType.PAY, eventstr, paramsBuilder); break; + case 5: GetGameGa().track(EventType.LOGIN, eventstr, paramsBuilder); break; case 6: GetGameGa().track(EventType.PUSH, eventstr, paramsBuilder); break; case 7: GetGameGa().track(EventType.ADBOX, eventstr, paramsBuilder); break; case 8: GetGameGa().track(EventType.PREFORMANCE, eventstr, paramsBuilder); break; diff --git a/sdk-intergration/Packages/tysdk/Runtime/PaymentVerifier.cs b/sdk-intergration/Packages/tysdk/Runtime/PaymentVerifier.cs deleted file mode 100644 index b23466d..0000000 --- a/sdk-intergration/Packages/tysdk/Runtime/PaymentVerifier.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using asap.core; -using Newtonsoft.Json; -using UnityEngine; -using UnityEngine.Networking; - -namespace tysdk -{ - public interface IPaymentVerifier - { - Task VerifyAsync(PaymentInfo info); - } - - public interface IPurchaseConsumer - { - Task ConsumeAsync(string token); - } - - public class PaymentVerifyResult - { - public bool success; - public string msg; - public string orderId; - } - - public class FlexionOrderResult - { - public int code { get; set; } - public string info { get; set; } - public string orderId { get; set; } - } - - public class PurchaseConsumeResult - { - public bool success; - public string msg; - } - - public class PaymentProcessor - { - public IPaymentVerifier verifier; - public IPurchaseConsumer consumer; - } - - public static class PaymentProcessorFactory - { - private static PaymentProcessor _processor; - private static string _cachedChannel; - - public static PaymentProcessor GetProcessor() - { - var channel = ChannelConfig.Channel; - if (_processor != null && _cachedChannel == channel) - return _processor; - - _processor = channel switch - { - "Flexion" => new PaymentProcessor - { - verifier = new RetryingPaymentVerifier(new FlexionPaymentVerifier()), - consumer = new FlexionPurchaseConsumer() - }, - "Dummy" => new PaymentProcessor - { - verifier = new RetryingPaymentVerifier(new FakePaymentVerifier()), - consumer = new FakePurchaseConsumer() - }, - _ => null - }; - _cachedChannel = channel; - return _processor; - } - } - - public class RetryingPaymentVerifier : IPaymentVerifier - { - private readonly IPaymentVerifier _inner; - private const int MaxRetries = 2; - private static readonly TimeSpan RetryInterval = TimeSpan.FromSeconds(2); - - public RetryingPaymentVerifier(IPaymentVerifier inner) - { - _inner = inner; - } - - public async Task VerifyAsync(PaymentInfo info) - { - PaymentVerifyResult lastResult = null; - for (int attempt = 0; attempt <= MaxRetries; attempt++) - { - if (attempt > 0) - { - Debug.Log($"[RetryingPaymentVerifier] Retry {attempt}/{MaxRetries}, waiting {RetryInterval.TotalSeconds}s"); - await Task.Delay(RetryInterval); - } - - lastResult = await _inner.VerifyAsync(info); - if (lastResult.success) return lastResult; - - Debug.LogWarning($"[RetryingPaymentVerifier] Attempt {attempt + 1} failed: {lastResult.msg}"); - } - return lastResult; - } - } - - public class FlexionPaymentVerifier : IPaymentVerifier - { - private const string DEFAULT_PAY_VERIFY_URL = "http://192.168.9.91:7036/api/FlexionCallback"; - - private readonly string _verifyUrl; - private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30); - - public FlexionPaymentVerifier() - { - var config = GContext.container.Resolve(); - _verifyUrl = config.Get("PAY_VERIFY_URL", DEFAULT_PAY_VERIFY_URL); - } - -#if UNITY_EDITOR - public async Task VerifyAsync(PaymentInfo info) - { - Debug.LogWarning("[FlexionPaymentVerifier] Editor placeholder: auto success"); - await Task.Yield(); - return new PaymentVerifyResult { success = true, msg = "editor_placeholder" }; - } -#else - public async Task VerifyAsync(PaymentInfo info) - { - var cts = new CancellationTokenSource(Timeout); - try - { - Debug.Log($"[FlexionPaymentVerifier] VerifyAsync start: url={_verifyUrl}"); - Debug.Log($"[FlexionPaymentVerifier] Verify fields: orderId={info.orderId} productId={info.productId}" + - $" hasPurchaseJson={!string.IsNullOrEmpty(info.purchaseJson)} hasSignature={!string.IsNullOrEmpty(info.signature)}" + - $" purchaseToken={info.purchaseToken}"); - - var body = JsonConvert.SerializeObject(info); - Debug.Log("[FlexionPaymentVerifier] Request body: " + body); - - using var req = new UnityWebRequest(_verifyUrl, "POST"); - req.certificateHandler = new BypassCertificateHandler(); - req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body)); - req.downloadHandler = new DownloadHandlerBuffer(); - req.SetRequestHeader("Content-Type", "application/json"); - - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - cts.Token.Register(() => tcs.TrySetResult(new PaymentVerifyResult { success = false, msg = "timeout" })); - - req.SendWebRequest().completed += _ => - { - if (req.result == UnityWebRequest.Result.Success) - { - var respText = req.downloadHandler.text; - Debug.Log("[FlexionPaymentVerifier] Response: " + respText); - var orderResult = JsonConvert.DeserializeObject(respText); - if (orderResult == null) - { - tcs.TrySetResult(new PaymentVerifyResult { success = false, msg = "parse_error" }); - } - else if (orderResult.code == 0) - { - tcs.TrySetResult(new PaymentVerifyResult - { success = true, msg = orderResult.info, orderId = orderResult.orderId }); - } - else if (orderResult.code == 1) - { - tcs.TrySetResult(new PaymentVerifyResult - { success = true, msg = orderResult.info, orderId = orderResult.orderId }); - } - else if (orderResult.code == 2) - { - tcs.TrySetResult(new PaymentVerifyResult - { success = true, msg = orderResult.info, orderId = orderResult.orderId }); - } - else - { - // -1: 公钥/签名/玩家验证失败, -2: 写入失败, -3: 数据解析错误 - tcs.TrySetResult(new PaymentVerifyResult - { success = false, msg = orderResult.info ?? $"code={orderResult.code}" }); - } - } - else - { - Debug.LogWarning("[FlexionPaymentVerifier] Failed: " + req.error); - tcs.TrySetResult(new PaymentVerifyResult { success = false, msg = req.error }); - } - }; - - return await tcs.Task; - } - catch (Exception e) - { - Debug.LogError("[FlexionPaymentVerifier] Exception: " + e.Message); - return new PaymentVerifyResult { success = false, msg = e.Message }; - } - finally - { - cts.Dispose(); - } - } -#endif - } - - public class FlexionPurchaseConsumer : IPurchaseConsumer - { -#if UNITY_EDITOR - public async Task ConsumeAsync(string token) - { - await Task.Yield(); - return new PurchaseConsumeResult { success = true, msg = "editor_placeholder" }; - } -#elif UNITY_ANDROID - public async Task ConsumeAsync(string token) - { - if (string.IsNullOrEmpty(token)) - return new PurchaseConsumeResult { success = false, msg = "empty_token" }; - - UnityBridgeFunc.ConsumePurchase(token); - await Task.Yield(); - return new PurchaseConsumeResult { success = true, msg = "consume_requested" }; - } -#else - public async Task ConsumeAsync(string token) - { - await Task.Yield(); - return new PurchaseConsumeResult { success = false, msg = "unsupported_platform" }; - } -#endif - } - - public class FakePaymentVerifier : IPaymentVerifier - { - public async Task VerifyAsync(PaymentInfo info) - { - Debug.Log("[FakePaymentVerifier] Verifying purchase: " + info.orderId); - await Task.Delay(500); - return new PaymentVerifyResult { success = true, msg = "fake_ok" }; - } - } - - public class FakePurchaseConsumer : IPurchaseConsumer - { - public async Task ConsumeAsync(string token) - { - Debug.Log("[FakePurchaseConsumer] Consume: " + token); - await Task.Yield(); - return new PurchaseConsumeResult { success = true, msg = "fake_ok" }; - } - } - - internal class BypassCertificateHandler : CertificateHandler - { - protected override bool ValidateCertificate(byte[] certificateData) => true; - } -} diff --git a/sdk-intergration/Packages/tysdk/Runtime/PaymentVerifier.cs.meta b/sdk-intergration/Packages/tysdk/Runtime/PaymentVerifier.cs.meta deleted file mode 100644 index 705f354..0000000 --- a/sdk-intergration/Packages/tysdk/Runtime/PaymentVerifier.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1b7f5b1ccc26bf047b00f9b79d7fa3da -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkCoreModel.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkCoreModel.cs deleted file mode 100644 index 449977f..0000000 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkCoreModel.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace tysdk -{ - // TYSdkFacade 的登录/链接/ATT 等方法要依赖这些 POCO。 - - public enum EAccoutType - { - hwGoogle, - hwFacebook, - hwGuest, - hwVkid, - Apple, - none - } - - public class LoginInfo - { - public bool isSuccess; - public int userId; - public string StrUserId => userId.ToString(); - public string msg; - public int code; - } - - public class LinkResult - { - // 0: success, 1: fail, -1 exists - public int code; - // when success and fail userId is current user - // when exists userId is 0 - public int userId; - // not none when exists - public Dictionary bindInfo; - } - - public class ChangeLinkResult - { - public int code; - public int userId; - public string msg; - } - - public class LinkCheckInfo - { - public int code; - } - - public class ATTInfo - { - public bool isAccepted; - } -} diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkCoreModel.cs.meta b/sdk-intergration/Packages/tysdk/Runtime/TYSdkCoreModel.cs.meta deleted file mode 100644 index d907638..0000000 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkCoreModel.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2d36f2094aa5783423ab1ef40a88ccbe -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade.cs index bed082e..1253bac 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade.cs @@ -34,20 +34,13 @@ namespace tysdk } } - private static Dictionary accoutTypeMap = new Dictionary() - { + private static Dictionary accoutTypeMap = new Dictionary(){ {"google", EAccoutType.hwGoogle}, {"fb", EAccoutType.hwFacebook}, {"tyGuest", EAccoutType.hwGuest}, - {"ios13", EAccoutType.Apple}, - {"vk.global.app", EAccoutType.hwVkid}, + {"ios13", EAccoutType.Apple} }; - public static void AddAccoutType(string key, EAccoutType value) - { - accoutTypeMap[key] = value; - } - private static EAccoutType ConvertAccoutType(string accountType) { if (accoutTypeMap.ContainsKey(accountType)) @@ -108,10 +101,12 @@ namespace tysdk } } - // ================== Singleton ================== - private static TYSdkFacade _instance; + private static AccountInfo _accountInfo; public static AccountInfo TYAccountInfo => _accountInfo; - protected static AccountInfo _accountInfo; + + private static TYSdkFacade _instance; + public static bool IsSdkInited { get; private set; } + public static event System.Action OnInitComplete; public static TYSdkFacade Instance { @@ -125,47 +120,55 @@ namespace tysdk return _instance; } } - - public static string Channel => ChannelConfig.Channel; - - private int LoginTimeoutSeconds => ChannelConfig.LoginTimeoutSeconds; - - public static bool IsSdkInited { get; private set; } - public static event System.Action OnInitComplete; - - // ================== Init ================== public void Init() { + Debug.Log($"[TYSdkFacade] Init()"); #if UNITY_ANDROID - UnityBridgeFunc.InitSDK(); + UnityBridgeFunc.InitSDK(ChannelConfig.Channel); #endif + + } + + public void SetChannelFromNative(string channel) + { + Debug.Log($"[TYSdkFacade] SetChannelFromNative: {channel}"); + ChannelConfig.SetChannel(channel); } public void InitResult(string json) { - var data = Newtonsoft.Json.Linq.JObject.Parse(json); - var code = (int)data["code"]; - var msg = (string)data["msg"]; - IsSdkInited = code == 0; - UnityEngine.Debug.Log($"[TYSdkFacade] InitResult: code={code} msg={msg}"); - OnInitComplete?.Invoke(IsSdkInited, msg); + Debug.Log($"[TYSdkFacade] InitResult: {json}"); + var success = false; + var msg = string.Empty; + + try + { + var data = JObject.Parse(json); + success = (int)data["code"] == 0; + msg = (string)data["msg"]; + } + catch (Exception e) + { + msg = e.Message; + Debug.LogWarning($"[TYSdkFacade] InitResult parse failed: {e}"); + } + + IsSdkInited = success; + OnInitComplete?.Invoke(success, msg); } - // Called from Java SDKManager.InitSDK to sync channel - public void SetChannelFromNative(string ch) - { - ChannelConfig.SetChannel(ch); - UnityEngine.Debug.Log($"[CHANNEL-VERIFY] C# SetChannelFromNative → channel={ch}, payType={ChannelConfig.PayType}, loginTimeout={ChannelConfig.LoginTimeoutSeconds}"); - } /*================================================ + _ _ / \ ___ ___ ___ _ _ _ __ | |_ / _ \ / __/ __/ _ \| | | | '_ \| __| / ___ \ (_| (_| (_) | |_| | | | | |_ /_/ \_\___\___\___/ \__,_|_| |_|\__| + =================================================*/ + public static bool IsLoggedIn => _accountInfo != null; public void Logout() @@ -182,7 +185,7 @@ namespace tysdk public async Task Login(EAccoutType accoutType) { -#if UNITY_EDITOR +#if UNITY_EDITOR await Task.Yield(); var deviceId = UnityEngine.Device.SystemInfo.deviceUniqueIdentifier.Split('-')[0].Substring(0, 8); @@ -200,25 +203,25 @@ namespace tysdk }; #elif UNITY_ANDROID || UNITY_IOS - var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromSeconds(LoginTimeoutSeconds)); + var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromSeconds(30)); UnityBridgeFunc.UnityLogin(accoutType); try { - var loginInfo = await task; - if (loginInfo.isSuccess) - { - _accountInfo.channel = accoutType; - _accountInfo.AddLinkedAccount(accoutType); - _accountInfo.Save(); - } - return loginInfo; + var loginInfo = await task; + if(loginInfo.isSuccess) + { + _accountInfo.channel = accoutType; + _accountInfo.AddLinkedAccount(accoutType); + _accountInfo.Save(); + } + return loginInfo; } - catch (Exception e) + catch(Exception e) { - Debug.LogWarning($"[TYSdkFacade] Login failed: {e}"); - return new LoginInfo() { isSuccess = false }; + Debug.LogWarning($"[TYSdkFacade] Login failed: {e}"); + return new LoginInfo(){isSuccess = false}; } #endif } @@ -247,7 +250,7 @@ namespace tysdk return new LoginInfo() { isSuccess = false }; } } - + public async Task LoginBySns() { Debug.Log("[TYSdkFacade] LoginBySns"); @@ -332,17 +335,18 @@ namespace tysdk UnityBridgeFunc.SetGaUserInfo(strUserId); } + #if UNITY_EDITOR public async Task LinkAccount(EAccoutType accoutType) { await Task.Delay(300); - return new LinkResult() { code = 1 }; + return new LinkResult(){code = 1}; } #elif UNITY_IOS || UNITY_ANDROID - public async Task LinkAccount(EAccoutType accoutType) + public async Task LinkAccount(EAccoutType accoutType) { - if (_accountInfo == null) return new LinkResult() { code = 1 }; - if (_accountInfo.linkedAccout.Contains(accoutType)) return new LinkResult() { code = 0 }; + if (_accountInfo == null) return new LinkResult(){ code = 1}; + if (_accountInfo.linkedAccout.Contains(accoutType)) return new LinkResult(){ code = 0}; var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromMinutes(1)); UnityBridgeFunc.LinkAccount(accoutType); @@ -364,7 +368,6 @@ namespace tysdk return new LinkResult() { code = 1 }; } } -#endif public void LinkAccoutResult(string message) { @@ -387,18 +390,21 @@ namespace tysdk } TYSDKCallbackManager.Instance.TryTriggerCallback(result); } - +#endif public void CleanLinkTempSnSInfo() { UnityBridgeFunc.CleanLinkTmpSnsInfo(); } +#if UNITY_EDITOR public async Task ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) { -#if UNITY_EDITOR await Task.Delay(300); - return new ChangeLinkResult() { code = 1 }; -#else + return new ChangeLinkResult(){code = 1}; + } +#elif UNITY_IOS || UNITY_ANDROID + public async Task ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) + { var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromSeconds(30)); UnityBridgeFunc.ChangeLinkAccount(accoutType, oldUserId, newUserId); try @@ -420,8 +426,8 @@ namespace tysdk { CleanLinkTempSnSInfo(); } -#endif } +#endif public void ChangeLinkAccountResult(string message) { @@ -435,6 +441,14 @@ namespace tysdk TYSDKCallbackManager.Instance.TryTriggerCallback(result); } + /// + /// 检查是否已经绑定 + /// + /// 1 已经绑定 + /// 0 未绑定 + /// -1 失败 + /// + /// public async Task LinkCheck(EAccoutType accoutType) { if (_accountInfo == null) return false; @@ -460,7 +474,7 @@ namespace tysdk return result.code == 1; } - catch (Exception e) + catch(Exception e) { Debug.LogWarning($"[TYSdkFacade] CheckLinkResult error: {e}"); return false; @@ -482,6 +496,7 @@ namespace tysdk IConfig config = GContext.container.Resolve(); var tyurl = config.Get("AGG_SERVER", defaultTyurl); + string tycs = tyurl == defaultTyurl ? "https://hwcsh.tygameworld.com/template/appCancel/note.html" : @@ -503,12 +518,23 @@ namespace tysdk string uid = $"uid={_accountInfo.userId}"; list.Add(uid); - list.Add("roleid=0"); - list.Add("gameid=20587"); - list.Add("cloudid=128"); - list.Add("gamename=FishingTravel"); - list.Add("certification=0"); - list.Add("ischannel=1"); + string roleid = "roleid=0"; + list.Add(roleid); + + string gameid = "gameid=20587"; + list.Add(gameid); + + string cloudid = "cloudid=128"; + list.Add(cloudid); + + string gamename = "gamename=FishingTravel"; + list.Add(gamename); + + string certification = "certification=0"; + list.Add(certification); + + string ischannel = "ischannel=1"; + list.Add(ischannel); string tysdktoken = $"tysdktoken={_accountInfo.token}"; list.Add(tysdktoken); @@ -518,6 +544,7 @@ namespace tysdk string listStr = string.Join("&", list.ToArray()); string sign = listStr + "csh-api-6dfa879490a249be9fbc92e97e4d898d-api-csh"; + //对sign进行MD5加密 string signMd5 = CalculateMD5Hash(sign); string url = $"{tycs}?{listStr}&sign={signMd5}&isAbroad=1&lan=en"; Debug.Log($"[TYSdkFacade:Signout] url \n{url}"); @@ -526,16 +553,25 @@ namespace tysdk private string CalculateMD5Hash(string input) { + // Create a new instance of the MD5CryptoServiceProvider object. MD5 md5Hasher = MD5.Create(); + + // Convert the input string to a byte array and compute the hash. byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input)); + // Create a new Stringbuilder to collect the bytes + // and create a string. StringBuilder sBuilder = new StringBuilder(); + + // Loop through each byte of the hashed data + // and format each one as a hexadecimal string. for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("x2")); } + + // Return the hexadecimal string. return sBuilder.ToString(); } } - } diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_ATT.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_ATT.cs index 5d4c0c6..ab20568 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_ATT.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_ATT.cs @@ -4,17 +4,17 @@ using UnityEngine; namespace tysdk { - public partial class TYSdkFacade + public partial class TYSdkFacade : MonoBehaviour { /*================================================ - _ _____ _____ + _ _____ _____ / \|_ _|_ _| - / _ \ | | | | - / ___ \| | | | - /_/ \_\_| |_| - - =================================================*/ + / _ \ | | | | + / ___ \| | | | + /_/ \_\_| |_| + + =================================================*/ public bool IsAttAccepted() { @@ -25,25 +25,25 @@ namespace tysdk { #if UNITY_EDITOR || UNITY_ANDROID await Task.Yield(); - return new ATTInfo() { isAccepted = true }; + + return new ATTInfo(){isAccepted = true}; #elif UNITY_IOS - var task = TYSDKCallbackManager.Instance.RegisterCallback(); + var task = tysdk.TYSDKCallbackManager.Instance.RegisterCallback(); UnityBridgeFunc.RequestATT(); - try - { - return await task; + try{ + return await task; } - catch (Exception e) + catch(Exception e) { - Debug.LogWarning(e.Message); - return new ATTInfo() { isAccepted = false }; + Debug.LogWarning(e.Message); + return new ATTInfo(){isAccepted = false}; } #endif } public void RequestATTResult(string r) { - TYSDKCallbackManager.Instance.TryTriggerCallback(new ATTInfo() { isAccepted = r == "1" }); + TYSDKCallbackManager.Instance.TryTriggerCallback(new ATTInfo(){isAccepted = r == "1"}); } } } diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_GA.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_GA.cs index 74d4128..dfc9de6 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_GA.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_GA.cs @@ -2,14 +2,14 @@ using UnityEngine; namespace tysdk { - public partial class TYSdkFacade + public partial class TYSdkFacade : MonoBehaviour { /*================================================ - _____ _ _____ _ + _____ _ _____ _ | ____|_ _____ _ __ | |_ |_ _| __ __ _ ___| | __ | _| \ \ / / _ \ '_ \| __| | || '__/ _` |/ __| |/ / - | |___ \ V / __/ | | | |_ | || | | (_| | (__| < + | |___ \ V / __/ | | | |_ | || | | (_| | (__| < |_____| \_/ \___|_| |_|\__| |_||_| \__,_|\___|_|\_\ =================================================*/ @@ -21,18 +21,18 @@ namespace tysdk #endif } } - + public enum GATrackType { - GA_TRACK = 1, - GA_CION = 2, - GA_PAY = 3, - GA_GAME = 4, - GA_LOGIN = 5, - GA_PUSH = 6, - GA_ADBOX = 7, - GA_PREFORMANCE = 8, - GA_SDK = 9, - GA_ABTest = 10, + GA_TRACK = 1, //默认类型 + GA_CION = 2, //金流相关事件 + GA_PAY = 3, //支付相关事件 + GA_GAME = 4, //游戏行为 + GA_LOGIN = 5, //登录注册相关事件 + GA_PUSH = 6, //推送相关事件 + GA_ADBOX = 7, //adbox相关事件 + GA_PREFORMANCE = 8, //性能上报相关事件 + GA_SDK = 9, //SDK相关事件 + GA_ABTest = 10, //abtest相关事件 } } diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs index ad07fb6..9231c26 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs @@ -8,30 +8,33 @@ using System.Text; namespace tysdk { - public partial class TYSdkFacade + public partial class TYSdkFacade : MonoBehaviour { /*================================================ - ____ _ - | _ \ __ _ _ _ _ __ ___ ___ _ __ | |_ + ____ _ + | _ \ __ _ _ _ _ __ ___ ___ _ __ | |_ | |_) / _` | | | | '_ ` _ \ / _ \ '_ \| __| - | __/ (_| | |_| | | | | | | __/ | | | |_ + | __/ (_| | |_| | | | | | | __/ | | | |_ |_| \__,_|\__, |_| |_| |_|\___|_| |_|\__| - |___/ + |___/ =================================================*/ private bool _payFirstNegativeHandled; + //public async Task Pay(string prodId, string prodPrice, string prodName, int count, string pType, string price_amount_micros =null) public async Task Pay(SKUDetail prod, int count, float usdprice, JObject purchaseInfo) { if (!IsLoggedIn) return new PaymentInfo() { code = "-1", msg = "未登录" }; var orderId = System.Guid.NewGuid().ToString(); - if (purchaseInfo == null) + if(purchaseInfo == null) purchaseInfo = new JObject(); + purchaseInfo["orderId"] = orderId; string extraInfo = purchaseInfo.ToString(Newtonsoft.Json.Formatting.None); + //to base64 extraInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(extraInfo)); Debug.Log("[TYSdk] extraInfo: " + extraInfo); var prodId = prod.ProdID; @@ -59,7 +62,7 @@ namespace tysdk var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromSeconds(90)); try { - UnityBridgeFunc.UnityKnowNew(prodId, prodPrice, prodName, count.ToString(), orderId, extraInfo, pType); + UnityBridgeFunc.UnityKnowNew(prodId, prodPrice, prodName, count.ToString(), orderId, extraInfo,pType); var result = await task; result.count = count; result.orderId = orderId; @@ -103,6 +106,7 @@ namespace tysdk #endif } + //支付的回调 public void PayResult(string json) { Debug.Log("[TYSdkFacade] pay callback:" + json); @@ -110,11 +114,25 @@ namespace tysdk var result = new PaymentInfo(); result.code = jresult["code"].ToString(); result.msg = jresult["errStr"].ToString(); - result.purchaseJson = jresult["purchaseJson"]?.ToString(); - result.signature = jresult["signature"]?.ToString(); - result.purchaseToken = jresult["purchaseToken"]?.ToString(); + var purchaseJson = jresult["purchaseJson"]?.ToString(); + var signature = jresult["signature"]?.ToString(); + var purchaseToken = jresult["purchaseToken"]?.ToString(); + result.purchaseJson = purchaseJson; + result.signature = signature; + result.purchaseToken = purchaseToken; + if (!string.IsNullOrEmpty(purchaseJson) || !string.IsNullOrEmpty(purchaseToken)) + { + result.Receipt = new PaymentReceipt + { + Provider = PaymentReceiptProvider.Flexion, + PurchaseJson = purchaseJson, + Signature = signature, + PurchaseToken = purchaseToken + }; + } + result.storeOrderId = jresult["orderId"]?.ToString(); result.userId = _accountInfo?.strUserId; - if (result.code == "-1") + if(result.code == "-1") { if (!_payFirstNegativeHandled) { @@ -128,60 +146,15 @@ namespace tysdk } return; } - var processor = PaymentProcessorFactory.GetProcessor(); - if (processor?.verifier != null && !string.IsNullOrEmpty(result.purchaseJson)) - { - _VerifyPurchase(result, processor); - } - else - { - TYSDKCallbackManager.Instance.TryTriggerCallback(result); - } - } - - private async void _VerifyPurchase(PaymentInfo result, PaymentProcessor processor) - { - TYSDKCallbackManager.Instance.ResetCallbackTimeout(TimeSpan.FromSeconds(60)); - try - { - var verifyResult = await processor.verifier.VerifyAsync(result); - if (verifyResult.success) - { - result.verified = true; - if (!string.IsNullOrEmpty(verifyResult.orderId)) - result.serverOrderId = verifyResult.orderId; - if (processor.consumer != null && !string.IsNullOrEmpty(result.purchaseToken)) - { - var consumeResult = await processor.consumer.ConsumeAsync(result.purchaseToken); - if (!consumeResult.success) - { - Debug.LogWarning("[TYSdkFacade] Purchase consume failed: " + consumeResult.msg); - } - } - TYSDKCallbackManager.Instance.TryTriggerCallback(result); - } - else - { - Debug.LogWarning("[TYSdkFacade] Payment verification failed: " + verifyResult.msg); - result.code = "-1"; - result.msg = "verification_failed: " + verifyResult.msg; - TYSDKCallbackManager.Instance.TryTriggerCallback(result); - } - } - catch (Exception e) - { - Debug.LogError("[TYSdkFacade] Verification exception: " + e.Message); - result.code = "-1"; - result.msg = "verification_error: " + e.Message; - TYSDKCallbackManager.Instance.TryTriggerCallback(result); - } + TYSDKCallbackManager.Instance.TryTriggerCallback(result); } + // Get Pay list public async Task GetSKUList() { if (!IsLoggedIn) return new ProductListInfo() { code = -1, msg = "未登录" }; -#if UNITY_EDITOR +#if UNITY_EDITOR await Task.Yield(); var result = new ProductListInfo(); @@ -194,6 +167,25 @@ namespace tysdk #endif } + //通过商品ID列表获取商品详情(Flexion 渠道使用) + public async Task GetSKUListByIapIds(string productIds) + { + if (!IsLoggedIn) return new ProductListInfo() { code = -1, msg = "未登录" }; + +#if UNITY_EDITOR + await Task.Yield(); + return new ProductListInfo(); +#elif UNITY_ANDROID + var task = TYSDKCallbackManager.Instance.RegisterCallback(); + UnityBridgeFunc.GetSKUListByIapIds(productIds); + var result = await task; + return result; +#else + return new ProductListInfo() { code = -1, msg = "unsupported platform" }; +#endif + } + + //商品列表的回调 public void SKUListResult(string json) { Debug.Log("[TYSdkFacade] GetSKUList callback"); @@ -216,3 +208,7 @@ namespace tysdk } } } + + + + diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs index ec4f157..d731427 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs @@ -1,14 +1,82 @@ + using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; namespace tysdk { + public enum EAccoutType + { + hwGoogle, + hwFacebook, + hwGuest, + hwVkid, + Apple, + none + } + + public enum PaymentReceiptProvider + { + None, + Flexion + } + + public class PaymentReceipt + { + public PaymentReceiptProvider Provider; + public string PurchaseJson; + public string Signature; + public string PurchaseToken; + + public bool HasPayload => !string.IsNullOrEmpty(PurchaseJson); + public bool CanConsume => !string.IsNullOrEmpty(PurchaseToken); + } + + public class PaymentReceiptVerification + { + public bool Verified; + public string ServerOrderId; + public int ProviderCode; + public string ProviderMessage; + } + + public class LoginInfo + { + public bool isSuccess; + public int userId; + public string StrUserId => userId.ToString(); + public string msg; + public int code; + } + + public class LinkResult + { + // 0: success, 1: fail, -1 exists + public int code; + // when success and fail userId is current user + // when exists userId is 0 + public int userId; + // not none when exists + public Dictionary bindInfo; + } + + public class ChangeLinkResult + { + public int code; + public int userId; + public string msg; + } + + public class LinkCheckInfo + { + public int code; + } + public class PaymentInfo { public bool isSuccessFromSdk => code == "0"; public bool isSuccess => isSuccessFromSdk && confirmed; - private bool confirmed { get; set; } = false; + private bool confirmed {get; set;} = false; public int count; public string orderId; public string productId; @@ -17,6 +85,8 @@ namespace tysdk public string code; public string msg; + // Compatibility fields for legacy server payload and existing call sites. + // New payment flow should read receipt data from Receipt. // Flexion: purchase.getOriginalJson(), used by server for RSA signature verification public string purchaseJson; // Flexion: purchase.getSignature(), used by server for RSA signature verification @@ -24,6 +94,21 @@ namespace tysdk // Flexion: purchase token, used to consume after server verification public string purchaseToken; + [JsonIgnore] + public PaymentReceipt Receipt; + + [JsonIgnore] + public PaymentReceiptVerification ReceiptVerification; + + [JsonIgnore] + public bool HasReceipt => Receipt != null && Receipt.HasPayload; + + [JsonIgnore] + public bool CanConsumeReceipt => Receipt != null && Receipt.CanConsume; + + // Store order ID from Flexion SDK (e.g. GPA.xxx) + public string storeOrderId; + public bool verified; // Server-side order ID returned from verification @@ -36,17 +121,17 @@ namespace tysdk { confirmed = orderId == this.orderId && productId == this.productId; - if (!confirmed) + if(!confirmed) UnityEngine.Debug.LogWarning("[TYSdk Pay] order not confirmed"); return confirmed; } public override string ToString() { - return JsonConvert.SerializeObject(this); + return Newtonsoft.Json.JsonConvert.SerializeObject(this); } } - + public class ProductListInfo { public int code = 1; @@ -70,6 +155,7 @@ namespace tysdk products = prodList; } #endif + } #if UNITY_IOS @@ -90,7 +176,7 @@ namespace tysdk public override string ToString() { - return JsonConvert.SerializeObject(this); + return Newtonsoft.Json.JsonConvert.SerializeObject(this); } } #else @@ -98,24 +184,37 @@ namespace tysdk { public string description; public string ourProductId; - public string productId; public string price; public string price_amount_micros; public string price_currency_code; + public string productId; public string title; public string type; public string currency; public string price_amount; + public override string ToString() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this); + } + public string ProdPriceStr => price; public float ProdPrice => float.Parse(price_amount_micros) / 1000000; public string ProdID => ourProductId; public string ProdKey => productId; - - public override string ToString() - { - return JsonConvert.SerializeObject(this); - } } #endif + + + public class ATTInfo + { + public bool isAccepted; + } + + public class FlexionOrderResult + { + public int code { get; set; } + public string info { get; set; } + public string orderId { get; set; } + } } diff --git a/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs b/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs index 6ec331c..104d934 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs @@ -4,87 +4,96 @@ using UnityEngine; namespace tysdk { + public static class UnityBridgeFunc { -#if UNITY_EDITOR - public static void InitSDK() { } + +#if UNITY_EDITOR + //初始化sdk + public static void InitSDK(string channel){} - public static void UnityResetServerUrl(string url) { } + //更新登录支付域名 + public static void UnityResetServerUrl(string url){} - public static void UnityLoginByTokenFun(string token) { } + //登录 + public static void UnityLoginByTokenFun(string token){} public static string UnityLoginBySnsInfoFun() => null; - public static void UnityLogin(EAccoutType accoutType) { } + public static void UnityLogin(EAccoutType accoutType){} public static void CleanLinkTmpSnsInfo() { } - public static void LinkAccount(EAccoutType accoutType) { } - public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) { } - public static void LinkCheck(EAccoutType accoutType) { } - public static void UnityGetIdentityFun(string type) { } + public static void LinkAccount(EAccoutType accoutType){} + public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId){} + public static void LinkCheck(EAccoutType accoutType){} - public static void UnityLogOutByChannel(EAccoutType accoutType) { } + public static void UnityGetIdentityFun(string type){} + public static void UnityLogOutByChannel(EAccoutType accoutType){} + //支付new public static void UnityKnowNew(string productId, string productPrice, string productName, string productCount, string prodorderId, string appInfo, string pType) { } + //支付 + public static void UnityKnow(String productId, String productName, String productCount, - String prodorderId, String appInfo) { } - + String prodorderId, String appInfo) {} + + //获取商品列表 public static void GetSKUList() { } - public static void SetGaUserInfo(string userId) { } - - public static void SetGaCommonInfo(string SetGaCommonInfo) { } - - public static void GAReportParams(int type, string eventstr, string paramstr) { } - - public static void RequestATT() { } - - public static int GetATT() { return 1; } - - public static void Review() { } - public static void ConsumePurchase(string token) { } + public static string GetPendingPurchases() => "[]"; + + //打点 + public static void SetGaUserInfo(string userId){} + public static void SetGaCommonInfo(string SetGaCommonInfo){} + + public static void GAReportParams(int type, string eventstr, string paramstr){} + + //ATT + public static void RequestATT(){} + + public static int GetATT() {return 1;} + + public static void Review() { } + public static void FBShareLink(string title, string content, string url) { } public static void MessengerShareLink(string title, string content, string url) { } + + #elif UNITY_ANDROID private static string SDK_CLASS = "com.unity3d.player.SDKManager"; - private static string EAccountTypeToStr(EAccoutType accoutType) - { - return accoutType == EAccoutType.hwVkid ? "vk.global.app" : accoutType.ToString(); - } - - public static void InitSDK() - { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + //初始化sdk + public static void InitSDK(string channel){ + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - using (var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using(var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { var activity = activityCls.GetStatic("currentActivity"); - UnityEngine.Debug.Log("[CHANNEL-VERIFY] C# InitSDK → ChannelConfig.Channel=" + ChannelConfig.Channel); - sdkManager.CallStatic("InitSDK", activity, ChannelConfig.Channel); + sdkManager.CallStatic("InitSDK", activity, channel); } } } - + //更新登录支付域名 public static void UnityResetServerUrl(string url) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("UnityResetServerUrl", url); } } + //登录 public static void UnityLoginByTokenFun(string token) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("UnityLoginByTokenFun", token); } @@ -92,7 +101,7 @@ namespace tysdk public static String UnityLoginBySnsInfoFun() { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { return sdkManager.CallStatic("UnityLoginBySnsInfo"); } @@ -100,16 +109,15 @@ namespace tysdk public static void UnityLogin(EAccoutType accoutType) { - var accountInfo = EAccountTypeToStr(accoutType); - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("UnityLogin", accountInfo); + sdkManager.CallStatic("UnityLogin", accoutType.ToString()); } } public static void UnityGetIdentityFun(string type) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("UnityGetIdentityFun", type); } @@ -117,87 +125,118 @@ namespace tysdk public static void UnityLogOutByChannel(EAccoutType accoutType) { - var accountInfo = EAccountTypeToStr(accoutType); - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("UnityLogOutByChannel", accountInfo); + sdkManager.CallStatic("UnityLogOutByChannel", accoutType.ToString()); } } public static void LinkAccount(EAccoutType accoutType) { - string userId = TYSdkFacade.TYAccountInfo.strUserId; - var accountInfo = EAccountTypeToStr(accoutType); - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("LinkAccount", accountInfo, userId); + string userId = TYSdkFacade.TYAccountInfo.strUserId; + sdkManager.CallStatic("LinkAccount", accoutType.ToString(), userId); } } - public static void CleanLinkTmpSnsInfo() - { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + public static void CleanLinkTmpSnsInfo(){ + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("CleanLinkTmpSnsInfo"); + using(var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + { + sdkManager.CallStatic("CleanLinkTmpSnsInfo"); + } } } public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) { - var accountInfo = EAccountTypeToStr(accoutType); - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("ChangeLinkAccount", accountInfo, oldUserId, newUserId); + sdkManager.CallStatic("ChangeLinkAccount", accoutType.ToString(), oldUserId, newUserId); } } public static void LinkCheck(EAccoutType accoutType) { - var accountInfo = EAccountTypeToStr(accoutType); - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("LinkCheck", accountInfo); + sdkManager.CallStatic("LinkCheck", accoutType.ToString()); } } + //支付new public static void UnityKnowNew(string productId, string productPrice, string productName, - string productCount, string prodorderId, string appInfo, string pType) + string productCount, string prodorderId, string appInfo,string pType) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("UnityKnowNew", productId, productPrice, productName, - productCount, prodorderId, appInfo, pType); + productCount, prodorderId, appInfo,pType); } } - public static void ConsumePurchase(string token) + //支付 + + public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { - sdkManager.CallStatic("ConsumePurchase", token); - } - } - - public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo) - { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) - { - sdkManager.CallStatic("UnityKnow", productId, productName, productCount, prodorderId, appInfo); + sdkManager.CallStatic("UnityKnow", productId, productName, productCount, prodorderId, appInfo); } } + //获取商品列表 public static void GetSKUList() { UnityEngine.Debug.Log("UnityBridgeFunc.GetSKUList()"); - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("thirdExtend"); } } + //通过商品ID列表获取商品详情(Flexion 渠道使用) + public static void GetSKUListByIapIds(string productIds) + { + UnityEngine.Debug.Log("UnityBridgeFunc.GetSKUListByIapIds(productIds)"); + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) + { + sdkManager.CallStatic("queryProductsByIDs", productIds); + } + } + + public static void ConsumePurchase(string token) + { + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) + { + sdkManager.CallStatic("ConsumePurchase", token); + } + } + + public static string GetPendingPurchases() + { + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) + { + return sdkManager.CallStatic("GetPendingPurchases"); + } + } + + public static void CancelBillingFlow() + { + UnityEngine.Debug.Log("UnityBridgeFunc.CancelBillingFlow"); + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) + { + sdkManager.CallStatic("CancelBillingFlow"); + } + } + + //打点 public static void SetGaUserInfo(string userId) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("SetGaUserInfo", userId); } @@ -205,7 +244,7 @@ namespace tysdk public static void SetGaCommonInfo(string SetGaCommonInfo) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("SetGaCommonInfo", SetGaCommonInfo); } @@ -213,35 +252,37 @@ namespace tysdk public static void GAReportParams(int type, string eventstr, string paramstr) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("GAReportParams", type, eventstr, paramstr); } } - public static void RequestATT() { } - public static int GetATT() { return 1; } + //ATT + public static void RequestATT(){} + + public static int GetATT() {return 1;} public static void Review() { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("Review"); } } - public static void FBShareLink(string title, string content, string url) + public static void FBShareLink(string title, string content, string url) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("FBShareLink", title, content, url); } } - public static void MessengerShareLink(string title, string content, string url) + public static void MessengerShareLink(string title, string content, string url) { - using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + using(var sdkManager = new AndroidJavaClass(SDK_CLASS)) { sdkManager.CallStatic("MessengerShareLink", title, content, url); } @@ -249,9 +290,11 @@ namespace tysdk #elif UNITY_IOS + //更新登录支付域名 [DllImport("__Internal")] public static extern void UnityResetServerUrl(string url); + //登录 [DllImport("__Internal")] public static extern void UnityLoginByTokenFun(string token); @@ -267,6 +310,7 @@ namespace tysdk return string.Empty; } + // Convert C string to C# string string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptr); return result ?? string.Empty; } @@ -357,6 +401,7 @@ namespace tysdk } } + [DllImport("__Internal")] private static extern void LinkGoogle(int userId); [DllImport("__Internal")] private static extern void LinkFacebook(int userId); [DllImport("__Internal")] private static extern void LinkApple(int userId); @@ -374,25 +419,31 @@ namespace tysdk case EAccoutType.hwFacebook: IsLinkedFacebook(); break; + case EAccoutType.Apple: IsLinkedApple(); break; } } + [DllImport("__Internal")] public static extern void CleanLinkTmpSnsInfo(); [DllImport("__Internal")] private static extern void IsLinkedGoogle(); [DllImport("__Internal")] private static extern void IsLinkedFacebook(); [DllImport("__Internal")] private static extern void IsLinkedApple(); + + //支付 [DllImport("__Internal")] public static extern void UnityKnow(string userId, string productId, string productPrice, string productName, string productCount, string prodorderId, string appInfo); + //获取商品列表 [DllImport("__Internal")] public static extern void GetSKUList(); + //打点 [DllImport("__Internal")] public static extern void SetGaUserInfo(string userId); @@ -402,6 +453,7 @@ namespace tysdk [DllImport("__Internal")] public static extern void GAReportParams(int type, string eventstr, string paramstr); + //ATT [DllImport("__Internal")] public static extern void RequestATT(); @@ -414,6 +466,10 @@ namespace tysdk [DllImport("__Internal")] public static extern void MessengerShareLink(string title, string content, string url); + public static void ConsumePurchase(string token) { } + + public static string GetPendingPurchases() => "[]"; + #endif } } diff --git a/sdk-intergration/ProjectSettings/BurstAotSettings_StandaloneWindows.json b/sdk-intergration/ProjectSettings/BurstAotSettings_StandaloneWindows.json index 2144f6d..34712b4 100644 --- a/sdk-intergration/ProjectSettings/BurstAotSettings_StandaloneWindows.json +++ b/sdk-intergration/ProjectSettings/BurstAotSettings_StandaloneWindows.json @@ -1,16 +1,18 @@ { "MonoBehaviour": { - "Version": 3, + "Version": 5, "EnableBurstCompilation": true, "EnableOptimisations": true, "EnableSafetyChecks": false, "EnableDebugInAllBuilds": false, - "UsePlatformSDKLinker": false, + "DebugDataKind": 0, + "EnableArmv9SecurityFeatures": false, "CpuMinTargetX32": 0, "CpuMaxTargetX32": 0, "CpuMinTargetX64": 0, "CpuMaxTargetX64": 0, "CpuTargetsX32": 6, - "CpuTargetsX64": 72 + "CpuTargetsX64": 72, + "OptimizeFor": 0 } } diff --git a/sdk-intergration/ProjectSettings/ProjectSettings.asset b/sdk-intergration/ProjectSettings/ProjectSettings.asset index 6bde347..9b4a344 100644 --- a/sdk-intergration/ProjectSettings/ProjectSettings.asset +++ b/sdk-intergration/ProjectSettings/ProjectSettings.asset @@ -163,7 +163,7 @@ PlayerSettings: androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 applicationIdentifier: - Android: com.arkgame.ft + Android: com.arkgame.ft.flexion Standalone: com.Unity-Technologies.com.unity.template.urp-blank iPhone: com.arkgame.ft buildNumber: @@ -932,4 +932,4 @@ PlayerSettings: hmiLoadingImage: {fileID: 0} platformRequiresReadableAssets: 0 virtualTexturingSupportEnabled: 0 - insecureHttpOption: 0 + insecureHttpOption: 2