diff --git a/ExtraPluginCfgs/Android/flexion/AndroidManifest.xml b/ExtraPluginCfgs/Android/flexion/AndroidManifest.xml index 567a8f2..16ca0ce 100644 --- a/ExtraPluginCfgs/Android/flexion/AndroidManifest.xml +++ b/ExtraPluginCfgs/Android/flexion/AndroidManifest.xml @@ -54,12 +54,13 @@ 追加 ${applicationId}(Flexion 包名)作为后缀后,与 GP 版 authority 不同, 两个渠道包可同时安装在同一设备上,不会触发 INSTALL_FAILED_CONFLICTING_PROVIDER。 参考:Flexion 官方文档《Unique package name - follow-on requirements》 - --> + + --> diff --git a/ExtraPluginCfgs/Android/flexion/ChannelHelpers.java b/ExtraPluginCfgs/Android/flexion/ChannelHelpers.java index 000d512..0111209 100644 --- a/ExtraPluginCfgs/Android/flexion/ChannelHelpers.java +++ b/ExtraPluginCfgs/Android/flexion/ChannelHelpers.java @@ -5,7 +5,7 @@ public class ChannelHelpers { public static final String CHANNEL = "Flexion"; static void register() { - SDKManager.registerHelper(CHANNEL, new FlexionSDKHelper()); + SDKManager.registerHelper(new FlexionSDKHelper()); } @@ -14,4 +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/FlexionSDKHelper.java index 1da70de..698df06 100644 --- a/ExtraPluginCfgs/Android/flexion/FlexionSDKHelper.java +++ b/ExtraPluginCfgs/Android/flexion/FlexionSDKHelper.java @@ -25,6 +25,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.Base64; import java.util.List; public class FlexionSDKHelper implements SDKManager.ISDKHelper { @@ -33,12 +34,18 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { private static boolean billingConnected = false; private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> { + + + Log.i(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) { - sendPayCallback(1, "user_cancelled", null, null); + Log.i(TAG, "[PAY-FLOW] user cancelled"); + sendPayCallback(1, "user_cancelled", null, null, null); } else { - sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null); + Log.e(TAG, "[PAY-FLOW] purchase failed: code=" + billingResult.getResponseCode()); + sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null); } }; @@ -83,19 +90,36 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { @Override public void pay(Activity activity, String productId, String productName, String productCount, String prodorderId, String appInfo) { - Log.i(TAG, "pay: " + productId); + Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected); if (billingService == null || !billingConnected) { - sendPayCallback(1, "billing_not_ready", null, null); + Log.e(TAG, "[PAY-FLOW] billing not ready"); + sendPayCallback(1, "billing_not_ready", null, null, null); return; } - activity.runOnUiThread(() -> - billingService.launchBillingFlow(activity, - new BillingFlowParams(productId, "inapp", ""))); + String developerPayload = ""; + try { + byte[] decoded = Base64.getDecoder().decode(appInfo); + JSONObject json = new JSONObject(new String(decoded, "UTF-8")); + JSONObject payload = new JSONObject(); + payload.put("playerId", json.optString("playerId", "")); + payload.put("customData", json.optString("customData", "")); + developerPayload = payload.toString(); + } catch (Exception e) { + Log.w(TAG, "Failed to extract playerId/customData from appInfo: " + e.getMessage()); + } + String finalPayload = developerPayload; + activity.runOnUiThread(() -> { + Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload); + billingService.launchBillingFlow(activity, + new BillingFlowParams(productId, "inapp", finalPayload)); + }); } @Override public void queryProducts() { + Log.i(TAG, "queryProducts called, billingConnected=" + billingConnected); if (billingService == null || !billingConnected) { + Log.w(TAG, "queryProducts: billing not ready"); sendExtensionError("billing_not_ready"); return; } @@ -108,6 +132,7 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { 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); @@ -117,6 +142,7 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { 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()); @@ -131,10 +157,12 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { // ---- Billing Infrastructure ---- private static void initBillingService(Activity activity) { + Log.i(TAG, "[PAY-FLOW] initBillingService"); billingService = FLX.createBillingService(activity, purchasesListener); billingService.startConnection(activity, new ConnectionStateListener() { @Override 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(); @@ -142,20 +170,22 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { } @Override public void onBillingServiceDisconnected() { + Log.w(TAG, "[PAY-FLOW] billing service disconnected"); billingConnected = false; } }); } private static void handlePurchase(Purchase purchase) { + Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState() + + " token=" + purchase.getToken()); if (purchase.getPurchaseState() == 0) { - sendPayCallback(0, "success", purchase.getPurchaseJson(), purchase.getSignature()); - billingService.consumeAsync( - new ConsumeParams(purchase.getToken()), - r -> Log.i(TAG, "Consume " + - (r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); + Log.i(TAG, "[PAY-FLOW] purchaseJson=" + purchase.getPurchaseJson()); + Log.i(TAG, "[PAY-FLOW] signature=" + purchase.getSignature()); + sendPayCallback(0, "success", purchase.getPurchaseJson(), purchase.getSignature(), purchase.getToken()); } else if (purchase.getPurchaseState() == 2) { - sendPayCallback(2, "pending", null, null); + Log.i(TAG, "[PAY-FLOW] purchase state=2 (pending)"); + sendPayCallback(2, "pending", null, null, null); } } @@ -207,13 +237,19 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { // ---- Callbacks to Unity ---- - private static void sendPayCallback(int code, String errStr, String purchaseJson, String signature) { + private static void sendPayCallback(int code, String errStr, + String purchaseJson, String signature, String purchaseToken) { + Log.i(TAG, "[PAY-FLOW] sendPayCallback: code=" + code + " errStr=" + errStr + + " 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 (purchaseJson != null) result.put("purchaseJson", purchaseJson); if (signature != null) result.put("signature", signature); + if (purchaseToken != null) result.put("purchaseToken", purchaseToken); + Log.i(TAG, "[PAY-FLOW] UnitySendMessage callback: " + result.toString()); UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_CALLBACK_FUNCTION_NAME, result.toString()); } catch (JSONException e) { @@ -222,6 +258,18 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { } } + public static void consumePurchase(String token) { + if (billingService == null || token == null || token.isEmpty()) { + Log.w(TAG, "[PAY-FLOW] consumePurchase: billingService or token is null"); + return; + } + Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token); + billingService.consumeAsync( + new ConsumeParams(token), + r -> Log.i(TAG, "[PAY-FLOW] consume result: " + + (r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); + } + private static void sendExtensionError(String errStr) { try { JSONObject result = new JSONObject(); diff --git a/ExtraPluginCfgs/Android/flexion/Plugins/Android/tuyoosdk_1.0.0.aar b/ExtraPluginCfgs/Android/flexion/Plugins/Android/tuyoosdk_1.0.0.aar index b16d93c..2fcfaf0 100644 Binary files a/ExtraPluginCfgs/Android/flexion/Plugins/Android/tuyoosdk_1.0.0.aar and b/ExtraPluginCfgs/Android/flexion/Plugins/Android/tuyoosdk_1.0.0.aar differ diff --git a/sdk-intergration/Assets/Plugins/Android/AndroidManifest.xml b/sdk-intergration/Assets/Plugins/Android/AndroidManifest.xml index ebd9b2e..4a20bb8 100644 --- a/sdk-intergration/Assets/Plugins/Android/AndroidManifest.xml +++ b/sdk-intergration/Assets/Plugins/Android/AndroidManifest.xml @@ -1,48 +1,52 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle b/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle index 0505686..54696e0 100644 --- a/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle +++ b/sdk-intergration/Assets/Plugins/Android/mainTemplate.gradle @@ -82,9 +82,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/Assets/Scenes/SampleScene.unity b/sdk-intergration/Assets/Scenes/SampleScene.unity index 50dc601..b521c8b 100644 --- a/sdk-intergration/Assets/Scenes/SampleScene.unity +++ b/sdk-intergration/Assets/Scenes/SampleScene.unity @@ -1964,6 +1964,85 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] +--- !u!1 &783615086 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 783615087} + - component: {fileID: 783615089} + - component: {fileID: 783615088} + m_Layer: 5 + m_Name: Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &783615087 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 783615086} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1451597222} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0, y: -57} + m_SizeDelta: {x: 294.3188, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &783615088 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 783615086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.8207547, g: 0.8207547, b: 0.8207547, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 27 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 61 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: GooglePlay +--- !u!222 &783615089 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 783615086} + m_CullTransparentMesh: 1 --- !u!1 &816060983 GameObject: m_ObjectHideFlags: 0 @@ -4263,6 +4342,7 @@ RectTransform: m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: + - {fileID: 783615087} - {fileID: 2070325009} - {fileID: 1044920373} m_Father: {fileID: 0} @@ -4968,6 +5048,7 @@ MonoBehaviour: _mfbShareLinkBtn: {fileID: 1240291501} _reviewBtn: {fileID: 678940591} _messageTxt: {fileID: 2070325010} + _titleText: {fileID: 783615088} --- !u!4 &1788702880 Transform: m_ObjectHideFlags: 0 @@ -5417,7 +5498,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} - m_AnchoredPosition: {x: 0, y: -120} + m_AnchoredPosition: {x: 0, y: -152} m_SizeDelta: {x: 800, y: 100} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2070325010 diff --git a/sdk-intergration/Assets/Scripts/SDKTest.cs b/sdk-intergration/Assets/Scripts/SDKTest.cs index 2eb32ce..acfbc82 100644 --- a/sdk-intergration/Assets/Scripts/SDKTest.cs +++ b/sdk-intergration/Assets/Scripts/SDKTest.cs @@ -62,6 +62,9 @@ public class SDKTest : MonoBehaviour [SerializeField] private Text _messageTxt; + + [SerializeField] + private Text _titleText; void Start() { @@ -71,6 +74,7 @@ public class SDKTest : MonoBehaviour GContext.container.RegisterInstance(config); Debug.Log("SDKTest Start"); + UpdateChannelTitle(); //=============================================================================== //====================== Test Server =========================== // UnityBridgeFunc.UnityResetServerUrl("https://128-hwsfsdk-sdk-test01.qijihdhk.com"); @@ -130,6 +134,18 @@ public class SDKTest : MonoBehaviour UnityBridgeFunc.UnityResetServerUrl("https://128-hwsfsdk-sdk-test01.qijihdhk.com"); } + private void UpdateChannelTitle() + { +#if UNITY_ANDROID + using (var cls = new AndroidJavaClass("com.unity3d.player.ChannelHelpers")) + { + string channel = cls.GetStatic("CHANNEL"); + Debug.Log($"[SDKTest] Channel from Java: {channel}"); + _titleText.text = $" {channel}"; + } +#endif + } + int retryAttempt; public void InitializeRewardedAds() @@ -161,6 +177,7 @@ public class SDKTest : MonoBehaviour _messageTxt.text = $"Login: {info.isSuccess}"; if (info.isSuccess) { + _messageTxt.text += $" UserId:{info.userId}"; _messageTxt.text += $"\n{info.msg}"; _messageTxt.text += $"\n{TYSdkFacade.TYAccountInfo.token}"; } @@ -187,10 +204,23 @@ public class SDKTest : MonoBehaviour public async void OnPayTestBtn() { _messageTxt.text = ""; + + if (ChannelConfig.Channel.Equals("Flexion")) + { + sku = new SKUDetail + { + productId = "com.arkgame.ft.pack0199", + ourProductId = "com.arkgame.ft.pack0199", + price = "1.99", + price_amount_micros = "1990000", + title = "Flexion Test Pack", + type = "inapp" + }; + } if (sku == null){ _messageTxt.text = "no product"; return; - } + } #if !UNITY_EDITOR && UNITY_IOS sku.productId = "comarkgameftpack0199"; @@ -201,7 +231,7 @@ public class SDKTest : MonoBehaviour var purchaseInfo = new JObject(); purchaseInfo["pfid"] = "TestPlayer"; purchaseInfo["customData"] = "{\"test\":\"test\"}"; - + purchaseInfo["playerId"] = TYSdkFacade.TYAccountInfo.strUserId; PaymentInfo payResult = await TYSdkFacade.Instance.Pay(sku, 1, sku.ProdPrice, purchaseInfo); _messageTxt.text = "\n" + $"Success : {payResult.isSuccess}"; @@ -220,6 +250,7 @@ public class SDKTest : MonoBehaviour _messageTxt.text = "products count:::" + result.products.Count; _messageTxt.text += "\n" + result.products[0].ToString(); sku = result.products[0]; + Debug.Log($"Sku -> {sku}"); } else { diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java b/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java index 000d512..0111209 100644 --- a/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java +++ b/sdk-intergration/Packages/tysdk/Plugins/Android/ChannelHelpers.java @@ -5,7 +5,7 @@ public class ChannelHelpers { public static final String CHANNEL = "Flexion"; static void register() { - SDKManager.registerHelper(CHANNEL, new FlexionSDKHelper()); + SDKManager.registerHelper(new FlexionSDKHelper()); } @@ -14,4 +14,7 @@ public class ChannelHelpers { public static String normalizeSkuList(String msg) { return msg; } + + + //public SDKExtBuilder; } diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/FlexionSDKHelper.java b/sdk-intergration/Packages/tysdk/Plugins/Android/FlexionSDKHelper.java index 1da70de..698df06 100644 --- a/sdk-intergration/Packages/tysdk/Plugins/Android/FlexionSDKHelper.java +++ b/sdk-intergration/Packages/tysdk/Plugins/Android/FlexionSDKHelper.java @@ -25,6 +25,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.Base64; import java.util.List; public class FlexionSDKHelper implements SDKManager.ISDKHelper { @@ -33,12 +34,18 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { private static boolean billingConnected = false; private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> { + + + Log.i(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) { - sendPayCallback(1, "user_cancelled", null, null); + Log.i(TAG, "[PAY-FLOW] user cancelled"); + sendPayCallback(1, "user_cancelled", null, null, null); } else { - sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null); + Log.e(TAG, "[PAY-FLOW] purchase failed: code=" + billingResult.getResponseCode()); + sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null); } }; @@ -83,19 +90,36 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { @Override public void pay(Activity activity, String productId, String productName, String productCount, String prodorderId, String appInfo) { - Log.i(TAG, "pay: " + productId); + Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected); if (billingService == null || !billingConnected) { - sendPayCallback(1, "billing_not_ready", null, null); + Log.e(TAG, "[PAY-FLOW] billing not ready"); + sendPayCallback(1, "billing_not_ready", null, null, null); return; } - activity.runOnUiThread(() -> - billingService.launchBillingFlow(activity, - new BillingFlowParams(productId, "inapp", ""))); + String developerPayload = ""; + try { + byte[] decoded = Base64.getDecoder().decode(appInfo); + JSONObject json = new JSONObject(new String(decoded, "UTF-8")); + JSONObject payload = new JSONObject(); + payload.put("playerId", json.optString("playerId", "")); + payload.put("customData", json.optString("customData", "")); + developerPayload = payload.toString(); + } catch (Exception e) { + Log.w(TAG, "Failed to extract playerId/customData from appInfo: " + e.getMessage()); + } + String finalPayload = developerPayload; + activity.runOnUiThread(() -> { + Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload); + billingService.launchBillingFlow(activity, + new BillingFlowParams(productId, "inapp", finalPayload)); + }); } @Override public void queryProducts() { + Log.i(TAG, "queryProducts called, billingConnected=" + billingConnected); if (billingService == null || !billingConnected) { + Log.w(TAG, "queryProducts: billing not ready"); sendExtensionError("billing_not_ready"); return; } @@ -108,6 +132,7 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { 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); @@ -117,6 +142,7 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { 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()); @@ -131,10 +157,12 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { // ---- Billing Infrastructure ---- private static void initBillingService(Activity activity) { + Log.i(TAG, "[PAY-FLOW] initBillingService"); billingService = FLX.createBillingService(activity, purchasesListener); billingService.startConnection(activity, new ConnectionStateListener() { @Override 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(); @@ -142,20 +170,22 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { } @Override public void onBillingServiceDisconnected() { + Log.w(TAG, "[PAY-FLOW] billing service disconnected"); billingConnected = false; } }); } private static void handlePurchase(Purchase purchase) { + Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState() + + " token=" + purchase.getToken()); if (purchase.getPurchaseState() == 0) { - sendPayCallback(0, "success", purchase.getPurchaseJson(), purchase.getSignature()); - billingService.consumeAsync( - new ConsumeParams(purchase.getToken()), - r -> Log.i(TAG, "Consume " + - (r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); + Log.i(TAG, "[PAY-FLOW] purchaseJson=" + purchase.getPurchaseJson()); + Log.i(TAG, "[PAY-FLOW] signature=" + purchase.getSignature()); + sendPayCallback(0, "success", purchase.getPurchaseJson(), purchase.getSignature(), purchase.getToken()); } else if (purchase.getPurchaseState() == 2) { - sendPayCallback(2, "pending", null, null); + Log.i(TAG, "[PAY-FLOW] purchase state=2 (pending)"); + sendPayCallback(2, "pending", null, null, null); } } @@ -207,13 +237,19 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { // ---- Callbacks to Unity ---- - private static void sendPayCallback(int code, String errStr, String purchaseJson, String signature) { + private static void sendPayCallback(int code, String errStr, + String purchaseJson, String signature, String purchaseToken) { + Log.i(TAG, "[PAY-FLOW] sendPayCallback: code=" + code + " errStr=" + errStr + + " 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 (purchaseJson != null) result.put("purchaseJson", purchaseJson); if (signature != null) result.put("signature", signature); + if (purchaseToken != null) result.put("purchaseToken", purchaseToken); + Log.i(TAG, "[PAY-FLOW] UnitySendMessage callback: " + result.toString()); UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_CALLBACK_FUNCTION_NAME, result.toString()); } catch (JSONException e) { @@ -222,6 +258,18 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper { } } + public static void consumePurchase(String token) { + if (billingService == null || token == null || token.isEmpty()) { + Log.w(TAG, "[PAY-FLOW] consumePurchase: billingService or token is null"); + return; + } + Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token); + billingService.consumeAsync( + new ConsumeParams(token), + r -> Log.i(TAG, "[PAY-FLOW] consume result: " + + (r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); + } + private static void sendExtensionError(String errStr) { try { JSONObject result = new JSONObject(); diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java b/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java index c6065f6..2cc9cba 100644 --- a/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java +++ b/sdk-intergration/Packages/tysdk/Plugins/Android/SDKManager.java @@ -57,19 +57,26 @@ public class SDKManager { private static SnsInfo tmpSnsInfo; private static String channel = "GooglePlay"; - // Channel helper registry + // Channel helper public interface ISDKHelper { void init(Activity activity); void pay(Activity activity, String productId, String productName, String productCount, String prodorderId, String appInfo); void queryProducts(); + void consume(String token); } - private static final java.util.Map helperMap = new java.util.HashMap<>(); private static ISDKHelper sdkHelper; - public static void registerHelper(String ch, ISDKHelper helper) { - helperMap.put(ch, helper); + public static void registerHelper(ISDKHelper helper) { + sdkHelper = helper; + } + + public static void ConsumeFlexionPurchase(String token) { + Log.i(TAG, "ConsumeFlexionPurchase: token=" + token); + if (sdkHelper != null) { + sdkHelper.consume(token); + } } // ------------------------------------------------------------------------- @@ -79,7 +86,16 @@ public class SDKManager { public static void InitSDK(Activity activity, String ch) { channel = ChannelHelpers.CHANNEL; ChannelHelpers.register(); - sdkHelper = helperMap.get(channel); + +// var builder = SDKManager.ExtendBuilder() +// .WithChannel(SDKManagerExt.CHANNEL) +// .WithInit(SDKManagerExt.GetInit()) +// .WithPay(SDKManagerExt.GetPay()) +// .WithQuery(SDKManagerExt.GetQuery()) +// .Builder() + + + Log.i(TAG, "[CHANNEL-VERIFY] Java InitSDK channel=" + channel + " fromCSharp=" + ch + " helper=" + (sdkHelper != null ? sdkHelper.getClass().getSimpleName() : "default")); @@ -87,7 +103,8 @@ public class SDKManager { // Sync channel to C# ChannelConfig UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, "SetChannelFromNative", channel); - + +// build.init(activity) if (sdkHelper != null) { sdkHelper.init(activity); } else { @@ -402,6 +419,7 @@ public class SDKManager { // ------------------------------------------------------------------------- public static void thirdExtend() { + Log.e(TAG, "thirdExtend called, sdkHelper=" + (sdkHelper != null ? sdkHelper.getClass().getSimpleName() : "null")); if (sdkHelper != null) { sdkHelper.queryProducts(); return; diff --git a/sdk-intergration/Packages/tysdk/Plugins/Android/tuyoosdk_1.0.0.aar b/sdk-intergration/Packages/tysdk/Plugins/Android/tuyoosdk_1.0.0.aar index b16d93c..2fcfaf0 100644 Binary files a/sdk-intergration/Packages/tysdk/Plugins/Android/tuyoosdk_1.0.0.aar and b/sdk-intergration/Packages/tysdk/Plugins/Android/tuyoosdk_1.0.0.aar differ diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs index a877cbe..7d4b2b1 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkFacade_Pay.cs @@ -30,6 +30,7 @@ namespace tysdk if (purchaseInfo == null) purchaseInfo = new JObject(); + // purchaseInfo["playerId"] = _accountInfo.strUserId; string extraInfo = purchaseInfo.ToString(Newtonsoft.Json.Formatting.None); extraInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(extraInfo)); @@ -56,18 +57,16 @@ namespace tysdk #elif UNITY_ANDROID var pType = ChannelConfig.PayType; - - var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromSeconds(30)); - + var task = TYSDKCallbackManager.Instance.RegisterCallback(TimeSpan.FromSeconds(90)); try { UnityBridgeFunc.UnityKnowNew(prodId, prodPrice, prodName, count.ToString(), orderId, extraInfo, pType); - var result = await task; result.count = count; result.orderId = orderId; result.productId = prodId; result.price = prodPrice; + result.Check(orderId, prodId); return result; } catch (Exception e) @@ -112,6 +111,9 @@ 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(); if (result.code == "-1") { if (!_payFirstNegativeHandled) diff --git a/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs b/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs index 1954082..375df67 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/TYSdkModel.cs @@ -21,6 +21,8 @@ namespace tysdk public string purchaseJson; // Flexion: purchase.getSignature(), used by server for RSA signature verification public string signature; + // Flexion: purchase token, used to consume after server verification + public string purchaseToken; public bool Check(string orderId, string productId) { diff --git a/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs b/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs index e665a43..681b7ea 100644 --- a/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs +++ b/sdk-intergration/Packages/tysdk/Runtime/UnityBridgeFunc.cs @@ -168,6 +168,14 @@ namespace tysdk } } + public static void ConsumeFlexionPurchase(string token) + { + using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) + { + sdkManager.CallStatic("ConsumeFlexionPurchase", token); + } + } + public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo) { using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) diff --git a/sdk-intergration/ProjectSettings/ProjectSettings.asset b/sdk-intergration/ProjectSettings/ProjectSettings.asset index 6bde347..b4f01c9 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: