update:逻辑更新

This commit is contained in:
2026-05-21 00:37:19 +08:00
parent eecf54c6a4
commit ed6fe668ad
29 changed files with 677 additions and 747 deletions

5
.gitignore vendored
View File

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

View File

@@ -1,11 +1,11 @@
package com.unity3d.player; package com.unity3d.player;
public class ChannelHelpers { public class ChannelEntry {
public static final String CHANNEL = "Flexion"; public static final String CHANNEL = "Flexion";
static void register() { public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
SDKManager.registerHelper(new FlexionSDKHelper()); return new FlexionSDKService();
} }
@@ -14,7 +14,7 @@ public class ChannelHelpers {
public static String normalizeSkuList(String msg) { public static String normalizeSkuList(String msg) {
return msg; return msg;
} }
//public SDKExtBuilder; //public SDKExtBuilder;
} }

View File

@@ -17,35 +17,41 @@ import com.flexionmobile.ddpx.service.BillingService;
import com.flexionmobile.fdk.FLX; import com.flexionmobile.fdk.FLX;
import com.tuyoo.gamesdk.api.SDKAPI; import com.tuyoo.gamesdk.api.SDKAPI;
import com.tuyoo.gamesdk.api.SDKCallBack;
import com.tuyoo.gamesdk.model.InitParam; import com.tuyoo.gamesdk.model.InitParam;
import com.tuyoo.gamesdk.util.SDKLog;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FlexionSDKHelper implements SDKManager.ISDKHelper { public class FlexionSDKService implements
private static final String TAG = "FlexionSDKHelper"; SDKManager.IChannelSDKAdapter,
SDKManager.IPaymentAdapter,
SDKManager.IProductQueryAdapter,
SDKManager.IPendingPurchaseAdapter {
private static final String TAG = "FlexionSDKService";
private static BillingService billingService; private static BillingService billingService;
private static boolean billingConnected = false; private static boolean billingConnected = false;
private static final List<String> pendingPurchases = new ArrayList<>();
private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread"));
private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> { private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> {
Log.w(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode()
Log.i(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode()
+ " hasPurchase=" + (purchase != null)); + " hasPurchase=" + (purchase != null));
if (billingResult.getResponseCode() == BillingResults.ResultCode.PURCHASE_SUCCESS_CODE) { if (billingResult.getResponseCode() == BillingResults.ResultCode.PURCHASE_SUCCESS_CODE) {
if (purchase != null) handlePurchase(purchase); if (purchase != null) handlePurchase(purchase);
} else if (billingResult.getResponseCode() == BillingResults.ResultCode.PURCHASE_USER_CANCELLED_CODE) { } else if (billingResult.getResponseCode() == BillingResults.ResultCode.PURCHASE_USER_CANCELLED_CODE) {
Log.i(TAG, "[PAY-FLOW] user cancelled"); Log.i(TAG, "[PAY-FLOW] user cancelled");
sendPayCallback(1, "user_cancelled", null, null, null); sendPayCallback(1, "user_cancelled", null, null, null, null);
} else { } else {
Log.e(TAG, "[PAY-FLOW] purchase failed: code=" + billingResult.getResponseCode()); Log.e(TAG, "[PAY-FLOW] purchase failed: code=" + billingResult.getResponseCode());
sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null); sendPayCallback(1, "purchase_failed_" + billingResult.getResponseCode(), null, null, null, null);
} }
}; };
@@ -95,9 +101,9 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
String productName, String productCount, String prodorderId, String productName, String productCount, String prodorderId,
String appInfo) { String appInfo) {
Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected); Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected);
if (billingService == null || !billingConnected) { if (billingService == null || !billingConnected || !billingService.isReady()) {
Log.e(TAG, "[PAY-FLOW] billing not ready"); Log.e(TAG, "[PAY-FLOW] billing not ready");
sendPayCallback(1, "billing_not_ready", null, null, null); sendPayCallback(1, "billing_not_ready", null, null, null, null);
return; return;
} }
String developerPayload = ""; String developerPayload = "";
@@ -105,97 +111,66 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
byte[] decoded = Base64.getDecoder().decode(appInfo); byte[] decoded = Base64.getDecoder().decode(appInfo);
JSONObject json = new JSONObject(new String(decoded, "UTF-8")); JSONObject json = new JSONObject(new String(decoded, "UTF-8"));
JSONObject payload = new JSONObject(); JSONObject payload = new JSONObject();
String customData = json.optString("customData", "");
payload.put("orderId", prodorderId);
payload.put("userId", json.optString("userId", "")); payload.put("userId", json.optString("userId", ""));
payload.put("pfid", json.optString("pfid", "")); payload.put("pfid", json.optString("pfid", ""));
payload.put("customData", json.optString("customData", "")); if (!customData.isEmpty()) {
payload.put("customDataB64", Base64.getEncoder().encodeToString(customData.getBytes("UTF-8")));
}
payload.put("prodPrice", productPrice); payload.put("prodPrice", productPrice);
payload.put("prodCount", productCount); payload.put("prodCount", productCount);
developerPayload = payload.toString(); developerPayload = payload.toString();
Log.i(TAG, "[PAY-CUSTOMDATA] customData length=" + customData.length()
+ " developerPayload length=" + developerPayload.length());
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage()); Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage());
} }
String finalPayload = developerPayload; String finalPayload = developerPayload;
activity.runOnUiThread(() -> { billingExecutor.execute(() -> {
Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload);
Log.i(TAG, "[PAY-FLOW] billingService=" + billingService + " billingConnected=" + billingConnected);
try { try {
BillingResult result = billingService.launchBillingFlow(activity, BillingService bs = billingService;
if (bs == null || !bs.isReady()) {
Log.e(TAG, "[PAY-FLOW] billingService became null or not ready");
sendPayCallback(1, "billing_not_ready", null, null, null, null);
return;
}
Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload);
BillingResult result = bs.launchBillingFlow(activity,
new BillingFlowParams(productId, "inapp", finalPayload)); new BillingFlowParams(productId, "inapp", finalPayload));
Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode() Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode()
+ " debugMsg=" + result.getDebugMessage()); + " debugMsg=" + result.getDebugMessage());
if (result.getResponseCode() == 2001) {
Log.i(TAG, "[PAY-FLOW] service not ready, retry after 3s...");
new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
try {
BillingResult retryResult = billingService.launchBillingFlow(activity,
new BillingFlowParams(productId, "inapp", finalPayload));
Log.i(TAG, "[PAY-FLOW] launchBillingFlow retry result: code=" + retryResult.getResponseCode()
+ " debugMsg=" + retryResult.getDebugMessage());
if (retryResult.getResponseCode() != 2000) {
sendPayCallback(1, "billing_flow_error_" + retryResult.getResponseCode()
+ "_" + retryResult.getDebugMessage(), null, null, null);
}
} catch (Exception ex) {
sendPayCallback(1, "launch_billing_failed: " + ex.getMessage(), null, null, null);
}
}, 3000);
return;
}
if (result.getResponseCode() != 2000) { if (result.getResponseCode() != 2000) {
Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode() Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode()
+ " debugMsg=" + result.getDebugMessage()); + " debugMsg=" + result.getDebugMessage());
sendPayCallback(1, "billing_flow_error_" + result.getResponseCode() sendPayCallback(1, "billing_flow_error_" + result.getResponseCode()
+ "_" + result.getDebugMessage(), null, null, null); + "_" + result.getDebugMessage(), null, null, null, null);
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName() Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName()
+ " msg=" + e.getMessage()); + " msg=" + e.getMessage());
sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null); sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null, null);
} }
}); });
} }
@Override @Override
public void queryProducts() { public void queryProducts() {
Log.i(TAG, "queryProducts called, billingConnected=" + billingConnected); // Flexion 渠道不通过 thirdExtend 获取商品列表 queryProducts(String) 替代
}
@Override
public void queryProducts(String productIds) {
Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected);
if (billingService == null || !billingConnected) { if (billingService == null || !billingConnected) {
Log.w(TAG, "queryProducts: billing not ready");
sendExtensionError("billing_not_ready"); sendExtensionError("billing_not_ready");
return; return;
} }
JSONObject jsonObject = new JSONObject(); List<String> ids = Arrays.asList(productIds.split(","));
try { Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config");
jsonObject.put("action", "ACTION_GET_SKU_DETAILS_LIST"); queryProductDetails(ids);
} catch (JSONException e) {
throw new RuntimeException(e);
}
SDKAPI.getIns().thirdExtend(jsonObject.toString(), new SDKCallBack.Extend() {
@Override
public void callback(int code, String msg) {
Log.i(TAG, "TuYoo thirdExtend callback: code=" + code + " msg=" + msg);
if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS && msg != null) {
try {
JSONArray products = new JSONArray(msg);
List<String> productIds = new java.util.ArrayList<>();
for (int i = 0; i < products.length(); i++) {
JSONObject p = products.getJSONObject(i);
String pid = p.optString("productId", "");
if (!pid.isEmpty()) productIds.add(pid);
}
Log.i(TAG, "queryProductDetails with " + productIds.size() + " items: " + productIds);
queryProductDetails(productIds);
} catch (JSONException e) {
sendExtensionError("parse_error: " + e.getMessage());
}
} else {
sendExtensionError("tuyoo_query_failed: " + msg);
}
}
});
} }
// ---- Billing Infrastructure ----
private static void initBillingService(Activity activity) { private static void initBillingService(Activity activity) {
Log.i(TAG, "[PAY-FLOW] initBillingService"); Log.i(TAG, "[PAY-FLOW] initBillingService");
billingService = FLX.createBillingService(activity, purchasesListener); billingService = FLX.createBillingService(activity, purchasesListener);
@@ -208,6 +183,7 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
queryPendingPurchases(); queryPendingPurchases();
} }
} }
@Override @Override
public void onBillingServiceDisconnected() { public void onBillingServiceDisconnected() {
Log.w(TAG, "[PAY-FLOW] billing service disconnected"); Log.w(TAG, "[PAY-FLOW] billing service disconnected");
@@ -218,29 +194,84 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
private static void handlePurchase(Purchase purchase) { private static void handlePurchase(Purchase purchase) {
Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState() Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState()
+ " orderId=" + purchase.getOrderId()
+ " token=" + purchase.getToken()); + " token=" + purchase.getToken());
if (purchase.getPurchaseState() == 0) { if (purchase.getPurchaseState() == 0) {
// 支付成功 回传数据给 Unity 做服务端验签验签通过后由 C# ConsumePurchase
Log.i(TAG, "[PAY-FLOW] purchase success, sending to Unity for verification"); Log.i(TAG, "[PAY-FLOW] purchase success, sending to Unity for verification");
sendPayCallback(0, "success", sendPayCallback(0, "success", purchase.getOrderId(),
purchase.getPurchaseJson(), purchase.getSignature(), purchase.getToken()); purchase.getPurchaseJson(), purchase.getSignature(), purchase.getToken());
// [旧逻辑] 直接消费跳过验签
// Log.i(TAG, "[PAY-FLOW] auto-consuming pending purchase: " + purchase.getToken());
// consumePurchase(purchase.getToken());
} else if (purchase.getPurchaseState() == 2) { } else if (purchase.getPurchaseState() == 2) {
Log.i(TAG, "[PAY-FLOW] purchase state=2 (pending), skip"); Log.i(TAG, "[PAY-FLOW] purchase state=2 (pending), skip");
} }
} }
private static void queryPendingPurchases() { private static void queryPendingPurchases() {
if (billingService == null || !billingConnected) return; Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService
+ " billingConnected=" + billingConnected);
if (billingService == null || !billingConnected) {
Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected");
return;
}
billingService.queryPurchasesAsync( billingService.queryPurchasesAsync(
new QueryPurchasesParams("inapp"), new QueryPurchasesParams("inapp"),
(result, purchases) -> { (result, purchases) -> {
if (purchases != null) for (Purchase p : purchases) handlePurchase(p); Log.i(TAG, "[PAY-FLOW] queryPendingPurchases result: code=" + result.getResponseCode()
+ " count=" + (purchases == null ? 0 : purchases.size()));
if (purchases != null) for (Purchase p : purchases) cachePendingPurchase(p);
}); });
} }
private static void cachePendingPurchase(Purchase purchase) {
if (purchase == null) {
Log.w(TAG, "[PAY-FLOW] cachePendingPurchase skipped: purchase is null");
return;
}
Log.i(TAG, "[PAY-FLOW] cachePendingPurchase state=" + purchase.getPurchaseState()
+ " orderId=" + purchase.getOrderId() + " token=" + purchase.getToken());
if (purchase.getPurchaseState() != 0) return;
try {
JSONObject result = new JSONObject();
if (purchase.getOrderId() != null) result.put("orderId", purchase.getOrderId());
if (purchase.getPurchaseJson() != null) result.put("purchaseJson", purchase.getPurchaseJson());
if (purchase.getSignature() != null) result.put("signature", purchase.getSignature());
if (purchase.getToken() != null) result.put("purchaseToken", purchase.getToken());
synchronized (pendingPurchases) {
String token = purchase.getToken();
for (String item : pendingPurchases) {
JSONObject cached = new JSONObject(item);
if (token != null && token.equals(cached.optString("purchaseToken", null))) {
Log.i(TAG, "[PAY-FLOW] pending purchase already cached token=" + token);
return;
}
}
pendingPurchases.add(result.toString());
}
Log.i(TAG, "[PAY-FLOW] cached pending purchase: orderId=" + purchase.getOrderId()
+ " cachedCount=" + pendingPurchases.size());
} catch (JSONException e) {
Log.e(TAG, "[PAY-FLOW] cachePendingPurchase json error: " + e.getMessage());
}
}
@Override
public String getPendingPurchases() {
synchronized (pendingPurchases) {
Log.i(TAG, "[PAY-FLOW] getPendingPurchases start cachedCount=" + pendingPurchases.size());
JSONArray result = new JSONArray();
for (String item : pendingPurchases) {
try {
result.put(new JSONObject(item));
} catch (JSONException e) {
Log.e(TAG, "[PAY-FLOW] getPendingPurchases json error: " + e.getMessage());
}
}
pendingPurchases.clear();
Log.i(TAG, "[PAY-FLOW] getPendingPurchases count=" + result.length());
return result.toString();
}
}
private static void queryProductDetails(List<String> productIds) { private static void queryProductDetails(List<String> productIds) {
billingService.queryProductDetailsAsync( billingService.queryProductDetailsAsync(
new ProductDetailsParams("inapp", productIds), new ProductDetailsParams("inapp", productIds),
@@ -278,17 +309,17 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
}); });
} }
// ---- Callbacks to Unity ---- private static void sendPayCallback(int code, String errStr, String storeOrderId,
private static void sendPayCallback(int code, String errStr,
String purchaseJson, String signature, String purchaseToken) { String purchaseJson, String signature, String purchaseToken) {
Log.i(TAG, "[PAY-FLOW] sendPayCallback: code=" + code + " errStr=" + errStr Log.i(TAG, "[PAY-FLOW] sendPayCallback: code=" + code + " errStr=" + errStr
+ " storeOrderId=" + storeOrderId
+ " hasPurchaseJson=" + (purchaseJson != null) + " hasSignature=" + (signature != null) + " hasPurchaseJson=" + (purchaseJson != null) + " hasSignature=" + (signature != null)
+ " hasToken=" + (purchaseToken != null)); + " hasToken=" + (purchaseToken != null));
try { try {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("code", code); result.put("code", code);
result.put("errStr", errStr != null ? errStr : ""); result.put("errStr", errStr != null ? errStr : "");
if (storeOrderId != null) result.put("orderId", storeOrderId);
if (purchaseJson != null) result.put("purchaseJson", purchaseJson); if (purchaseJson != null) result.put("purchaseJson", purchaseJson);
if (signature != null) result.put("signature", signature); if (signature != null) result.put("signature", signature);
if (purchaseToken != null) result.put("purchaseToken", purchaseToken); if (purchaseToken != null) result.put("purchaseToken", purchaseToken);
@@ -318,6 +349,10 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
(r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))); (r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail")));
} }
public static void cancelPurchase() {
Log.i(TAG, "[PAY-FLOW] cancelPurchase: TODO - dismiss billing dialog");
}
private static void sendExtensionError(String errStr) { private static void sendExtensionError(String errStr) {
try { try {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();

View File

@@ -16,13 +16,6 @@ android {
namespace "com.unity3d.player" namespace "com.unity3d.player"
} }
// Flexion 渠道专用:排除不需要的 AppsFlyer 模块
// - purchase-connector: 基于 Google Play BillingFlexion 不走 GP 计费,没用还增大包
// 即使 Unity Android Resolver 下次又把 implementation 行写回来,这里的 exclude 也兜底排除
configurations.all {
exclude group: 'com.appsflyer', module: 'purchase-connector'
}
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
@@ -82,9 +75,7 @@ dependencies {
implementation 'com.applovin.mediation:vungle-adapter:7.4.2.2' // Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml:4 implementation 'com.applovin.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.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:af-android-sdk:6.17.3' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:5
// Flexion: purchase-connector removed — Flexion does not use Google Play Billing. implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
// Also covered by top-level `configurations.all { exclude ... }` as a safety net.
// implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6 implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6
implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5 implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5
// Android Resolver Dependencies End // Android Resolver Dependencies End

View File

@@ -1,10 +1,10 @@
package com.unity3d.player; package com.unity3d.player;
public class ChannelHelpers { public class ChannelEntry {
public static final String CHANNEL = "GooglePlay"; public static final String CHANNEL = "GooglePlay";
static void register() { public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
// GooglePlay uses default behavior, no additional SDKHelper return null;
} }
public static String normalizeSkuList(String msg) { public static String normalizeSkuList(String msg) {

View File

@@ -72,7 +72,6 @@ dependencies {
implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8 implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6 implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6
implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5 implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5
implementation 'com.tapjoy:tapjoy-android-unitybridge:14.5.0' // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:10
// Android Resolver Dependencies End // Android Resolver Dependencies End
**DEPS**} **DEPS**}

View File

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

View File

@@ -6,11 +6,11 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
public class ChannelHelpers { public class ChannelEntry {
public static final String CHANNEL = "Rustore"; public static final String CHANNEL = "Rustore";
static void register() { public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
// Rustore uses default behavior, no additional SDKHelper return null;
} }
public static String normalizeSkuList(String msg) { public static String normalizeSkuList(String msg) {
@@ -37,7 +37,7 @@ public class ChannelHelpers {
} }
return products.toString(); return products.toString();
} catch (JSONException e) { } catch (JSONException e) {
Log.e("ChannelHelpers", "normalizeSkuList error: " + e.getMessage()); Log.e("ChannelEntry", "normalizeSkuList error: " + e.getMessage());
return msg; return msg;
} }
} }

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,7 @@
{
"permissions": {
"allow": [
"WebSearch"
]
}
}

View File

@@ -42,6 +42,8 @@ public class ChannelBuildTool
{ {
foreach (var oldAar in Directory.GetFiles(TysdkPlugins, "tuyoosdk_*.aar")) foreach (var oldAar in Directory.GetFiles(TysdkPlugins, "tuyoosdk_*.aar"))
File.Delete(oldAar); File.Delete(oldAar);
foreach (var oldJar in Directory.GetFiles(TysdkPlugins, "tuyoosdk_*.jar"))
File.Delete(oldJar);
foreach (var aar in Directory.GetFiles(srcAarDir, "*.aar")) foreach (var aar in Directory.GetFiles(srcAarDir, "*.aar"))
{ {
var dest = Path.Combine(TysdkPlugins, Path.GetFileName(aar)); var dest = Path.Combine(TysdkPlugins, Path.GetFileName(aar));
@@ -53,8 +55,12 @@ public class ChannelBuildTool
// 2. Clean up previous channel Java files // 2. Clean up previous channel Java files
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "ChannelHelpers.java")) foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "ChannelHelpers.java"))
File.Delete(oldJava); File.Delete(oldJava);
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "ChannelEntry.java"))
File.Delete(oldJava);
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKHelper.java")) foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKHelper.java"))
File.Delete(oldJava); File.Delete(oldJava);
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKService.java"))
File.Delete(oldJava);
var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java"); var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java");
if (File.Exists(oldConfig)) File.Delete(oldConfig); if (File.Exists(oldConfig)) File.Delete(oldConfig);

View File

@@ -72,7 +72,6 @@ dependencies {
implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8 implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6 implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6
implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5 implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5
implementation 'com.tapjoy:tapjoy-android-unitybridge:14.5.0' // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:10
// Android Resolver Dependencies End // Android Resolver Dependencies End
**DEPS**} **DEPS**}

View File

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

View File

@@ -82,10 +82,10 @@ public class SDKTest : MonoBehaviour
//====================== Test Server =========================== //====================== Test Server ===========================
//=============================================================================== //===============================================================================
// //
_InitBtn.onClick.AddListener(Init); _InitBtn.onClick.AddListener(Init);
TYSdkFacade.OnInitComplete += (success, msg) => TYSdkFacade.OnInitComplete += (success, msg) =>
{ {
_messageTxt.text = success ? $"Init OK: {msg}" : $"Init Failed: {msg}"; _messageTxt.text = success ? $"Init OK: {msg}" : $"Init Failed: {msg}";
}; };
_loginGuestBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwGuest)); _loginGuestBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwGuest));
_loginGoogleBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwGoogle)); _loginGoogleBtn.onClick.AddListener(() => OnLogin(EAccoutType.hwGoogle));
@@ -143,7 +143,7 @@ public class SDKTest : MonoBehaviour
private void UpdateChannelTitle() private void UpdateChannelTitle()
{ {
#if UNITY_ANDROID #if UNITY_ANDROID
using (var cls = new AndroidJavaClass("com.unity3d.player.ChannelHelpers")) using (var cls = new AndroidJavaClass("com.unity3d.player.ChannelEntry"))
{ {
string channel = cls.GetStatic<string>("CHANNEL"); string channel = cls.GetStatic<string>("CHANNEL");
Debug.Log($"[SDKTest] Channel from Java: {channel}"); Debug.Log($"[SDKTest] Channel from Java: {channel}");
@@ -237,11 +237,22 @@ public class SDKTest : MonoBehaviour
var purchaseInfo = new JObject(); var purchaseInfo = new JObject();
purchaseInfo["pfid"] = "TestPlayer"; purchaseInfo["pfid"] = "TestPlayer";
purchaseInfo["customData"] = "{\"test\":\"test\"}"; 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; purchaseInfo["userId"] = TYSdkFacade.TYAccountInfo.strUserId;
PaymentInfo payResult = await TYSdkFacade.Instance.Pay(sku, 1, sku.ProdPrice, purchaseInfo); PaymentInfo payResult = await TYSdkFacade.Instance.Pay(sku, 1, sku.ProdPrice, purchaseInfo);
_messageTxt.text = "\n" + $"Success : {payResult.isSuccess}"; _messageTxt.text = "\n" + $"Success : {payResult.isSuccess}";
_messageTxt.text += "\n" + $"message : {payResult.msg}"; _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; SKUDetail sku = null;

View File

@@ -1,10 +1,10 @@
package com.unity3d.player; package com.unity3d.player;
public class ChannelHelpers { public class ChannelEntry {
public static final String CHANNEL = "GooglePlay"; public static final String CHANNEL = "GooglePlay";
static void register() { public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
// GooglePlay uses default behavior, no additional SDKHelper return null;
} }
public static String normalizeSkuList(String msg) { public static String normalizeSkuList(String msg) {

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 576805d2d0194634b8d235fd6837195e guid: eac9aa9954e93c54fbafd3974ca30a5b
PluginImporter: PluginImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -34,12 +34,10 @@ import com.tuyoo.gamesdk.model.InitParam;
import com.tuyoo.gamesdk.pay.model.PayType; import com.tuyoo.gamesdk.pay.model.PayType;
import com.tuyoo.gamesdk.util.SDKLog; import com.tuyoo.gamesdk.util.SDKLog;
import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@@ -57,57 +55,74 @@ public class SDKManager {
private static SnsInfo tmpSnsInfo; private static SnsInfo tmpSnsInfo;
private static String channel = "GooglePlay"; private static String channel = "GooglePlay";
// Channel helper // Channel SDK adapter
public interface ISDKHelper { public interface IChannelSDKAdapter {
void init(Activity activity); void init(Activity activity);
}
public interface IPaymentAdapter {
void pay(Activity activity, String productId, String productPrice, void pay(Activity activity, String productId, String productPrice,
String productName, String productCount, String prodorderId, String productName, String productCount, String prodorderId,
String appInfo); String appInfo);
void queryProducts();
void consume(String token); void consume(String token);
} }
private static ISDKHelper sdkHelper; public interface IProductQueryAdapter {
void queryProducts();
void queryProducts(String productIds);
}
public static void registerHelper(ISDKHelper helper) { public interface IPendingPurchaseAdapter {
sdkHelper = helper; String getPendingPurchases();
}
private static IChannelSDKAdapter sdkAdapter;
public static void queryProductsByIDs(String productIds) {
if (sdkAdapter instanceof IProductQueryAdapter) {
((IProductQueryAdapter) sdkAdapter).queryProducts(productIds);
return;
}
Log.e(TAG, "queryProductsByIDs: no product query adapter");
} }
public static void ConsumePurchase(String token) { public static void ConsumePurchase(String token) {
Log.i(TAG, "ConsumePurchase: token=" + token); Log.i(TAG, "ConsumePurchase: token=" + token);
if (sdkHelper != null) { if (sdkAdapter instanceof IPaymentAdapter) {
sdkHelper.consume(token); ((IPaymentAdapter) sdkAdapter).consume(token);
} }
} }
public static String GetPendingPurchases() {
if (sdkAdapter instanceof IPendingPurchaseAdapter) {
return ((IPendingPurchaseAdapter) sdkAdapter).getPendingPurchases();
}
return "[]";
}
public static void CancelBillingFlow() {
Log.w(TAG, "CancelBillingFlow");
// FlexionSDKService.cancelPurchase();
}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Init — with channel helper support // Init — with channel helper support
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
public static void InitSDK(Activity activity, String ch) { public static void InitSDK(Activity activity, String ch) {
channel = ChannelHelpers.CHANNEL; channel = ChannelEntry.CHANNEL;
ChannelHelpers.register(); sdkAdapter = ChannelEntry.createSDKAdapter();
// var builder = SDKManager.ExtendBuilder()
// .WithChannel(SDKManagerExt.CHANNEL)
// .WithInit(SDKManagerExt.GetInit())
// .WithPay(SDKManagerExt.GetPay())
// .WithQuery(SDKManagerExt.GetQuery())
// .Builder()
Log.i(TAG, "[CHANNEL-VERIFY] Java InitSDK channel=" + channel Log.i(TAG, "[CHANNEL-VERIFY] Java InitSDK channel=" + channel
+ " fromCSharp=" + ch + " fromCSharp=" + ch
+ " helper=" + (sdkHelper != null ? sdkHelper.getClass().getSimpleName() : "default")); + " adapter=" + (sdkAdapter != null ? sdkAdapter.getClass().getSimpleName() : "default"));
// Sync channel to C# ChannelConfig // Sync channel to C# ChannelConfig
UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME, UnityPlayer.UnitySendMessage(ConfigManager.UNITY_FACADE_NAME,
"SetChannelFromNative", channel); "SetChannelFromNative", channel);
// build.init(activity) if (sdkAdapter != null) {
if (sdkHelper != null) { sdkAdapter.init(activity);
sdkHelper.init(activity);
} else { } else {
initDefault(activity); initDefault(activity);
} }
@@ -394,7 +409,7 @@ public class SDKManager {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
public static void UnityKnow(String productId, String productName, String productCount, public static void UnityKnow(String productId, String productName, String productCount,
String prodorderId, String appInfo) { String prodorderId, String appInfo) {
SDKLog.i("UnityKnow : " + productId); SDKLog.i("UnityKnow : " + productId);
HashMap<String, String> extraInfo = new HashMap<String, String>(); HashMap<String, String> extraInfo = new HashMap<String, String>();
extraInfo.put("appInfo", appInfo); extraInfo.put("appInfo", appInfo);
@@ -405,8 +420,8 @@ public class SDKManager {
public static void UnityKnowNew(String productId, String productPrice, String productName, public static void UnityKnowNew(String productId, String productPrice, String productName,
String productCount, String prodorderId, String appInfo, String productCount, String prodorderId, String appInfo,
String ptype) { String ptype) {
if (sdkHelper != null) { if (sdkAdapter instanceof IPaymentAdapter) {
sdkHelper.pay(UnityPlayer.currentActivity, productId, productPrice, productName, productCount, prodorderId, appInfo); ((IPaymentAdapter) sdkAdapter).pay(UnityPlayer.currentActivity, productId, productPrice, productName, productCount, prodorderId, appInfo);
return; return;
} }
PayEventData.PayData payData = new PayEventData.PayData(); PayEventData.PayData payData = new PayEventData.PayData();
@@ -430,9 +445,9 @@ public class SDKManager {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
public static void thirdExtend() { public static void thirdExtend() {
Log.e(TAG, "thirdExtend called, sdkHelper=" + (sdkHelper != null ? sdkHelper.getClass().getSimpleName() : "null")); Log.e(TAG, "thirdExtend called, sdkAdapter=" + (sdkAdapter != null ? sdkAdapter.getClass().getSimpleName() : "null"));
if (sdkHelper != null) { if (sdkAdapter instanceof IProductQueryAdapter) {
sdkHelper.queryProducts(); ((IProductQueryAdapter) sdkAdapter).queryProducts();
return; return;
} }
defaultThirdExtend(); defaultThirdExtend();
@@ -454,7 +469,7 @@ public class SDKManager {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("code", code); result.put("code", code);
if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS) { if (code == SDKCallBack.Extend.EXTEND_CODE_SUCCESS) {
result.put("respObj", ChannelHelpers.normalizeSkuList(msg)); result.put("respObj", ChannelEntry.normalizeSkuList(msg));
} else { } else {
Log.e(TAG, "thirdExtend failed: " + msg); Log.e(TAG, "thirdExtend failed: " + msg);
result.put("thirdExtend_errStr", msg); result.put("thirdExtend_errStr", msg);
@@ -520,7 +535,7 @@ public class SDKManager {
case 2: GetGameGa().track(EventType.COIN, eventstr, paramsBuilder); break; case 2: GetGameGa().track(EventType.COIN, eventstr, paramsBuilder); break;
case 3: GetGameGa().track(EventType.PAY, eventstr, paramsBuilder); break; case 3: GetGameGa().track(EventType.PAY, eventstr, paramsBuilder); break;
case 4: GetGameGa().track(EventType.GAME, eventstr, paramsBuilder); break; case 4: GetGameGa().track(EventType.GAME, eventstr, paramsBuilder); break;
case 5: GetGameGa().track(EventType.PAY, eventstr, paramsBuilder); break; case 5: GetGameGa().track(EventType.LOGIN, eventstr, paramsBuilder); break;
case 6: GetGameGa().track(EventType.PUSH, eventstr, paramsBuilder); break; case 6: GetGameGa().track(EventType.PUSH, eventstr, paramsBuilder); break;
case 7: GetGameGa().track(EventType.ADBOX, eventstr, paramsBuilder); break; case 7: GetGameGa().track(EventType.ADBOX, eventstr, paramsBuilder); break;
case 8: GetGameGa().track(EventType.PREFORMANCE, eventstr, paramsBuilder); break; case 8: GetGameGa().track(EventType.PREFORMANCE, eventstr, paramsBuilder); break;

View File

@@ -1,258 +0,0 @@
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using asap.core;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
namespace tysdk
{
public interface IPaymentVerifier
{
Task<PaymentVerifyResult> VerifyAsync(PaymentInfo info);
}
public interface IPurchaseConsumer
{
Task<PurchaseConsumeResult> ConsumeAsync(string token);
}
public class PaymentVerifyResult
{
public bool success;
public string msg;
public string orderId;
}
public class FlexionOrderResult
{
public int code { get; set; }
public string info { get; set; }
public string orderId { get; set; }
}
public class PurchaseConsumeResult
{
public bool success;
public string msg;
}
public class PaymentProcessor
{
public IPaymentVerifier verifier;
public IPurchaseConsumer consumer;
}
public static class PaymentProcessorFactory
{
private static PaymentProcessor _processor;
private static string _cachedChannel;
public static PaymentProcessor GetProcessor()
{
var channel = ChannelConfig.Channel;
if (_processor != null && _cachedChannel == channel)
return _processor;
_processor = channel switch
{
"Flexion" => new PaymentProcessor
{
verifier = new RetryingPaymentVerifier(new FlexionPaymentVerifier()),
consumer = new FlexionPurchaseConsumer()
},
"Dummy" => new PaymentProcessor
{
verifier = new RetryingPaymentVerifier(new FakePaymentVerifier()),
consumer = new FakePurchaseConsumer()
},
_ => null
};
_cachedChannel = channel;
return _processor;
}
}
public class RetryingPaymentVerifier : IPaymentVerifier
{
private readonly IPaymentVerifier _inner;
private const int MaxRetries = 2;
private static readonly TimeSpan RetryInterval = TimeSpan.FromSeconds(2);
public RetryingPaymentVerifier(IPaymentVerifier inner)
{
_inner = inner;
}
public async Task<PaymentVerifyResult> VerifyAsync(PaymentInfo info)
{
PaymentVerifyResult lastResult = null;
for (int attempt = 0; attempt <= MaxRetries; attempt++)
{
if (attempt > 0)
{
Debug.Log($"[RetryingPaymentVerifier] Retry {attempt}/{MaxRetries}, waiting {RetryInterval.TotalSeconds}s");
await Task.Delay(RetryInterval);
}
lastResult = await _inner.VerifyAsync(info);
if (lastResult.success) return lastResult;
Debug.LogWarning($"[RetryingPaymentVerifier] Attempt {attempt + 1} failed: {lastResult.msg}");
}
return lastResult;
}
}
public class FlexionPaymentVerifier : IPaymentVerifier
{
private const string DEFAULT_PAY_VERIFY_URL = "http://192.168.9.91:7036/api/FlexionCallback";
private readonly string _verifyUrl;
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
public FlexionPaymentVerifier()
{
var config = GContext.container.Resolve<IConfig>();
_verifyUrl = config.Get<string>("PAY_VERIFY_URL", DEFAULT_PAY_VERIFY_URL);
}
#if UNITY_EDITOR
public async Task<PaymentVerifyResult> VerifyAsync(PaymentInfo info)
{
Debug.LogWarning("[FlexionPaymentVerifier] Editor placeholder: auto success");
await Task.Yield();
return new PaymentVerifyResult { success = true, msg = "editor_placeholder" };
}
#else
public async Task<PaymentVerifyResult> VerifyAsync(PaymentInfo info)
{
var cts = new CancellationTokenSource(Timeout);
try
{
Debug.Log($"[FlexionPaymentVerifier] VerifyAsync start: url={_verifyUrl}");
Debug.Log($"[FlexionPaymentVerifier] Verify fields: orderId={info.orderId} productId={info.productId}" +
$" hasPurchaseJson={!string.IsNullOrEmpty(info.purchaseJson)} hasSignature={!string.IsNullOrEmpty(info.signature)}" +
$" purchaseToken={info.purchaseToken}");
var body = JsonConvert.SerializeObject(info);
Debug.Log("[FlexionPaymentVerifier] Request body: " + body);
using var req = new UnityWebRequest(_verifyUrl, "POST");
req.certificateHandler = new BypassCertificateHandler();
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body));
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
var tcs = new TaskCompletionSource<PaymentVerifyResult>(TaskCreationOptions.RunContinuationsAsynchronously);
cts.Token.Register(() => tcs.TrySetResult(new PaymentVerifyResult { success = false, msg = "timeout" }));
req.SendWebRequest().completed += _ =>
{
if (req.result == UnityWebRequest.Result.Success)
{
var respText = req.downloadHandler.text;
Debug.Log("[FlexionPaymentVerifier] Response: " + respText);
var orderResult = JsonConvert.DeserializeObject<FlexionOrderResult>(respText);
if (orderResult == null)
{
tcs.TrySetResult(new PaymentVerifyResult { success = false, msg = "parse_error" });
}
else if (orderResult.code == 0)
{
tcs.TrySetResult(new PaymentVerifyResult
{ success = true, msg = orderResult.info, orderId = orderResult.orderId });
}
else if (orderResult.code == 1)
{
tcs.TrySetResult(new PaymentVerifyResult
{ success = true, msg = orderResult.info, orderId = orderResult.orderId });
}
else if (orderResult.code == 2)
{
tcs.TrySetResult(new PaymentVerifyResult
{ success = true, msg = orderResult.info, orderId = orderResult.orderId });
}
else
{
// -1: 公钥/签名/玩家验证失败, -2: 写入失败, -3: 数据解析错误
tcs.TrySetResult(new PaymentVerifyResult
{ success = false, msg = orderResult.info ?? $"code={orderResult.code}" });
}
}
else
{
Debug.LogWarning("[FlexionPaymentVerifier] Failed: " + req.error);
tcs.TrySetResult(new PaymentVerifyResult { success = false, msg = req.error });
}
};
return await tcs.Task;
}
catch (Exception e)
{
Debug.LogError("[FlexionPaymentVerifier] Exception: " + e.Message);
return new PaymentVerifyResult { success = false, msg = e.Message };
}
finally
{
cts.Dispose();
}
}
#endif
}
public class FlexionPurchaseConsumer : IPurchaseConsumer
{
#if UNITY_EDITOR
public async Task<PurchaseConsumeResult> ConsumeAsync(string token)
{
await Task.Yield();
return new PurchaseConsumeResult { success = true, msg = "editor_placeholder" };
}
#elif UNITY_ANDROID
public async Task<PurchaseConsumeResult> ConsumeAsync(string token)
{
if (string.IsNullOrEmpty(token))
return new PurchaseConsumeResult { success = false, msg = "empty_token" };
UnityBridgeFunc.ConsumePurchase(token);
await Task.Yield();
return new PurchaseConsumeResult { success = true, msg = "consume_requested" };
}
#else
public async Task<PurchaseConsumeResult> ConsumeAsync(string token)
{
await Task.Yield();
return new PurchaseConsumeResult { success = false, msg = "unsupported_platform" };
}
#endif
}
public class FakePaymentVerifier : IPaymentVerifier
{
public async Task<PaymentVerifyResult> VerifyAsync(PaymentInfo info)
{
Debug.Log("[FakePaymentVerifier] Verifying purchase: " + info.orderId);
await Task.Delay(500);
return new PaymentVerifyResult { success = true, msg = "fake_ok" };
}
}
public class FakePurchaseConsumer : IPurchaseConsumer
{
public async Task<PurchaseConsumeResult> ConsumeAsync(string token)
{
Debug.Log("[FakePurchaseConsumer] Consume: " + token);
await Task.Yield();
return new PurchaseConsumeResult { success = true, msg = "fake_ok" };
}
}
internal class BypassCertificateHandler : CertificateHandler
{
protected override bool ValidateCertificate(byte[] certificateData) => true;
}
}

View File

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

View File

@@ -1,53 +0,0 @@
using System.Collections.Generic;
namespace tysdk
{
// TYSdkFacade 的登录/链接/ATT 等方法要依赖这些 POCO。
public enum EAccoutType
{
hwGoogle,
hwFacebook,
hwGuest,
hwVkid,
Apple,
none
}
public class LoginInfo
{
public bool isSuccess;
public int userId;
public string StrUserId => userId.ToString();
public string msg;
public int code;
}
public class LinkResult
{
// 0: success, 1: fail, -1 exists
public int code;
// when success and fail userId is current user
// when exists userId is 0
public int userId;
// not none when exists
public Dictionary<string, string> bindInfo;
}
public class ChangeLinkResult
{
public int code;
public int userId;
public string msg;
}
public class LinkCheckInfo
{
public int code;
}
public class ATTInfo
{
public bool isAccepted;
}
}

View File

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

View File

@@ -34,20 +34,13 @@ namespace tysdk
} }
} }
private static Dictionary<string, EAccoutType> accoutTypeMap = new Dictionary<string, EAccoutType>() private static Dictionary<string, EAccoutType> accoutTypeMap = new Dictionary<string, EAccoutType>(){
{
{"google", EAccoutType.hwGoogle}, {"google", EAccoutType.hwGoogle},
{"fb", EAccoutType.hwFacebook}, {"fb", EAccoutType.hwFacebook},
{"tyGuest", EAccoutType.hwGuest}, {"tyGuest", EAccoutType.hwGuest},
{"ios13", EAccoutType.Apple}, {"ios13", EAccoutType.Apple}
{"vk.global.app", EAccoutType.hwVkid},
}; };
public static void AddAccoutType(string key, EAccoutType value)
{
accoutTypeMap[key] = value;
}
private static EAccoutType ConvertAccoutType(string accountType) private static EAccoutType ConvertAccoutType(string accountType)
{ {
if (accoutTypeMap.ContainsKey(accountType)) if (accoutTypeMap.ContainsKey(accountType))
@@ -108,10 +101,12 @@ namespace tysdk
} }
} }
// ================== Singleton ================== private static AccountInfo _accountInfo;
private static TYSdkFacade _instance;
public static AccountInfo TYAccountInfo => _accountInfo; public static AccountInfo TYAccountInfo => _accountInfo;
protected static AccountInfo _accountInfo;
private static TYSdkFacade _instance;
public static bool IsSdkInited { get; private set; }
public static event System.Action<bool, string> OnInitComplete;
public static TYSdkFacade Instance public static TYSdkFacade Instance
{ {
@@ -125,47 +120,55 @@ namespace tysdk
return _instance; return _instance;
} }
} }
public static string Channel => ChannelConfig.Channel;
private int LoginTimeoutSeconds => ChannelConfig.LoginTimeoutSeconds;
public static bool IsSdkInited { get; private set; }
public static event System.Action<bool, string> OnInitComplete;
// ================== Init ==================
public void Init() public void Init()
{ {
Debug.Log($"[TYSdkFacade] Init()");
#if UNITY_ANDROID #if UNITY_ANDROID
UnityBridgeFunc.InitSDK(); UnityBridgeFunc.InitSDK(ChannelConfig.Channel);
#endif #endif
}
public void SetChannelFromNative(string channel)
{
Debug.Log($"[TYSdkFacade] SetChannelFromNative: {channel}");
ChannelConfig.SetChannel(channel);
} }
public void InitResult(string json) public void InitResult(string json)
{ {
var data = Newtonsoft.Json.Linq.JObject.Parse(json); Debug.Log($"[TYSdkFacade] InitResult: {json}");
var code = (int)data["code"]; var success = false;
var msg = (string)data["msg"]; var msg = string.Empty;
IsSdkInited = code == 0;
UnityEngine.Debug.Log($"[TYSdkFacade] InitResult: code={code} msg={msg}"); try
OnInitComplete?.Invoke(IsSdkInited, msg); {
var data = JObject.Parse(json);
success = (int)data["code"] == 0;
msg = (string)data["msg"];
}
catch (Exception e)
{
msg = e.Message;
Debug.LogWarning($"[TYSdkFacade] InitResult parse failed: {e}");
}
IsSdkInited = success;
OnInitComplete?.Invoke(success, msg);
} }
// Called from Java SDKManager.InitSDK to sync channel
public void SetChannelFromNative(string ch)
{
ChannelConfig.SetChannel(ch);
UnityEngine.Debug.Log($"[CHANNEL-VERIFY] C# SetChannelFromNative → channel={ch}, payType={ChannelConfig.PayType}, loginTimeout={ChannelConfig.LoginTimeoutSeconds}");
}
/*================================================ /*================================================
_ _ _ _
/ \ ___ ___ ___ _ _ _ __ | |_ / \ ___ ___ ___ _ _ _ __ | |_
/ _ \ / __/ __/ _ \| | | | '_ \| __| / _ \ / __/ __/ _ \| | | | '_ \| __|
/ ___ \ (_| (_| (_) | |_| | | | | |_ / ___ \ (_| (_| (_) | |_| | | | | |_
/_/ \_\___\___\___/ \__,_|_| |_|\__| /_/ \_\___\___\___/ \__,_|_| |_|\__|
=================================================*/ =================================================*/
public static bool IsLoggedIn => _accountInfo != null; public static bool IsLoggedIn => _accountInfo != null;
public void Logout() public void Logout()
@@ -182,7 +185,7 @@ namespace tysdk
public async Task<LoginInfo> Login(EAccoutType accoutType) public async Task<LoginInfo> Login(EAccoutType accoutType)
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
await Task.Yield(); await Task.Yield();
var deviceId = UnityEngine.Device.SystemInfo.deviceUniqueIdentifier.Split('-')[0].Substring(0, 8); var deviceId = UnityEngine.Device.SystemInfo.deviceUniqueIdentifier.Split('-')[0].Substring(0, 8);
@@ -200,25 +203,25 @@ namespace tysdk
}; };
#elif UNITY_ANDROID || UNITY_IOS #elif UNITY_ANDROID || UNITY_IOS
var task = TYSDKCallbackManager.Instance.RegisterCallback<LoginInfo>(TimeSpan.FromSeconds(LoginTimeoutSeconds)); var task = TYSDKCallbackManager.Instance.RegisterCallback<LoginInfo>(TimeSpan.FromSeconds(30));
UnityBridgeFunc.UnityLogin(accoutType); UnityBridgeFunc.UnityLogin(accoutType);
try try
{ {
var loginInfo = await task; var loginInfo = await task;
if (loginInfo.isSuccess) if(loginInfo.isSuccess)
{ {
_accountInfo.channel = accoutType; _accountInfo.channel = accoutType;
_accountInfo.AddLinkedAccount(accoutType); _accountInfo.AddLinkedAccount(accoutType);
_accountInfo.Save(); _accountInfo.Save();
} }
return loginInfo; return loginInfo;
} }
catch (Exception e) catch(Exception e)
{ {
Debug.LogWarning($"[TYSdkFacade] Login failed: {e}"); Debug.LogWarning($"[TYSdkFacade] Login failed: {e}");
return new LoginInfo() { isSuccess = false }; return new LoginInfo(){isSuccess = false};
} }
#endif #endif
} }
@@ -247,7 +250,7 @@ namespace tysdk
return new LoginInfo() { isSuccess = false }; return new LoginInfo() { isSuccess = false };
} }
} }
public async Task<LoginInfo> LoginBySns() public async Task<LoginInfo> LoginBySns()
{ {
Debug.Log("[TYSdkFacade] LoginBySns"); Debug.Log("[TYSdkFacade] LoginBySns");
@@ -332,17 +335,18 @@ namespace tysdk
UnityBridgeFunc.SetGaUserInfo(strUserId); UnityBridgeFunc.SetGaUserInfo(strUserId);
} }
#if UNITY_EDITOR #if UNITY_EDITOR
public async Task<LinkResult> LinkAccount(EAccoutType accoutType) public async Task<LinkResult> LinkAccount(EAccoutType accoutType)
{ {
await Task.Delay(300); await Task.Delay(300);
return new LinkResult() { code = 1 }; return new LinkResult(){code = 1};
} }
#elif UNITY_IOS || UNITY_ANDROID #elif UNITY_IOS || UNITY_ANDROID
public async Task<LinkResult> LinkAccount(EAccoutType accoutType) public async Task<LinkResult> LinkAccount(EAccoutType accoutType)
{ {
if (_accountInfo == null) return new LinkResult() { code = 1 }; if (_accountInfo == null) return new LinkResult(){ code = 1};
if (_accountInfo.linkedAccout.Contains(accoutType)) return new LinkResult() { code = 0 }; if (_accountInfo.linkedAccout.Contains(accoutType)) return new LinkResult(){ code = 0};
var task = TYSDKCallbackManager.Instance.RegisterCallback<LinkResult>(TimeSpan.FromMinutes(1)); var task = TYSDKCallbackManager.Instance.RegisterCallback<LinkResult>(TimeSpan.FromMinutes(1));
UnityBridgeFunc.LinkAccount(accoutType); UnityBridgeFunc.LinkAccount(accoutType);
@@ -364,7 +368,6 @@ namespace tysdk
return new LinkResult() { code = 1 }; return new LinkResult() { code = 1 };
} }
} }
#endif
public void LinkAccoutResult(string message) public void LinkAccoutResult(string message)
{ {
@@ -387,18 +390,21 @@ namespace tysdk
} }
TYSDKCallbackManager.Instance.TryTriggerCallback(result); TYSDKCallbackManager.Instance.TryTriggerCallback(result);
} }
#endif
public void CleanLinkTempSnSInfo() public void CleanLinkTempSnSInfo()
{ {
UnityBridgeFunc.CleanLinkTmpSnsInfo(); UnityBridgeFunc.CleanLinkTmpSnsInfo();
} }
#if UNITY_EDITOR
public async Task<ChangeLinkResult> ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) public async Task<ChangeLinkResult> ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId)
{ {
#if UNITY_EDITOR
await Task.Delay(300); await Task.Delay(300);
return new ChangeLinkResult() { code = 1 }; return new ChangeLinkResult(){code = 1};
#else }
#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)); var task = TYSDKCallbackManager.Instance.RegisterCallback<ChangeLinkResult>(TimeSpan.FromSeconds(30));
UnityBridgeFunc.ChangeLinkAccount(accoutType, oldUserId, newUserId); UnityBridgeFunc.ChangeLinkAccount(accoutType, oldUserId, newUserId);
try try
@@ -420,8 +426,8 @@ namespace tysdk
{ {
CleanLinkTempSnSInfo(); CleanLinkTempSnSInfo();
} }
#endif
} }
#endif
public void ChangeLinkAccountResult(string message) public void ChangeLinkAccountResult(string message)
{ {
@@ -435,6 +441,14 @@ namespace tysdk
TYSDKCallbackManager.Instance.TryTriggerCallback(result); TYSDKCallbackManager.Instance.TryTriggerCallback(result);
} }
/// <summary>
/// 检查是否已经绑定
/// <returns>
/// 1 已经绑定
/// 0 未绑定
/// -1 失败
/// </returns>
/// </summary>
public async Task<bool> LinkCheck(EAccoutType accoutType) public async Task<bool> LinkCheck(EAccoutType accoutType)
{ {
if (_accountInfo == null) return false; if (_accountInfo == null) return false;
@@ -460,7 +474,7 @@ namespace tysdk
return result.code == 1; return result.code == 1;
} }
catch (Exception e) catch(Exception e)
{ {
Debug.LogWarning($"[TYSdkFacade] CheckLinkResult error: {e}"); Debug.LogWarning($"[TYSdkFacade] CheckLinkResult error: {e}");
return false; return false;
@@ -482,6 +496,7 @@ namespace tysdk
IConfig config = GContext.container.Resolve<IConfig>(); IConfig config = GContext.container.Resolve<IConfig>();
var tyurl = config.Get<string>("AGG_SERVER", defaultTyurl); var tyurl = config.Get<string>("AGG_SERVER", defaultTyurl);
string tycs = tyurl == defaultTyurl ? string tycs = tyurl == defaultTyurl ?
"https://hwcsh.tygameworld.com/template/appCancel/note.html" "https://hwcsh.tygameworld.com/template/appCancel/note.html"
: :
@@ -503,12 +518,23 @@ namespace tysdk
string uid = $"uid={_accountInfo.userId}"; string uid = $"uid={_accountInfo.userId}";
list.Add(uid); list.Add(uid);
list.Add("roleid=0"); string roleid = "roleid=0";
list.Add("gameid=20587"); list.Add(roleid);
list.Add("cloudid=128");
list.Add("gamename=FishingTravel"); string gameid = "gameid=20587";
list.Add("certification=0"); list.Add(gameid);
list.Add("ischannel=1");
string cloudid = "cloudid=128";
list.Add(cloudid);
string gamename = "gamename=FishingTravel";
list.Add(gamename);
string certification = "certification=0";
list.Add(certification);
string ischannel = "ischannel=1";
list.Add(ischannel);
string tysdktoken = $"tysdktoken={_accountInfo.token}"; string tysdktoken = $"tysdktoken={_accountInfo.token}";
list.Add(tysdktoken); list.Add(tysdktoken);
@@ -518,6 +544,7 @@ namespace tysdk
string listStr = string.Join("&", list.ToArray()); string listStr = string.Join("&", list.ToArray());
string sign = listStr + string sign = listStr +
"csh-api-6dfa879490a249be9fbc92e97e4d898d-api-csh"; "csh-api-6dfa879490a249be9fbc92e97e4d898d-api-csh";
//对sign进行MD5加密
string signMd5 = CalculateMD5Hash(sign); string signMd5 = CalculateMD5Hash(sign);
string url = $"{tycs}?{listStr}&sign={signMd5}&isAbroad=1&lan=en"; string url = $"{tycs}?{listStr}&sign={signMd5}&isAbroad=1&lan=en";
Debug.Log($"[TYSdkFacade:Signout] url \n{url}"); Debug.Log($"[TYSdkFacade:Signout] url \n{url}");
@@ -526,16 +553,25 @@ namespace tysdk
private string CalculateMD5Hash(string input) private string CalculateMD5Hash(string input)
{ {
// Create a new instance of the MD5CryptoServiceProvider object.
MD5 md5Hasher = MD5.Create(); MD5 md5Hasher = MD5.Create();
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input)); byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder(); StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++) for (int i = 0; i < data.Length; i++)
{ {
sBuilder.Append(data[i].ToString("x2")); sBuilder.Append(data[i].ToString("x2"));
} }
// Return the hexadecimal string.
return sBuilder.ToString(); return sBuilder.ToString();
} }
} }
} }

View File

@@ -4,17 +4,17 @@ using UnityEngine;
namespace tysdk namespace tysdk
{ {
public partial class TYSdkFacade public partial class TYSdkFacade : MonoBehaviour
{ {
/*================================================ /*================================================
_ _____ _____ _ _____ _____
/ \|_ _|_ _| / \|_ _|_ _|
/ _ \ | | | | / _ \ | | | |
/ ___ \| | | | / ___ \| | | |
/_/ \_\_| |_| /_/ \_\_| |_|
=================================================*/ =================================================*/
public bool IsAttAccepted() public bool IsAttAccepted()
{ {
@@ -25,25 +25,25 @@ namespace tysdk
{ {
#if UNITY_EDITOR || UNITY_ANDROID #if UNITY_EDITOR || UNITY_ANDROID
await Task.Yield(); await Task.Yield();
return new ATTInfo() { isAccepted = true };
return new ATTInfo(){isAccepted = true};
#elif UNITY_IOS #elif UNITY_IOS
var task = TYSDKCallbackManager.Instance.RegisterCallback<ATTInfo>(); var task = tysdk.TYSDKCallbackManager.Instance.RegisterCallback<ATTInfo>();
UnityBridgeFunc.RequestATT(); UnityBridgeFunc.RequestATT();
try try{
{ return await task;
return await task;
} }
catch (Exception e) catch(Exception e)
{ {
Debug.LogWarning(e.Message); Debug.LogWarning(e.Message);
return new ATTInfo() { isAccepted = false }; return new ATTInfo(){isAccepted = false};
} }
#endif #endif
} }
public void RequestATTResult(string r) public void RequestATTResult(string r)
{ {
TYSDKCallbackManager.Instance.TryTriggerCallback(new ATTInfo() { isAccepted = r == "1" }); TYSDKCallbackManager.Instance.TryTriggerCallback(new ATTInfo(){isAccepted = r == "1"});
} }
} }
} }

View File

@@ -2,14 +2,14 @@ using UnityEngine;
namespace tysdk namespace tysdk
{ {
public partial class TYSdkFacade public partial class TYSdkFacade : MonoBehaviour
{ {
/*================================================ /*================================================
_____ _ _____ _ _____ _ _____ _
| ____|_ _____ _ __ | |_ |_ _| __ __ _ ___| | __ | ____|_ _____ _ __ | |_ |_ _| __ __ _ ___| | __
| _| \ \ / / _ \ '_ \| __| | || '__/ _` |/ __| |/ / | _| \ \ / / _ \ '_ \| __| | || '__/ _` |/ __| |/ /
| |___ \ V / __/ | | | |_ | || | | (_| | (__| < | |___ \ V / __/ | | | |_ | || | | (_| | (__| <
|_____| \_/ \___|_| |_|\__| |_||_| \__,_|\___|_|\_\ |_____| \_/ \___|_| |_|\__| |_||_| \__,_|\___|_|\_\
=================================================*/ =================================================*/
@@ -21,18 +21,18 @@ namespace tysdk
#endif #endif
} }
} }
public enum GATrackType public enum GATrackType
{ {
GA_TRACK = 1, GA_TRACK = 1, //默认类型
GA_CION = 2, GA_CION = 2, //金流相关事件
GA_PAY = 3, GA_PAY = 3, //支付相关事件
GA_GAME = 4, GA_GAME = 4, //游戏行为
GA_LOGIN = 5, GA_LOGIN = 5, //登录注册相关事件
GA_PUSH = 6, GA_PUSH = 6, //推送相关事件
GA_ADBOX = 7, GA_ADBOX = 7, //adbox相关事件
GA_PREFORMANCE = 8, GA_PREFORMANCE = 8, //性能上报相关事件
GA_SDK = 9, GA_SDK = 9, //SDK相关事件
GA_ABTest = 10, GA_ABTest = 10, //abtest相关事件
} }
} }

View File

@@ -8,30 +8,33 @@ using System.Text;
namespace tysdk namespace tysdk
{ {
public partial class TYSdkFacade public partial class TYSdkFacade : MonoBehaviour
{ {
/*================================================ /*================================================
____ _ ____ _
| _ \ __ _ _ _ _ __ ___ ___ _ __ | |_ | _ \ __ _ _ _ _ __ ___ ___ _ __ | |_
| |_) / _` | | | | '_ ` _ \ / _ \ '_ \| __| | |_) / _` | | | | '_ ` _ \ / _ \ '_ \| __|
| __/ (_| | |_| | | | | | | __/ | | | |_ | __/ (_| | |_| | | | | | | __/ | | | |_
|_| \__,_|\__, |_| |_| |_|\___|_| |_|\__| |_| \__,_|\__, |_| |_| |_|\___|_| |_|\__|
|___/ |___/
=================================================*/ =================================================*/
private bool _payFirstNegativeHandled; private bool _payFirstNegativeHandled;
//public async Task<PaymentInfo> Pay(string prodId, string prodPrice, string prodName, int count, string pType, string price_amount_micros =null)
public async Task<PaymentInfo> Pay(SKUDetail prod, int count, float usdprice, JObject purchaseInfo) public async Task<PaymentInfo> Pay(SKUDetail prod, int count, float usdprice, JObject purchaseInfo)
{ {
if (!IsLoggedIn) return new PaymentInfo() { code = "-1", msg = "未登录" }; if (!IsLoggedIn) return new PaymentInfo() { code = "-1", msg = "未登录" };
var orderId = System.Guid.NewGuid().ToString(); var orderId = System.Guid.NewGuid().ToString();
if (purchaseInfo == null) if(purchaseInfo == null)
purchaseInfo = new JObject(); purchaseInfo = new JObject();
purchaseInfo["orderId"] = orderId;
string extraInfo = purchaseInfo.ToString(Newtonsoft.Json.Formatting.None); string extraInfo = purchaseInfo.ToString(Newtonsoft.Json.Formatting.None);
//to base64
extraInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(extraInfo)); extraInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(extraInfo));
Debug.Log("[TYSdk] extraInfo: " + extraInfo); Debug.Log("[TYSdk] extraInfo: " + extraInfo);
var prodId = prod.ProdID; var prodId = prod.ProdID;
@@ -59,7 +62,7 @@ namespace tysdk
var task = TYSDKCallbackManager.Instance.RegisterCallback<PaymentInfo>(TimeSpan.FromSeconds(90)); var task = TYSDKCallbackManager.Instance.RegisterCallback<PaymentInfo>(TimeSpan.FromSeconds(90));
try try
{ {
UnityBridgeFunc.UnityKnowNew(prodId, prodPrice, prodName, count.ToString(), orderId, extraInfo, pType); UnityBridgeFunc.UnityKnowNew(prodId, prodPrice, prodName, count.ToString(), orderId, extraInfo,pType);
var result = await task; var result = await task;
result.count = count; result.count = count;
result.orderId = orderId; result.orderId = orderId;
@@ -103,6 +106,7 @@ namespace tysdk
#endif #endif
} }
//支付的回调
public void PayResult(string json) public void PayResult(string json)
{ {
Debug.Log("[TYSdkFacade] pay callback:" + json); Debug.Log("[TYSdkFacade] pay callback:" + json);
@@ -110,11 +114,25 @@ namespace tysdk
var result = new PaymentInfo(); var result = new PaymentInfo();
result.code = jresult["code"].ToString(); result.code = jresult["code"].ToString();
result.msg = jresult["errStr"].ToString(); result.msg = jresult["errStr"].ToString();
result.purchaseJson = jresult["purchaseJson"]?.ToString(); var purchaseJson = jresult["purchaseJson"]?.ToString();
result.signature = jresult["signature"]?.ToString(); var signature = jresult["signature"]?.ToString();
result.purchaseToken = jresult["purchaseToken"]?.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; result.userId = _accountInfo?.strUserId;
if (result.code == "-1") if(result.code == "-1")
{ {
if (!_payFirstNegativeHandled) if (!_payFirstNegativeHandled)
{ {
@@ -128,60 +146,15 @@ namespace tysdk
} }
return; return;
} }
var processor = PaymentProcessorFactory.GetProcessor(); TYSDKCallbackManager.Instance.TryTriggerCallback(result);
if (processor?.verifier != null && !string.IsNullOrEmpty(result.purchaseJson))
{
_VerifyPurchase(result, processor);
}
else
{
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
}
}
private async void _VerifyPurchase(PaymentInfo result, PaymentProcessor processor)
{
TYSDKCallbackManager.Instance.ResetCallbackTimeout<PaymentInfo>(TimeSpan.FromSeconds(60));
try
{
var verifyResult = await processor.verifier.VerifyAsync(result);
if (verifyResult.success)
{
result.verified = true;
if (!string.IsNullOrEmpty(verifyResult.orderId))
result.serverOrderId = verifyResult.orderId;
if (processor.consumer != null && !string.IsNullOrEmpty(result.purchaseToken))
{
var consumeResult = await processor.consumer.ConsumeAsync(result.purchaseToken);
if (!consumeResult.success)
{
Debug.LogWarning("[TYSdkFacade] Purchase consume failed: " + consumeResult.msg);
}
}
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
}
else
{
Debug.LogWarning("[TYSdkFacade] Payment verification failed: " + verifyResult.msg);
result.code = "-1";
result.msg = "verification_failed: " + verifyResult.msg;
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
}
}
catch (Exception e)
{
Debug.LogError("[TYSdkFacade] Verification exception: " + e.Message);
result.code = "-1";
result.msg = "verification_error: " + e.Message;
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
}
} }
// Get Pay list
public async Task<ProductListInfo> GetSKUList() public async Task<ProductListInfo> GetSKUList()
{ {
if (!IsLoggedIn) return new ProductListInfo() { code = -1, msg = "未登录" }; if (!IsLoggedIn) return new ProductListInfo() { code = -1, msg = "未登录" };
#if UNITY_EDITOR #if UNITY_EDITOR
await Task.Yield(); await Task.Yield();
var result = new ProductListInfo(); var result = new ProductListInfo();
@@ -194,6 +167,25 @@ namespace tysdk
#endif #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>();
UnityBridgeFunc.GetSKUListByIapIds(productIds);
var result = await task;
return result;
#else
return new ProductListInfo() { code = -1, msg = "unsupported platform" };
#endif
}
//商品列表的回调
public void SKUListResult(string json) public void SKUListResult(string json)
{ {
Debug.Log("[TYSdkFacade] GetSKUList callback"); Debug.Log("[TYSdkFacade] GetSKUList callback");
@@ -216,3 +208,7 @@ namespace tysdk
} }
} }
} }

View File

@@ -1,14 +1,82 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace tysdk namespace tysdk
{ {
public enum EAccoutType
{
hwGoogle,
hwFacebook,
hwGuest,
hwVkid,
Apple,
none
}
public enum PaymentReceiptProvider
{
None,
Flexion
}
public class PaymentReceipt
{
public PaymentReceiptProvider Provider;
public string PurchaseJson;
public string Signature;
public string PurchaseToken;
public bool HasPayload => !string.IsNullOrEmpty(PurchaseJson);
public bool CanConsume => !string.IsNullOrEmpty(PurchaseToken);
}
public class PaymentReceiptVerification
{
public bool Verified;
public string ServerOrderId;
public int ProviderCode;
public string ProviderMessage;
}
public class LoginInfo
{
public bool isSuccess;
public int userId;
public string StrUserId => userId.ToString();
public string msg;
public int code;
}
public class LinkResult
{
// 0: success, 1: fail, -1 exists
public int code;
// when success and fail userId is current user
// when exists userId is 0
public int userId;
// not none when exists
public Dictionary<string, string> bindInfo;
}
public class ChangeLinkResult
{
public int code;
public int userId;
public string msg;
}
public class LinkCheckInfo
{
public int code;
}
public class PaymentInfo public class PaymentInfo
{ {
public bool isSuccessFromSdk => code == "0"; public bool isSuccessFromSdk => code == "0";
public bool isSuccess => isSuccessFromSdk && confirmed; public bool isSuccess => isSuccessFromSdk && confirmed;
private bool confirmed { get; set; } = false; private bool confirmed {get; set;} = false;
public int count; public int count;
public string orderId; public string orderId;
public string productId; public string productId;
@@ -17,6 +85,8 @@ namespace tysdk
public string code; public string code;
public string msg; 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 // Flexion: purchase.getOriginalJson(), used by server for RSA signature verification
public string purchaseJson; public string purchaseJson;
// Flexion: purchase.getSignature(), used by server for RSA signature verification // Flexion: purchase.getSignature(), used by server for RSA signature verification
@@ -24,6 +94,21 @@ namespace tysdk
// Flexion: purchase token, used to consume after server verification // Flexion: purchase token, used to consume after server verification
public string purchaseToken; 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; public bool verified;
// Server-side order ID returned from verification // Server-side order ID returned from verification
@@ -36,17 +121,17 @@ namespace tysdk
{ {
confirmed = orderId == this.orderId && productId == this.productId; confirmed = orderId == this.orderId && productId == this.productId;
if (!confirmed) if(!confirmed)
UnityEngine.Debug.LogWarning("[TYSdk Pay] order not confirmed"); UnityEngine.Debug.LogWarning("[TYSdk Pay] order not confirmed");
return confirmed; return confirmed;
} }
public override string ToString() public override string ToString()
{ {
return JsonConvert.SerializeObject(this); return Newtonsoft.Json.JsonConvert.SerializeObject(this);
} }
} }
public class ProductListInfo public class ProductListInfo
{ {
public int code = 1; public int code = 1;
@@ -70,6 +155,7 @@ namespace tysdk
products = prodList; products = prodList;
} }
#endif #endif
} }
#if UNITY_IOS #if UNITY_IOS
@@ -90,7 +176,7 @@ namespace tysdk
public override string ToString() public override string ToString()
{ {
return JsonConvert.SerializeObject(this); return Newtonsoft.Json.JsonConvert.SerializeObject(this);
} }
} }
#else #else
@@ -98,24 +184,37 @@ namespace tysdk
{ {
public string description; public string description;
public string ourProductId; public string ourProductId;
public string productId;
public string price; public string price;
public string price_amount_micros; public string price_amount_micros;
public string price_currency_code; public string price_currency_code;
public string productId;
public string title; public string title;
public string type; public string type;
public string currency; public string currency;
public string price_amount; public string price_amount;
public override string ToString()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this);
}
public string ProdPriceStr => price; public string ProdPriceStr => price;
public float ProdPrice => float.Parse(price_amount_micros) / 1000000; public float ProdPrice => float.Parse(price_amount_micros) / 1000000;
public string ProdID => ourProductId; public string ProdID => ourProductId;
public string ProdKey => productId; public string ProdKey => productId;
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
} }
#endif #endif
public class ATTInfo
{
public bool isAccepted;
}
public class FlexionOrderResult
{
public int code { get; set; }
public string info { get; set; }
public string orderId { get; set; }
}
} }

View File

@@ -4,87 +4,96 @@ using UnityEngine;
namespace tysdk namespace tysdk
{ {
public static class UnityBridgeFunc public static class UnityBridgeFunc
{ {
#if UNITY_EDITOR
public static void InitSDK() { } #if UNITY_EDITOR
//初始化sdk
public static void InitSDK(string channel){}
public static void UnityResetServerUrl(string url) { } //更新登录支付域名
public static void UnityResetServerUrl(string url){}
public static void UnityLoginByTokenFun(string token) { } //登录
public static void UnityLoginByTokenFun(string token){}
public static string UnityLoginBySnsInfoFun() => null; public static string UnityLoginBySnsInfoFun() => null;
public static void UnityLogin(EAccoutType accoutType) { } public static void UnityLogin(EAccoutType accoutType){}
public static void CleanLinkTmpSnsInfo() { } public static void CleanLinkTmpSnsInfo() { }
public static void LinkAccount(EAccoutType accoutType) { }
public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) { }
public static void LinkCheck(EAccoutType accoutType) { }
public static void UnityGetIdentityFun(string type) { } public static void LinkAccount(EAccoutType accoutType){}
public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId){}
public static void LinkCheck(EAccoutType accoutType){}
public static void UnityLogOutByChannel(EAccoutType accoutType) { } public static void UnityGetIdentityFun(string type){}
public static void UnityLogOutByChannel(EAccoutType accoutType){}
//支付new
public static void UnityKnowNew(string productId, string productPrice, string productName, public static void UnityKnowNew(string productId, string productPrice, string productName,
string productCount, string prodorderId, string appInfo, string pType) { } string productCount, string prodorderId, string appInfo, string pType) { }
//支付
public static void UnityKnow(String productId, String productName, String productCount, public static void UnityKnow(String productId, String productName, String productCount,
String prodorderId, String appInfo) { } String prodorderId, String appInfo) {}
//获取商品列表
public static void GetSKUList() { } public static void GetSKUList() { }
public static void SetGaUserInfo(string userId) { }
public static void SetGaCommonInfo(string SetGaCommonInfo) { }
public static void GAReportParams(int type, string eventstr, string paramstr) { }
public static void RequestATT() { }
public static int GetATT() { return 1; }
public static void Review() { }
public static void ConsumePurchase(string token) { } public static void ConsumePurchase(string token) { }
public static string GetPendingPurchases() => "[]";
//打点
public static void SetGaUserInfo(string userId){}
public static void SetGaCommonInfo(string SetGaCommonInfo){}
public static void GAReportParams(int type, string eventstr, string paramstr){}
//ATT
public static void RequestATT(){}
public static int GetATT() {return 1;}
public static void Review() { }
public static void FBShareLink(string title, string content, string url) { } public static void FBShareLink(string title, string content, string url) { }
public static void MessengerShareLink(string title, string content, string url) { } public static void MessengerShareLink(string title, string content, string url) { }
#elif UNITY_ANDROID #elif UNITY_ANDROID
private static string SDK_CLASS = "com.unity3d.player.SDKManager"; private static string SDK_CLASS = "com.unity3d.player.SDKManager";
private static string EAccountTypeToStr(EAccoutType accoutType) //初始化sdk
{ public static void InitSDK(string channel){
return accoutType == EAccoutType.hwVkid ? "vk.global.app" : accoutType.ToString(); using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
}
public static void InitSDK()
{
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
using (var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using(var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{ {
var activity = activityCls.GetStatic<AndroidJavaObject>("currentActivity"); var activity = activityCls.GetStatic<AndroidJavaObject>("currentActivity");
UnityEngine.Debug.Log("[CHANNEL-VERIFY] C# InitSDK → ChannelConfig.Channel=" + ChannelConfig.Channel); sdkManager.CallStatic("InitSDK", activity, channel);
sdkManager.CallStatic("InitSDK", activity, ChannelConfig.Channel);
} }
} }
} }
//更新登录支付域名
public static void UnityResetServerUrl(string url) public static void UnityResetServerUrl(string url)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("UnityResetServerUrl", url); sdkManager.CallStatic("UnityResetServerUrl", url);
} }
} }
//登录
public static void UnityLoginByTokenFun(string token) public static void UnityLoginByTokenFun(string token)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("UnityLoginByTokenFun", token); sdkManager.CallStatic("UnityLoginByTokenFun", token);
} }
@@ -92,7 +101,7 @@ namespace tysdk
public static String UnityLoginBySnsInfoFun() public static String UnityLoginBySnsInfoFun()
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
return sdkManager.CallStatic<string>("UnityLoginBySnsInfo"); return sdkManager.CallStatic<string>("UnityLoginBySnsInfo");
} }
@@ -100,16 +109,15 @@ namespace tysdk
public static void UnityLogin(EAccoutType accoutType) public static void UnityLogin(EAccoutType accoutType)
{ {
var accountInfo = EAccountTypeToStr(accoutType); using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("UnityLogin", accountInfo); sdkManager.CallStatic("UnityLogin", accoutType.ToString());
} }
} }
public static void UnityGetIdentityFun(string type) public static void UnityGetIdentityFun(string type)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("UnityGetIdentityFun", type); sdkManager.CallStatic("UnityGetIdentityFun", type);
} }
@@ -117,87 +125,118 @@ namespace tysdk
public static void UnityLogOutByChannel(EAccoutType accoutType) public static void UnityLogOutByChannel(EAccoutType accoutType)
{ {
var accountInfo = EAccountTypeToStr(accoutType);
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("UnityLogOutByChannel", accountInfo); sdkManager.CallStatic("UnityLogOutByChannel", accoutType.ToString());
} }
} }
public static void LinkAccount(EAccoutType accoutType) public static void LinkAccount(EAccoutType accoutType)
{ {
string userId = TYSdkFacade.TYAccountInfo.strUserId; using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
var accountInfo = EAccountTypeToStr(accoutType);
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("LinkAccount", accountInfo, userId); string userId = TYSdkFacade.TYAccountInfo.strUserId;
sdkManager.CallStatic("LinkAccount", accoutType.ToString(), userId);
} }
} }
public static void CleanLinkTmpSnsInfo() public static void CleanLinkTmpSnsInfo(){
{ using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("CleanLinkTmpSnsInfo"); using(var activityCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
sdkManager.CallStatic("CleanLinkTmpSnsInfo");
}
} }
} }
public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId) public static void ChangeLinkAccount(EAccoutType accoutType, int oldUserId, int newUserId)
{ {
var accountInfo = EAccountTypeToStr(accoutType); using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("ChangeLinkAccount", accountInfo, oldUserId, newUserId); sdkManager.CallStatic("ChangeLinkAccount", accoutType.ToString(), oldUserId, newUserId);
} }
} }
public static void LinkCheck(EAccoutType accoutType) public static void LinkCheck(EAccoutType accoutType)
{ {
var accountInfo = EAccountTypeToStr(accoutType); using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("LinkCheck", accountInfo); sdkManager.CallStatic("LinkCheck", accoutType.ToString());
} }
} }
//支付new
public static void UnityKnowNew(string productId, string productPrice, string productName, public static void UnityKnowNew(string productId, string productPrice, string productName,
string productCount, string prodorderId, string appInfo, string pType) string productCount, string prodorderId, string appInfo,string pType)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("UnityKnowNew", productId, productPrice, productName, sdkManager.CallStatic("UnityKnowNew", productId, productPrice, productName,
productCount, prodorderId, appInfo, pType); productCount, prodorderId, appInfo,pType);
} }
} }
public static void ConsumePurchase(string token) //支付
public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("ConsumePurchase", token); sdkManager.CallStatic("UnityKnow", productId, productName, productCount, prodorderId, appInfo);
}
}
public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo)
{
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("UnityKnow", productId, productName, productCount, prodorderId, appInfo);
} }
} }
//获取商品列表
public static void GetSKUList() public static void GetSKUList()
{ {
UnityEngine.Debug.Log("UnityBridgeFunc.GetSKUList()"); UnityEngine.Debug.Log("UnityBridgeFunc.GetSKUList()");
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("thirdExtend"); sdkManager.CallStatic("thirdExtend");
} }
} }
//通过商品ID列表获取商品详情Flexion 渠道使用)
public static void GetSKUListByIapIds(string productIds)
{
UnityEngine.Debug.Log("UnityBridgeFunc.GetSKUListByIapIds(productIds)");
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("queryProductsByIDs", productIds);
}
}
public static void ConsumePurchase(string token)
{
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("ConsumePurchase", token);
}
}
public static string GetPendingPurchases()
{
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
return sdkManager.CallStatic<string>("GetPendingPurchases");
}
}
public static void CancelBillingFlow()
{
UnityEngine.Debug.Log("UnityBridgeFunc.CancelBillingFlow");
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("CancelBillingFlow");
}
}
//打点
public static void SetGaUserInfo(string userId) public static void SetGaUserInfo(string userId)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("SetGaUserInfo", userId); sdkManager.CallStatic("SetGaUserInfo", userId);
} }
@@ -205,7 +244,7 @@ namespace tysdk
public static void SetGaCommonInfo(string SetGaCommonInfo) public static void SetGaCommonInfo(string SetGaCommonInfo)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("SetGaCommonInfo", SetGaCommonInfo); sdkManager.CallStatic("SetGaCommonInfo", SetGaCommonInfo);
} }
@@ -213,35 +252,37 @@ namespace tysdk
public static void GAReportParams(int type, string eventstr, string paramstr) public static void GAReportParams(int type, string eventstr, string paramstr)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("GAReportParams", type, eventstr, paramstr); sdkManager.CallStatic("GAReportParams", type, eventstr, paramstr);
} }
} }
public static void RequestATT() { }
public static int GetATT() { return 1; } //ATT
public static void RequestATT(){}
public static int GetATT() {return 1;}
public static void Review() public static void Review()
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("Review"); sdkManager.CallStatic("Review");
} }
} }
public static void FBShareLink(string title, string content, string url) public static void FBShareLink(string title, string content, string url)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("FBShareLink", title, content, url); sdkManager.CallStatic("FBShareLink", title, content, url);
} }
} }
public static void MessengerShareLink(string title, string content, string url) public static void MessengerShareLink(string title, string content, string url)
{ {
using (var sdkManager = new AndroidJavaClass(SDK_CLASS)) using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
{ {
sdkManager.CallStatic("MessengerShareLink", title, content, url); sdkManager.CallStatic("MessengerShareLink", title, content, url);
} }
@@ -249,9 +290,11 @@ namespace tysdk
#elif UNITY_IOS #elif UNITY_IOS
//更新登录支付域名
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void UnityResetServerUrl(string url); public static extern void UnityResetServerUrl(string url);
//登录
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void UnityLoginByTokenFun(string token); public static extern void UnityLoginByTokenFun(string token);
@@ -267,6 +310,7 @@ namespace tysdk
return string.Empty; return string.Empty;
} }
// Convert C string to C# string
string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptr); string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptr);
return result ?? string.Empty; return result ?? string.Empty;
} }
@@ -357,6 +401,7 @@ namespace tysdk
} }
} }
[DllImport("__Internal")] private static extern void LinkGoogle(int userId); [DllImport("__Internal")] private static extern void LinkGoogle(int userId);
[DllImport("__Internal")] private static extern void LinkFacebook(int userId); [DllImport("__Internal")] private static extern void LinkFacebook(int userId);
[DllImport("__Internal")] private static extern void LinkApple(int userId); [DllImport("__Internal")] private static extern void LinkApple(int userId);
@@ -374,25 +419,31 @@ namespace tysdk
case EAccoutType.hwFacebook: case EAccoutType.hwFacebook:
IsLinkedFacebook(); IsLinkedFacebook();
break; break;
case EAccoutType.Apple: case EAccoutType.Apple:
IsLinkedApple(); IsLinkedApple();
break; break;
} }
} }
[DllImport("__Internal")] public static extern void CleanLinkTmpSnsInfo(); [DllImport("__Internal")] public static extern void CleanLinkTmpSnsInfo();
[DllImport("__Internal")] private static extern void IsLinkedGoogle(); [DllImport("__Internal")] private static extern void IsLinkedGoogle();
[DllImport("__Internal")] private static extern void IsLinkedFacebook(); [DllImport("__Internal")] private static extern void IsLinkedFacebook();
[DllImport("__Internal")] private static extern void IsLinkedApple(); [DllImport("__Internal")] private static extern void IsLinkedApple();
//支付
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void UnityKnow(string userId, string productId, string productPrice, string productName, public static extern void UnityKnow(string userId, string productId, string productPrice, string productName,
string productCount, string prodorderId, string appInfo); string productCount, string prodorderId, string appInfo);
//获取商品列表
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void GetSKUList(); public static extern void GetSKUList();
//打点
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void SetGaUserInfo(string userId); public static extern void SetGaUserInfo(string userId);
@@ -402,6 +453,7 @@ namespace tysdk
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void GAReportParams(int type, string eventstr, string paramstr); public static extern void GAReportParams(int type, string eventstr, string paramstr);
//ATT
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void RequestATT(); public static extern void RequestATT();
@@ -414,6 +466,10 @@ namespace tysdk
[DllImport("__Internal")] [DllImport("__Internal")]
public static extern void MessengerShareLink(string title, string content, string url); public static extern void MessengerShareLink(string title, string content, string url);
public static void ConsumePurchase(string token) { }
public static string GetPendingPurchases() => "[]";
#endif #endif
} }
} }

View File

@@ -1,16 +1,18 @@
{ {
"MonoBehaviour": { "MonoBehaviour": {
"Version": 3, "Version": 5,
"EnableBurstCompilation": true, "EnableBurstCompilation": true,
"EnableOptimisations": true, "EnableOptimisations": true,
"EnableSafetyChecks": false, "EnableSafetyChecks": false,
"EnableDebugInAllBuilds": false, "EnableDebugInAllBuilds": false,
"UsePlatformSDKLinker": false, "DebugDataKind": 0,
"EnableArmv9SecurityFeatures": false,
"CpuMinTargetX32": 0, "CpuMinTargetX32": 0,
"CpuMaxTargetX32": 0, "CpuMaxTargetX32": 0,
"CpuMinTargetX64": 0, "CpuMinTargetX64": 0,
"CpuMaxTargetX64": 0, "CpuMaxTargetX64": 0,
"CpuTargetsX32": 6, "CpuTargetsX32": 6,
"CpuTargetsX64": 72 "CpuTargetsX64": 72,
"OptimizeFor": 0
} }
} }

View File

@@ -163,7 +163,7 @@ PlayerSettings:
androidSupportedAspectRatio: 1 androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.1 androidMaxAspectRatio: 2.1
applicationIdentifier: applicationIdentifier:
Android: com.arkgame.ft Android: com.arkgame.ft.flexion
Standalone: com.Unity-Technologies.com.unity.template.urp-blank Standalone: com.Unity-Technologies.com.unity.template.urp-blank
iPhone: com.arkgame.ft iPhone: com.arkgame.ft
buildNumber: buildNumber:
@@ -932,4 +932,4 @@ PlayerSettings:
hmiLoadingImage: {fileID: 0} hmiLoadingImage: {fileID: 0}
platformRequiresReadableAssets: 0 platformRequiresReadableAssets: 0
virtualTexturingSupportEnabled: 0 virtualTexturingSupportEnabled: 0
insecureHttpOption: 0 insecureHttpOption: 2