17 Commits

Author SHA1 Message Date
4d3eae1c88 update:更新补单逻辑 2026-06-02 19:50:44 +08:00
f06074d9cc update:更新SDK 修改 2026-05-21 14:45:27 +08:00
dc622282f2 update:更新忽略文件 2026-05-21 00:38:42 +08:00
ed6fe668ad update:逻辑更新 2026-05-21 00:37:19 +08:00
eecf54c6a4 update:更新tysdk 默认修改 2026-05-14 11:53:38 +08:00
49d80ee008 update:更新tysdk:注意直接consume了 2026-05-14 11:49:07 +08:00
f9b5413a81 refactor: 支付链路规范化 — 统一命名/层次/传参
- ISDKHelper.pay() 增加 productPrice 参数,与 UnityKnowNew 签名对齐
- FlexionSDKHelper: prodPrice/prodCount 从参数直接写入 developerPayload
- playerId → userId 统一命名 (SDKTest/Java payload)
- pfId → pfid 统一小写 (服务端 JSON key)
- PaymentInfo 序列化替代 StringBuilder 手拼 JSON
- FlexionPaymentVerifier.Consume 走 UnityBridgeFunc.ConsumePurchase 桥接层
- UnityBridgeFunc 新增 ConsumePurchase 通用方法,移除渠道特定方法
- SDKManager.ConsumeFlexionPurchase → ConsumePurchase 通用化

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 15:24:39 +08:00
8de35fa97d feat: FlexionSDKHelper 添加 consume 接口实现
ExtraPluginCfgs/flexion 同步 ISDKHelper.consume(String) 方法

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:26:58 +08:00
de3ff3b7a4 feat: 支付验签框架 — 策略模式 + 装饰器重试
- 新增 IPaymentVerifier 接口 (Verify + Consume),渠道无关
- 新增 PaymentVerifierFactory 按 Channel 分发
- 新增 FlexionPaymentVerifier: HTTP 验签 + 线性 backoff 重试
- 新增 DummyPaymentVerifier: 占位验签实现
- 新增 RetryablePaymentVerifier: 装饰器,流程级重试
- TYSdkFacade_Pay: PayResult 增加验签分支,先验签后消费再回调
- TYSdkModel: PaymentInfo 增加 verified 字段
- UnityBridgeFunc: 移除 ConsumeFlexionPurchase(职责收归 Verifier)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:25:20 +08:00
fd5ba9297d refactor: FlexionSDKHelper 迁移至 ExtraPluginCfgs + 构建配置更新
- 删除 tysdk/Plugins/Android 下的 FlexionSDKHelper(已迁移至 ExtraPluginCfgs/flexion)
- 更新 AndroidManifest/mainTemplate.gradle/settingsTemplate.gradle 配置
- 更新 ChannelHelpers/ConfigManager 渠道配置
- 更新 tuyoosdk AAR 和 google-services.json

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:24:52 +08:00
f60a1dd0c2 feat: Flexion 先验签后消费支付流程 + playerId 绑定
实现 Flexion 渠道支付验签重构:支付成功后不立即消费,将 purchaseJson/signature/purchaseToken
回调至 Unity,由服务端 SHA1withRSA 验签并校验 developerPayload.playerId 后再调用 consume。
同步更新 SDKManager 接口、UnityBridgeFunc 桥接、TYSdkModel 字段及 ExtraPluginCfgs。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 16:04:29 +08:00
7fddc4e0f3 update:flexion 打包修改 2026-05-08 11:28:50 +08:00
8133f4d1b2 feat: Flexion channel build fixes
- Add google-services.json per-channel copy in SwitchChannel (Flexion has extra client)
- Add GooglePlay default google-services.json for fallback
- Switch Bundle Identifier per channel (Flexion → com.arkgame.ft.flexion)
- Fix FlexionSDKHelper missing BillingService import
- Fix Flexion AndroidManifest: add fullBackupContent override with flexion_backup_rules
- Add MainProcessName tools:replace to resolve gasdk/tuyoosdk manifest conflict
- Add billing:6.0.1 dependency for tuyoosdk AAR init compatibility

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:08:53 +08:00
d361d9078b feat: merge rustore test UI and unify SKU format across channels
- Merge VK login/link/check and Review test buttons from dev_rustore
- Unify Rustore/GooglePlay SKU format via ChannelHelpers.normalizeSkuList()
- Fix ChannelConfig runtime sync: Java InitSDK sets channel from ChannelHelpers.CHANNEL
- Fix AndroidMinSdkVersion 25→26 for tuyoosdk compatibility
- Fix Flexion AndroidManifest tools:replace missing fullBackupContent value
- Add [CHANNEL-VERIFY] debug logs for channel verification

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 15:38:21 +08:00
66aa7a931b update:更新flexion 以及sdk 接入 2026-04-29 20:43:16 +08:00
c0b6ccf7ed update:更新sdk 脚本 2026-04-03 21:19:41 +08:00
db82fe0116 更新BridgeFunc
Made-with: Cursor
2026-04-03 21:09:11 +08:00
90 changed files with 3854 additions and 5675 deletions

4
.gitignore vendored
View File

@@ -5,3 +5,7 @@ sdk-intergration/.vscode
sdk-intergration/'
sdk-intergration/Assets/.claude/settings.json
sdk-intergration/.idea
.worktrees/*
.docs/*
.claude/*

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arkgame.ft.flexion"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application android:extractNativeLibs="true" android:name="com.unity3d.player.TYApp"
tools:replace="android:usesCleartextTraffic,android:fullBackupContent"
android:fullBackupContent="@xml/flexion_backup_rules"
android:usesCleartextTraffic="true">
<activity android:name="com.unity3d.player.TYUnityActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="fishingtraveliae.onelink.me" android:pathPrefix="/jT1Q"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="mainactivity" android:scheme="zzft" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
<!-- 渠道标识ChannelManager 读取此值以识别 Flexion 渠道 -->
<meta-data android:name="CHANNEL" android:value="Flexion" />
<!-- 覆盖 gasdk 与 tuyoosdk 的 MainProcessName 冲突 -->
<meta-data android:name="com.sensorsdata.analytics.android.MainProcessName"
android:value="${applicationId}"
tools:replace="android:value" />
<!-- Flexion SDK 应用标识,从 DropPoint 获取 -->
<meta-data android:name="com.flexionmobile.fdk.apptoken" android:value="fishingtravel" />
<!-- 告知 Flexion SDK 当前为 Unity 项目,必须声明 -->
<meta-data android:name="com.flexionmobile.fdk.sdk-type" android:value="unity" />
<!--
覆盖 tuyoosdk AAR 中硬编码的 Facebook ContentProvider authority。
AAR 原值com.facebook.app.FacebookContentProvider162066986963808
追加 ${applicationId}Flexion 包名)作为后缀后,与 GP 版 authority 不同,
两个渠道包可同时安装在同一设备上,不会触发 INSTALL_FAILED_CONFLICTING_PROVIDER。
参考Flexion 官方文档《Unique package name - follow-on requirements》
-->
<provider
android:name="com.facebook.FacebookContentProvider"
android:authorities="com.facebook.app.FacebookContentProvider162066986963808.${applicationId}"
android:exported="true"
tools:replace="android:authorities" />
</application>
<queries>
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" />
<provider android:authorities="com.facebook.orca.provider.PlatformProvider" />
</queries>
</manifest>

View File

@@ -0,0 +1,20 @@
package com.unity3d.player;
public class ChannelEntry {
public static final String CHANNEL = "Flexion";
public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
return new FlexionSDKAdapter();
}
// Flexion provides price in micros and currency code, so no normalization needed
// 支付的变化,可能会修改这里
public static String normalizeSkuList(String msg) {
return msg;
}
//public SDKExtBuilder;
}

View File

@@ -0,0 +1,29 @@
package com.unity3d.player;
public class ConfigManager {
// Common SDK parameters
public static int SDK_APPID = 20587;
public static String SDK_GAMEID = "20587";
public static String SDK_PROJECTID = "20587";
public static String SDK_CLOUDID = "128";
public static String SDK_CLIENTID = "Android_5.00_tyGuest,facebook.googleplay.0-hall20587.flexion.FishingMaster";
public static String SDK_LOGIN_SERVER_URL = "https://128-hwsfsdk-sdk-online01.qijihdhk.com";
// FCM (anti-addiction system)
public static String FCM_NAMESPACE = "128";
public static String FCM_IAM_HOST = "tcp://fcmtcp.tuyoo.com:3563";
public static String FCM_ANTISERVERURL = "https://fcmapi.tuyoo.com";
// Unity Facade callback names
public static final String UNITY_FACADE_NAME = "TYSdkFacade";
public static final String LOGIN_CALLBACK_FUNCTION_NAME = "LoginResult";
public static final String LINKACCOUT_CALLBACK_FUNCTION_NAME = "LinkAccoutResult";
public static final String CHANGELINK_CALLBACK_FUNCTION_NAME = "ChangeLinkAccountResult";
public static final String CHECKLINK_CALLBACK_FUNCTION_NAME = "CheckLinkResult";
public static final String PAY_CALLBACK_FUNCTION_NAME = "PayResult";
public static final String PAY_TYPE_CALLBACK_FUNCTION_NAME = "SKUListResult";
public static final String FCM_NOTICE_FUNCTION_NAME = "FCMCallback";
public static final String FCM_REALNAME_CALLBACK_FUNCTION_NAME = "RealNameCallback";
public static final String INIT_CALLBACK_FUNCTION_NAME = "InitResult";
}

View File

@@ -0,0 +1,100 @@
{
"project_info": {
"project_number": "481260393117",
"project_id": "fishing-travel",
"storage_bucket": "fishing-travel.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:481260393117:android:4e8da50d0ee3b5c207fa74",
"android_client_info": {
"package_name": "com.arkgame.ft"
}
},
"oauth_client": [
{
"client_id": "481260393117-jhq50gh6n2p6pat1bjhcgj3th1ke19jg.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.arkgame.ft",
"certificate_hash": "c9cf2f5eaba383cbdd5b968d0342c49afabc32a2"
}
},
{
"client_id": "481260393117-vfn1jueugjqjach8kgc0kfi54g085q6l.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.arkgame.ft",
"certificate_hash": "53fd6ff623712411792a4665163e4cbf4a7a22ec"
}
},
{
"client_id": "481260393117-0ah0ekk7lu5r44ko2uqlfoebgak8dmhj.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBU6gAwu0O28jg7-TRd_Vx2YMCH_Baa8Bw"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "481260393117-0ah0ekk7lu5r44ko2uqlfoebgak8dmhj.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "481260393117-88n0v3ht8ashhk0r5ri47hced7o5qj62.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.arkgame.ft",
"app_store_id": "6505145935"
}
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:481260393117:android:eafec4a060b0224907fa74",
"android_client_info": {
"package_name": "com.arkgame.ft.flexion"
}
},
"oauth_client": [
{
"client_id": "481260393117-0ah0ekk7lu5r44ko2uqlfoebgak8dmhj.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBU6gAwu0O28jg7-TRd_Vx2YMCH_Baa8Bw"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "481260393117-0ah0ekk7lu5r44ko2uqlfoebgak8dmhj.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "481260393117-88n0v3ht8ashhk0r5ri47hced7o5qj62.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.arkgame.ft",
"app_store_id": "6505145935"
}
}
]
}
}
}
],
"configuration_version": "1"
}

View File

@@ -0,0 +1,493 @@
package com.unity3d.player;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.flexionmobile.ddpx.listener.ConnectionStateListener;
import com.flexionmobile.ddpx.listener.PurchasesUpdateListener;
import com.flexionmobile.ddpx.model.BillingResult;
import com.flexionmobile.ddpx.model.BillingResults;
import com.flexionmobile.ddpx.model.Purchase;
import com.flexionmobile.ddpx.model.ProductDetails;
import com.flexionmobile.ddpx.model.params.BillingFlowParams;
import com.flexionmobile.ddpx.model.params.ConsumeParams;
import com.flexionmobile.ddpx.model.params.QueryPurchasesParams;
import com.flexionmobile.ddpx.model.params.ProductDetailsParams;
import com.flexionmobile.ddpx.service.BillingService;
import com.flexionmobile.fdk.FLX;
import com.tuyoo.gamesdk.api.SDKAPI;
import com.tuyoo.gamesdk.model.InitParam;
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 FlexionSDKAdapter implements
SDKManager.IChannelSDKAdapter,
SDKManager.IPaymentAdapter,
SDKManager.IProductQueryAdapter,
SDKManager.IPendingPurchaseAdapter,
SDKManager.IBillingLifecycleAdapter {
private static final String TAG = "FlexionSDKAdapter";
private static BillingService billingService;
private static boolean billingConnected = false;
private static boolean billingConnecting = false;
private static Activity billingActivity;
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static final List<String> pendingPurchases = new ArrayList<>();
private static final List<PendingBillingOperation> pendingBillingOperations = new ArrayList<>();
private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread"));
private interface BillingReadyOperation {
void run(BillingService service);
}
private static class PendingBillingOperation {
final String operation;
final BillingReadyOperation onReady;
final Runnable onFail;
PendingBillingOperation(String operation, BillingReadyOperation onReady, Runnable onFail) {
this.operation = operation;
this.onReady = onReady;
this.onFail = onFail;
}
}
private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> {
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, null);
} else {
Log.e(TAG, "[PAY-FLOW] purchase failed: code=" + billingResult.getResponseCode());
sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null, null);
}
};
@Override
public void init(Activity activity) {
activity.runOnUiThread(() -> {
SDKAPI.getIns().lightModeEnable();
boolean sdkInitOk = false;
try {
SDKAPI.getIns().init(new InitParam.Builder()
.withActivity(activity)
.withAppId(ConfigManager.SDK_APPID)
.withClientId(ConfigManager.SDK_CLIENTID)
.withServerUrl(ConfigManager.SDK_LOGIN_SERVER_URL)
.build());
SDKAPI.getIns().onActivityStart(activity);
SDKAPI.getIns().onActivityResume(activity);
sdkInitOk = true;
} catch (Exception e) {
Log.e(TAG, "TuYoo SDK init failed, skip FLX init: " + e);
TYUnityActivity.setSdkInited();
SDKManager.notifyInitResult(false, "tuyoo_init_failed: " + e.getMessage());
}
if (sdkInitOk) {
FLX.init(activity, (initCode, initMsg) -> {
Log.i(TAG, "FLX.init result=" + initCode);
if (initCode == 0) {
FLX.showFlexionScreens(activity, (screenCode, screenMsg) -> {
Log.i(TAG, "FLX.showFlexionScreens result=" + screenCode);
initBillingService(activity);
TYUnityActivity.setSdkInited();
SDKManager.notifyInitResult(true, "ok");
});
} else {
Log.e(TAG, "FLX.init failed: " + initCode);
TYUnityActivity.setSdkInited();
SDKManager.notifyInitResult(false, "flx_init_failed: " + initCode);
}
});
}
});
}
@Override
public void pay(Activity activity, String productId, String productPrice,
String productName, String productCount, String prodorderId,
String appInfo) {
Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingConnected=" + billingConnected);
String developerPayload = "";
try {
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", ""));
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;
ensureBillingReady(activity, "pay", bs ->
billingExecutor.execute(() -> {
try {
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() != 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, 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, null);
}
}),
() -> sendPayCallback(1, "billing_not_ready", null, null, null, null));
}
@Override
public void queryProducts() {
// Flexion 渠道不通过 thirdExtend 获取商品列表,由 queryProducts(String) 替代
}
@Override
public void queryProducts(String productIds) {
Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected);
List<String> ids = Arrays.asList(productIds.split(","));
Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config");
ensureBillingReady(null, "queryProducts",
bs -> queryProductDetails(bs, ids),
() -> sendExtensionError("billing_not_ready"));
}
private static void initBillingService(Activity activity) {
Log.i(TAG, "[PAY-FLOW] initBillingService");
ensureBillingReady(activity, "initBillingService",
bs -> queryPendingPurchases(),
() -> Log.w(TAG, "[PAY-FLOW] initBillingService failed: billing not ready"));
}
private static void ensureBillingReady(Activity activity, String operation,
BillingReadyOperation onReady, Runnable onFail) {
ensureBillingReady(activity, operation, onReady, onFail, false);
}
private static void ensureBillingReady(Activity activity, String operation,
BillingReadyOperation onReady, Runnable onFail,
boolean forceReconnect) {
if (activity != null) {
billingActivity = activity;
}
Activity targetActivity = activity != null ? activity : billingActivity;
if (targetActivity == null) {
Log.e(TAG, "[PAY-FLOW] ensureBillingReady failed: activity is null operation=" + operation);
if (onFail != null) onFail.run();
return;
}
targetActivity.runOnUiThread(() -> {
try {
if (billingService == null) {
Log.i(TAG, "[PAY-FLOW] create BillingService operation=" + operation);
billingService = FLX.createBillingService(targetActivity, purchasesListener);
}
boolean ready = billingService != null && billingService.isReady();
Log.i(TAG, "[PAY-FLOW] ensureBillingReady operation=" + operation
+ " connected=" + billingConnected
+ " ready=" + ready
+ " connecting=" + billingConnecting
+ " forceReconnect=" + forceReconnect);
if (ready && !forceReconnect) {
billingConnected = true;
if (onReady != null) onReady.run(billingService);
return;
}
pendingBillingOperations.add(new PendingBillingOperation(operation, onReady, onFail));
if (billingConnecting) {
Log.i(TAG, "[PAY-FLOW] billing connection already in progress, queued operation=" + operation);
return;
}
billingConnecting = true;
Log.i(TAG, "[PAY-FLOW] billing reconnect start operation=" + operation);
billingService.startConnection(targetActivity, new ConnectionStateListener() {
@Override
public void onBillingSetupFinished(BillingResult result) {
int code = result.getResponseCode();
Log.i(TAG, "[PAY-FLOW] onBillingSetupFinished: code=" + code);
billingConnecting = false;
billingConnected = code == BillingResults.ResultCode.CONNECTION_SUCCESS_CODE;
drainBillingOperations(billingConnected);
if (billingConnected) {
queryPendingPurchases();
}
}
@Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "[PAY-FLOW] billing service disconnected");
billingConnected = false;
}
});
mainHandler.postDelayed(() -> {
if (!billingConnecting) return;
boolean timeoutReady = billingService != null && billingService.isReady();
Log.w(TAG, "[PAY-FLOW] billing reconnect timeout operation=" + operation
+ " ready=" + timeoutReady);
billingConnecting = false;
billingConnected = timeoutReady;
drainBillingOperations(timeoutReady);
}, 10000);
} catch (Exception e) {
Log.e(TAG, "[PAY-FLOW] ensureBillingReady exception operation=" + operation
+ " msg=" + e.getMessage());
billingConnecting = false;
billingConnected = false;
drainBillingOperations(false);
}
});
}
private static void drainBillingOperations(boolean success) {
List<PendingBillingOperation> operations = new ArrayList<>(pendingBillingOperations);
pendingBillingOperations.clear();
Log.i(TAG, "[PAY-FLOW] drainBillingOperations success=" + success
+ " count=" + operations.size());
for (PendingBillingOperation operation : operations) {
if (success) {
if (operation.onReady != null) operation.onReady.run(billingService);
} else {
Log.w(TAG, "[PAY-FLOW] billing operation failed operation=" + operation.operation);
if (operation.onFail != null) operation.onFail.run();
}
}
}
private static void handlePurchase(Purchase purchase) {
Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState()
+ " orderId=" + purchase.getOrderId()
+ " token=" + purchase.getToken());
if (purchase.getPurchaseState() == 0) {
Log.i(TAG, "[PAY-FLOW] purchase success, sending to Unity for verification");
sendPayCallback(0, "success", purchase.getOrderId(),
purchase.getPurchaseJson(), purchase.getSignature(), purchase.getToken());
} else if (purchase.getPurchaseState() == 2) {
Log.i(TAG, "[PAY-FLOW] purchase state=2 (pending), skip");
}
}
private static void queryPendingPurchases() {
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService
+ " billingConnected=" + billingConnected);
ensureBillingReady(null, "queryPendingPurchases",
bs -> bs.queryPurchasesAsync(
new QueryPurchasesParams("inapp"),
(result, purchases) -> {
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);
}),
() -> Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected"));
}
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();
}
}
@Override
public void refreshBilling() {
Log.i(TAG, "[PAY-FLOW] refreshBilling requested");
billingConnected = false;
ensureBillingReady(null, "refreshBilling",
bs -> Log.i(TAG, "[PAY-FLOW] refreshBilling ready"),
() -> Log.w(TAG, "[PAY-FLOW] refreshBilling failed"),
true);
}
private static void queryProductDetails(BillingService service, List<String> productIds) {
final boolean[] completed = {false};
mainHandler.postDelayed(() -> {
if (completed[0]) return;
completed[0] = true;
Log.w(TAG, "[PAY-FLOW] queryProductDetails timeout count=" + productIds.size());
sendExtensionError("flexion_query_timeout");
}, 30000);
service.queryProductDetailsAsync(
new ProductDetailsParams("inapp", productIds),
(billingResult, productDetails) -> {
if (completed[0]) {
Log.w(TAG, "[PAY-FLOW] queryProductDetails callback ignored after timeout");
return;
}
completed[0] = true;
try {
JSONObject result = new JSONObject();
if (billingResult.getResponseCode() == BillingResults.ResultCode.QUERY_PRODUCT_DETAILS_SUCCESS_CODE
&& productDetails != null) {
JSONArray arr = new JSONArray();
for (ProductDetails detail : productDetails) {
JSONObject item = new JSONObject();
item.put("productId", detail.getId());
item.put("ourProductId", detail.getId());
item.put("title", detail.getTitle());
item.put("description", detail.getDescription());
item.put("price", detail.getPrice());
item.put("price_amount_micros", detail.getPriceAmountMicros());
item.put("price_currency_code", detail.getPriceCurrencyCode());
item.put("type", detail.getType());
arr.put(item);
}
result.put("code", 0);
result.put("respObj", arr.toString());
} else {
result.put("code", 1);
result.put("thirdExtend_errStr",
"flexion_query_failed: " + billingResult.getDebugMessage());
}
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, result.toString());
} catch (JSONException e) {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
}
});
}
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);
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) {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_CALLBACK_FUNCTION_NAME, "{\"code\":1,\"errStr\":\"json_error\"}");
}
}
@Override
public void consume(String token) {
consumePurchase(token);
}
public static void consumePurchase(String token) {
if (token == null || token.isEmpty()) {
Log.w(TAG, "[PAY-FLOW] consumePurchase: token is null");
return;
}
Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token);
ensureBillingReady(null, "consumePurchase",
bs -> bs.consumeAsync(
new ConsumeParams(token),
r -> Log.i(TAG, "[PAY-FLOW] consume result: " +
(r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))),
() -> Log.w(TAG, "[PAY-FLOW] consumePurchase skipped: billing not connected"));
}
public static void cancelPurchase() {
Log.i(TAG, "[PAY-FLOW] cancelPurchase: TODO - dismiss billing dialog");
}
private static void sendExtensionError(String errStr) {
try {
JSONObject result = new JSONObject();
result.put("code", 1);
result.put("thirdExtend_errStr", errStr);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, result.toString());
} catch (JSONException e) {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
}
}
}

View File

@@ -0,0 +1,11 @@
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
# Android Resolver Properties Start
android.useAndroidX=true
android.enableJetifier=true
# Android Resolver Properties End
android.suppressUnsupportedCompileSdk=35
**ADDITIONAL_PROPERTIES**

View File

@@ -0,0 +1,66 @@
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
dependencies {
implementation project(':unityLibrary')
implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
implementation("com.google.firebase:firebase-analytics")
}
android {
namespace "**NAMESPACE**"
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
applicationId '**APPLICATIONID**'
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}**SIGN**
lintOptions {
abortOnError false
}
buildTypes {
debug {
minifyEnabled **MINIFY_DEBUG**
proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
jniDebuggable true
}
release {
minifyEnabled **MINIFY_RELEASE**
proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
}
}**PACKAGING_OPTIONS****PLAY_ASSET_PACKS****SPLITS**
**BUILT_APK_LOCATION**
bundle {
language {
enableSplit = false
}
density {
enableSplit = false
}
abi {
enableSplit = true
}
}
}**SPLITS_VERSION_CODE****LAUNCHER_SOURCE_BUILD_SETUP**

View File

@@ -0,0 +1,131 @@
apply plugin: 'com.android.library'
**APPLY_PLUGINS**
// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('/lib/armeabi/*' + '*')
exclude ('/lib/mips/*' + '*')
exclude ('/lib/mips64/*' + '*')
exclude ('/lib/x86/*' + '*')
exclude ('/lib/x86_64/*' + '*')
}
}
// Android Resolver Exclusions End
android {
namespace "com.unity3d.player"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api "com.android.support:support-v4:28.0.0"
api "com.android.support:appcompat-v7:28.0.0"
api "com.google.code.gson:gson:2.8.6"
api "com.squareup.okio:okio:1.15.0"
api "com.squareup.okhttp3:okhttp:3.12.13"
api "com.squareup.retrofit2:retrofit:2.3.0"
api "com.squareup.retrofit2:converter-gson:2.2.0"
api "com.squareup.retrofit2:adapter-rxjava:2.1.0"
api "io.reactivex:rxandroid:1.2.1"
api "io.reactivex:rxjava:1.1.9"
// Google Play In-App Review
implementation 'com.google.android.play:review:2.0.1'
// Google Play Billing — tuyoosdk AAR references BillingManager during init
implementation 'com.android.billingclient:billing:7.0.0'
implementation 'net.aihelp:android-aihelp-aar:5.3.+'
implementation 'com.google.android.gms:play-services-auth:20.2.0'
// Flexion SDK (includes wrapped billing)
implementation("com.flexionmobile:sdk:4.0.1")
implementation 'com.google.android.gms:play-services-basement:18.2.0'
implementation 'androidx.fragment:fragment:1.3.6'
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.41"
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.41'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41'
implementation 'org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.41'
implementation 'androidx.core:core:1.8.0'
implementation 'com.miui.referrer:homereferrer:1.0.0.6'
implementation platform('com.google.firebase:firebase-bom:34.6.0')
implementation 'com.google.firebase:firebase-analytics'
implementation('com.facebook.android:facebook-android-sdk:16.2.0')
implementation 'com.google.android.play:asset-delivery:2.2.2'
implementation "androidx.credentials:credentials:1.3.0"
implementation "androidx.credentials:credentials-play-services-auth:1.3.0"
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.2"
// Android Resolver Dependencies Start
implementation 'com.android.installreferrer:installreferrer:2.1' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:7
implementation 'com.applovin.mediation:facebook-adapter:[6.18.0.1]' // Assets/MaxSdk/Mediation/Facebook/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:fyber-adapter:8.4.2.0' // Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:google-adapter:[23.6.0.1]' // Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml:5
implementation 'com.applovin.mediation:ironsource-adapter:8.6.1.0.0' // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:unityads-adapter:4.12.3.0' // Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml:4
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
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**}
// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('/lib/armeabi/*' + '*')
exclude ('/lib/mips/*' + '*')
exclude ('/lib/mips64/*' + '*')
exclude ('/lib/x86/*' + '*')
exclude ('/lib/x86_64/*' + '*')
}
}
// Android Resolver Exclusions End
android {
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
coreLibraryDesugaringEnabled true
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}**PACKAGING_OPTIONS**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

View File

@@ -0,0 +1,49 @@
pluginManagement {
repositories {
**ARTIFACTORYREPOSITORY**
gradlePluginPortal()
google()
mavenCentral()
}
}
include ':launcher', ':unityLibrary'
**INCLUDES**
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
**ARTIFACTORYREPOSITORY**
google()
mavenCentral()
// Flexion SDK Maven Repository
maven {
url = uri("https://maven.pkg.github.com/FlexionSDK/JavaSDK")
credentials {
username = "FlexionSDK"
// Replace with your actual Flexion PAT from DropPoint
password = "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"
}
}
// 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
}
mavenLocal()
// Android Resolver Repos End
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
maven {
url "http://sdkck.tuyoo.com/artifactory/tuyoo-component/"
allowInsecureProtocol = true
}
}
}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arkgame.ft"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application android:extractNativeLibs="true" android:name="com.unity3d.player.TYApp" android:usesCleartextTraffic="true">
<activity android:name="com.unity3d.player.TYUnityActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="fishingtraveliae.onelink.me" android:pathPrefix="/jT1Q"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="mainactivity" android:scheme="zzft" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
<meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="true" />
<meta-data android:name="google_analytics_default_allow_ad_storage" android:value="true" />
<meta-data android:name="google_analytics_default_allow_ad_user_data" android:value="true" />
<meta-data android:name="google_analytics_default_allow_ad_personalization_signals" android:value="true" />
</activity>
<meta-data android:name="CHANNEL" android:value="GooglePlay" />
</application>
<queries>
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" /> <!-- allows app to access Facebook app features -->
<provider android:authorities="com.facebook.orca.provider.PlatformProvider" /> <!-- allows sharing to Messenger app -->
<intent>
<action android:name="android.intent.action.MAIN"/>
</intent>
</queries>
</manifest>

View File

@@ -0,0 +1,13 @@
package com.unity3d.player;
public class ChannelEntry {
public static final String CHANNEL = "GooglePlay";
public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
return null;
}
public static String normalizeSkuList(String msg) {
return msg;
}
}

View File

@@ -0,0 +1,29 @@
package com.unity3d.player;
public class ConfigManager {
// Common SDK parameters
public static int SDK_APPID = 20587;
public static String SDK_GAMEID = "20587";
public static String SDK_PROJECTID = "20587";
public static String SDK_CLOUDID = "128";
public static String SDK_CLIENTID = "Android_5.00_tyGuest,facebook.googleplay.0-hall20587.googleplay.FishingMaster";
public static String SDK_LOGIN_SERVER_URL = "https://128-hwsfsdk-sdk-online01.qijihdhk.com";
// FCM (anti-addiction system)
public static String FCM_NAMESPACE = "128";
public static String FCM_IAM_HOST = "tcp://fcmtcp.tuyoo.com:3563";
public static String FCM_ANTISERVERURL = "https://fcmapi.tuyoo.com";
// Unity Facade callback names
public static final String UNITY_FACADE_NAME = "TYSdkFacade";
public static final String LOGIN_CALLBACK_FUNCTION_NAME = "LoginResult";
public static final String LINKACCOUT_CALLBACK_FUNCTION_NAME = "LinkAccoutResult";
public static final String CHANGELINK_CALLBACK_FUNCTION_NAME = "ChangeLinkAccountResult";
public static final String CHECKLINK_CALLBACK_FUNCTION_NAME = "CheckLinkResult";
public static final String PAY_CALLBACK_FUNCTION_NAME = "PayResult";
public static final String PAY_TYPE_CALLBACK_FUNCTION_NAME = "SKUListResult";
public static final String FCM_NOTICE_FUNCTION_NAME = "FCMCallback";
public static final String FCM_REALNAME_CALLBACK_FUNCTION_NAME = "RealNameCallback";
public static final String INIT_CALLBACK_FUNCTION_NAME = "InitResult";
}

View File

@@ -0,0 +1,47 @@
{
"project_info": {
"project_number": "481260393117",
"project_id": "fishing-travel",
"storage_bucket": "fishing-travel.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:481260393117:android:4e8da50d0ee3b5c207fa74",
"android_client_info": {
"package_name": "com.arkgame.ft"
}
},
"oauth_client": [
{
"client_id": "481260393117-jhq50gh6n2p6pat1bjhcgj3th1ke19jg.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.arkgame.ft",
"certificate_hash": "c9cf2f5eaba383cbdd5b968d0342c49afabc32a2"
}
},
{
"client_id": "481260393117-0ah0ekk7lu5r44ko2uqlfoebgak8dmhj.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBU6gAwu0O28jg7-TRd_Vx2YMCH_Baa8Bw"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "481260393117-0ah0ekk7lu5r44ko2uqlfoebgak8dmhj.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View File

@@ -0,0 +1,11 @@
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
# Android Resolver Properties Start
android.useAndroidX=true
android.enableJetifier=true
# Android Resolver Properties End
android.suppressUnsupportedCompileSdk=35
**ADDITIONAL_PROPERTIES**

View File

@@ -0,0 +1,66 @@
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
dependencies {
implementation project(':unityLibrary')
implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
implementation("com.google.firebase:firebase-analytics")
}
android {
namespace "**NAMESPACE**"
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
applicationId '**APPLICATIONID**'
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}**SIGN**
lintOptions {
abortOnError false
}
buildTypes {
debug {
minifyEnabled **MINIFY_DEBUG**
proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
jniDebuggable true
}
release {
minifyEnabled **MINIFY_RELEASE**
proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
}
}**PACKAGING_OPTIONS****PLAY_ASSET_PACKS****SPLITS**
**BUILT_APK_LOCATION**
bundle {
language {
enableSplit = false
}
density {
enableSplit = false
}
abi {
enableSplit = true
}
}
}**SPLITS_VERSION_CODE****LAUNCHER_SOURCE_BUILD_SETUP**

View File

@@ -0,0 +1,110 @@
apply plugin: 'com.android.library'
**APPLY_PLUGINS**
// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('/lib/armeabi/*' + '*')
exclude ('/lib/mips/*' + '*')
exclude ('/lib/mips64/*' + '*')
exclude ('/lib/x86/*' + '*')
exclude ('/lib/x86_64/*' + '*')
}
}
// Android Resolver Exclusions End
android {
namespace "com.unity3d.player"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api "com.android.support:support-v4:28.0.0"
api "com.android.support:appcompat-v7:28.0.0"
api "com.google.code.gson:gson:2.8.6"
api "com.squareup.okio:okio:1.15.0"
api "com.squareup.okhttp3:okhttp:3.12.13"
api "com.squareup.retrofit2:retrofit:2.3.0"
api "com.squareup.retrofit2:converter-gson:2.2.0"
api "com.squareup.retrofit2:adapter-rxjava:2.1.0"
api "io.reactivex:rxandroid:1.2.1"
api "io.reactivex:rxjava:1.1.9"
// Google Play In-App Review
implementation 'com.google.android.play:review:2.0.1'
implementation 'net.aihelp:android-aihelp-aar:5.3.+'
implementation 'com.google.android.gms:play-services-auth:20.2.0'
implementation('com.android.billingclient:billing:7.0.0')
implementation 'com.google.android.gms:play-services-basement:18.2.0'
implementation 'androidx.fragment:fragment:1.3.6'
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.41"
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.41'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41'
implementation 'org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.41'
implementation 'androidx.core:core:1.8.0'
implementation 'com.miui.referrer:homereferrer:1.0.0.6'
implementation platform('com.google.firebase:firebase-bom:34.6.0')
implementation 'com.google.firebase:firebase-analytics'
implementation('com.facebook.android:facebook-android-sdk:16.2.0')
implementation 'com.google.android.play:asset-delivery:2.2.2'
implementation "androidx.credentials:credentials:1.3.0"
implementation "androidx.credentials:credentials-play-services-auth:1.3.0"
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
// Android Resolver Dependencies Start
implementation 'com.android.installreferrer:installreferrer:2.1' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:7
implementation 'com.applovin.mediation:facebook-adapter:[6.18.0.1]' // Assets/MaxSdk/Mediation/Facebook/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:fyber-adapter:8.4.2.0' // Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:google-adapter:[23.6.0.1]' // Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml:5
implementation 'com.applovin.mediation:ironsource-adapter:8.6.1.0.0' // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:unityads-adapter:4.12.3.0' // Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml:4
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
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
**DEPS**}
android {
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}**PACKAGING_OPTIONS**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

View File

@@ -0,0 +1,35 @@
pluginManagement {
repositories {
**ARTIFACTORYREPOSITORY**
gradlePluginPortal()
google()
mavenCentral()
}
}
include ':launcher', ':unityLibrary'
**INCLUDES**
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
**ARTIFACTORYREPOSITORY**
google()
mavenCentral()
// Android Resolver Repos Start
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven {
url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
}
mavenLocal()
// Android Resolver Repos End
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
maven {
url "http://sdkck.tuyoo.com/artifactory/tuyoo-component/"
allowInsecureProtocol = true
}
}
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arkgame.ft"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application android:extractNativeLibs="true" android:name="com.unity3d.player.TYApp"
tools:replace="android:usesCleartextTraffic"
tools:overrideLibrary="com.tuyoo.gamesdk"
android:usesCleartextTraffic="true">
<activity android:name="com.unity3d.player.TYUnityActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="fishingtraveliae.onelink.me" android:pathPrefix="/jT1Q"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="mainactivity" android:scheme="zzft" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
<meta-data android:name="CHANNEL" android:value="Rustore" />
</application>
<queries>
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" /> <!-- allows app to access Facebook app features -->
<provider android:authorities="com.facebook.orca.provider.PlatformProvider" /> <!-- allows sharing to Messenger app -->
</queries>
</manifest>

View File

@@ -0,0 +1,44 @@
package com.unity3d.player;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class ChannelEntry {
public static final String CHANNEL = "Rustore";
public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
return null;
}
public static String normalizeSkuList(String msg) {
if (msg == null || msg.isEmpty()) return msg;
try {
JSONArray products = new JSONArray(msg);
for (int i = 0; i < products.length(); i++) {
JSONObject p = products.getJSONObject(i);
// price is in kopecks, convert to micros: kopecks * 10000
String price = p.optString("price", "0");
long micros = Long.parseLong(price) * 10000;
p.put("price_amount_micros", String.valueOf(micros));
// Map currency → price_currency_code
String currency = p.optString("currency", "");
if (!currency.isEmpty()) {
p.put("price_currency_code", currency);
}
// Convert display price: "199" → "₽1.99"
float rubles = Float.parseFloat(price) / 100;
p.put("price", String.format("₽%s", rubles));
}
return products.toString();
} catch (JSONException e) {
Log.e("ChannelEntry", "normalizeSkuList error: " + e.getMessage());
return msg;
}
}
}

View File

@@ -0,0 +1,29 @@
package com.unity3d.player;
public class ConfigManager {
// Common SDK parameters
public static int SDK_APPID = 20587;
public static String SDK_GAMEID = "20587";
public static String SDK_PROJECTID = "20587";
public static String SDK_CLOUDID = "128";
public static String SDK_CLIENTID = "Android_5.00_tyGuest,facebook.googleplay.0-hall20587.rustore.FishingTravel";
public static String SDK_LOGIN_SERVER_URL = "https://128-hwsfsdk-sdk-online01.qijihdhk.com";
// FCM (anti-addiction system)
public static String FCM_NAMESPACE = "128";
public static String FCM_IAM_HOST = "tcp://fcmtcp.tuyoo.com:3563";
public static String FCM_ANTISERVERURL = "https://fcmapi.tuyoo.com";
// Unity Facade callback names
public static final String UNITY_FACADE_NAME = "TYSdkFacade";
public static final String LOGIN_CALLBACK_FUNCTION_NAME = "LoginResult";
public static final String LINKACCOUT_CALLBACK_FUNCTION_NAME = "LinkAccoutResult";
public static final String CHANGELINK_CALLBACK_FUNCTION_NAME = "ChangeLinkAccountResult";
public static final String CHECKLINK_CALLBACK_FUNCTION_NAME = "CheckLinkResult";
public static final String PAY_CALLBACK_FUNCTION_NAME = "PayResult";
public static final String PAY_TYPE_CALLBACK_FUNCTION_NAME = "SKUListResult";
public static final String FCM_NOTICE_FUNCTION_NAME = "FCMCallback";
public static final String FCM_REALNAME_CALLBACK_FUNCTION_NAME = "RealNameCallback";
public static final String INIT_CALLBACK_FUNCTION_NAME = "InitResult";
}

View File

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

View File

@@ -0,0 +1,11 @@
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
# Android Resolver Properties Start
android.useAndroidX=true
android.enableJetifier=true
# Android Resolver Properties End
android.suppressUnsupportedCompileSdk=35
**ADDITIONAL_PROPERTIES**

View File

@@ -0,0 +1,74 @@
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
dependencies {
implementation project(':unityLibrary')
implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
implementation("com.google.firebase:firebase-analytics")
}
android {
namespace "**NAMESPACE**"
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
applicationId '**APPLICATIONID**'
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
// ↓↓↓ 这里放你的 VK ID 占位符 ↓↓↓
manifestPlaceholders = [
VKIDRedirectHost : "vk.ru",
VKIDRedirectScheme: "vk54432404",
VKIDClientID : "54432404",
VKIDClientSecret : "WD6BO0CwPXsKMaaFX8Qq"
]
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}**SIGN**
lintOptions {
abortOnError false
}
buildTypes {
debug {
minifyEnabled **MINIFY_DEBUG**
proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
jniDebuggable true
}
release {
minifyEnabled **MINIFY_RELEASE**
proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
}
}**PACKAGING_OPTIONS****PLAY_ASSET_PACKS****SPLITS**
**BUILT_APK_LOCATION**
bundle {
language {
enableSplit = false
}
density {
enableSplit = false
}
abi {
enableSplit = true
}
}
}**SPLITS_VERSION_CODE****LAUNCHER_SOURCE_BUILD_SETUP**

View File

@@ -0,0 +1,131 @@
apply plugin: 'com.android.library'
**APPLY_PLUGINS**
// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('/lib/armeabi/*' + '*')
exclude ('/lib/mips/*' + '*')
exclude ('/lib/mips64/*' + '*')
exclude ('/lib/x86/*' + '*')
exclude ('/lib/x86_64/*' + '*')
}
}
// Android Resolver Exclusions End
android {
namespace "com.unity3d.player"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api "com.android.support:support-v4:28.0.0"
api "com.android.support:appcompat-v7:28.0.0"
api "com.google.code.gson:gson:2.8.6"
api "com.squareup.okio:okio:1.15.0"
api "com.squareup.okhttp3:okhttp:3.12.13"
api "com.squareup.retrofit2:retrofit:2.3.0"
api "com.squareup.retrofit2:converter-gson:2.2.0"
api "com.squareup.retrofit2:adapter-rxjava:2.1.0"
api "io.reactivex:rxandroid:1.2.1"
api "io.reactivex:rxjava:1.1.9"
// Google Play In-App Review
implementation 'com.google.android.play:review:2.0.1'
implementation 'net.aihelp:android-aihelp-aar:5.3.+'
implementation 'com.google.android.gms:play-services-auth:20.2.0'
implementation('com.android.billingclient:billing:6.0.1')
implementation 'com.google.android.gms:play-services-basement:18.2.0'
implementation 'androidx.fragment:fragment:1.3.6'
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.41"
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.41'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41'
implementation 'org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.41'
implementation 'androidx.core:core:1.8.0'
implementation 'com.miui.referrer:homereferrer:1.0.0.6'
implementation platform('com.google.firebase:firebase-bom:32.7.4')
implementation 'com.google.firebase:firebase-analytics'
implementation('com.facebook.android:facebook-android-sdk:16.2.0')
implementation 'com.google.android.play:asset-delivery:2.2.2'
implementation "androidx.credentials:credentials:1.3.0"
implementation "androidx.credentials:credentials-play-services-auth:1.3.0"
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
implementation(platform("ru.rustore.sdk:bom:2025.11.01"))
implementation("ru.rustore.sdk:pay")
implementation "ru.rustore.sdk:review:10.0.0"
implementation "com.vk.id:vkid:2.5.1"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.2"
// Android Resolver Dependencies Start
implementation 'com.android.installreferrer:installreferrer:2.1' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:7
implementation 'com.applovin.mediation:facebook-adapter:[6.18.0.1]' // Assets/MaxSdk/Mediation/Facebook/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:fyber-adapter:8.4.2.0' // Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:google-adapter:[23.6.0.1]' // Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml:5
implementation 'com.applovin.mediation:ironsource-adapter:8.6.1.0.0' // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:unityads-adapter:4.12.3.0' // Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml:4
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
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
**DEPS**}
// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('/lib/armeabi/*' + '*')
exclude ('/lib/mips/*' + '*')
exclude ('/lib/mips64/*' + '*')
exclude ('/lib/x86/*' + '*')
exclude ('/lib/x86_64/*' + '*')
}
}
// Android Resolver Exclusions End
android {
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
coreLibraryDesugaringEnabled true
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}**PACKAGING_OPTIONS**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

View File

@@ -0,0 +1,46 @@
pluginManagement {
repositories {
**ARTIFACTORYREPOSITORY**
gradlePluginPortal()
google()
mavenCentral()
}
}
include ':launcher', ':unityLibrary'
**INCLUDES**
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
**ARTIFACTORYREPOSITORY**
google()
mavenCentral()
maven{
url "https://artifactory-external.vkpartner.ru/artifactory/vkid-sdk-android/"
}
maven{
url "https://artifactory-external.vkpartner.ru/artifactory/maven/"
}
maven{
url "https://artifactory-external.vkpartner.ru/artifactory/vk-id-captcha/android/"
}
// Android Resolver Repos Start
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven {
url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
}
mavenLocal()
// Android Resolver Repos End
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
maven {
url "http://sdkck.tuyoo.com/artifactory/tuyoo-component/"
allowInsecureProtocol = true
}
}
}

View File

@@ -0,0 +1,43 @@
#!/bin/bash
# Usage: ./switch-channel.sh <channel>
# channel: googleplay | flexion | rustore
#
# 切换渠道后重新用 Unity 打 Android 包即可打出对应渠道的 APK。
# 脚本会同步AAR、Gradle 配置、AndroidManifest、Java 文件ConfigManager、ChannelHelpers 等)
CHANNEL=${1:-googleplay}
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CHANNEL_DIR="$SCRIPT_DIR/$CHANNEL"
PROJ_DIR="$SCRIPT_DIR/../../sdk-intergration"
TYSDK_PLUGINS="$PROJ_DIR/Packages/tysdk/Plugins/Android"
ASSETS_PLUGINS="$PROJ_DIR/Assets/Plugins/Android"
# 校验渠道目录
if [ ! -d "$CHANNEL_DIR" ]; then
echo "ERROR: Unknown channel '$CHANNEL'. Available: googleplay, flexion, rustore"
exit 1
fi
echo "=== Switching to channel: $CHANNEL ==="
# 1. Sync channel AAR to tysdk package (only tuyoosdk, gasdk stays permanently)
echo "[AAR] Syncing tuyoosdk to $TYSDK_PLUGINS"
find "$TYSDK_PLUGINS" -maxdepth 1 -name "tuyoosdk_*.aar" -delete
cp "$CHANNEL_DIR/Plugins/Android/"*.aar "$TYSDK_PLUGINS/" 2>/dev/null
# 2. Sync Gradle + Manifest to Assets/Plugins/Android
echo "[Config] Syncing gradle/manifest to $ASSETS_PLUGINS"
for f in "$CHANNEL_DIR"/*.gradle "$CHANNEL_DIR"/*.properties "$CHANNEL_DIR"/AndroidManifest.xml; do
[ -f "$f" ] && cp "$f" "$ASSETS_PLUGINS/"
done
# 3. Sync Java files to tysdk package
echo "[Java] Syncing ConfigManager + ChannelHelpers to $TYSDK_PLUGINS"
for f in "$CHANNEL_DIR"/*.java; do
[ -f "$f" ] && cp "$f" "$TYSDK_PLUGINS/"
done
echo "=== Done. Channel: $CHANNEL ==="
echo ""
echo "Next: Open Unity and build Android APK."

View File

@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"WebSearch"
]
}
}

View File

@@ -0,0 +1,154 @@
# 合并记录: dev_rustore 测试内容 → dev_flexion
**日期:** 2026-04-30
**源分支:** dev_rustore (c105818)
**目标分支:** dev_flexion (66aa7a9)
---
## 已合并的文件
### 1. sdk-intergration/Assets/Scripts/SDKTest.cs
新增 VK 登录/绑定/检查 和 Review 测试按钮及事件处理:
**新增字段:**
- `_loginVkBtn` — VK 登录按钮
- `_linkVkBtn` — VK 绑定按钮
- `_checkVkBtn` — VK 检查绑定按钮
- `_reviewBtn` — 应用评价按钮
**新增监听 (Start):**
```csharp
_loginVkBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwVkid));
_linkVkBtn.onClick.AddListener(OnLinkVkId);
_checkVkBtn.onClick.AddListener(OnCheckVKId);
_reviewBtn.onClick.AddListener(OnReview);
```
**新增方法:**
```csharp
private async void OnLinkVkId() // VK 绑定 → TYSdkFacade.Instance.LinkAccount(EAccoutType.hwVkid)
private async void OnCheckVKId() // VK 检查 → TYSdkFacade.Instance.LinkCheck(EAccoutType.hwVkid)
private void OnReview() // 评价 → UnityBridgeFunc.Review()
```
### 2. sdk-intergration/Assets/Scenes/SampleScene.unity
直接从 dev_rustore 检出完整文件,包含新增 UI 按钮 GameObject:
- VK Login 按钮
- VK Link 按钮
- VK Check 按钮
- Review 按钮
### 3. sdk-intergration/ProjectSettings/ProjectSettings.asset
修复构建错误,将 `AndroidMinSdkVersion` 从 25 提升到 26:
- **原因:** `tuyoosdk_1.0.0.aar` 声明 minSdkVersion 26,低于此版本会 manifest 合并失败
- **影响:** 全局生效,所有渠道统一 minSdk 26
---
## 跳过的文件 (dev_flexion 已有更完善的实现)
### TYSdkModel.cs — 跳过
- dev_rustore: 硬编码 `AccountInfo.hwPayType = "rustore.global.app"`, SKUDetail 写死 Rustore 格式
- dev_flexion: 使用 `ChannelConfig.Channel` 动态区分渠道,同时兼容 Google/Rustore/Flexion
- **理由:** dev_flexion 的 `ChannelConfig` 抽象更成熟,无需降级
### TYSdkFacade.cs — 跳过
- dev_rustore: 硬编码 `Channel = "hwVkid"`, 超时 120s
- dev_flexion: 已有 `Channel => ChannelConfig.Channel`, 动态 `LoginTimeoutSeconds`
- **理由:** dev_flexion 已包含 VK 映射 `{"vk.global.app", EAccoutType.hwVkid}`, 且抽象更优
### TYSdkFacade_Pay.cs — 跳过
- dev_rustore: `var pType = tysdk.AccountInfo.hwPayType` (硬编码 rustore)
- dev_flexion: `var pType = ChannelConfig.PayType` (动态配置)
- **理由:** dev_flexion 的方案已覆盖 rustore 支付类型
### UnityBridgeFunc.cs — 跳过
- dev_rustore: 仅代码格式重排,无功能变更
- dev_flexion: 已包含 `Review()` 等方法声明
---
## 构建错误修复
```
Error: uses-sdk:minSdkVersion 25 cannot be smaller than version 26
declared in library [:tuyoosdk_1.0.0:]
```
**修复:** `ProjectSettings.asset``AndroidMinSdkVersion: 25``26`
**替代方案分析:**
- `tools:overrideLibrary="com.tuyoo.gamesdk"` (rustore AndroidManifest 已有) 只能绕过 manifest 合并冲突
- 无法绕过 gradle 层 `processReleaseMainManifest` task 的 minSdkVersion 检查
- 改 ProjectSettings 是唯一可行方案
---
## SKU 列表格式统一
**问题:** Rustore 和 GooglePlay 返回的商品列表 JSON 字段不同C# 端用同一个 `SKUDetail` 类反序列化。
**方案:** 在 Java 端 `ChannelHelpers.normalizeSkuList()` 中统一格式C# 端不做渠道判断。
### 数据流
```
SDK API → defaultThirdExtend() → ChannelHelpers.normalizeSkuList(msg) → Unity SKUListResult → DeserializeObject<List<SKUDetail>>
```
### 变更文件
#### 1. ExtraPluginCfgs/Android/rustore/ChannelHelpers.java — 新增 normalizeSkuList
```java
// 将 Rustore 原始格式转为统一格式:
// price: "199" (戈比) → "₽1.99"
// 新增 price_amount_micros: "199" * 10000 = "1990000"
// 新增 price_currency_code: ← currency 字段
```
#### 2. ExtraPluginCfgs/Android/googleplay/ChannelHelpers.java — 新增 normalizeSkuList
```java
// GooglePlay 格式已是标准,直接原样返回
public static String normalizeSkuList(String msg) { return msg; }
```
#### 3. ExtraPluginCfgs/Android/flexion/ChannelHelpers.java — 新增 normalizeSkuList
```java
// Flexion 有自己的 SDKHelper.queryProducts(),不走 defaultThirdExtend编译需要方法存在
public static String normalizeSkuList(String msg) { return msg; }
```
#### 4. SDKManager.java — defaultThirdExtend 调用 normalizeSkuList
```java
// 回调成功时: result.put("respObj", ChannelHelpers.normalizeSkuList(msg));
```
#### 5. TYSdkModel.cs — SKUDetail 简化
```csharp
// 去掉 Normalize() 方法和 normalized 字段
// 属性直接用统一字段:
// ProdPrice => float.Parse(price_amount_micros) / 1000000
// ProdPriceStr => price
```
#### 6. TYSdkFacade_Pay.cs — SKUListResult 去掉 Normalize 调用
```csharp
// 反序列化后直接赋值,不再调 sku.Normalize()
```
### 扩展新渠道
添加新渠道时只需:
1. 创建 `ExtraPluginCfgs/Android/{channel}/ChannelHelpers.java`
2. 实现 `normalizeSkuList()` 将该渠道 SKU JSON 转为统一格式
3. C# 端无需改动

View File

@@ -93,3 +93,4 @@ crashlytics-build.properties
/Assets/StreamingAssets/*.bundle
/Assets/StreamingAssets/*.bundle.meta
.claude/*

View File

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

View File

@@ -0,0 +1,198 @@
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class ChannelBuildTool
{
public static readonly string[] SupportedChannels = { "GooglePlay", "Rustore", "Flexion" };
private const string DefaultChannel = "GooglePlay";
private const string DefaultAndroidBundleIdentifier = "com.arkgame.ft";
private const string ExtraCfgRoot = "../ExtraPluginCfgs/Android";
private const string TysdkPlugins = "Packages/tysdk/Plugins/Android";
private const string TysdkFiles = "Packages/tysdk/Files";
private const string AssetsPlugins = "Assets/Plugins/Android";
private static readonly string[] GradleFiles =
{
"mainTemplate.gradle",
"launcherTemplate.gradle",
"settingsTemplate.gradle",
"gradleTemplate.properties",
"AndroidManifest.xml"
};
// ==================== Channel Switching ====================
private static void SwitchChannel(string channel)
{
var channelDir = channel.ToLower();
var cfgDir = Path.Combine(ExtraCfgRoot, channelDir);
if (!Directory.Exists(cfgDir))
{
Debug.LogError($"[ChannelSwitch] Channel config not found: {cfgDir}");
return;
}
Debug.Log($"[ChannelSwitch] Switching to {channel}...");
currentChannel = channel;
// 1. Sync AAR
var srcAarDir = Path.Combine(cfgDir, "Plugins/Android");
if (Directory.Exists(srcAarDir))
{
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));
File.Copy(aar, dest, true);
Debug.Log($"[ChannelSwitch] AAR: {aar} → {dest}");
}
}
// 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);
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKAdapter.java"))
File.Delete(oldJava);
var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java");
if (File.Exists(oldConfig)) File.Delete(oldConfig);
// 3. Sync Gradle + Manifest
foreach (var fileName in GradleFiles)
{
var src = Path.Combine(cfgDir, fileName);
if (File.Exists(src))
{
var dest = Path.Combine(AssetsPlugins, fileName);
File.Copy(src, dest, true);
Debug.Log($"[ChannelSwitch] Config: {src} → {dest}");
}
}
// 4. Sync channel Java files to tysdk package
foreach (var javaFile in Directory.GetFiles(cfgDir, "*.java"))
{
var fileName = Path.GetFileName(javaFile);
var dest = Path.Combine(TysdkPlugins, fileName);
File.Copy(javaFile, dest, true);
Debug.Log($"[ChannelSwitch] Java: {javaFile} → {dest}");
}
// 5. Sync google-services.json (fallback to GooglePlay default)
var srcGoogleServices = Path.Combine(cfgDir, "Files/google-services.json");
if (!File.Exists(srcGoogleServices))
srcGoogleServices = Path.Combine(ExtraCfgRoot, "googleplay/Files/google-services.json");
var destGoogleServices = Path.Combine(TysdkFiles, "google-services.json");
File.Copy(srcGoogleServices, destGoogleServices, true);
Debug.Log($"[ChannelSwitch] google-services.json: {srcGoogleServices} → {destGoogleServices}");
// 6. Set Bundle Identifier per channel
var bundleId = channel switch
{
"Flexion" => "com.arkgame.ft.flexion",
_ => "com.arkgame.ft"
};
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, bundleId);
Debug.Log($"[CHANNEL-VERIFY] Bundle Identifier → {bundleId}");
// 7. Set runtime channel
tysdk.ChannelConfig.SetChannel(channel);
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
Debug.Log($"[ChannelSwitch] Done. Channel: {channel}");
}
// ==================== Build ====================
private static void RestoreDefaultChannel()
{
Debug.Log($"[ChannelBuild] Restore default channel -> {DefaultChannel}");
SwitchChannel(DefaultChannel);
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, DefaultAndroidBundleIdentifier);
Debug.Log($"[ChannelBuild] Restore bundle identifier -> {DefaultAndroidBundleIdentifier}");
AssetDatabase.SaveAssets();
}
private static void BuildAndroidForChannel(string channel, bool debug)
{
try
{
SwitchChannel(channel);
string fileName = debug ? $"tysdkTest_{channel}_debug.apk" : $"tysdkTest_{channel}.apk";
string outPath = $"../Bin/{fileName}";
var buildOptions = debug
? BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection
: BuildOptions.None;
var buildPlayerOptions = new BuildPlayerOptions
{
scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(),
locationPathName = outPath,
target = BuildTarget.Android,
options = buildOptions,
};
Debug.Log($"[ChannelBuild] Building {channel} → {outPath}");
var buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);
if (buildReport?.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
{
Debug.Log($"[ChannelBuild] Success: {Path.GetFullPath(outPath)}");
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
}
else
{
Debug.LogError($"[ChannelBuild] Failed: {buildReport?.summary}");
}
}
finally
{
RestoreDefaultChannel();
}
}
private static string currentChannel = "";
// ==================== Menu: Switch Channel ====================
[MenuItem("Tools/Switch Channel/GooglePlay")]
public static void Switch_GooglePlay() => SwitchChannel("GooglePlay");
[MenuItem("Tools/Switch Channel/Rustore")]
public static void Switch_Rustore() => SwitchChannel("Rustore");
[MenuItem("Tools/Switch Channel/Flexion")]
public static void Switch_Flexion() => SwitchChannel("Flexion");
// ==================== Menu: Build ====================
[MenuItem("Tools/Build Android (Channel)/GooglePlay")]
public static void Build_GooglePlay() => BuildAndroidForChannel("GooglePlay", false);
[MenuItem("Tools/Build Android (Channel)/GooglePlay Debug")]
public static void Build_GooglePlay_Debug() => BuildAndroidForChannel("GooglePlay", true);
[MenuItem("Tools/Build Android (Channel)/Rustore")]
public static void Build_Rustore() => BuildAndroidForChannel("Rustore", false);
[MenuItem("Tools/Build Android (Channel)/Rustore Debug")]
public static void Build_Rustore_Debug() => BuildAndroidForChannel("Rustore", true);
[MenuItem("Tools/Build Android (Channel)/Flexion")]
public static void Build_Flexion() => BuildAndroidForChannel("Flexion", false);
[MenuItem("Tools/Build Android (Channel)/Flexion Debug")]
public static void Build_Flexion_Debug() => BuildAndroidForChannel("Flexion", true);
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6db64ebff4c74a8e987935dcdf9fd211
guid: c7bad621141fb304caceb22ea91d7db3
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 4fac2ab3b272f487c8ef6515ae7668e1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ef8467ffb0e4447b79a8804884a7520a
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/ByteDance/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:bytedance-adapter:8.1.0.3.0">
<repositories>
<repository>https://artifact.bytedance.com/repository/pangle</repository>
</repositories>
</androidPackage>
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationByteDanceAdapter" version="8.1.0.6.0" />
</iosPods>
</dependencies>

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 0828555cb1ce94702a4af6f3dce3d735
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 324c3a2236ad740799b8b6dc27e85c82
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: db1de4066dc4e4290b3879b34fa87de2
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Mintegral/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:mintegral-adapter:17.1.61.0">
<repositories>
<repository>https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea</repository>
</repositories>
</androidPackage>
<androidPackage spec="androidx.recyclerview:recyclerview:1.2.1" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationMintegralAdapter" version="8.1.4.0.0" />
</iosPods>
</dependencies>

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 221b2a20a58a04f2cb4afb0779587206
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a6796c876aa794d13affab798ea46f1c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9ecadc3649c184389b90252aa2a2b448
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Moloco/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:moloco-adapter:4.9.0.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationMolocoAdapter" version="4.6.1.1" />
</iosPods>
</dependencies>

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 0f5b2cb1209274ba18b234b42d2efe96
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Moloco/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 4b1126795647541ba8c2eb166b7b70bc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 277e2383262e844708fc74a6104f4985
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Yandex/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:yandex-adapter:8.1.0.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationYandexAdapter" version="8.1.0.1" />
</iosPods>
</dependencies>

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: a77aaf886990c4e6ba469a4f083b9284
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Yandex/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -29,10 +29,20 @@
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
<meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="true" />
<meta-data android:name="google_analytics_default_allow_ad_storage" android:value="true" />
<meta-data android:name="google_analytics_default_allow_ad_user_data" android:value="true" />
<meta-data android:name="google_analytics_default_allow_ad_personalization_signals" android:value="true" />
</activity>
<meta-data android:name="CHANNEL" android:value="GooglePlay" />
</application>
<queries>
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" /> <!-- allows app to access Facebook app features -->
<provider android:authorities="com.facebook.orca.provider.PlatformProvider" /> <!-- allows sharing to Messenger app -->
<provider android:authorities="com.facebook.orca.provider.PlatformProvider" /> <!-- allows sharing to Messenger app -->
<intent>
<action android:name="android.intent.action.MAIN"/>
</intent>
</queries>
</manifest>

View File

@@ -1,10 +1,11 @@
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
android.useAndroidX=true
android.enableJetifier=true
# Android Resolver Properties Start
android.useAndroidX=true
android.enableJetifier=true
# Android Resolver Properties End
android.suppressUnsupportedCompileSdk=35
**ADDITIONAL_PROPERTIES**

View File

@@ -17,6 +17,7 @@ android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {

View File

@@ -13,7 +13,7 @@ android {
}
// Android Resolver Exclusions End
android {
namespace "com.unity3d.player"
namespace "com.unity3d.player"
}
dependencies {
@@ -36,7 +36,7 @@ dependencies {
implementation 'net.aihelp:android-aihelp-aar:5.3.+'
implementation 'com.google.android.gms:play-services-auth:20.2.0'
implementation('com.android.billingclient:billing:6.0.1')
implementation('com.android.billingclient:billing:7.0.0')
implementation 'com.google.android.gms:play-services-basement:18.2.0'
implementation 'androidx.fragment:fragment:1.3.6'
@@ -48,7 +48,7 @@ dependencies {
implementation 'com.miui.referrer:homereferrer:1.0.0.6'
implementation platform('com.google.firebase:firebase-bom:32.7.4')
implementation platform('com.google.firebase:firebase-bom:34.6.0')
implementation 'com.google.firebase:firebase-analytics'
implementation('com.facebook.android:facebook-android-sdk:16.2.0')
@@ -60,18 +60,13 @@ dependencies {
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
// Android Resolver Dependencies Start
implementation 'androidx.recyclerview:recyclerview:1.2.1' // Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml:9
implementation 'com.android.installreferrer:installreferrer:2.1' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:7
implementation 'com.applovin.mediation:bytedance-adapter:8.1.0.3.0' // Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:facebook-adapter:[6.18.0.1]' // Assets/MaxSdk/Mediation/Facebook/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:fyber-adapter:8.4.2.0' // Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:google-adapter:[23.6.0.1]' // Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml:5
implementation 'com.applovin.mediation:ironsource-adapter:8.6.1.0.0' // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:mintegral-adapter:17.1.61.0' // Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml:8
implementation 'com.applovin.mediation:moloco-adapter:4.9.0.0' // Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:unityads-adapter:4.12.3.0' // Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:vungle-adapter:7.4.2.2' // Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml:4
implementation 'com.applovin.mediation:yandex-adapter:8.1.0.0' // Assets/MaxSdk/Mediation/Yandex/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
implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
@@ -80,23 +75,11 @@ dependencies {
// Android Resolver Dependencies End
**DEPS**}
// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('/lib/armeabi/*' + '*')
exclude ('/lib/mips/*' + '*')
exclude ('/lib/mips64/*' + '*')
exclude ('/lib/x86/*' + '*')
exclude ('/lib/x86_64/*' + '*')
}
}
// Android Resolver Exclusions End
android {
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11

View File

@@ -18,15 +18,9 @@ dependencyResolutionManagement {
mavenCentral()
// Android Resolver Repos Start
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven {
url "https://artifact.bytedance.com/repository/pangle" // Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml:8
}
maven {
url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
}
maven {
url "https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea" // Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml:8
}
mavenLocal()
// Android Resolver Repos End
flatDir {

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 813d4ac98b7f4092a3dddb472539f9d2
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 9176f7473eb140f4af98b026642a0c8c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
public static class AdReadyStateLog
{
public static string FormatReadyCheck(string format, string adUnitId, bool isReady)
{
string status = isReady ? "OK" : "WARNING";
return $"[{format}] Loaded callback ready check: adUnitId={adUnitId}, IsReady={isReady}, {status}";
}
}

View File

@@ -1,423 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class AdTest : MonoBehaviour
{
[Header("Rewarded")]
[SerializeField] private string androidRewardedAdUnitId = "c6c49a100de8496c";
[SerializeField] private string iosRewardedAdUnitId = "ec3c8fd688cdb5e2";
[Header("Interstitial")]
[SerializeField] private string androidInterstitialAdUnitId = "0dcd37fa0885aa1f";
[SerializeField] private string iosInterstitialAdUnitId = "interstitial_test_ios";
[Header("UI")]
[SerializeField] private Button initMaxButton;
[SerializeField] private Button loadRewardedButton;
[SerializeField] private Button showRewardedButton;
[SerializeField] private Button forceShowRewardedButton;
[SerializeField] private Button checkRewardedReadyButton;
[SerializeField] private Button loadInterstitialButton;
[SerializeField] private Button showInterstitialButton;
[SerializeField] private Button forceShowInterstitialButton;
[SerializeField] private Button checkInterstitialReadyButton;
[SerializeField] private Button mediationDebuggerButton;
[SerializeField] private Button clearLogButton;
[SerializeField] private Text logText;
private const int MaxLogLines = 80;
private readonly List<string> logLines = new List<string>();
private bool callbacksRegistered;
private bool sdkInitializeRequested;
private int rewardedLoadFailCount;
private int interstitialLoadFailCount;
private string RewardedAdUnitId
{
get
{
#if UNITY_IOS
return iosRewardedAdUnitId;
#else
return androidRewardedAdUnitId;
#endif
}
}
private string InterstitialAdUnitId
{
get
{
#if UNITY_IOS
return iosInterstitialAdUnitId;
#else
return androidInterstitialAdUnitId;
#endif
}
}
public void Configure(
Button initMax,
Button loadRewarded,
Button showRewarded,
Button forceShowRewarded,
Button checkRewardedReady,
Button loadInterstitial,
Button showInterstitial,
Button forceShowInterstitial,
Button checkInterstitialReady,
Button mediationDebugger,
Button clearLog,
Text output)
{
initMaxButton = initMax;
loadRewardedButton = loadRewarded;
showRewardedButton = showRewarded;
forceShowRewardedButton = forceShowRewarded;
checkRewardedReadyButton = checkRewardedReady;
loadInterstitialButton = loadInterstitial;
showInterstitialButton = showInterstitial;
forceShowInterstitialButton = forceShowInterstitial;
checkInterstitialReadyButton = checkInterstitialReady;
mediationDebuggerButton = mediationDebugger;
clearLogButton = clearLog;
logText = output;
}
private void Start()
{
Bind(initMaxButton, InitMax);
Bind(loadRewardedButton, LoadRewarded);
Bind(showRewardedButton, ShowRewarded);
Bind(forceShowRewardedButton, ForceShowRewarded);
Bind(checkRewardedReadyButton, CheckRewardedReady);
Bind(loadInterstitialButton, LoadInterstitial);
Bind(showInterstitialButton, ShowInterstitial);
Bind(forceShowInterstitialButton, ForceShowInterstitial);
Bind(checkInterstitialReadyButton, CheckInterstitialReady);
Bind(mediationDebuggerButton, ShowMediationDebugger);
Bind(clearLogButton, ClearLog);
AppendLog("AdTest ready.");
}
private void OnDestroy()
{
if (!callbacksRegistered)
{
return;
}
MaxSdkCallbacks.OnSdkInitializedEvent -= OnSdkInitialized;
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnRewardedAdLoaded;
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnRewardedAdLoadFailed;
MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent -= OnRewardedAdDisplayed;
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent -= OnRewardedAdDisplayFailed;
MaxSdkCallbacks.Rewarded.OnAdHiddenEvent -= OnRewardedAdHidden;
MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent -= OnRewardedAdReceivedReward;
MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent -= OnRewardedAdRevenuePaid;
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent -= OnInterstitialAdLoaded;
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent -= OnInterstitialAdLoadFailed;
MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent -= OnInterstitialAdDisplayed;
MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent -= OnInterstitialAdDisplayFailed;
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent -= OnInterstitialAdHidden;
MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent -= OnInterstitialAdRevenuePaid;
}
private void Bind(Button button, Action action)
{
if (button == null)
{
return;
}
button.onClick.AddListener(() => action());
}
public void InitMax()
{
RegisterCallbacks();
if (sdkInitializeRequested)
{
AppendLog("MAX initialization already requested.");
return;
}
sdkInitializeRequested = true;
MaxSdk.SetHasUserConsent(true);
MaxSdk.SetDoNotSell(false);
#if UNITY_IOS
MaxSdk.SetExtraParameter("disable_b2b_ad_unit_ids", "ec3c8fd688cdb5e2,00442f7962682a80");
#else
MaxSdk.SetExtraParameter("disable_b2b_ad_unit_ids", "c6c49a100de8496c,0dcd37fa0885aa1f");
#endif
MaxSdk.InitializeSdk();
AppendLog("MAX initialization requested.");
}
public void LoadRewarded()
{
EnsureInitialized();
AppendLog($"Load rewarded: {RewardedAdUnitId}");
MaxSdk.LoadRewardedAd(RewardedAdUnitId);
}
public void ShowRewarded()
{
EnsureInitialized();
bool isReady = MaxSdk.IsRewardedAdReady(RewardedAdUnitId);
AppendLog($"Show rewarded requested. IsReady={isReady}");
if (isReady)
{
MaxSdk.ShowRewardedAd(RewardedAdUnitId);
return;
}
LoadRewarded();
}
public void ForceShowRewarded()
{
EnsureInitialized();
AppendLog($"Force show rewarded without ready check: {RewardedAdUnitId}");
MaxSdk.ShowRewardedAd(RewardedAdUnitId);
}
public void CheckRewardedReady()
{
EnsureInitialized();
bool isReady = MaxSdk.IsRewardedAdReady(RewardedAdUnitId);
AppendReadyCheck("Rewarded", RewardedAdUnitId, isReady);
}
public void LoadInterstitial()
{
EnsureInitialized();
if (string.IsNullOrEmpty(InterstitialAdUnitId))
{
AppendWarning("Interstitial ad unit ID is empty.");
return;
}
AppendLog($"Load interstitial: {InterstitialAdUnitId}");
MaxSdk.LoadInterstitial(InterstitialAdUnitId);
}
public void ShowInterstitial()
{
EnsureInitialized();
if (string.IsNullOrEmpty(InterstitialAdUnitId))
{
AppendWarning("Interstitial ad unit ID is empty.");
return;
}
bool isReady = MaxSdk.IsInterstitialReady(InterstitialAdUnitId);
AppendLog($"Show interstitial requested. IsReady={isReady}");
if (isReady)
{
MaxSdk.ShowInterstitial(InterstitialAdUnitId);
return;
}
LoadInterstitial();
}
public void ForceShowInterstitial()
{
EnsureInitialized();
if (string.IsNullOrEmpty(InterstitialAdUnitId))
{
AppendWarning("Interstitial ad unit ID is empty.");
return;
}
AppendLog($"Force show interstitial without ready check: {InterstitialAdUnitId}");
MaxSdk.ShowInterstitial(InterstitialAdUnitId);
}
public void CheckInterstitialReady()
{
EnsureInitialized();
if (string.IsNullOrEmpty(InterstitialAdUnitId))
{
AppendWarning("Interstitial ad unit ID is empty.");
return;
}
bool isReady = MaxSdk.IsInterstitialReady(InterstitialAdUnitId);
AppendReadyCheck("Interstitial", InterstitialAdUnitId, isReady);
}
public void ClearLog()
{
logLines.Clear();
if (logText != null)
{
logText.text = string.Empty;
}
}
public void ShowMediationDebugger()
{
AppendLog("Show mediation debugger requested.");
MaxSdk.ShowMediationDebugger();
}
private void EnsureInitialized()
{
if (!sdkInitializeRequested)
{
InitMax();
}
}
private void RegisterCallbacks()
{
if (callbacksRegistered)
{
return;
}
callbacksRegistered = true;
MaxSdkCallbacks.OnSdkInitializedEvent += OnSdkInitialized;
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoaded;
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedAdLoadFailed;
MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayed;
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnRewardedAdDisplayFailed;
MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdHidden;
MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedReward;
MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnRewardedAdRevenuePaid;
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialAdLoaded;
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialAdLoadFailed;
MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialAdDisplayed;
MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += OnInterstitialAdDisplayFailed;
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialAdHidden;
MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnInterstitialAdRevenuePaid;
}
private void OnSdkInitialized(MaxSdkBase.SdkConfiguration configuration)
{
AppendLog($"MAX initialized. Success={configuration.IsSuccessfullyInitialized}");
}
private void OnRewardedAdLoaded(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
rewardedLoadFailCount = 0;
AppendLog($"Rewarded loaded callback: {adUnitId}");
bool isReady = MaxSdk.IsRewardedAdReady(adUnitId);
AppendReadyCheck("Rewarded", adUnitId, isReady);
}
private void OnRewardedAdLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
rewardedLoadFailCount++;
AppendWarning($"Rewarded load failed: {adUnitId}, attempt={rewardedLoadFailCount}, message={errorInfo.Message}");
}
private void OnRewardedAdDisplayed(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Rewarded displayed: {adUnitId}");
}
private void OnRewardedAdDisplayFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo, MaxSdkBase.AdInfo adInfo)
{
AppendWarning($"Rewarded display failed: {adUnitId}, message={errorInfo.Message}");
LoadRewarded();
}
private void OnRewardedAdHidden(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Rewarded hidden: {adUnitId}");
LoadRewarded();
}
private void OnRewardedAdReceivedReward(string adUnitId, MaxSdkBase.Reward reward, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Rewarded received reward: {adUnitId}, label={reward.Label}, amount={reward.Amount}");
}
private void OnRewardedAdRevenuePaid(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Rewarded revenue paid: {adUnitId}, revenue={adInfo.Revenue}");
}
private void OnInterstitialAdLoaded(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
interstitialLoadFailCount = 0;
AppendLog($"Interstitial loaded callback: {adUnitId}");
bool isReady = MaxSdk.IsInterstitialReady(adUnitId);
AppendReadyCheck("Interstitial", adUnitId, isReady);
}
private void OnInterstitialAdLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
interstitialLoadFailCount++;
AppendWarning($"Interstitial load failed: {adUnitId}, attempt={interstitialLoadFailCount}, message={errorInfo.Message}");
}
private void OnInterstitialAdDisplayed(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Interstitial displayed: {adUnitId}");
}
private void OnInterstitialAdDisplayFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo, MaxSdkBase.AdInfo adInfo)
{
AppendWarning($"Interstitial display failed: {adUnitId}, message={errorInfo.Message}");
LoadInterstitial();
}
private void OnInterstitialAdHidden(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Interstitial hidden: {adUnitId}");
LoadInterstitial();
}
private void OnInterstitialAdRevenuePaid(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
AppendLog($"Interstitial revenue paid: {adUnitId}, revenue={adInfo.Revenue}");
}
private void AppendReadyCheck(string format, string adUnitId, bool isReady)
{
string line = AdReadyStateLog.FormatReadyCheck(format, adUnitId, isReady);
if (isReady)
{
AppendLog(line);
}
else
{
AppendWarning(line);
}
}
private void AppendWarning(string message)
{
AppendLog(message, true);
}
private void AppendLog(string message, bool warning = false)
{
string line = $"[{DateTime.Now:HH:mm:ss}] {message}";
logLines.Add(line);
while (logLines.Count > MaxLogLines)
{
logLines.RemoveAt(0);
}
if (logText != null)
{
logText.text = string.Join("\n", logLines);
}
// This scene is for device-side manual QA, so the UI log is the source of truth.
}
}

View File

@@ -1,98 +0,0 @@
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.EventSystems;
public class AdTestSceneBootstrap : MonoBehaviour
{
private void Awake()
{
EnsureEventSystem();
AdTest adTest = GetComponent<AdTest>();
if (adTest == null)
{
adTest = gameObject.AddComponent<AdTest>();
}
adTest.Configure(
FindButton("InitMax", "Init"),
FindButton("LoadRewardedAd", "LoadRewarded", "Load Rewarded", "RewardedLoad"),
FindButton("RewardedAd", "Max", "ShowRewarded", "Show Rewarded", "RewardedShow"),
FindButton("ForceRewardedAd", "ForceRewarded", "Force Rewarded"),
FindButton("CheckRewardedReady", "CheckRewarded", "Check Rewarded", "RewardedReady"),
FindButton("LoadInterstitialAd", "LoadInterstitial", "Load Interstitial", "InterstitialLoad", "PayList"),
FindButton("ShowInterstitialAd", "ShowInterstitial", "Show Interstitial", "InterstitialShow", "Pay"),
FindButton("ForceInterstitialAd", "ForceInterstitial", "Force Interstitial"),
FindButton("CheckInterstitialReady", "CheckInterstitial", "Check Interstitial", "InterstitialReady"),
FindButton("MediationDebugger", "ATT", "Debugger", "Mediation Debugger"),
FindButton("ClearLog", "Clear Log"),
FindText("Message", "Log", "LogText"));
RenameButton("InitMax", "Init MAX");
RenameButton("LoadRewardedAd", "Load Reward");
RenameButton("CheckRewardedReady", "Check Reward");
RenameButton("RewardedAd", "Rewarded");
RenameButton("ForceRewardedAd", "Force Reward");
RenameButton("LoadInterstitialAd", "Load Inter");
RenameButton("CheckInterstitialReady", "Check Inter");
RenameButton("ShowInterstitialAd", "Show Inter");
RenameButton("ForceInterstitialAd", "Force Inter");
RenameButton("MediationDebugger", "Debugger");
RenameButton("ClearLog", "Clear Log");
}
private void EnsureEventSystem()
{
if (FindObjectOfType<EventSystem>() != null)
{
return;
}
GameObject eventSystem = new GameObject("EventSystem");
eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
}
private Button FindButton(params string[] names)
{
foreach (string name in names)
{
GameObject target = GameObject.Find(name);
if (target != null && target.TryGetComponent(out Button button))
{
return button;
}
}
return null;
}
private Text FindText(params string[] names)
{
foreach (string name in names)
{
GameObject target = GameObject.Find(name);
if (target != null && target.TryGetComponent(out Text text))
{
return text;
}
}
return null;
}
private void RenameButton(string buttonName, string label)
{
GameObject target = GameObject.Find(buttonName);
if (target == null)
{
return;
}
Text text = target.GetComponentInChildren<Text>();
if (text != null)
{
text.text = label;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7f4c0e3f6b3d4d6b9b8a923dc1274d31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -16,6 +16,8 @@ public class SDKTest : MonoBehaviour
private Button _loginGuestBtn;
[SerializeField]
private Button _loginGoogleBtn;
[SerializeField] // VK登录
private Button _loginVkBtn;
[SerializeField]
private Button _loginFbBtn;
[SerializeField]
@@ -23,6 +25,10 @@ public class SDKTest : MonoBehaviour
[SerializeField]
private Button _loginTokenBtn;
[SerializeField]
private Button _linkVkBtn;
[SerializeField]
private Button _checkVkBtn;
[SerializeField]
private Button _linkGoogleBtn;
[SerializeField]
private Button _checkGoogleBtn;
@@ -50,8 +56,15 @@ public class SDKTest : MonoBehaviour
private Button _fbShareLinkBtn;
[SerializeField]
private Button _mfbShareLinkBtn;
[SerializeField]
private Button _reviewBtn;
[SerializeField]
private Text _messageTxt;
[SerializeField]
private Text _titleText;
void Start()
{
@@ -61,6 +74,7 @@ public class SDKTest : MonoBehaviour
GContext.container.RegisterInstance<IConfig>(config);
Debug.Log("SDKTest Start");
UpdateChannelTitle();
//===============================================================================
//====================== Test Server ===========================
// UnityBridgeFunc.UnityResetServerUrl("https://128-hwsfsdk-sdk-test01.qijihdhk.com");
@@ -68,10 +82,15 @@ public class SDKTest : MonoBehaviour
//====================== Test Server ===========================
//===============================================================================
//
_InitBtn.onClick.AddListener(Init);
_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));
_loginFbBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwFacebook));
_loginVkBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwVkid));
#if UNITY_IOS
_loginAppleBtn.onClick.AddListener(() => OnLogin(EAccoutType.Apple));
#endif
@@ -79,6 +98,8 @@ public class SDKTest : MonoBehaviour
_logOutBtn.onClick.AddListener(OnLogout);
_linkGoogleBtn.onClick.AddListener(OnLinkGoogle);
_checkGoogleBtn.onClick.AddListener(OncheckGoogle);
_linkVkBtn.onClick.AddListener(OnLinkVkId);
_checkVkBtn.onClick.AddListener(OnCheckVKId);
_linkFacbookBtn.onClick.AddListener(OnLinkFacebook);
_linkAppleBtn.onClick.AddListener(OnLinkApple);
_checkFacbookBtn.onClick.AddListener(OncheckFacebook);
@@ -90,11 +111,13 @@ public class SDKTest : MonoBehaviour
_signoutBtn.onClick.AddListener(OnSignoutBtn);
_fbShareLinkBtn.onClick.AddListener(OnFBShareLinkBtn);
_mfbShareLinkBtn.onClick.AddListener(OnMessengerShareLinkBtn);
_reviewBtn.onClick.AddListener(OnReview);
Debug.Log("SDKTest Started");
}
void Init()
{
_messageTxt.text = "Initializing...";
TYSdkFacade.Instance.Init();
TDAnalytics.Init("0caf287574bd4a9bb08be8995705ef6f", "https://122-slg-online01.qijihdhk.com:8991");
@@ -114,6 +137,19 @@ public class SDKTest : MonoBehaviour
Debug.Log($"customId = {customId}, customId_2 = {customId_2}");
UnityBridgeFunc.UnityResetServerUrl("https://128-hwsfsdk-sdk-test01.qijihdhk.com");
GContext.container.Resolve<IConfig>().Set("PAY_VERIFY_URL", "https://192.168.9.91:7036/api/FlexionCallback");
}
private void UpdateChannelTitle()
{
#if UNITY_ANDROID
using (var cls = new AndroidJavaClass("com.unity3d.player.ChannelEntry"))
{
string channel = cls.GetStatic<string>("CHANNEL");
Debug.Log($"[SDKTest] Channel from Java: {channel}");
_titleText.text = $" {channel}";
}
#endif
}
int retryAttempt;
@@ -133,16 +169,11 @@ public class SDKTest : MonoBehaviour
MaxSdkCallbacks.OnSdkInitializedEvent += (MaxSdkBase.SdkConfiguration sdkConfiguration) =>
{
Debug.Log($"[MaxSdk::OnSdkInitializedEvent] Init:{sdkConfiguration.IsSuccessfullyInitialized}");
};
MaxSdk.SetHasUserConsent(true);
MaxSdk.SetDoNotSell(false);
#if UNITY_IOS
MaxSdk.SetExtraParameter("disable_b2b_ad_unit_ids", "ec3c8fd688cdb5e2,00442f7962682a80");
#else
MaxSdk.SetExtraParameter("disable_b2b_ad_unit_ids", "c6c49a100de8496c,0dcd37fa0885aa1f");
#endif
MaxSdk.InitializeSdk();
}
};
MaxSdk.SetHasUserConsent(true);
MaxSdk.SetDoNotSell(false);
MaxSdk.InitializeSdk();
}
private async void OnLogin(EAccoutType accoutType)
{
@@ -152,6 +183,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}";
}
@@ -178,10 +210,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";
@@ -192,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;
@@ -211,6 +267,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
{
@@ -341,6 +398,13 @@ eb68f93a08156e04
_messageTxt.text = $"\n link google account : {isSuccess}";
}
private async void OnLinkVkId()
{
_messageTxt.text = string.Empty;
var isSuccess = await TYSdkFacade.Instance.LinkAccount(EAccoutType.hwVkid);
_messageTxt.text = $"\n link VkId account : {isSuccess} {isSuccess.code}";
}
private async void OnLinkFacebook()
{
_messageTxt.text = string.Empty;
@@ -362,6 +426,13 @@ eb68f93a08156e04
_messageTxt.text = $"\n google account linked : {isSuccess}";
}
private async void OnCheckVKId()
{
_messageTxt.text = string.Empty;
var isSuccess = await TYSdkFacade.Instance.LinkCheck(EAccoutType.hwVkid);
_messageTxt.text = $"\n vk account linked : {isSuccess}";
}
private async void OncheckFacebook()
{
_messageTxt.text = string.Empty;
@@ -384,6 +455,11 @@ eb68f93a08156e04
UnityBridgeFunc.MessengerShareLink("title", "content", "https://www.baidu.com");
}
private void OnReview()
{
UnityBridgeFunc.Review();
}
/*
public void OnTaBtn()
{

View File

@@ -0,0 +1,13 @@
package com.unity3d.player;
public class ChannelEntry {
public static final String CHANNEL = "GooglePlay";
public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
return null;
}
public static String normalizeSkuList(String msg) {
return msg;
}
}

View File

@@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: eac9aa9954e93c54fbafd3974ca30a5b
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +1,7 @@
package com.unity3d.player;
public class ConfigManager {
//通用参数
// Common SDK parameters
public static int SDK_APPID = 20587;
public static String SDK_GAMEID = "20587";
public static String SDK_PROJECTID = "20587";
@@ -11,14 +10,12 @@ public class ConfigManager {
public static String SDK_LOGIN_SERVER_URL = "https://128-hwsfsdk-sdk-online01.qijihdhk.com";
// 防沉迷 - nameSpace
// FCM (anti-addiction system)
public static String FCM_NAMESPACE = "128";
// 防沉迷 - 长连接服务地址+端口
public static String FCM_IAM_HOST = "tcp://fcmtcp.tuyoo.com:3563";
// 防沉迷 - 防沉迷服务地址
public static String FCM_ANTISERVERURL = "https://fcmapi.tuyoo.com";
// Unity Facade callback names
public static final String UNITY_FACADE_NAME = "TYSdkFacade";
public static final String LOGIN_CALLBACK_FUNCTION_NAME = "LoginResult";
public static final String LINKACCOUT_CALLBACK_FUNCTION_NAME = "LinkAccoutResult";
@@ -28,4 +25,5 @@ public class ConfigManager {
public static final String PAY_TYPE_CALLBACK_FUNCTION_NAME = "SKUListResult";
public static final String FCM_NOTICE_FUNCTION_NAME = "FCMCallback";
public static final String FCM_REALNAME_CALLBACK_FUNCTION_NAME = "RealNameCallback";
public static final String INIT_CALLBACK_FUNCTION_NAME = "InitResult";
}

View File

@@ -7,7 +7,7 @@ PluginImporter:
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:

View File

@@ -1,4 +1,3 @@
package com.unity3d.player;
import android.app.Activity;
@@ -29,10 +28,7 @@ import com.tuyoo.gamesdk.api.SDKAPI;
import com.tuyoo.gamesdk.api.SDKCallBack;
import com.tuyoo.gamesdk.api.SDKWrapper;
import com.tuyoo.gamesdk.api.TuYooClientID;
import com.tuyoo.gamesdk.event.EventBus;
import com.tuyoo.gamesdk.event.EventConsts;
import com.tuyoo.gamesdk.event.data.PayEventData;
import com.tuyoo.gamesdk.event.data.WeixinShareData;
import com.tuyoo.gamesdk.login.model.SnsInfo;
import com.tuyoo.gamesdk.model.InitParam;
import com.tuyoo.gamesdk.pay.model.PayType;
@@ -44,16 +40,106 @@ import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import kotlinx.serialization.json.Json;
/**
* Unified SDKManager for all channels (GooglePlay / Flexion / RuStore).
* C# passes the channel string via InitSDK(activity, channel); Java dispatches
* internally via switch. All login/link/GA/share logic is shared.
* Channel-specific behaviour is limited to: Init, Pay, thirdExtend, Review.
*/
public class SDKManager {
private final static Handler handler = new Handler(Looper.getMainLooper());
private static final Handler handler = new Handler(Looper.getMainLooper());
private static IGASDK gasdk;
private static final String TAG = com.unity3d.player.SDKManager.class.getSimpleName();
private static final String TAG = SDKManager.class.getSimpleName();
private static String curType = TuYooClientID.tyGuest;
public static void InitSDK(Activity activity)
{
private static SnsInfo tmpSnsInfo;
private static String channel = "GooglePlay";
// 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 consume(String token);
}
public interface IProductQueryAdapter {
void queryProducts();
void queryProducts(String productIds);
}
public interface IPendingPurchaseAdapter {
String getPendingPurchases();
}
public interface IBillingLifecycleAdapter {
void refreshBilling();
}
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 (sdkAdapter instanceof IPaymentAdapter) {
((IPaymentAdapter) sdkAdapter).consume(token);
}
}
public static String GetPendingPurchases() {
if (sdkAdapter instanceof IPendingPurchaseAdapter) {
return ((IPendingPurchaseAdapter) sdkAdapter).getPendingPurchases();
}
return "[]";
}
public static void RefreshBillingService() {
Log.i(TAG, "RefreshBillingService");
if (sdkAdapter instanceof IBillingLifecycleAdapter) {
((IBillingLifecycleAdapter) sdkAdapter).refreshBilling();
}
}
public static void CancelBillingFlow() {
Log.w(TAG, "CancelBillingFlow");
// FlexionSDKAdapter.cancelPurchase();
}
// -------------------------------------------------------------------------
// Init — with channel helper support
// -------------------------------------------------------------------------
public static void InitSDK(Activity activity, String ch) {
channel = ChannelEntry.CHANNEL;
sdkAdapter = ChannelEntry.createSDKAdapter();
Log.i(TAG, "[CHANNEL-VERIFY] Java InitSDK channel=" + channel
+ " fromCSharp=" + ch
+ " adapter=" + (sdkAdapter != null ? sdkAdapter.getClass().getSimpleName() : "default"));
// Sync channel to C# ChannelConfig
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
"SetChannelFromNative", channel);
if (sdkAdapter != null) {
sdkAdapter.init(activity);
} else {
initDefault(activity);
}
}
private static void initDefault(Activity activity) {
activity.runOnUiThread(() -> {
SDKAPI.getIns().lightModeEnable();
try {
@@ -66,12 +152,29 @@ public class SDKManager {
TYUnityActivity.setSdkInited();
SDKAPI.getIns().onActivityStart(activity);
SDKAPI.getIns().onActivityResume(activity);
notifyInitResult(true, "ok");
} catch (Exception e) {
System.out.println("initSDK Exception " + e);
System.out.println("initSDK Exception " + e);
notifyInitResult(false, e.getMessage());
}
});
}
static void notifyInitResult(boolean success, String msg) {
try {
JSONObject result = new JSONObject();
result.put("code", success ? 0 : 1);
result.put("msg", msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.INIT_CALLBACK_FUNCTION_NAME, result.toString());
} catch (JSONException e) {
Log.e(TAG, "notifyInitResult error: " + e);
}
}
// -------------------------------------------------------------------------
// Login / Callbacks (shared)
// -------------------------------------------------------------------------
public static void parseLoginInfo(int i, String s) {
try {
@@ -80,21 +183,21 @@ public class SDKManager {
if (i == 0) {
JSONObject serverResponse = new JSONObject(s);
JSONObject userInfo = serverResponse.getJSONObject("result");
result.put("loginData", userInfo );
result.put("loginData", userInfo);
} else {
result.put("errStr", s);
}
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LOGIN_CALLBACK_FUNCTION_NAME, result.toString());
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LOGIN_CALLBACK_FUNCTION_NAME, result.toString());
} catch (Exception e) {
Log.e(TAG,"SDKManager-parseLoginInfo-Exception" + e);
Log.e(TAG, "SDKManager-parseLoginInfo-Exception" + e);
SDKLog.e("SDKManager-parseLoginInfo-Exception" + e);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LOGIN_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LOGIN_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
}
}
//登录回调-统一的回调方法(保持回调仅有一个,否则可能会出现多次回调的情况)
static SDKCallBack.Login mLogin = new SDKCallBack.Login() {
protected static SDKCallBack.Login mLogin = new SDKCallBack.Login() {
@Override
public void callback(int i, String s) {
SDKLog.i("SDKCallBack.Login callback: " + i + " \n" + s);
@@ -103,21 +206,22 @@ public class SDKManager {
}
};
static SDKCallBack.Pay mPay = new SDKCallBack.Pay() {
protected static SDKCallBack.Pay mPay = new SDKCallBack.Pay() {
@Override
public void callback(int i, String s) {
SDKLog.i("SDKCallBack.Pay callback: " + i + " \n" + s);
try {
JSONObject result = new JSONObject();
result.put("code", i);
result.put("respObj", s);
result.put("errStr", s);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_CALLBACK_FUNCTION_NAME, result.toString());
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_CALLBACK_FUNCTION_NAME, result.toString());
} catch (Exception e) {
Log.e(TAG,"SDKManager-SDKCallBack.Pay-Exception" + e);
Log.e(TAG, "SDKManager-SDKCallBack.Pay-Exception" + e);
SDKLog.e("SDKManager-SDKCallBack.Pay-Exception" + e);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
}
}
};
@@ -132,18 +236,15 @@ public class SDKManager {
handler.post(() -> SDKAPI.getIns().loginByToken(token, mLogin));
}
public static String UnityLoginBySnsInfo()
{
if(tmpSnsInfo == null)
{
handler.post(() -> UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LOGIN_CALLBACK_FUNCTION_NAME, "{\"code\":1}"));
public static String UnityLoginBySnsInfo() {
if (tmpSnsInfo == null) {
handler.post(() -> UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LOGIN_CALLBACK_FUNCTION_NAME, "{\"code\":1}"));
return "";
}
SDKLog.i("UnityLoginBySns : " + tmpSnsInfo);
handler.post(() -> SDKAPI.getIns().loginBySnsInfo(tmpSnsInfo, mLogin));
return tmpSnsInfo.type;
}
public static void UnityGetIdentityFun(String type) {
@@ -161,15 +262,15 @@ public class SDKManager {
handler.post(() -> SDKAPI.getIns().logout(type));
}
private static SnsInfo tmpSnsInfo;
public static void LinkAccount(String type, String userId)
{
SDKAPI.getIns().getSnsInfo(type, (snscode, snsInfo, snsmsg) ->{
if(snscode == SDKCallBack.CODE_SUCCESS && snsInfo !=null)
{
SDKAPI.getIns().bindBySnsId(snsInfo, userId, (bindcode, bindmsg)->{
if(bindcode == SDKCallBack.CODE_SUCCESS)
{
// -------------------------------------------------------------------------
// Account linking (shared)
// -------------------------------------------------------------------------
public static void LinkAccount(String type, String userId) {
SDKAPI.getIns().getSnsInfo(type, (snscode, snsInfo, snsmsg) -> {
if (snscode == SDKCallBack.CODE_SUCCESS && snsInfo != null) {
SDKAPI.getIns().bindBySnsId(snsInfo, userId, (bindcode, bindmsg) -> {
if (bindcode == SDKCallBack.CODE_SUCCESS) {
try {
JSONObject result = new JSONObject();
result.put("code", bindcode);
@@ -178,41 +279,28 @@ public class SDKManager {
JSONObject serverResponse = new JSONObject(bindmsg);
JSONObject userInfo = serverResponse.getJSONObject("result");
result.put("loginData", userInfo);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, result.toString());
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, result.toString());
} catch (JSONException e) {
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
}
}
else if(bindcode == SDKCallBack.CODE_FAILED)
{
try
{
} else if (bindcode == SDKCallBack.CODE_FAILED) {
try {
JSONObject bindMsgObj = new JSONObject(bindmsg);
JSONObject bindMsgResult = bindMsgObj.getJSONObject("result");
int bindMsgResultCode = bindMsgResult.getInt("code");
if(bindMsgResultCode == 4)
{
SDKAPI.getIns().checkSnsInfo(snsInfo, (checkcode, checkMsg ) -> {
if(checkcode != SDKCallBack.CODE_SUCCESS)
{
if (bindMsgResultCode == 4) {
SDKAPI.getIns().checkSnsInfo(snsInfo, (checkcode, checkMsg) -> {
if (checkcode != SDKCallBack.CODE_SUCCESS) {
Log.w(TAG, checkMsg);
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
return;
}
try {
//{
// "track":"128:10:28654792:1766569516",
// "result":{
// "bindInfo":"{ // \"guest\": \"guest:devidmap3:bf12a8a28b663548e368ebe8742368b7\", // \"google\": \"google:118182347502471840780\" // }",
// "code":0,
// "info":"user exist",
// "exist":1,
// "userId":10030
// }
// }
tmpSnsInfo = snsInfo;
JSONObject checkMsgObj = new JSONObject(checkMsg);
JSONObject checkResult = checkMsgObj.getJSONObject("result");
@@ -222,165 +310,139 @@ public class SDKManager {
callbackMsg.put("code", -1);
callbackMsg.put("userId", bindUserId);
callbackMsg.put("bindInfo", bindInfo);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, callbackMsg.toString());
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME,
callbackMsg.toString());
} catch (JSONException e) {
Log.i(TAG, e.toString());
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
}
});
}
else
{
} else {
Log.w(TAG, bindmsg);
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
}
}
catch (JSONException e)
{
} catch (JSONException e) {
Log.i(TAG, e.toString());
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
}
}
else
{
} else {
Log.w(TAG, bindmsg);
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
}
});
}
else
{
} else {
Log.w(TAG, snsmsg);
String msg = "{\"code\":1, \"userId\": 0}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.LINKACCOUT_CALLBACK_FUNCTION_NAME, msg);
}
});
}
public static void CleanLinkTmpSnsInfo()
{
tmpSnsInfo = null;
}
public static void ChangeLinkAccount(String type, int oldUserId, int newUserId)
{
if(tmpSnsInfo == null)
{
public static void CleanLinkTmpSnsInfo() {
tmpSnsInfo = null;
}
public static void ChangeLinkAccount(String type, int oldUserId, int newUserId) {
if (tmpSnsInfo == null) {
String msg = "{\"code\":1, \"userId\": 0, \"msg\": \"tmpSnsInfo is null\"}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
return;
}
SDKAPI.getIns().unbindBySnsId(tmpSnsInfo, String.valueOf(oldUserId), (unbindCode, unbindMsg) -> {
Log.i(TAG, "unbindBySnsId code: " + unbindCode + " msg: " + unbindMsg);
if(unbindCode == SDKCallBack.CODE_SUCCESS)
{
if (unbindCode == SDKCallBack.CODE_SUCCESS) {
SDKAPI.getIns().bindBySnsId(tmpSnsInfo, String.valueOf(newUserId), (bindCode, bindMsg) -> {
Log.i(TAG, "bindBySnsId code: " + bindCode + " msg: " + bindMsg);
if(bindCode == SDKCallBack.CODE_SUCCESS)
{
if (bindCode == SDKCallBack.CODE_SUCCESS) {
String msg = "{\"code\":0, \"userId\": " + newUserId + ", \"msg\": \"success\"}";
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
}
else
{
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
} else {
String msg = String.format("{\"code\":-1, \"userId\": 0, \"msg\": \"%s\"}", bindMsg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
}
});
}
else
{
} else {
String msg = String.format("{\"code\":1, \"userId\": 0, \"msg\": \"%s\"}", unbindMsg);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
}
});
}
public static void LinkCheck(String type)
{
SDKAPI.getIns().getSnsInfo(type, (code, snsInfo, msg) -> {
if(code == SDKCallBack.CODE_SUCCESS && snsInfo != null){
SDKAPI.getIns().checkSnsInfo(snsInfo, (code1, msg1) -> {
if(code1 == SDKCallBack.CODE_SUCCESS && !TextUtils.isEmpty(msg1)){
try {
JSONObject jsonObject = new JSONObject(msg1).optJSONObject("result");
//返回信息中code为0时表示服务端查询成功exit为1表示此渠道用户信息已产生过途游账号可直接进行登录exit为0表示此渠道用户信息未产生过途游账号可以进行绑定
if(jsonObject.optString("code").equals("0")
&& jsonObject.optString("exist").equals("0")){
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "0");
}
else
{
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "1");
}
} catch (JSONException e) {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "-1");
e.printStackTrace();
}
}
else
{
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "-1");
}
});
}
else
{
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "-1");
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHANGELINK_CALLBACK_FUNCTION_NAME, msg);
}
});
}
public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo) {
public static void LinkCheck(String type) {
SDKAPI.getIns().getSnsInfo(type, (code, snsInfo, msg) -> {
if (code == SDKCallBack.CODE_SUCCESS && snsInfo != null) {
SDKAPI.getIns().checkSnsInfo(snsInfo, (code1, msg1) -> {
if (code1 == SDKCallBack.CODE_SUCCESS && !TextUtils.isEmpty(msg1)) {
try {
JSONObject jsonObject = new JSONObject(msg1).optJSONObject("result");
if (jsonObject.optString("code").equals("0")
&& jsonObject.optString("exist").equals("0")) {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "0");
} else {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "1");
}
} catch (JSONException e) {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "-1");
e.printStackTrace();
}
} else {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "-1");
}
});
} else {
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.CHECKLINK_CALLBACK_FUNCTION_NAME, "-1");
}
});
}
// -------------------------------------------------------------------------
// Payment — channel dispatch
// -------------------------------------------------------------------------
public static void UnityKnow(String productId, String productName, String productCount,
String prodorderId, String appInfo) {
SDKLog.i("UnityKnow : " + productId);
HashMap<String, String> extraInfo = new HashMap<String, String>();
extraInfo.put("appInfo", appInfo);
SDKAPI.getIns().pay(productId, productName, productCount, Integer.parseInt(ConfigManager.SDK_GAMEID), mPay, prodorderId, extraInfo);
SDKAPI.getIns().pay(productId, productName, productCount,
Integer.parseInt(ConfigManager.SDK_GAMEID), mPay, prodorderId, extraInfo);
}
public static void UnityGetKnowTypeList(String productId)
{
Log.e(TAG,"UnityGetKnowTypeList:" + productId );
final PayEventData.PayReq mPayReq = new PayEventData.PayReq();
mPayReq.prodId = productId;
SDKAPI.getIns().charge(mPayReq , new SDKCallBack.Base1() {
@Override
public void callback(int code, String msg) {
SDKLog.e("====> code= " + code + " msg= " + msg);
try {
JSONObject result = new JSONObject();
result.put("code", code);
if(code == 0){
result.put("respObj", msg);
}else {
result.put("errStr", msg);
}
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, result.toString());
} catch (Exception e) {
SDKLog.e("SDKManager-UnityGetKnowTypeList-Exception" + e);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
}
}
});
}
public static void UnityKnowNew(String productId, String productPrice, String productName, String productCount, String prodorderId, String appInfo, String ptype)
{
public static void UnityKnowNew(String productId, String productPrice, String productName,
String productCount, String prodorderId, String appInfo,
String ptype) {
if (sdkAdapter instanceof IPaymentAdapter) {
((IPaymentAdapter) sdkAdapter).pay(UnityPlayer.currentActivity, productId, productPrice, productName, productCount, prodorderId, appInfo);
return;
}
PayEventData.PayData payData = new PayEventData.PayData();
final PayEventData.PayReq mPayReq = new PayEventData.PayReq();
mPayReq.prodId = productId;
mPayReq.prodPrice = productPrice;
mPayReq.prodName = productName;
mPayReq.prodCount = productCount;
// 此字段与服务端回调接口内的appInfo不对应与orderId相对应
mPayReq.appInfo = prodorderId;
HashMap<String, String> map = new HashMap<String, String>();
// 此字段appInfo可用于传递透传信息此信息与服务端回调接口内的appInfo相对应
map.put("appInfo", appInfo);
mPayReq.extra = map;
payData.payReq = mPayReq;
@@ -389,41 +451,57 @@ public class SDKManager {
SDKAPI.getIns().payNew(payData, payType, mPay);
}
// -------------------------------------------------------------------------
// thirdExtend — with channel helper support
// -------------------------------------------------------------------------
public static void thirdExtend()
{
Log.e(TAG,"====>java thirdExtend");
public static void thirdExtend() {
Log.e(TAG, "thirdExtend called, sdkAdapter=" + (sdkAdapter != null ? sdkAdapter.getClass().getSimpleName() : "null"));
if (sdkAdapter instanceof IProductQueryAdapter) {
((IProductQueryAdapter) sdkAdapter).queryProducts();
return;
}
defaultThirdExtend();
}
private static void defaultThirdExtend() {
Log.e(TAG, "====>java thirdExtend");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("action","ACTION_GET_SKU_DETAILS_LIST");
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.e(TAG,"====>thirdExtend code= " + code + " msg= " + msg );
@Override
public void callback(int code, String msg) {
Log.e(TAG, "====>thirdExtend code= " + code + " msg= " + msg);
try {
//获取成功msg即为对应的json字符串
JSONObject result = new JSONObject();
result.put("code", code);
if(code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS){
result.put("respObj", msg);
}else {
Log.e(TAG,"获取商品列表失败啦:" + msg);
if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS) {
result.put("respObj", ChannelEntry.normalizeSkuList(msg));
} else {
Log.e(TAG, "thirdExtend failed: " + msg);
result.put("thirdExtend_errStr", msg);
}
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, result.toString());
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, result.toString());
} catch (Exception e) {
SDKLog.e("SDKManager-UnityGetKnowTypeList-Exception" + e);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
SDKLog.e("SDKManager-thirdExtend-Exception" + e);
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
ConfigManager.PAY_TYPE_CALLBACK_FUNCTION_NAME, "{\"code\":1}");
}
}
});
}
private static IGASDK GetGameGa(){
if(gasdk == null){
// -------------------------------------------------------------------------
// GA (shared)
// -------------------------------------------------------------------------
protected static IGASDK GetGameGa() {
if (gasdk == null) {
GAConfiguration.Builder builder = new GAConfiguration.Builder()
.withBaseUrl(BaseUrl.INTERNATIONAL)
.withContext(SDKWrapper.getInstance().getContext())
@@ -435,82 +513,61 @@ public class SDKManager {
return gasdk;
}
public static void SetGaUserInfo(String userId)
{
Log.e(TAG,"SetGaUserInfo : " + userId);
public static void SetGaUserInfo(String userId) {
Log.e(TAG, "SetGaUserInfo : " + userId);
SDKLog.i("SetGaUserInfo : " + userId);
GetGameGa().setUserId(userId);
}
public static void SetGaCommonInfo(String SetGaCommonInfo)
{
Log.e(TAG,"SetGaCommonInfo : " + SetGaCommonInfo);
public static void SetGaCommonInfo(String SetGaCommonInfo) {
Log.e(TAG, "SetGaCommonInfo : " + SetGaCommonInfo);
SDKLog.i("SetGaCommonInfo : " + SetGaCommonInfo);
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(SetGaCommonInfo, new TypeToken<Map<String, Object>>(){}.getType());
Map<String, Object> map = gson.fromJson(SetGaCommonInfo,
new TypeToken<Map<String, Object>>() {}.getType());
for (String key : map.keySet()) {
System.out.println("Key = " + key + ", Value = " + map.get(key));
if(!TextUtils.isEmpty(key) && map.get(key) != null){
if (!TextUtils.isEmpty(key) && map.get(key) != null) {
GetGameGa().addCommonParameter(key, map.get(key).toString());
}
}
}
public static void GAReportParams(int type, String eventstr, String paramstr)
{
public static void GAReportParams(int type, String eventstr, String paramstr) {
ParamsBuilder paramsBuilder = ParamsBuilder.newInstance();
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(paramstr, new TypeToken<Map<String, Object>>(){}.getType());
Map<String, Object> map = gson.fromJson(paramstr,
new TypeToken<Map<String, Object>>() {}.getType());
for (String key : map.keySet()) {
if(!TextUtils.isEmpty(key) && map.get(key) != null){
if (!TextUtils.isEmpty(key) && map.get(key) != null) {
paramsBuilder.append(key, map.get(key).toString());
}
}
// 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相关事件
switch (type)
{
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.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;
case 9:
GetGameGa().track(EventType.SDK , eventstr, paramsBuilder);
break;
default:
GetGameGa().track(EventType.TRACK , eventstr, paramsBuilder);
break;
switch (type) {
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.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;
case 9: GetGameGa().track(EventType.SDK, eventstr, paramsBuilder); break;
default: GetGameGa().track(EventType.TRACK, eventstr, paramsBuilder); break;
}
}
public static void Review()
{
// -------------------------------------------------------------------------
// Review — channel dispatch
// -------------------------------------------------------------------------
public static void Review() {
if ("Rustore".equals(channel)) {
RuStoreReview();
} else {
GooglePlayReview();
}
}
private static void GooglePlayReview() {
ReviewManager manager = ReviewManagerFactory.create(SDKWrapper.getInstance().getContext());
Task<ReviewInfo> request = manager.requestReviewFlow();
request.addOnCompleteListener(reqTask -> {
@@ -519,16 +576,59 @@ public class SDKManager {
Activity activity = UnityPlayer.currentActivity;
manager.launchReviewFlow(activity, reviewInfo);
} else {
@ReviewErrorCode int reviewErrorCode = ((ReviewException) request.getException()).getErrorCode();
Log.e(TAG,"Review Error code: " + reviewErrorCode);
@ReviewErrorCode int reviewErrorCode =
((ReviewException) request.getException()).getErrorCode();
Log.e(TAG, "Review Error code: " + reviewErrorCode);
}
});
}
public static void FBShareLink(String title, String content, String url)
{
Log.e(TAG,"FBShareLink : " + title + " " + content + " " + url);
// RuStore two-step review
public interface ReviewResultCallback {
void onReviewSuccess(JSONObject result);
void onReviewError(int code, String msg);
}
public static void RequestRustoreReView(ReviewResultCallback callback) {
JSONObject json = new JSONObject();
try { json.put("action", "ACTION_REQUEST_REVIEW"); } catch (JSONException e) { throw new RuntimeException(e); }
SDKAPI.getIns().thirdExtend(json.toString(), (code, msg) -> {
Log.e(TAG, "RequestRustoreReView code=" + code + " msg=" + msg);
try {
JSONObject result = new JSONObject();
result.put("code", code);
if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS) {
if (callback != null) callback.onReviewSuccess(result);
} else {
result.put("thirdExtend_errStr", msg);
if (callback != null) callback.onReviewError(code, msg);
}
} catch (Exception e) {
SDKLog.e("RequestRustoreReView Exception: " + e);
}
});
}
public static void LaunchRustoreView() {
JSONObject json = new JSONObject();
try { json.put("action", "ACTION_LAUNCH_REVIEW"); } catch (JSONException e) { throw new RuntimeException(e); }
SDKAPI.getIns().thirdExtend(json.toString(), (code, msg) ->
Log.e(TAG, "LaunchRustoreView code=" + code + " msg=" + msg));
}
public static void RuStoreReview() {
RequestRustoreReView(new ReviewResultCallback() {
@Override public void onReviewSuccess(JSONObject result) { LaunchRustoreView(); }
@Override public void onReviewError(int code, String msg) { }
});
}
// -------------------------------------------------------------------------
// Facebook / Messenger share (shared)
// -------------------------------------------------------------------------
public static void FBShareLink(String title, String content, String url) {
Log.e(TAG, "FBShareLink : " + title + " " + content + " " + url);
if (ShareDialog.canShow(ShareLinkContent.class)) {
Activity activity = UnityPlayer.currentActivity;
ShareDialog dialog = new ShareDialog(activity);
@@ -537,30 +637,22 @@ public class SDKManager {
.setContentUrl(Uri.parse(url))
.build();
dialog.show(linkContent);
}
else
{
} else {
Log.e(TAG, "FBShareDialog can not show");
}
}
public static void MessengerShareLink(String title, String content, String url)
{
Log.e(TAG,"MessengerShareLink : " + title + " " + content + " " + url);
public static void MessengerShareLink(String title, String content, String url) {
Log.e(TAG, "MessengerShareLink : " + title + " " + content + " " + url);
if (MessageDialog.canShow(ShareLinkContent.class)) {
Activity activity = UnityPlayer.currentActivity;
ShareLinkContent linkContent = new ShareLinkContent.Builder()
.setQuote(content)
.setContentUrl(Uri.parse(url))
.build();
MessageDialog.show(activity,linkContent);
}
else
{
MessageDialog.show(activity, linkContent);
} else {
Log.e(TAG, "Messenger ShareDialog can not show");
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b4630fc5c78457748a2f763ac5168df6
guid: 1f7e4cbb67a383441bd126c06021a072
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,18 @@
namespace tysdk
{
public static class ChannelConfig
{
public static string Channel { get; private set; } = "GooglePlay";
public static string PayType => Channel switch
{
"Rustore" => "rustore.global.app",
"Flexion" => "flexion.global.app",
_ => "googleiab.global.app"
};
public static int LoginTimeoutSeconds => Channel == "Rustore" ? 120 : 30;
public static void SetChannel(string channel) { Channel = channel; }
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 70a4079f02f94f47a9a065de6d76f8f2
guid: 6dcc4e666f5907a47b1843de49da6f67
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 292a655e788d84d34aac4f83c57706ff
guid: cca93fc1d8dde744a86aff3c3a1b73ed
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -105,6 +105,9 @@ namespace tysdk
public static AccountInfo TYAccountInfo => _accountInfo;
private static TYSdkFacade _instance;
public static bool IsSdkInited { get; private set; }
public static event System.Action<bool, string> OnInitComplete;
public static TYSdkFacade Instance
{
get
@@ -119,12 +122,41 @@ namespace tysdk
}
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)
{
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);
}
/*================================================
@@ -290,6 +322,9 @@ namespace tysdk
result.isSuccess = true;
result.userId = _accountInfo.userId;
SetUserInfo();
#if UNITY_ANDROID
UnityBridgeFunc.RefreshBillingService();
#endif
}
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
@@ -364,6 +399,13 @@ namespace tysdk
UnityBridgeFunc.CleanLinkTmpSnsInfo();
}
#if UNITY_EDITOR
public async Task<ChangeLinkResult> ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId)
{
await Task.Delay(300);
return new ChangeLinkResult(){code = 1};
}
#elif UNITY_IOS || UNITY_ANDROID
public async Task<ChangeLinkResult> ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId)
{
var task = TYSDKCallbackManager.Instance.RegisterCallback<ChangeLinkResult>(TimeSpan.FromSeconds(30));
@@ -375,6 +417,9 @@ namespace tysdk
{
_accountInfo.AddLinkedAccount(accoutType);
_accountInfo.Save();
#if UNITY_ANDROID
UnityBridgeFunc.RefreshBillingService();
#endif
}
return result;
}
@@ -388,6 +433,7 @@ namespace tysdk
CleanLinkTempSnSInfo();
}
}
#endif
public void ChangeLinkAccountResult(string message)
{
@@ -534,4 +580,4 @@ namespace tysdk
return sBuilder.ToString();
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: db12db58fbcc54046aa4dac203cd4971
guid: f2fa4b960a2b4594d80ea8b8caa2912d
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,3 +1,11 @@
fileFormatVersion: 2
guid: 8f92075f5f98490ca94ae4984ac0aa21
timeCreated: 1722855901
fileFormatVersion: 2
guid: 280d33ff6fcb3a342ab9683a01841b78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,3 +1,11 @@
fileFormatVersion: 2
guid: 18459dcf8d5a40dc80ffae5333a6b0c9
timeCreated: 1722855439
fileFormatVersion: 2
guid: aa58ede4826973c4bbe3a5ebfdcd4b43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -32,6 +32,7 @@ namespace tysdk
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));
@@ -57,20 +58,17 @@ namespace tysdk
return result;
#elif UNITY_ANDROID
var pType = "googleiab.global.app";
var task = TYSDKCallbackManager.Instance.RegisterCallback<PaymentInfo>(TimeSpan.FromSeconds(30));
var pType = ChannelConfig.PayType;
var task = TYSDKCallbackManager.Instance.RegisterCallback<PaymentInfo>(TimeSpan.FromSeconds(90));
try
{
//UnityBridgeFunc.UnityKnow(prodId, prodName, count.ToString(), orderId, extraInfo);
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)
@@ -116,6 +114,24 @@ namespace tysdk
var result = new PaymentInfo();
result.code = jresult["code"].ToString();
result.msg = jresult["errStr"].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 (!_payFirstNegativeHandled)
@@ -144,10 +160,44 @@ namespace tysdk
var result = new ProductListInfo();
return result;
#elif UNITY_ANDROID || UNITY_IOS
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>();
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(TimeSpan.FromSeconds(45));
UnityBridgeFunc.GetSKUList();
var result = await task;
return result;
try
{
var result = await task;
return result;
}
catch (TaskCanceledException e)
{
Debug.LogWarning($"[TYSdkFacade] GetSKUList timeout: {e.Message}");
return new ProductListInfo() { code = 1, msg = "sku_callback_timeout" };
}
#endif
}
//通过商品ID列表获取商品详情Flexion 渠道使用)
public async Task<ProductListInfo> 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<ProductListInfo>(TimeSpan.FromSeconds(45));
UnityBridgeFunc.GetSKUListByIapIds(productIds);
try
{
var result = await task;
return result;
}
catch (TaskCanceledException e)
{
Debug.LogWarning($"[TYSdkFacade] GetSKUListByIapIds timeout: {e.Message}");
return new ProductListInfo() { code = 1, msg = "sku_callback_timeout" };
}
#else
return new ProductListInfo() { code = -1, msg = "unsupported platform" };
#endif
}

View File

@@ -1,3 +1,11 @@
fileFormatVersion: 2
guid: 41b6dc16dbab43388317bf9ff032cdca
timeCreated: 1722855703
fileFormatVersion: 2
guid: e05900a4aa124d746bfe9c15328fd0e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace tysdk
{
@@ -14,6 +15,31 @@ namespace tysdk
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;
@@ -59,6 +85,38 @@ 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
public string signature;
// 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
public string serverOrderId;
// Player ID for server-side verification
public string userId;
public bool Check(string orderId, string productId)
{
confirmed = orderId == this.orderId && productId == this.productId;
@@ -132,6 +190,8 @@ namespace tysdk
public string productId;
public string title;
public string type;
public string currency;
public string price_amount;
public override string ToString()
{
@@ -150,4 +210,11 @@ namespace tysdk
{
public bool isAccepted;
}
public class FlexionOrderResult
{
public int code { get; set; }
public string info { get; set; }
public string orderId { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b94c8e87cc23b4053a4cbf592ff14db3
guid: ebabed604bd06e44099073951d667658
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -10,8 +10,8 @@ namespace tysdk
#if UNITY_EDITOR
//初始化sdk
public static void InitSDK(){}
public static void InitSDK(string channel){}
//更新登录支付域名
public static void UnityResetServerUrl(string url){}
@@ -23,7 +23,7 @@ namespace tysdk
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){}
@@ -44,10 +44,14 @@ namespace tysdk
//获取商品列表
public static void GetSKUList() { }
public static void ConsumePurchase(string token) { }
public static string GetPendingPurchases() => "[]";
public static void RefreshBillingService() { }
//打点
public static void SetGaUserInfo(string userId){}
public static void SetGaCommonInfo(string SetGaCommonInfo){}
public static void GAReportParams(int type, string eventstr, string paramstr){}
@@ -69,13 +73,13 @@ namespace tysdk
private static string SDK_CLASS = "com.unity3d.player.SDKManager";
//初始化sdk
public static void InitSDK(){
public static void InitSDK(string channel){
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
using(var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
var activity = activityCls.GetStatic<AndroidJavaObject>("currentActivity");
sdkManager.CallStatic("InitSDK", activity);
sdkManager.CallStatic("InitSDK", activity, channel);
}
}
}
@@ -196,6 +200,50 @@ namespace tysdk
}
}
//通过商品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<string>("GetPendingPurchases");
}
}
public static void RefreshBillingService()
{
UnityEngine.Debug.Log("UnityBridgeFunc.RefreshBillingService");
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("RefreshBillingService");
}
}
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)
{
@@ -429,6 +477,12 @@ 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() => "[]";
public static void RefreshBillingService() { }
#endif
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 16335dd05906d48bf81e297920aecf31
guid: e24b3b915d94468419519a81bb69e269
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -14,4 +14,4 @@
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
}

View File

@@ -1,17 +1,12 @@
<dependencies>
<packages>
<package>androidx.recyclerview:recyclerview:1.2.1</package>
<package>com.android.installreferrer:installreferrer:2.1</package>
<package>com.applovin.mediation:bytedance-adapter:8.1.0.3.0</package>
<package>com.applovin.mediation:facebook-adapter:[6.18.0.1]</package>
<package>com.applovin.mediation:fyber-adapter:8.4.2.0</package>
<package>com.applovin.mediation:google-adapter:[23.6.0.1]</package>
<package>com.applovin.mediation:ironsource-adapter:8.6.1.0.0</package>
<package>com.applovin.mediation:mintegral-adapter:17.1.61.0</package>
<package>com.applovin.mediation:moloco-adapter:4.9.0.0</package>
<package>com.applovin.mediation:unityads-adapter:4.12.3.0</package>
<package>com.applovin.mediation:vungle-adapter:7.4.2.2</package>
<package>com.applovin.mediation:yandex-adapter:8.1.0.0</package>
<package>com.applovin:applovin-sdk:13.5.1</package>
<package>com.appsflyer:af-android-sdk:6.17.3</package>
<package>com.appsflyer:purchase-connector:2.2.0</package>

View File

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

View File

@@ -4,5 +4,11 @@
EditorBuildSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes: []
m_Scenes:
- enabled: 1
path: Assets/Scenes/SampleScene.unity
guid: 99c9720ab356a0642a771bea13969a05
- enabled: 1
path: Assets/Scenes/UnbindTest.unity
guid: e55d807d563854943a26ad6dadec548d
m_configObjects: {}

View File

@@ -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:
@@ -173,7 +173,7 @@ PlayerSettings:
tvOS: 0
overrideDefaultApplicationIdentifier: 1
AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 25
AndroidMinSdkVersion: 26
AndroidTargetSdkVersion: 35
AndroidPreferredInstallLocation: 1
aotOptions:
@@ -932,4 +932,4 @@ PlayerSettings:
hmiLoadingImage: {fileID: 0}
platformRequiresReadableAssets: 0
virtualTexturingSupportEnabled: 0
insecureHttpOption: 0
insecureHttpOption: 2