Commit 892450124f65e4d8669aa808167e4a2f64c7e5b3

Authored by 权海
1 parent 689df72c

feat(ui):添加restore接口

... ... @@ -307,6 +307,8 @@ interface PlatformHostApi {
fun requestAppleProductInfo(productId: String, baseUnit: Long, callback: (Result<AppleProductInfo?>) -> Unit)
/** 从服务器下单后请求苹果支付 */
fun performApplePayment(productId: String, uuid: String, callback: (Result<AppleProductPaymentResult?>) -> Unit)
/** 恢复购买 */
fun performRestore(callback: (Result<Boolean>) -> Unit)
companion object {
/** The codec used by PlatformHostApi. */
... ... @@ -411,6 +413,24 @@ interface PlatformHostApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.performRestore$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.performRestore{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(PlatformApiPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(PlatformApiPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
... ...
... ... @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
... ...
... ... @@ -26,6 +26,15 @@ enum StoreKitError: Error, LocalizedError {
}
}
struct IAPRestoreTransactions: Codable {
var product_id: String?
var transaction_id: String?
var original_transaction_id: String?
var app_account_token: String?
var web_order_line_item_id: String?
}
class ApplePayment {
var delegate: AppDelegate?
... ... @@ -217,6 +226,47 @@ class ApplePayment {
}
}
private func verifyRestoreWithServer(transaction: IAPRestoreTransactions) async -> Bool {
guard AppShared.shared.isLogin else {
print("Apple payment restore verify skipped: missing login info")
return false
}
guard let baseURL = URL(string: AppShared.shared.baseUrl),
let url = URL(string: "/client/doublefeel/payment/order_restore/apple/", relativeTo: baseURL)?.absoluteURL else {
print("Apple payment restore verify failed: invalid baseUrl \(AppShared.shared.baseUrl)")
return false
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.timeoutInterval = 30
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(AppShared.shared.userSummary?.accessToken, forHTTPHeaderField: "access_token")
request.setValue(AppShared.shared.agent.finalUA, forHTTPHeaderField: "User-Agent")
do {
request.httpBody = try JSONEncoder().encode(transaction)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
print("Apple payment restore verify failed: invalid response")
return false
}
let bodyText = String(data: data, encoding: .utf8) ?? ""
print("Apple payment restore verify response: status=\(httpResponse.statusCode), body=\(bodyText)")
guard (200..<300).contains(httpResponse.statusCode) else {
return false
}
return true
} catch {
print("Apple payment restore verify failed: \(error.localizedDescription)")
return false
}
}
private func paymentResult(
productId: String,
appAccountToken: String? = nil,
... ... @@ -241,6 +291,42 @@ class ApplePayment {
}
return error.localizedDescription
}
func restore() async -> Bool {
var transactions = [Transaction]()
for await result in Transaction.currentEntitlements {
if case let .verified(trans) = result {
if trans.appAccountToken != nil &&
trans.revocationDate == nil &&
trans.productType == .autoRenewable {
transactions.append(trans)
}else {
await trans.finish()
}
}
}
guard let restoreTransaction = transactions.compactMap({ IAPRestoreTransactions(
product_id: $0.productID,
transaction_id: "\($0.id)",
original_transaction_id: "\($0.originalID)",
app_account_token: $0.appAccountToken?.uuidString.lowercased() ?? "",
web_order_line_item_id: $0.webOrderLineItemID ?? "") }
).last else{
return false
}
guard await verifyRestoreWithServer(transaction: restoreTransaction) else {
return false
}
for tran in transactions {
await tran.finish()
}
return true
}
}
extension ApplePayment {
... ...
... ... @@ -339,6 +339,8 @@ protocol PlatformHostApi {
func requestAppleProductInfo(productId: String, baseUnit: Int64, completion: @escaping (Result<AppleProductInfo?, Error>) -> Void)
/// 从服务器下单后请求苹果支付
func performApplePayment(productId: String, uuid: String, completion: @escaping (Result<AppleProductPaymentResult?, Error>) -> Void)
/// 恢复购买
func performRestore(completion: @escaping (Result<Bool, Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
... ... @@ -437,5 +439,21 @@ class PlatformHostApiSetup {
} else {
performApplePaymentChannel.setMessageHandler(nil)
}
/// 恢复购买
let performRestoreChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.performRestore\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
performRestoreChannel.setMessageHandler { _, reply in
api.performRestore { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
performRestoreChannel.setMessageHandler(nil)
}
}
}
... ...
... ... @@ -37,6 +37,13 @@ class PriceFormatter{
* `{systemWebViewUA} doublefeel/{versionCode}({versionName})(Apple##Apple##{model}; iOS{osVersion}; {height}x{width})(huawei)`
*/
final class PlatformHostApiImpl: PlatformHostApi {
func performRestore(completion: @escaping (Result<Bool, any Error>) -> Void) {
Task{
let success = await AppShared.shared.payment.restore()
completion(.success(success))
}
}
func updateLoginInfo(jsonString: String, baseUrl: String) throws {
AppShared.shared.baseUrl = baseUrl
if let data = jsonString.data(using: .utf8){
... ...
... ... @@ -432,4 +432,33 @@ class PlatformHostApi {
return (pigeonVar_replyList[0] as AppleProductPaymentResult?);
}
}
/// 恢复购买
Future<bool> performRestore() async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.performRestore$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as bool?)!;
}
}
}
... ...
... ... @@ -120,4 +120,8 @@ abstract class PlatformHostApi {
/// 从服务器下单后请求苹果支付
@async
AppleProductPaymentResult? performApplePayment(String productId, String uuid);
/// 恢复购买
@async
bool performRestore();
}
... ...