Files
raspberry/iOS/Wallet/Sources/AccountOnboarding/Service/AccountServiceCreateAccount.swift
2022-12-21 20:26:26 +03:00

264 lines
9.8 KiB
Swift

//
// AccountServiceCreateAccount.swift
// Wallet
//
// Created by Saveliy Stavitsky on 12/10/20.
// Copyright © 2020 List. All rights reserved.
//
import Foundation
import Alamofire
import DeviceCheck
import WalletNetwork
typealias JSON = [String: Any]
extension JSON {
func asData() -> Data? { try? JSONSerialization.data(withJSONObject: self, options: .prettyPrinted) }
}
extension AccountOnboarding.Service {
class CreateAccount {
private(set) var usernames: [String] = []
private func fetchDeviceToken(completion: @escaping (String?,NSError?) -> Void) {
guard DCDevice.current.isSupported else { completion(nil, .error(L10n.Error.Device.support));return }
DCDevice.current.generateToken {
guard $1 == nil, let data = $0 else { completion(nil, .error(L10n.Error.Device.token));return }
let result = [
"deviceType": "ios",
"payload": [
"device_token": data.base64EncodedString()
]
].asData()?.base64EncodedString()
DispatchQueue.main.async { completion(result ?? "", nil) }
}
}
struct CreateRequest: Codable {
let query: String
let variables: CreateRequestVariables?
let operationName: String?
}
struct CreateRequestVariables: Codable {
let publicKey: String
let username: String
let payload: String
}
func submitUsername(username: String, publicKey: String, completion: @escaping (Result<String, String>) -> Void) {
fetchDeviceToken { (token, error) in
guard let token = token else {
completion(.failure(error?.localizedDescription ?? L10n.Common.Error.default))
return
}
let environment = ApplicationEnvironment.shared().current
AF.request(
environment.backend.urlPath,
method: .post,
parameters: CreateRequest(
query: #"""
mutation createAccount($publicKey:String!, $username:String!, $payload:String!) {
createAccount(publicKey:$publicKey, username:$username, payload:$payload) {
account { username }
errors
}
}
"""#,
variables: CreateRequestVariables(publicKey: publicKey, username: username, payload: token),
operationName: "createAccount"),
encoder: JSONParameterEncoder.default,
headers: HTTPHeaders(environment.headers)
).responseDecodable(of: GraphQLResponse<CreateResponseData>.self) { (response) in
if let username = response.value?.data?.createAccount.account?.username {
return completion(.success(username))
} else {
return completion(.failure(response.value?.data?.createAccount.errors ?? ""))
}
}
}
}
private struct CreateResponseData: Decodable {
let createAccount: CreateData
}
private struct CreateData: Decodable {
let account: Account?
let errors: String?
}
}
}
extension AccountOnboarding.Service.CreateAccount {
func fetchUsernames(_ completion: @escaping ([String]) -> Void) {
let environment = ApplicationEnvironment.shared().current
AF.request(
environment.backend.urlPath,
method: .post,
parameters: ["query": "query { accountOrder { accounts { username } } }"],
encoder: JSONParameterEncoder.default,
headers: HTTPHeaders(environment.headers)
).responseDecodable(of: GraphQLResponse<UsernamesResponseData>.self) { (response) in
completion(response.value?.data?.accountOrder.accounts.map({ $0.username }) ?? [])
}
}
private struct UsernamesResponseData: Decodable {
let accountOrder: Order
}
private struct Order: Decodable {
let accounts: [Account]
}
private struct Account: Decodable {
let username: String
}
}
extension AccountOnboarding.Service.CreateAccount {
func checkUsername(username: String) async -> Bool {
let record = ApplicationEnvironment.shared().current
guard let environment = try? record.networkEnvironment() else { return false }
let service = AccountService(environment: environment)
switch await service.isAvailable(username: username) {
case .available: return true
case .alreadyTaken, .system: return false
}
}
}
extension AccountOnboarding.Service.CreateAccount {
func startWalletPurchase(username: String, publicKey: String, completion: @escaping (Result<String, String>) -> Void) {
let environment = ApplicationEnvironment.shared().current
AF.request(
environment.backend.urlPath,
method: .post,
parameters: CreateRequest(
query: #"""
mutation {
startWalletPurchase(username:"some_username", pubKey:"public_key", device:IOS) {
tempToken
errors
}
}
"""#
.replacingOccurrences(of: "some_username", with: username)
.replacingOccurrences(of: "public_key", with: publicKey),
variables: nil,
operationName: nil),
encoder: JSONParameterEncoder.default,
headers: HTTPHeaders(environment.headers)
)
.responseDecodable(of: GraphQLResponse<StartWalletPurchaseData>.self) { (response) in
if let tempToken = response.value?.data?.startWalletPurchase?.tempToken {
return completion(.success(tempToken))
} else {
return completion(.failure(response.value?.data?.startWalletPurchase?.errors
?? response.value?.errorsDescription ?? ""))
}
}
}
private struct StartWalletPurchaseData: Decodable {
let startWalletPurchase: StartWalletPurchaseResult?
}
private struct StartWalletPurchaseResult: Decodable {
let tempToken: String?
let errors: String?
}
}
extension AccountOnboarding.Service.CreateAccount {
func verifyPurchase(tempToken: String, token: String, completion: @escaping (Result<Bool, String>) -> Void) {
let environment = ApplicationEnvironment.shared().current
AF.request(
environment.backend.urlPath,
method: .post,
parameters: CreateRequest(
query: #"""
query {
verifyPurchaseToken(tempToken:"temp_token", token:"token_token") {
errors
}
}
"""#
.replacingOccurrences(of: "temp_token", with: tempToken)
.replacingOccurrences(of: "token_token", with: token),
variables: nil,
operationName: nil),
encoder: JSONParameterEncoder.default,
headers: HTTPHeaders(environment.headers)
)
.responseDecodable(of: GraphQLResponse<VerifyPurchaseTokenData>.self) { (response) in
if let errors = response.value?.data?.verifyPurchaseToken?.errors ?? response.value?.errorsDescription {
return completion(.failure(errors))
} else {
return completion(.success(true))
}
}
}
private struct VerifyPurchaseTokenData: Decodable {
let verifyPurchaseToken: VerifyPurchaseTokenResult?
}
private struct VerifyPurchaseTokenResult: Decodable {
let errors: String?
}
}
extension AccountOnboarding.Service.CreateAccount {
func completePurchase(tempToken: String, token: String, completion: @escaping (Result<Bool, String>) -> Void) {
let environment = ApplicationEnvironment.shared().current
AF.request(
environment.backend.urlPath,
method: .post,
parameters: CreateRequest(
query: #"""
mutation {
completePurchase(tempToken:"temp_token", token:"token_token") {
errors
}
}
"""#
.replacingOccurrences(of: "temp_token", with: tempToken)
.replacingOccurrences(of: "token_token", with: token),
variables: nil,
operationName: nil),
encoder: JSONParameterEncoder.default,
headers: HTTPHeaders(environment.headers)
)
// .responseString(completionHandler: {
// print(String(data: $0.data!, encoding: .utf8))
// print(String(data: $0.data!, encoding: .utf8))
// })
.responseDecodable(of: GraphQLResponse<CompletePurchaseData>.self) { (response) in
if let errors = response.value?.data?.completePurchase?.errors ?? response.value?.errorsDescription {
return completion(.failure(errors))
} else {
return completion(.success(true))
}
}
}
private struct CompletePurchaseData: Decodable {
let completePurchase: CompletePurchaseResult?
}
private struct CompletePurchaseResult: Decodable {
let errors: String?
}
}