refactor: 支付链路规范化 — 统一命名/层次/传参

- ISDKHelper.pay() 增加 productPrice 参数,与 UnityKnowNew 签名对齐
- FlexionSDKHelper: prodPrice/prodCount 从参数直接写入 developerPayload
- playerId → userId 统一命名 (SDKTest/Java payload)
- pfId → pfid 统一小写 (服务端 JSON key)
- PaymentInfo 序列化替代 StringBuilder 手拼 JSON
- FlexionPaymentVerifier.Consume 走 UnityBridgeFunc.ConsumePurchase 桥接层
- UnityBridgeFunc 新增 ConsumePurchase 通用方法,移除渠道特定方法
- SDKManager.ConsumeFlexionPurchase → ConsumePurchase 通用化

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-11 15:24:39 +08:00
parent 8de35fa97d
commit f9b5413a81
6 changed files with 30 additions and 35 deletions

View File

@@ -88,8 +88,9 @@ public class FlexionSDKHelper implements SDKManager.ISDKHelper {
} }
@Override @Override
public void pay(Activity activity, String productId, String productName, public void pay(Activity activity, String productId, String productPrice,
String productCount, String prodorderId, String appInfo) { String productName, String productCount, String prodorderId,
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) {
Log.e(TAG, "[PAY-FLOW] billing not ready"); Log.e(TAG, "[PAY-FLOW] billing not ready");
@@ -101,11 +102,14 @@ 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();
payload.put("playerId", json.optString("playerId", "")); payload.put("userId", json.optString("userId", ""));
payload.put("pfid", json.optString("pfid", ""));
payload.put("customData", json.optString("customData", "")); payload.put("customData", json.optString("customData", ""));
payload.put("prodPrice", productPrice);
payload.put("prodCount", productCount);
developerPayload = payload.toString(); developerPayload = payload.toString();
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Failed to extract playerId/customData from appInfo: " + e.getMessage()); Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage());
} }
String finalPayload = developerPayload; String finalPayload = developerPayload;
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {

View File

@@ -231,7 +231,7 @@ 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["playerId"] = 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}";

View File

@@ -60,8 +60,9 @@ public class SDKManager {
// Channel helper // Channel helper
public interface ISDKHelper { public interface ISDKHelper {
void init(Activity activity); void init(Activity activity);
void pay(Activity activity, String productId, String productName, void pay(Activity activity, String productId, String productPrice,
String productCount, String prodorderId, String appInfo); String productName, String productCount, String prodorderId,
String appInfo);
void queryProducts(); void queryProducts();
void consume(String token); void consume(String token);
} }
@@ -72,8 +73,8 @@ public class SDKManager {
sdkHelper = helper; sdkHelper = helper;
} }
public static void ConsumeFlexionPurchase(String token) { public static void ConsumePurchase(String token) {
Log.i(TAG, "ConsumeFlexionPurchase: token=" + token); Log.i(TAG, "ConsumePurchase: token=" + token);
if (sdkHelper != null) { if (sdkHelper != null) {
sdkHelper.consume(token); sdkHelper.consume(token);
} }
@@ -395,7 +396,7 @@ public class SDKManager {
String productCount, String prodorderId, String appInfo, String productCount, String prodorderId, String appInfo,
String ptype) { String ptype) {
if (sdkHelper != null) { if (sdkHelper != null) {
sdkHelper.pay(UnityPlayer.currentActivity, productId, productName, productCount, prodorderId, appInfo); sdkHelper.pay(UnityPlayer.currentActivity, productId, productPrice, productName, productCount, prodorderId, appInfo);
return; return;
} }
PayEventData.PayData payData = new PayEventData.PayData(); PayEventData.PayData payData = new PayEventData.PayData();

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
@@ -90,20 +90,21 @@ namespace tysdk
Debug.Log($"[FlexionPaymentVerifier] HTTP retry {attempt}/{MaxRetries}, waiting {delay.TotalSeconds}s"); Debug.Log($"[FlexionPaymentVerifier] HTTP retry {attempt}/{MaxRetries}, waiting {delay.TotalSeconds}s");
await Task.Delay(delay); await Task.Delay(delay);
} }
lastResult = await SendVerifyRequest(BuildRequestBody(info)); lastResult = await SendVerifyRequest(info);
if (lastResult.success) return lastResult; if (lastResult.success) return lastResult;
Debug.LogWarning($"[FlexionPaymentVerifier] HTTP attempt {attempt + 1} failed: {lastResult.msg}"); Debug.LogWarning($"[FlexionPaymentVerifier] HTTP attempt {attempt + 1} failed: {lastResult.msg}");
} }
return lastResult; return lastResult;
} }
private async Task<VerifyResult> SendVerifyRequest(string body) private async Task<VerifyResult> SendVerifyRequest(PaymentInfo info)
{ {
var cts = new CancellationTokenSource(Timeout); var cts = new CancellationTokenSource(Timeout);
try try
{ {
Debug.Log("[FlexionPaymentVerifier] Sending verification request"); Debug.Log("[FlexionPaymentVerifier] Sending verification request");
var body = JsonConvert.SerializeObject(info);
using var req = new UnityWebRequest(VERIFY_URL, "POST"); using var req = new UnityWebRequest(VERIFY_URL, "POST");
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body)); req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body));
req.downloadHandler = new DownloadHandlerBuffer(); req.downloadHandler = new DownloadHandlerBuffer();
@@ -139,35 +140,15 @@ namespace tysdk
cts.Dispose(); cts.Dispose();
} }
} }
private static string BuildRequestBody(PaymentInfo info)
{
var body = new StringBuilder();
body.Append('{');
body.Append("\"purchaseJson\":").Append(Escape(info.purchaseJson)).Append(',');
body.Append("\"signature\":").Append(Escape(info.signature)).Append(',');
body.Append("\"orderId\":").Append(Escape(info.orderId)).Append(',');
body.Append("\"productId\":").Append(Escape(info.productId)).Append(',');
body.Append("\"purchaseToken\":").Append(Escape(info.purchaseToken));
body.Append('}');
return body.ToString();
}
#endif #endif
private static string Escape(string s)
{
if (s == null) return "null";
return '"' + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + '"';
}
#if UNITY_EDITOR #if UNITY_EDITOR
public void Consume(string token) { } public void Consume(string token) { }
#elif UNITY_ANDROID #elif UNITY_ANDROID
public void Consume(string token) public void Consume(string token)
{ {
if (string.IsNullOrEmpty(token)) return; if (string.IsNullOrEmpty(token)) return;
using var sdkManager = new AndroidJavaClass("com.unity3d.player.SDKManager"); UnityBridgeFunc.ConsumePurchase(token);
sdkManager.CallStatic("ConsumeFlexionPurchase", token);
} }
#endif #endif
} }

View File

@@ -30,7 +30,6 @@ namespace tysdk
if (purchaseInfo == null) if (purchaseInfo == null)
purchaseInfo = new JObject(); purchaseInfo = new JObject();
// purchaseInfo["playerId"] = _accountInfo.strUserId;
string extraInfo = purchaseInfo.ToString(Newtonsoft.Json.Formatting.None); string extraInfo = purchaseInfo.ToString(Newtonsoft.Json.Formatting.None);
extraInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(extraInfo)); extraInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(extraInfo));

View File

@@ -47,6 +47,8 @@ namespace tysdk
public static void Review() { } public static void Review() { }
public static void ConsumePurchase(string token) { }
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) { }
@@ -168,6 +170,14 @@ namespace tysdk
} }
} }
public static void ConsumePurchase(string token)
{
using (var sdkManager = new AndroidJavaClass(SDK_CLASS))
{
sdkManager.CallStatic("ConsumePurchase", token);
}
}
public static void UnityKnow(String productId, String productName, String productCount, String prodorderId, String appInfo) 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))