- 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>
171 lines
6.1 KiB
C#
171 lines
6.1 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace tysdk
|
|
{
|
|
public interface IPaymentVerifier
|
|
{
|
|
Task<VerifyResult> Verify(PaymentInfo info);
|
|
void Consume(string token);
|
|
}
|
|
|
|
public class VerifyResult
|
|
{
|
|
public bool success;
|
|
public string msg;
|
|
}
|
|
|
|
public static class PaymentVerifierFactory
|
|
{
|
|
public static IPaymentVerifier GetVerifier()
|
|
{
|
|
return ChannelConfig.Channel switch
|
|
{
|
|
"Flexion" => new RetryablePaymentVerifier(new FlexionPaymentVerifier()),
|
|
"Dummy" => new RetryablePaymentVerifier(new DummyPaymentVerifier()),
|
|
_ => null
|
|
};
|
|
}
|
|
}
|
|
|
|
public class RetryablePaymentVerifier : IPaymentVerifier
|
|
{
|
|
private readonly IPaymentVerifier _inner;
|
|
private const int MaxRetries = 2;
|
|
private static readonly TimeSpan RetryInterval = TimeSpan.FromSeconds(2);
|
|
|
|
public RetryablePaymentVerifier(IPaymentVerifier inner)
|
|
{
|
|
_inner = inner;
|
|
}
|
|
|
|
public async Task<VerifyResult> Verify(PaymentInfo info)
|
|
{
|
|
VerifyResult lastResult = null;
|
|
for (int attempt = 0; attempt <= MaxRetries; attempt++)
|
|
{
|
|
if (attempt > 0)
|
|
{
|
|
Debug.Log($"[RetryableVerifier] Flow retry {attempt}/{MaxRetries}, waiting {RetryInterval.TotalSeconds}s");
|
|
await Task.Delay(RetryInterval);
|
|
}
|
|
lastResult = await _inner.Verify(info);
|
|
if (lastResult.success) return lastResult;
|
|
Debug.LogWarning($"[RetryableVerifier] Flow attempt {attempt + 1} failed: {lastResult.msg}");
|
|
}
|
|
return lastResult;
|
|
}
|
|
|
|
public void Consume(string token) => _inner.Consume(token);
|
|
}
|
|
|
|
public class FlexionPaymentVerifier : IPaymentVerifier
|
|
{
|
|
// TODO: 替换为实际游戏服务端验签 URL
|
|
private const string VERIFY_URL = "https://YOUR_GAME_SERVER/api/pay/verify";
|
|
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
|
|
private const int MaxRetries = 2;
|
|
private static readonly TimeSpan RetryBackoff = TimeSpan.FromSeconds(2);
|
|
|
|
#if UNITY_EDITOR
|
|
public async Task<VerifyResult> Verify(PaymentInfo info)
|
|
{
|
|
Debug.LogWarning("[FlexionPaymentVerifier] Editor placeholder: auto success");
|
|
await Task.Yield();
|
|
return new VerifyResult { success = true, msg = "editor_placeholder" };
|
|
}
|
|
#else
|
|
public async Task<VerifyResult> Verify(PaymentInfo info)
|
|
{
|
|
VerifyResult lastResult = null;
|
|
for (int attempt = 0; attempt <= MaxRetries; attempt++)
|
|
{
|
|
if (attempt > 0)
|
|
{
|
|
var delay = TimeSpan.FromSeconds(RetryBackoff.TotalSeconds * attempt);
|
|
Debug.Log($"[FlexionPaymentVerifier] HTTP retry {attempt}/{MaxRetries}, waiting {delay.TotalSeconds}s");
|
|
await Task.Delay(delay);
|
|
}
|
|
lastResult = await SendVerifyRequest(info);
|
|
if (lastResult.success) return lastResult;
|
|
Debug.LogWarning($"[FlexionPaymentVerifier] HTTP attempt {attempt + 1} failed: {lastResult.msg}");
|
|
}
|
|
return lastResult;
|
|
}
|
|
|
|
private async Task<VerifyResult> SendVerifyRequest(PaymentInfo info)
|
|
{
|
|
var cts = new CancellationTokenSource(Timeout);
|
|
try
|
|
{
|
|
Debug.Log("[FlexionPaymentVerifier] Sending verification request");
|
|
|
|
var body = JsonConvert.SerializeObject(info);
|
|
using var req = new UnityWebRequest(VERIFY_URL, "POST");
|
|
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body));
|
|
req.downloadHandler = new DownloadHandlerBuffer();
|
|
req.SetRequestHeader("Content-Type", "application/json");
|
|
|
|
var tcs = new TaskCompletionSource<VerifyResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
cts.Token.Register(() => tcs.TrySetResult(new VerifyResult { success = false, msg = "timeout" }));
|
|
|
|
req.SendWebRequest().completed += _ =>
|
|
{
|
|
if (req.result == UnityWebRequest.Result.Success)
|
|
{
|
|
Debug.Log("[FlexionPaymentVerifier] Response: " + req.downloadHandler.text);
|
|
// TODO: 按实际服务端响应格式解析
|
|
tcs.TrySetResult(new VerifyResult { success = true, msg = "ok" });
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("[FlexionPaymentVerifier] Failed: " + req.error);
|
|
tcs.TrySetResult(new VerifyResult { success = false, msg = req.error });
|
|
}
|
|
};
|
|
|
|
return await tcs.Task;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError("[FlexionPaymentVerifier] Exception: " + e.Message);
|
|
return new VerifyResult { success = false, msg = e.Message };
|
|
}
|
|
finally
|
|
{
|
|
cts.Dispose();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if UNITY_EDITOR
|
|
public void Consume(string token) { }
|
|
#elif UNITY_ANDROID
|
|
public void Consume(string token)
|
|
{
|
|
if (string.IsNullOrEmpty(token)) return;
|
|
UnityBridgeFunc.ConsumePurchase(token);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public class DummyPaymentVerifier : IPaymentVerifier
|
|
{
|
|
public async Task<VerifyResult> Verify(PaymentInfo info)
|
|
{
|
|
Debug.Log("[DummyPaymentVerifier] Verifying purchase: " + info.orderId);
|
|
await Task.Delay(500);
|
|
return new VerifyResult { success = true, msg = "dummy_ok" };
|
|
}
|
|
|
|
public void Consume(string token)
|
|
{
|
|
Debug.Log("[DummyPaymentVerifier] Consume: " + token);
|
|
}
|
|
}
|
|
}
|