From c6397cc711c051f7c502b8350512006e29aa3f31 Mon Sep 17 00:00:00 2001 From: svojsu Date: Mon, 16 Dec 2024 21:07:48 +0200 Subject: [PATCH 1/8] use redirect api instead of html page with integrated script --- novawallet.xcodeproj/project.pbxproj | 7 +- .../MercuryoCardHookFactory+Widget.swift | 57 ++----- .../Mercuryo/MercuryoCardHookFactory.swift | 73 ++------- .../MercuryoCardResourceProvider.swift | 149 +++++++++++++++++- .../Modules/PayCard/Model/PayCardHook.swift | 2 +- .../Model/PayCardResourceProvider.swift | 4 +- .../Modules/PayCard/PayCardInteractor.swift | 38 ++--- .../PayCard/PayCardViewController.swift | 10 +- .../Modules/PayCard/PayCardViewFactory.swift | 12 +- .../Modules/PayCard/mercuryoWidget.html | 18 --- 10 files changed, 210 insertions(+), 160 deletions(-) delete mode 100644 novawallet/Modules/PayCard/mercuryoWidget.html diff --git a/novawallet.xcodeproj/project.pbxproj b/novawallet.xcodeproj/project.pbxproj index d928ba8f8..afa17269c 100644 --- a/novawallet.xcodeproj/project.pbxproj +++ b/novawallet.xcodeproj/project.pbxproj @@ -1091,7 +1091,6 @@ 2D95DF5D2C6F9129009BB063 /* HydraExtrinsicFeeInstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D95DF5C2C6F9129009BB063 /* HydraExtrinsicFeeInstaller.swift */; }; 2D95DF5F2C6FAFF6009BB063 /* ExtrinsicFeeEstimatingWrapperFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D95DF5E2C6FAFF6009BB063 /* ExtrinsicFeeEstimatingWrapperFactory.swift */; }; 2D95DF612C74B79A009BB063 /* HydraFlowStateStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D95DF602C74B79A009BB063 /* HydraFlowStateStore.swift */; }; - 2D95DF642C765E1B009BB063 /* mercuryoWidget.html in Resources */ = {isa = PBXBuildFile; fileRef = 2D95DF632C765E1B009BB063 /* mercuryoWidget.html */; }; 2DA85A652C7D9B8B00591900 /* CardTopUpTransferSetupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A642C7D9B8B00591900 /* CardTopUpTransferSetupViewController.swift */; }; 2DA85A672C7DD75300591900 /* CardTopUpTransferSetupViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A662C7DD75300591900 /* CardTopUpTransferSetupViewLayout.swift */; }; 2DA85A6B2C7F36CF00591900 /* CardTopUpTransferSetupProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A6A2C7F36CF00591900 /* CardTopUpTransferSetupProtocols.swift */; }; @@ -4424,9 +4423,8 @@ 921E4891E85C0DC6FDD8A0D0 /* CrowdloanContributionConfirmInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1366336078BCA34EFB4C6FF9 /* CrowdloanContributionConfirmInteractor.swift */; }; 924BADB89E7FA2DC54BF1A02 /* NPoolsClaimRewardsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F1F933F624B01855AA3BA5 /* NPoolsClaimRewardsInteractor.swift */; }; 92984DFE797C52644C084377 /* SwapSetupProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53775773F2060B4B7F6D62DA /* SwapSetupProtocols.swift */; }; - 93434E8E407A6C63D8862A21 /* ChainAssetSelectionProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DECC58C93DB18E79A03B5A0 /* ChainAssetSelectionProtocols.swift */; }; 92B880FEDD0AE45A36898308 /* PayCardViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731F3692859677E2B67AE629 /* PayCardViewFactory.swift */; }; - 93434E8E407A6C63D8862A21 /* AssetSelectionProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DECC58C93DB18E79A03B5A0 /* AssetSelectionProtocols.swift */; }; + 93434E8E407A6C63D8862A21 /* ChainAssetSelectionProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DECC58C93DB18E79A03B5A0 /* ChainAssetSelectionProtocols.swift */; }; 934F229F4E5A588D5AF2A093 /* TokensAddSelectNetworkViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E800814C025B38C87CC282D /* TokensAddSelectNetworkViewFactory.swift */; }; 9358E048B1AA0F71F519101E /* GovernanceDelegateConfirmInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A27FB20961F2F221A96624A6 /* GovernanceDelegateConfirmInteractor.swift */; }; 93B64E378DDDCC7F20FF78A2 /* NPoolsRedeemPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D256E075B9F6E26225C20B8C /* NPoolsRedeemPresenter.swift */; }; @@ -6319,7 +6317,6 @@ 2D95DF5C2C6F9129009BB063 /* HydraExtrinsicFeeInstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HydraExtrinsicFeeInstaller.swift; sourceTree = ""; }; 2D95DF5E2C6FAFF6009BB063 /* ExtrinsicFeeEstimatingWrapperFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtrinsicFeeEstimatingWrapperFactory.swift; sourceTree = ""; }; 2D95DF602C74B79A009BB063 /* HydraFlowStateStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HydraFlowStateStore.swift; sourceTree = ""; }; - 2D95DF632C765E1B009BB063 /* mercuryoWidget.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = mercuryoWidget.html; sourceTree = ""; }; 2DA85A642C7D9B8B00591900 /* CardTopUpTransferSetupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTopUpTransferSetupViewController.swift; sourceTree = ""; }; 2DA85A662C7DD75300591900 /* CardTopUpTransferSetupViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTopUpTransferSetupViewLayout.swift; sourceTree = ""; }; 2DA85A6A2C7F36CF00591900 /* CardTopUpTransferSetupProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTopUpTransferSetupProtocols.swift; sourceTree = ""; }; @@ -23107,7 +23104,6 @@ 1C6CA74395730B3676CC5124 /* PayCardViewController.swift */, 70C037C70EFB78932175FAE1 /* PayCardViewLayout.swift */, 731F3692859677E2B67AE629 /* PayCardViewFactory.swift */, - 2D95DF632C765E1B009BB063 /* mercuryoWidget.html */, ); path = PayCard; sourceTree = ""; @@ -24304,7 +24300,6 @@ 84452F7425D5E2B300F47EC5 /* runtime-westend.json in Resources */, 84E58C7D275E125100BD441A /* PublicSans-ExtraLight.otf in Resources */, 848A8381274BB03F004493DD /* PublicSans-Regular.otf in Resources */, - 2D95DF642C765E1B009BB063 /* mercuryoWidget.html in Resources */, 849014B824AA87E3008F705E /* PinSetupViewController.xib in Resources */, 84D8F16924D821ED00AF43E9 /* IconWithTitleTableViewCell.xib in Resources */, 84DF21A325347042005454AE /* DetailsDisplayTableViewCell.xib in Resources */, diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift index de41fc190..5136737cc 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift @@ -1,55 +1,26 @@ import Foundation extension MercuryoCardHookFactory { - func createWidgetHooks( - for delegate: PayCardHookDelegate, - params: MercuryoCardParams - ) throws -> [PayCardHook] { - let refundAddress = try params.refundAccountId.toAddress( - using: params.chainAsset.chain.chainFormat - ) - + func createWidgetHooks(for delegate: PayCardHookDelegate) -> [PayCardHook] { let statusAction = MercuryoMessageName.onCardStatusChange.rawValue - let topupAction = MercuryoMessageName.onCardTopup.rawValue let scriptSource = """ - mercuryoWidget.run({ - widgetId: '\(MercuryoCardApi.widgetId)', - host: document.getElementById('widget-container'), - type: 'sell', - currency: '\(params.chainAsset.asset.symbol)', - fiatCurrency: 'EUR', - paymentMethod: 'fiat_card_open', - theme: 'nova', - showSpendCardDetails: true, - width: '100%', - fixPaymentMethod: true, - height: window.innerHeight, - hideRefundAddress: true, - refundAddress: '\(refundAddress)', - onStatusChange: data => { + { + data => { window.webkit.messageHandlers.\(statusAction).postMessage(JSON.stringify(data)) - }, - onSellTransferEnabled: data => { - window.webkit.messageHandlers.\(topupAction).postMessage(JSON.stringify(data)) } - }); + }; """ - return [.init( - script: .init( - content: scriptSource, - insertionPoint: .atDocEnd - ), - messageNames: [statusAction, topupAction], - handlers: [ - MercuryoCardStatusHandler(delegate: delegate, logger: logger), - MercuryoCardTopupHandler( - delegate: delegate, - chainAsset: params.chainAsset, - logger: Logger.shared - ) - ] - )] + return [ + .init( + script: .init( + content: scriptSource, + insertionPoint: .atDocEnd + ), + messageNames: [statusAction], + handlers: [MercuryoCardStatusHandler(delegate: delegate, logger: logger)] + ) + ] } } diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift index 214733798..c3c860523 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift @@ -1,78 +1,33 @@ import Foundation -import Operation_iOS - -struct MercuryoCardParams { - let chainAsset: ChainAsset - let refundAccountId: AccountId -} enum MercuryoCardApi { - static let widgetUrl = URL(string: "https://exchange.mercuryo.io")! + static let widgetUrl = URL(string: "https://exchange.mercuryo.io/")! static let widgetId = "4ce98182-ed76-4933-ba1b-b85e4a51d75a" // TODO: Change for production + static let theme = "nova" + static let type = "sell" + static let fiatCurrency = "EUR" + static let fixFiatCurrency = "true" + static let fixPaymentMethod = "true" + static let paymentMethod = "fiat_card_open" + static let showSpendCardDetails = "true" + static let hideRefundAddress = "true" static let cardsEndpoint = "https://api.mercuryo.io/v1.6/cards" static let pendingTimeout: TimeInterval = 5.secondsFromMinutes } final class MercuryoCardHookFactory { - let chainRegistry: ChainRegistryProtocol - let wallet: MetaAccountModel - let chainId: ChainModel.Id let logger: LoggerProtocol - init( - chainRegistry: ChainRegistryProtocol, - wallet: MetaAccountModel, - chainId: ChainModel.Id, - logger: LoggerProtocol - ) { - self.chainRegistry = chainRegistry - self.wallet = wallet - self.chainId = chainId + init(logger: LoggerProtocol) { self.logger = logger } - - private func createHooksOperation( - dependingOn chainOperation: BaseOperation, - wallet: MetaAccountModel, - delegate: PayCardHookDelegate - ) -> BaseOperation<[PayCardHook]> { - ClosureOperation { - guard - let chain = try chainOperation.extractNoCancellableResultData(), - let utilityAsset = chain.utilityChainAsset() else { - throw ChainModelFetchError.noAsset(assetId: 0) - } - - guard - let selectedAccount = wallet.fetch(for: chain.accountRequest()) else { - throw ChainAccountFetchingError.accountNotExists - } - - let params = MercuryoCardParams( - chainAsset: utilityAsset, - refundAccountId: selectedAccount.accountId - ) - - let responseHook = self.createCardsResponseInterceptingHook(for: delegate) - let widgetHooks = try self.createWidgetHooks(for: delegate, params: params) - - return widgetHooks + [responseHook] - } - } } extension MercuryoCardHookFactory: PayCardHookFactoryProtocol { - func createHooks(for delegate: PayCardHookDelegate) -> CompoundOperationWrapper<[PayCardHook]> { - let fetchChainWrapper = chainRegistry.asyncWaitChainWrapper(for: chainId) - - let hooksOperation = createHooksOperation( - dependingOn: fetchChainWrapper.targetOperation, - wallet: wallet, - delegate: delegate - ) - - hooksOperation.addDependency(fetchChainWrapper.targetOperation) + func createHooks(for delegate: PayCardHookDelegate) -> [PayCardHook] { + let responseHook = createCardsResponseInterceptingHook(for: delegate) + let widgetHooks = createWidgetHooks(for: delegate) - return fetchChainWrapper.insertingTail(operation: hooksOperation) + return widgetHooks + [responseHook] } } diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift index 80975aa8a..05a2955b3 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift @@ -1,19 +1,156 @@ import Foundation +import Operation_iOS enum MercuryoCardResourceProviderError: Error { case unavailable } -final class MercuryoCardResourceProvider {} +final class MercuryoCardResourceProvider { + let chainRegistry: ChainRegistryProtocol + let wallet: MetaAccountModel + let chainId: ChainModel.Id + + init( + chainRegistry: ChainRegistryProtocol, + wallet: MetaAccountModel, + chainId: ChainModel.Id + ) { + self.chainRegistry = chainRegistry + self.wallet = wallet + self.chainId = chainId + } +} + +// MARK: Private + +private extension MercuryoCardResourceProvider { + func createQueryItemsWrapper() -> CompoundOperationWrapper<[URLQueryItem]> { + let chainFetchWrapper = chainRegistry.asyncWaitChainWrapper(for: chainId) + + let queryItemsOperation = createQueryItemsOperation( + dependingOn: chainFetchWrapper.targetOperation, + wallet: wallet + ) + + queryItemsOperation.addDependency(chainFetchWrapper.targetOperation) + + return chainFetchWrapper.insertingTail(operation: queryItemsOperation) + } + + func createQueryItemsOperation( + dependingOn chainOperation: BaseOperation, + wallet: MetaAccountModel + ) -> BaseOperation<[URLQueryItem]> { + ClosureOperation { [weak self] in + guard + let self, + let chain = try chainOperation.extractNoCancellableResultData(), + let utilityAsset = chain.utilityChainAsset() + else { + throw ChainModelFetchError.noAsset(assetId: 0) + } + + guard + let selectedAccount = wallet.fetch(for: chain.accountRequest()) else { + throw ChainAccountFetchingError.accountNotExists + } + + let refundAddress = try selectedAccount.accountId.toAddress( + using: utilityAsset.chain.chainFormat + ) + + return createQueryItems( + for: utilityAsset, + refundAddress: refundAddress + ) + } + } + + func createQueryItems( + for chainAsset: ChainAsset, + refundAddress: AccountAddress + ) -> [URLQueryItem] { + let widgetIdItem = URLQueryItem( + name: "widget_id", + value: MercuryoCardApi.widgetId + ) + let typeItem = URLQueryItem( + name: "type", + value: MercuryoCardApi.type + ) + let currencyItem = URLQueryItem( + name: "currency", + value: "\(chainAsset.asset.symbol)" + ) + let fiatCurrencyItem = URLQueryItem( + name: "fiat_currency", + value: MercuryoCardApi.fiatCurrency + ) + let paymentMethodItem = URLQueryItem( + name: "payment_method", + value: MercuryoCardApi.paymentMethod + ) + let themeItem = URLQueryItem( + name: "theme", + value: MercuryoCardApi.theme + ) + let showSpendCardDetails = URLQueryItem( + name: "show_spend_card_details", + value: MercuryoCardApi.showSpendCardDetails + ) + let fixPaymentMethodItem = URLQueryItem( + name: "fix_payment_method", + value: MercuryoCardApi.fixPaymentMethod + ) + let hideRefundAddressItem = URLQueryItem( + name: "hide_refund_address", + value: MercuryoCardApi.hideRefundAddress + ) + let refundAddressItem = URLQueryItem( + name: "refund_address", + value: refundAddress + ) + + return [ + widgetIdItem, + themeItem, + typeItem, + showSpendCardDetails, + currencyItem, + fiatCurrencyItem, + fixPaymentMethodItem, + paymentMethodItem, + hideRefundAddressItem, + refundAddressItem + ] + } +} + +// MARK: PayCardResourceProviding extension MercuryoCardResourceProvider: PayCardResourceProviding { - func loadResource() throws -> PayCardHtmlResource { - guard let htmlFile = R.file.mercuryoWidgetHtml() else { - throw MercuryoCardResourceProviderError.unavailable + func loadResourceWrapper() -> CompoundOperationWrapper { + let queryItemsWrapper = createQueryItemsWrapper() + + let mapOperation = ClosureOperation { + let queryItems = try queryItemsWrapper.targetOperation.extractNoCancellableResultData() + + var urlComponents = URLComponents( + url: MercuryoCardApi.widgetUrl, + resolvingAgainstBaseURL: false + ) + + urlComponents?.queryItems = queryItems + + guard let url = urlComponents?.url else { + throw MercuryoCardResourceProviderError.unavailable + } + + return PayCardHtmlResource(url: url) } - let htmlString = try String(contentsOf: htmlFile, encoding: .utf8) + mapOperation.addDependency(queryItemsWrapper.targetOperation) - return PayCardHtmlResource(url: MercuryoCardApi.widgetUrl, content: htmlString) + return queryItemsWrapper.insertingTail(operation: mapOperation) } } diff --git a/novawallet/Modules/PayCard/Model/PayCardHook.swift b/novawallet/Modules/PayCard/Model/PayCardHook.swift index 97c89d14b..330466f9b 100644 --- a/novawallet/Modules/PayCard/Model/PayCardHook.swift +++ b/novawallet/Modules/PayCard/Model/PayCardHook.swift @@ -15,5 +15,5 @@ protocol PayCardHookDelegate: AnyObject { } protocol PayCardHookFactoryProtocol { - func createHooks(for delegate: PayCardHookDelegate) -> CompoundOperationWrapper<[PayCardHook]> + func createHooks(for delegate: PayCardHookDelegate) -> [PayCardHook] } diff --git a/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift b/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift index 2f15e4652..c5125feec 100644 --- a/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift +++ b/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift @@ -1,10 +1,10 @@ import Foundation +import Operation_iOS struct PayCardHtmlResource { let url: URL - let content: String } protocol PayCardResourceProviding { - func loadResource() throws -> PayCardHtmlResource + func loadResourceWrapper() -> CompoundOperationWrapper } diff --git a/novawallet/Modules/PayCard/PayCardInteractor.swift b/novawallet/Modules/PayCard/PayCardInteractor.swift index 15c8ecfbf..499de302d 100644 --- a/novawallet/Modules/PayCard/PayCardInteractor.swift +++ b/novawallet/Modules/PayCard/PayCardInteractor.swift @@ -43,28 +43,26 @@ final class PayCardInteractor { } } +// MARK: PayCardInteractorInputProtocol + extension PayCardInteractor: PayCardInteractorInputProtocol { func setup() { - do { - let resource = try payCardResourceProvider.loadResource() - - let hooksWrapper = payCardHookFactory.createHooks(for: self) - - execute( - wrapper: hooksWrapper, - inOperationQueue: operationQueue, - runningCallbackIn: .main - ) { [weak self] result in - switch result { - case let .success(hooks): - self?.messageHandlers = hooks.flatMap(\.handlers) - self?.provideModel(for: resource, hooks: hooks) - case let .failure(error): - self?.logger.error("Unexpected hooks \(error)") - } + let resourceWrapper = payCardResourceProvider.loadResourceWrapper() + + let hooks = payCardHookFactory.createHooks(for: self) + + execute( + wrapper: resourceWrapper, + inOperationQueue: operationQueue, + runningCallbackIn: .main + ) { [weak self] result in + switch result { + case let .success(resource): + self?.messageHandlers = hooks.flatMap(\.handlers) + self?.provideModel(for: resource, hooks: hooks) + case let .failure(error): + self?.logger.error("Unexpected hooks \(error)") } - } catch { - logger.error("Unexpected \(error)") } } @@ -107,6 +105,8 @@ extension PayCardInteractor: PayCardInteractorInputProtocol { } } +// MARK: PayCardHookDelegate + extension PayCardInteractor: PayCardHookDelegate { func didRequestTopup(from model: PayCardTopupModel) { presenter?.didRequestTopup(for: model) diff --git a/novawallet/Modules/PayCard/PayCardViewController.swift b/novawallet/Modules/PayCard/PayCardViewController.swift index 39bbba169..5a20d8df0 100644 --- a/novawallet/Modules/PayCard/PayCardViewController.swift +++ b/novawallet/Modules/PayCard/PayCardViewController.swift @@ -67,16 +67,22 @@ final class PayCardViewController: UIViewController, ViewHolder { } func load(resource: PayCardHtmlResource) { - rootView.webView.loadHTMLString(resource.content, baseURL: resource.url) + let request = URLRequest(url: resource.url) + + rootView.webView.load(request) } } +// MARK: DAppBrowserScriptHandlerDelegate + extension PayCardViewController: DAppBrowserScriptHandlerDelegate { func browserScriptHandler(_: DAppBrowserScriptHandler, didReceive message: WKScriptMessage) { presenter.processMessage(body: message.body, of: message.name) } } +// MARK: WKNavigationDelegate + extension PayCardViewController: WKNavigationDelegate, WKUIDelegate { func webView( _: WKWebView, @@ -105,6 +111,8 @@ extension PayCardViewController: WKNavigationDelegate, WKUIDelegate { } } +// MARK: PayCardViewProtocol + extension PayCardViewController: PayCardViewProtocol { func didReceive(model: PayCardModel) { let transport = DAppTransportModel( diff --git a/novawallet/Modules/PayCard/PayCardViewFactory.swift b/novawallet/Modules/PayCard/PayCardViewFactory.swift index f54876c3a..1abf8a7c1 100644 --- a/novawallet/Modules/PayCard/PayCardViewFactory.swift +++ b/novawallet/Modules/PayCard/PayCardViewFactory.swift @@ -24,20 +24,22 @@ struct PayCardViewFactory { private static func createInteractor() -> PayCardInteractor { let chainRegistry = ChainRegistryFacade.sharedRegistry - let hooksFactory = MercuryoCardHookFactory( + let logger = Logger.shared + + let hooksFactory = MercuryoCardHookFactory(logger: logger) + let resourceProvider = MercuryoCardResourceProvider( chainRegistry: chainRegistry, wallet: SelectedWalletSettings.shared.value, - chainId: KnowChainId.polkadot, - logger: Logger.shared + chainId: KnowChainId.polkadot ) return PayCardInteractor( payCardHookFactory: hooksFactory, - payCardResourceProvider: MercuryoCardResourceProvider(), + payCardResourceProvider: resourceProvider, settingsManager: SettingsManager.shared, operationQueue: OperationManagerFacade.sharedDefaultQueue, pendingTimeout: MercuryoCardApi.pendingTimeout, - logger: Logger.shared + logger: logger ) } } diff --git a/novawallet/Modules/PayCard/mercuryoWidget.html b/novawallet/Modules/PayCard/mercuryoWidget.html deleted file mode 100644 index 56587ed59..000000000 --- a/novawallet/Modules/PayCard/mercuryoWidget.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Mercuryo Widget - - - -
- - - - From 1630bd3aedcb894c064dedb0bb3254cf4640e75f Mon Sep 17 00:00:00 2001 From: svojsu Date: Wed, 18 Dec 2024 11:22:42 +0200 Subject: [PATCH 2/8] intercept and process topup request --- novawallet.xcodeproj/project.pbxproj | 60 ++++++++---- ...ryoCardHookFactory+ResponseIntercept.swift | 52 +++++++++++ .../MercuryoCardHookFactory+Widget.swift | 22 +++++ .../{ => Hooks}/MercuryoCardHookFactory.swift | 20 +++- ...ryoCardHookFactory+ResponseIntercept.swift | 30 ------ .../MercuryoCardHookFactory+Widget.swift | 26 ------ .../Mercuryo/MercuryoCardParamsProvider.swift | 54 +++++++++++ .../MercuryoCardResourceProvider.swift | 93 +++---------------- .../Mercuryo/MercuryoCardTopupHandler.swift | 40 -------- .../Mercuryo/MercuryoTransferData.swift | 19 ---- .../MercuryoCardStatusHandler.swift | 0 .../MercuryoCardsResponseHandler.swift | 0 .../MercuryoSellRequestResponseHandler.swift | 63 +++++++++++++ .../{ => Model}/MercuryoGenericResponse.swift | 0 .../{ => Model}/MercuryoMessageName.swift | 0 .../Model/MercuryoSellResponseData.swift | 16 ++++ .../{ => Model}/MercuryoStatusChange.swift | 2 +- .../Modules/PayCard/Model/PayCardHook.swift | 5 +- .../Modules/PayCard/Model/PayCardModel.swift | 2 +- .../Model/PayCardResourceProvider.swift | 4 +- .../Modules/PayCard/PayCardInteractor.swift | 34 ++++--- .../PayCard/PayCardViewController.swift | 2 +- .../Modules/PayCard/PayCardViewFactory.swift | 6 +- 23 files changed, 317 insertions(+), 233 deletions(-) create mode 100644 novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+ResponseIntercept.swift create mode 100644 novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+Widget.swift rename novawallet/Modules/PayCard/Mercuryo/{ => Hooks}/MercuryoCardHookFactory.swift (62%) delete mode 100644 novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+ResponseIntercept.swift delete mode 100644 novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift create mode 100644 novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift delete mode 100644 novawallet/Modules/PayCard/Mercuryo/MercuryoCardTopupHandler.swift delete mode 100644 novawallet/Modules/PayCard/Mercuryo/MercuryoTransferData.swift rename novawallet/Modules/PayCard/Mercuryo/{ => MessageHandlers}/MercuryoCardStatusHandler.swift (100%) rename novawallet/Modules/PayCard/Mercuryo/{ => MessageHandlers}/MercuryoCardsResponseHandler.swift (100%) create mode 100644 novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift rename novawallet/Modules/PayCard/Mercuryo/{ => Model}/MercuryoGenericResponse.swift (100%) rename novawallet/Modules/PayCard/Mercuryo/{ => Model}/MercuryoMessageName.swift (100%) create mode 100644 novawallet/Modules/PayCard/Mercuryo/Model/MercuryoSellResponseData.swift rename novawallet/Modules/PayCard/Mercuryo/{ => Model}/MercuryoStatusChange.swift (89%) diff --git a/novawallet.xcodeproj/project.pbxproj b/novawallet.xcodeproj/project.pbxproj index afa17269c..2547d50d1 100644 --- a/novawallet.xcodeproj/project.pbxproj +++ b/novawallet.xcodeproj/project.pbxproj @@ -423,7 +423,7 @@ 0C8FDBB82CB0FD3200775D7F /* MercuryoMessageName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBB72CB0FD3200775D7F /* MercuryoMessageName.swift */; }; 0C8FDBBA2CB0FE3600775D7F /* MercuryoCardsResponseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBB92CB0FE3600775D7F /* MercuryoCardsResponseHandler.swift */; }; 0C8FDBBC2CB0FECA00775D7F /* MercuryoCardStatusHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBBB2CB0FECA00775D7F /* MercuryoCardStatusHandler.swift */; }; - 0C8FDBBE2CB0FF2A00775D7F /* MercuryoCardTopupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBBD2CB0FF2A00775D7F /* MercuryoCardTopupHandler.swift */; }; + 0C8FDBBE2CB0FF2A00775D7F /* MercuryoSellRequestResponseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBBD2CB0FF2A00775D7F /* MercuryoSellRequestResponseHandler.swift */; }; 0C8FDBC02CB1001A00775D7F /* PayCardHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBBF2CB1001A00775D7F /* PayCardHook.swift */; }; 0C8FDBC22CB1011C00775D7F /* MercuryoCardHookFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBC12CB1011C00775D7F /* MercuryoCardHookFactory.swift */; }; 0C8FDBC42CB101E700775D7F /* MercuryoCardHookFactory+ResponseIntercept.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FDBC32CB101E700775D7F /* MercuryoCardHookFactory+ResponseIntercept.swift */; }; @@ -936,6 +936,7 @@ 2D039DE82BFCA83600A928E5 /* BindableGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D039DE72BFCA83600A928E5 /* BindableGestureRecognizer.swift */; }; 2D039DEA2BFCAB9000A928E5 /* ExportViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D039DE92BFCAB9000A928E5 /* ExportViewModelFactory.swift */; }; 2D077E472BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D077E462BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift */; }; + 2D07803E2D12A175003E35B1 /* MercuryoCardParamsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D07803D2D12A175003E35B1 /* MercuryoCardParamsProvider.swift */; }; 2D0A82EC2CB47E4E0043B330 /* PayCardSheetViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0A82EB2CB47E4E0043B330 /* PayCardSheetViewFactory.swift */; }; 2D0A82EE2CB47F080043B330 /* MessageSheetTimerLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0A82ED2CB47F080043B330 /* MessageSheetTimerLabel.swift */; }; 2D0A82F02CB5722C0043B330 /* PayCardStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0A82EF2CB5722C0043B330 /* PayCardStatus.swift */; }; @@ -1094,7 +1095,7 @@ 2DA85A652C7D9B8B00591900 /* CardTopUpTransferSetupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A642C7D9B8B00591900 /* CardTopUpTransferSetupViewController.swift */; }; 2DA85A672C7DD75300591900 /* CardTopUpTransferSetupViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A662C7DD75300591900 /* CardTopUpTransferSetupViewLayout.swift */; }; 2DA85A6B2C7F36CF00591900 /* CardTopUpTransferSetupProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A6A2C7F36CF00591900 /* CardTopUpTransferSetupProtocols.swift */; }; - 2DA85A6E2C7F3F2F00591900 /* MercuryoTransferData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A6D2C7F3F2F00591900 /* MercuryoTransferData.swift */; }; + 2DA85A6E2C7F3F2F00591900 /* MercuryoSellResponseData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A6D2C7F3F2F00591900 /* MercuryoSellResponseData.swift */; }; 2DA85A722C7F6E0900591900 /* SwipeGovBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A712C7F6E0900591900 /* SwipeGovBannerView.swift */; }; 2DA85A742C7FC5D100591900 /* RoundedGradientBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A732C7FC5D100591900 /* RoundedGradientBackgroundView.swift */; }; 2DA85A772C7FCCCE00591900 /* SwipeGovViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA85A762C7FCCCE00591900 /* SwipeGovViewModelFactory.swift */; }; @@ -5640,7 +5641,7 @@ 0C8FDBB72CB0FD3200775D7F /* MercuryoMessageName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoMessageName.swift; sourceTree = ""; }; 0C8FDBB92CB0FE3600775D7F /* MercuryoCardsResponseHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoCardsResponseHandler.swift; sourceTree = ""; }; 0C8FDBBB2CB0FECA00775D7F /* MercuryoCardStatusHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoCardStatusHandler.swift; sourceTree = ""; }; - 0C8FDBBD2CB0FF2A00775D7F /* MercuryoCardTopupHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoCardTopupHandler.swift; sourceTree = ""; }; + 0C8FDBBD2CB0FF2A00775D7F /* MercuryoSellRequestResponseHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoSellRequestResponseHandler.swift; sourceTree = ""; }; 0C8FDBBF2CB1001A00775D7F /* PayCardHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayCardHook.swift; sourceTree = ""; }; 0C8FDBC12CB1011C00775D7F /* MercuryoCardHookFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoCardHookFactory.swift; sourceTree = ""; }; 0C8FDBC32CB101E700775D7F /* MercuryoCardHookFactory+ResponseIntercept.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MercuryoCardHookFactory+ResponseIntercept.swift"; sourceTree = ""; }; @@ -6164,6 +6165,7 @@ 2D039DE72BFCA83600A928E5 /* BindableGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindableGestureRecognizer.swift; sourceTree = ""; }; 2D039DE92BFCAB9000A928E5 /* ExportViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportViewModelFactory.swift; sourceTree = ""; }; 2D077E462BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualBackupChainTableViewCell.swift; sourceTree = ""; }; + 2D07803D2D12A175003E35B1 /* MercuryoCardParamsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoCardParamsProvider.swift; sourceTree = ""; }; 2D0A82EB2CB47E4E0043B330 /* PayCardSheetViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayCardSheetViewFactory.swift; sourceTree = ""; }; 2D0A82ED2CB47F080043B330 /* MessageSheetTimerLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSheetTimerLabel.swift; sourceTree = ""; }; 2D0A82EF2CB5722C0043B330 /* PayCardStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayCardStatus.swift; sourceTree = ""; }; @@ -6320,7 +6322,7 @@ 2DA85A642C7D9B8B00591900 /* CardTopUpTransferSetupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTopUpTransferSetupViewController.swift; sourceTree = ""; }; 2DA85A662C7DD75300591900 /* CardTopUpTransferSetupViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTopUpTransferSetupViewLayout.swift; sourceTree = ""; }; 2DA85A6A2C7F36CF00591900 /* CardTopUpTransferSetupProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTopUpTransferSetupProtocols.swift; sourceTree = ""; }; - 2DA85A6D2C7F3F2F00591900 /* MercuryoTransferData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoTransferData.swift; sourceTree = ""; }; + 2DA85A6D2C7F3F2F00591900 /* MercuryoSellResponseData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MercuryoSellResponseData.swift; sourceTree = ""; }; 2DA85A712C7F6E0900591900 /* SwipeGovBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeGovBannerView.swift; sourceTree = ""; }; 2DA85A732C7FC5D100591900 /* RoundedGradientBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedGradientBackgroundView.swift; sourceTree = ""; }; 2DA85A762C7FCCCE00591900 /* SwipeGovViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeGovViewModelFactory.swift; sourceTree = ""; }; @@ -11328,17 +11330,11 @@ 0C8FDBCB2CB10E2C00775D7F /* Mercuryo */ = { isa = PBXGroup; children = ( - 0C8FDBCE2CB25D7400775D7F /* MercuryoStatusChange.swift */, - 2DA85A6D2C7F3F2F00591900 /* MercuryoTransferData.swift */, - 0C8FDBB72CB0FD3200775D7F /* MercuryoMessageName.swift */, - 0C8FDBB92CB0FE3600775D7F /* MercuryoCardsResponseHandler.swift */, - 0C8FDBBB2CB0FECA00775D7F /* MercuryoCardStatusHandler.swift */, - 0C8FDBBD2CB0FF2A00775D7F /* MercuryoCardTopupHandler.swift */, - 0C8FDBC12CB1011C00775D7F /* MercuryoCardHookFactory.swift */, - 0C8FDBC32CB101E700775D7F /* MercuryoCardHookFactory+ResponseIntercept.swift */, - 0C8FDBC52CB1045800775D7F /* MercuryoCardHookFactory+Widget.swift */, + 2D07803F2D12C68E003E35B1 /* MessageHandlers */, + 2D0780402D12C6B5003E35B1 /* Hooks */, + 2D0780412D12C6F8003E35B1 /* Model */, 0C8FDBC92CB10CB100775D7F /* MercuryoCardResourceProvider.swift */, - 0C8FDBD02CB25E0400775D7F /* MercuryoGenericResponse.swift */, + 2D07803D2D12A175003E35B1 /* MercuryoCardParamsProvider.swift */, ); path = Mercuryo; sourceTree = ""; @@ -12552,6 +12548,37 @@ path = View; sourceTree = ""; }; + 2D07803F2D12C68E003E35B1 /* MessageHandlers */ = { + isa = PBXGroup; + children = ( + 0C8FDBBD2CB0FF2A00775D7F /* MercuryoSellRequestResponseHandler.swift */, + 0C8FDBB92CB0FE3600775D7F /* MercuryoCardsResponseHandler.swift */, + 0C8FDBBB2CB0FECA00775D7F /* MercuryoCardStatusHandler.swift */, + ); + path = MessageHandlers; + sourceTree = ""; + }; + 2D0780402D12C6B5003E35B1 /* Hooks */ = { + isa = PBXGroup; + children = ( + 0C8FDBC32CB101E700775D7F /* MercuryoCardHookFactory+ResponseIntercept.swift */, + 0C8FDBC52CB1045800775D7F /* MercuryoCardHookFactory+Widget.swift */, + 0C8FDBC12CB1011C00775D7F /* MercuryoCardHookFactory.swift */, + ); + path = Hooks; + sourceTree = ""; + }; + 2D0780412D12C6F8003E35B1 /* Model */ = { + isa = PBXGroup; + children = ( + 0C8FDBCE2CB25D7400775D7F /* MercuryoStatusChange.swift */, + 2DA85A6D2C7F3F2F00591900 /* MercuryoSellResponseData.swift */, + 0C8FDBB72CB0FD3200775D7F /* MercuryoMessageName.swift */, + 0C8FDBD02CB25E0400775D7F /* MercuryoGenericResponse.swift */, + ); + path = Model; + sourceTree = ""; + }; 2D0A82EA2CB47E200043B330 /* View */ = { isa = PBXGroup; children = ( @@ -26405,6 +26432,7 @@ 2D8FF9D02C9AA54000089F53 /* BaseReferendumVoteConfirmPresenter.swift in Sources */, 8882FC6329E543D800DDA90B /* EquilibriumSubscriptionHandlingFactory.swift in Sources */, 847C9620255340F2002D288F /* ExportGenericViewController.swift in Sources */, + 2D07803E2D12A175003E35B1 /* MercuryoCardParamsProvider.swift in Sources */, 84F231E329B233F20070FC2F /* GovernanceDelegateListOperationFactoryProtocol+BlockNumber.swift in Sources */, 2D4F551C2CCBA0D300B65B76 /* AssetOperationCollectionManager.swift in Sources */, 849AFEB326DADC1300B65924 /* SubqueryHistory.swift in Sources */, @@ -26957,7 +26985,7 @@ 842A737C27DCC489006EE1EA /* OperationDetailsTransferView.swift in Sources */, 84880C4429026C3E00CADB06 /* ReferendumDelegatingLocal.swift in Sources */, 8472C5B2265CF9C500E2481B /* StakingRewardDestConfirmInteractor.swift in Sources */, - 2DA85A6E2C7F3F2F00591900 /* MercuryoTransferData.swift in Sources */, + 2DA85A6E2C7F3F2F00591900 /* MercuryoSellResponseData.swift in Sources */, 88F9F696297B082F00F6550D /* SelectableTitleTableViewCell.swift in Sources */, 882C29AE28DC7CB4009CA4B6 /* StorageMigrating+CheckVersion.swift in Sources */, 846CA77A27099B1E0011124C /* StakingAnalyticsLocalSubscriptionFactory.swift in Sources */, @@ -27636,7 +27664,7 @@ 846B749828B4BA0500C39B93 /* LedgerAccountsStore.swift in Sources */, 844ADE7728CA7F7100EE29F7 /* ParaStkYieldBoostSetupPresenter+ViewUpdate.swift in Sources */, 8499FED627BFB12600712589 /* UniquesSyncService.swift in Sources */, - 0C8FDBBE2CB0FF2A00775D7F /* MercuryoCardTopupHandler.swift in Sources */, + 0C8FDBBE2CB0FF2A00775D7F /* MercuryoSellRequestResponseHandler.swift in Sources */, 841E556B282EAC3600C8438F /* ParachainStakingDelegatorState.swift in Sources */, 880E40FF298CF1ED0077B18B /* VotesProtocols.swift in Sources */, 774A980D2B0D0873009146CA /* OpenScreenUrlParsingService.swift in Sources */, diff --git a/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+ResponseIntercept.swift b/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+ResponseIntercept.swift new file mode 100644 index 000000000..01b8f307f --- /dev/null +++ b/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+ResponseIntercept.swift @@ -0,0 +1,52 @@ +import Foundation + +extension MercuryoCardHookFactory { + func createResponseInterceptingHook( + using params: MercuryoCardParams, + for delegate: PayCardHookDelegate + ) -> PayCardHook { + let cardsAction = MercuryoMessageName.onCardsResponse.rawValue + let topUpAction = MercuryoMessageName.onCardTopup.rawValue + + let scriptSource = """ + let originalXhrOpen = XMLHttpRequest.prototype.open; + + XMLHttpRequest.prototype.open = function(method, url) { + if (url === '\(MercuryoCardApi.cardsEndpoint)') { + this.addEventListener('load', function() { + window.webkit.messageHandlers.\(cardsAction).postMessage(this.responseText); + }); + } + + if (url.includes('\(MercuryoCardApi.topUpEndpoint)')) { + this.addEventListener('load', function() { + window.webkit.messageHandlers.\(topUpAction).postMessage(this.responseText); + }); + } + + originalXhrOpen.apply(this, arguments); + }; + """ + + let handlers: [PayCardMessageHandling] = [ + MercuryoCardsResponseHandler( + delegate: delegate, + logger: logger + ), + MercuryoSellRequestResponseHandler( + delegate: delegate, + chainAsset: params.chainAsset, + logger: logger + ) + ] + + return .init( + script: .init( + content: scriptSource, + insertionPoint: .atDocEnd + ), + messageNames: [cardsAction, topUpAction], + handlers: handlers + ) + } +} diff --git a/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+Widget.swift b/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+Widget.swift new file mode 100644 index 000000000..3b0c1c3ca --- /dev/null +++ b/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory+Widget.swift @@ -0,0 +1,22 @@ +import Foundation + +extension MercuryoCardHookFactory { + func createWidgetHook(for delegate: PayCardHookDelegate) -> PayCardHook { + let statusAction = MercuryoMessageName.onCardStatusChange.rawValue + + let scriptSource = """ + window.addEventListener("message", ({ data }) => { + window.webkit.messageHandlers.\(statusAction).postMessage(data); + }); + """ + + return .init( + script: .init( + content: scriptSource, + insertionPoint: .atDocEnd + ), + messageNames: [statusAction], + handlers: [MercuryoCardStatusHandler(delegate: delegate, logger: logger)] + ) + } +} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift b/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory.swift similarity index 62% rename from novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift rename to novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory.swift index c3c860523..539cca6a3 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory.swift +++ b/novawallet/Modules/PayCard/Mercuryo/Hooks/MercuryoCardHookFactory.swift @@ -1,5 +1,10 @@ import Foundation +struct MercuryoCardParams { + let chainAsset: ChainAsset + let refundAddress: AccountAddress +} + enum MercuryoCardApi { static let widgetUrl = URL(string: "https://exchange.mercuryo.io/")! static let widgetId = "4ce98182-ed76-4933-ba1b-b85e4a51d75a" // TODO: Change for production @@ -12,6 +17,7 @@ enum MercuryoCardApi { static let showSpendCardDetails = "true" static let hideRefundAddress = "true" static let cardsEndpoint = "https://api.mercuryo.io/v1.6/cards" + static let topUpEndpoint = "https://api.mercuryo.io/v1.6/widget/sell-request" static let pendingTimeout: TimeInterval = 5.secondsFromMinutes } @@ -24,10 +30,16 @@ final class MercuryoCardHookFactory { } extension MercuryoCardHookFactory: PayCardHookFactoryProtocol { - func createHooks(for delegate: PayCardHookDelegate) -> [PayCardHook] { - let responseHook = createCardsResponseInterceptingHook(for: delegate) - let widgetHooks = createWidgetHooks(for: delegate) + func createHooks( + using params: MercuryoCardParams, + for delegate: PayCardHookDelegate + ) -> [PayCardHook] { + let responseHook = createResponseInterceptingHook( + using: params, + for: delegate + ) + let widgetHook = createWidgetHook(for: delegate) - return widgetHooks + [responseHook] + return [widgetHook, responseHook] } } diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+ResponseIntercept.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+ResponseIntercept.swift deleted file mode 100644 index f733f4363..000000000 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+ResponseIntercept.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Foundation - -extension MercuryoCardHookFactory { - func createCardsResponseInterceptingHook(for delegate: PayCardHookDelegate) -> PayCardHook { - let actionName = MercuryoMessageName.onCardsResponse.rawValue - let scriptSource = """ - let originalXhrOpen = XMLHttpRequest.prototype.open; - XMLHttpRequest.prototype.open = function(method, url) { - if (url === '\(MercuryoCardApi.cardsEndpoint)') { - this.addEventListener('load', function() { - window.webkit.messageHandlers.\(actionName).postMessage(this.responseText); - }); - } - - originalXhrOpen.apply(this, arguments); - }; - """ - - return .init( - script: .init( - content: scriptSource, - insertionPoint: .atDocEnd - ), - messageNames: [actionName], - handlers: [ - MercuryoCardsResponseHandler(delegate: delegate, logger: logger) - ] - ) - } -} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift deleted file mode 100644 index 5136737cc..000000000 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardHookFactory+Widget.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation - -extension MercuryoCardHookFactory { - func createWidgetHooks(for delegate: PayCardHookDelegate) -> [PayCardHook] { - let statusAction = MercuryoMessageName.onCardStatusChange.rawValue - - let scriptSource = """ - { - data => { - window.webkit.messageHandlers.\(statusAction).postMessage(JSON.stringify(data)) - } - }; - """ - - return [ - .init( - script: .init( - content: scriptSource, - insertionPoint: .atDocEnd - ), - messageNames: [statusAction], - handlers: [MercuryoCardStatusHandler(delegate: delegate, logger: logger)] - ) - ] - } -} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift new file mode 100644 index 000000000..bbf4a9838 --- /dev/null +++ b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift @@ -0,0 +1,54 @@ +import Foundation +import Operation_iOS + +protocol MercuryoCardParamsProviderProtocol { + func fetchParamsOperation() -> BaseOperation +} + +final class MercuryoCardParamsProvider { + let chainRegistry: ChainRegistryProtocol + let wallet: MetaAccountModel + let chainId: ChainModel.Id + + init( + chainRegistry: ChainRegistryProtocol, + wallet: MetaAccountModel, + chainId: ChainModel.Id + ) { + self.chainRegistry = chainRegistry + self.wallet = wallet + self.chainId = chainId + } +} + +// MARK: MercuryoCardParamsProviderProtocol + +extension MercuryoCardParamsProvider: MercuryoCardParamsProviderProtocol { + func fetchParamsOperation() -> BaseOperation { + let chainFetchWrapper = chainRegistry.asyncWaitChainWrapper(for: chainId) + + return ClosureOperation { [weak self] in + guard + let self, + let chain = try chainFetchWrapper.targetOperation.extractNoCancellableResultData(), + let utilityAsset = chain.utilityChainAsset() + else { + throw ChainModelFetchError.noAsset(assetId: 0) + } + + guard + let selectedAccount = wallet.fetch(for: chain.accountRequest()) else { + throw ChainAccountFetchingError.accountNotExists + } + + let refundAddress = try selectedAccount.accountId.toAddress( + using: utilityAsset.chain.chainFormat + ) + + return MercuryoCardParams( + chainAsset: utilityAsset, + refundAddress: refundAddress + ) + } + } +} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift index 05a2955b3..e196bdc8c 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardResourceProvider.swift @@ -6,67 +6,7 @@ enum MercuryoCardResourceProviderError: Error { } final class MercuryoCardResourceProvider { - let chainRegistry: ChainRegistryProtocol - let wallet: MetaAccountModel - let chainId: ChainModel.Id - - init( - chainRegistry: ChainRegistryProtocol, - wallet: MetaAccountModel, - chainId: ChainModel.Id - ) { - self.chainRegistry = chainRegistry - self.wallet = wallet - self.chainId = chainId - } -} - -// MARK: Private - -private extension MercuryoCardResourceProvider { - func createQueryItemsWrapper() -> CompoundOperationWrapper<[URLQueryItem]> { - let chainFetchWrapper = chainRegistry.asyncWaitChainWrapper(for: chainId) - - let queryItemsOperation = createQueryItemsOperation( - dependingOn: chainFetchWrapper.targetOperation, - wallet: wallet - ) - - queryItemsOperation.addDependency(chainFetchWrapper.targetOperation) - - return chainFetchWrapper.insertingTail(operation: queryItemsOperation) - } - - func createQueryItemsOperation( - dependingOn chainOperation: BaseOperation, - wallet: MetaAccountModel - ) -> BaseOperation<[URLQueryItem]> { - ClosureOperation { [weak self] in - guard - let self, - let chain = try chainOperation.extractNoCancellableResultData(), - let utilityAsset = chain.utilityChainAsset() - else { - throw ChainModelFetchError.noAsset(assetId: 0) - } - - guard - let selectedAccount = wallet.fetch(for: chain.accountRequest()) else { - throw ChainAccountFetchingError.accountNotExists - } - - let refundAddress = try selectedAccount.accountId.toAddress( - using: utilityAsset.chain.chainFormat - ) - - return createQueryItems( - for: utilityAsset, - refundAddress: refundAddress - ) - } - } - - func createQueryItems( + private func createQueryItems( for chainAsset: ChainAsset, refundAddress: AccountAddress ) -> [URLQueryItem] { @@ -129,28 +69,23 @@ private extension MercuryoCardResourceProvider { // MARK: PayCardResourceProviding extension MercuryoCardResourceProvider: PayCardResourceProviding { - func loadResourceWrapper() -> CompoundOperationWrapper { - let queryItemsWrapper = createQueryItemsWrapper() - - let mapOperation = ClosureOperation { - let queryItems = try queryItemsWrapper.targetOperation.extractNoCancellableResultData() - - var urlComponents = URLComponents( - url: MercuryoCardApi.widgetUrl, - resolvingAgainstBaseURL: false - ) + func loadResource(using params: MercuryoCardParams) throws -> PayCardResource { + let queryItems = createQueryItems( + for: params.chainAsset, + refundAddress: params.refundAddress + ) - urlComponents?.queryItems = queryItems + var urlComponents = URLComponents( + url: MercuryoCardApi.widgetUrl, + resolvingAgainstBaseURL: false + ) - guard let url = urlComponents?.url else { - throw MercuryoCardResourceProviderError.unavailable - } + urlComponents?.queryItems = queryItems - return PayCardHtmlResource(url: url) + guard let url = urlComponents?.url else { + throw MercuryoCardResourceProviderError.unavailable } - mapOperation.addDependency(queryItemsWrapper.targetOperation) - - return queryItemsWrapper.insertingTail(operation: mapOperation) + return PayCardResource(url: url) } } diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardTopupHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardTopupHandler.swift deleted file mode 100644 index 2a525ce4e..000000000 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardTopupHandler.swift +++ /dev/null @@ -1,40 +0,0 @@ -import Foundation - -final class MercuryoCardTopupHandler { - let logger: LoggerProtocol - let chainAsset: ChainAsset - weak var delegate: PayCardHookDelegate? - - init(delegate: PayCardHookDelegate, chainAsset: ChainAsset, logger: LoggerProtocol) { - self.delegate = delegate - self.chainAsset = chainAsset - self.logger = logger - } -} - -extension MercuryoCardTopupHandler: PayCardMessageHandling { - func canHandleMessageOf(name: String) -> Bool { - name == MercuryoMessageName.onCardTopup.rawValue - } - - func handle(message: Any, of _: String) { - do { - guard let message = "\(message)".data(using: .utf8) else { - logger.error("Unexpected message: \(message)") - return - } - - let transferData = try JSONDecoder().decode(MercuryoTransferData.self, from: message) - - let model = PayCardTopupModel( - chainAsset: chainAsset, - amount: transferData.amount.decimalValue, - recipientAddress: transferData.address - ) - - delegate?.didRequestTopup(from: model) - } catch { - logger.error("Unexpected error: \(error)") - } - } -} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoTransferData.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoTransferData.swift deleted file mode 100644 index 76979255a..000000000 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoTransferData.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -struct MercuryoTransferData: Decodable { - let id: String - let flowId: String - let amount: AmountDecimal - let currency: String - let network: String - let address: AccountAddress - - enum CodingKeys: String, CodingKey { - case id - case flowId = "flow_id" - case amount - case currency - case network - case address - } -} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardStatusHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift similarity index 100% rename from novawallet/Modules/PayCard/Mercuryo/MercuryoCardStatusHandler.swift rename to novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardsResponseHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardsResponseHandler.swift similarity index 100% rename from novawallet/Modules/PayCard/Mercuryo/MercuryoCardsResponseHandler.swift rename to novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardsResponseHandler.swift diff --git a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift new file mode 100644 index 000000000..ec92f6f5d --- /dev/null +++ b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift @@ -0,0 +1,63 @@ +import Foundation + +final class MercuryoSellRequestResponseHandler { + weak var delegate: PayCardHookDelegate? + + var lastTransactionStatus: MercuryoStatus? + + let logger: LoggerProtocol + let chainAsset: ChainAsset + + init( + delegate: PayCardHookDelegate, + chainAsset: ChainAsset, + logger: LoggerProtocol + ) { + self.delegate = delegate + self.chainAsset = chainAsset + self.logger = logger + } +} + +extension MercuryoSellRequestResponseHandler: PayCardMessageHandling { + func canHandleMessageOf(name: String) -> Bool { + name == MercuryoMessageName.onCardTopup.rawValue + } + + func handle(message: Any, of _: String) { + do { + guard let message = "\(message)".data(using: .utf8) else { + logger.error("Unexpected message: \(message)") + return + } + + let sellStatusResponse = try JSONDecoder().decode( + MercuryoGenericResponse.self, + from: message + ) + + guard let data = sellStatusResponse.data else { + logger.error("Unexpected message: \(message)") + return + } + + guard lastTransactionStatus != data.status else { + return + } + + lastTransactionStatus = data.status + + if data.status == .new { + let model = PayCardTopupModel( + chainAsset: chainAsset, + amount: data.amounts.request.amount.decimalValue, + recipientAddress: data.address + ) + + delegate?.didRequestTopup(from: model) + } + } catch { + logger.error("Unexpected error: \(error)") + } + } +} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoGenericResponse.swift b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoGenericResponse.swift similarity index 100% rename from novawallet/Modules/PayCard/Mercuryo/MercuryoGenericResponse.swift rename to novawallet/Modules/PayCard/Mercuryo/Model/MercuryoGenericResponse.swift diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoMessageName.swift b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoMessageName.swift similarity index 100% rename from novawallet/Modules/PayCard/Mercuryo/MercuryoMessageName.swift rename to novawallet/Modules/PayCard/Mercuryo/Model/MercuryoMessageName.swift diff --git a/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoSellResponseData.swift b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoSellResponseData.swift new file mode 100644 index 000000000..5e87e7971 --- /dev/null +++ b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoSellResponseData.swift @@ -0,0 +1,16 @@ +import Foundation + +struct MercuryoSellResponseData: Decodable { + let status: MercuryoStatus + let amounts: MercuryoAmounts + let address: AccountAddress +} + +struct MercuryoAmounts: Decodable { + let request: MercuryoAmount +} + +struct MercuryoAmount: Decodable { + let amount: AmountDecimal + let currency: String +} diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoStatusChange.swift b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift similarity index 89% rename from novawallet/Modules/PayCard/Mercuryo/MercuryoStatusChange.swift rename to novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift index 7403994ba..7b1d20e38 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoStatusChange.swift +++ b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift @@ -9,7 +9,7 @@ struct MercuryoStatusChange: Decodable { let status: String } -enum MercuryoStatus: String { +enum MercuryoStatus: String, Decodable { case new case pending case succeeded diff --git a/novawallet/Modules/PayCard/Model/PayCardHook.swift b/novawallet/Modules/PayCard/Model/PayCardHook.swift index 330466f9b..59728e344 100644 --- a/novawallet/Modules/PayCard/Model/PayCardHook.swift +++ b/novawallet/Modules/PayCard/Model/PayCardHook.swift @@ -15,5 +15,8 @@ protocol PayCardHookDelegate: AnyObject { } protocol PayCardHookFactoryProtocol { - func createHooks(for delegate: PayCardHookDelegate) -> [PayCardHook] + func createHooks( + using params: MercuryoCardParams, + for delegate: PayCardHookDelegate + ) -> [PayCardHook] } diff --git a/novawallet/Modules/PayCard/Model/PayCardModel.swift b/novawallet/Modules/PayCard/Model/PayCardModel.swift index 7d02883be..de7091db5 100644 --- a/novawallet/Modules/PayCard/Model/PayCardModel.swift +++ b/novawallet/Modules/PayCard/Model/PayCardModel.swift @@ -1,7 +1,7 @@ import Foundation struct PayCardModel { - let resource: PayCardHtmlResource + let resource: PayCardResource let messageNames: Set let scripts: [DAppBrowserScript] } diff --git a/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift b/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift index c5125feec..4917359ed 100644 --- a/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift +++ b/novawallet/Modules/PayCard/Model/PayCardResourceProvider.swift @@ -1,10 +1,10 @@ import Foundation import Operation_iOS -struct PayCardHtmlResource { +struct PayCardResource { let url: URL } protocol PayCardResourceProviding { - func loadResourceWrapper() -> CompoundOperationWrapper + func loadResource(using params: MercuryoCardParams) throws -> PayCardResource } diff --git a/novawallet/Modules/PayCard/PayCardInteractor.swift b/novawallet/Modules/PayCard/PayCardInteractor.swift index 499de302d..062156d28 100644 --- a/novawallet/Modules/PayCard/PayCardInteractor.swift +++ b/novawallet/Modules/PayCard/PayCardInteractor.swift @@ -4,6 +4,7 @@ import SoraKeystore final class PayCardInteractor { weak var presenter: PayCardInteractorOutputProtocol? + let paramsProvider: MercuryoCardParamsProviderProtocol let payCardHookFactory: PayCardHookFactoryProtocol let payCardResourceProvider: PayCardResourceProviding let operationQueue: OperationQueue @@ -14,6 +15,7 @@ final class PayCardInteractor { private var messageHandlers: [PayCardMessageHandling] = [] init( + paramsProvider: MercuryoCardParamsProviderProtocol, payCardHookFactory: PayCardHookFactoryProtocol, payCardResourceProvider: PayCardResourceProviding, settingsManager: SettingsManagerProtocol, @@ -21,6 +23,7 @@ final class PayCardInteractor { pendingTimeout: TimeInterval, logger: LoggerProtocol ) { + self.paramsProvider = paramsProvider self.payCardHookFactory = payCardHookFactory self.payCardResourceProvider = payCardResourceProvider self.settingsManager = settingsManager @@ -29,7 +32,10 @@ final class PayCardInteractor { self.logger = logger } - private func provideModel(for resource: PayCardHtmlResource, hooks: [PayCardHook]) { + private func provideModel( + for resource: PayCardResource, + hooks: [PayCardHook] + ) { let messageNames = hooks.reduce(Set()) { $0.union($1.messageNames) } let scripts = hooks.map(\.script) @@ -47,21 +53,27 @@ final class PayCardInteractor { extension PayCardInteractor: PayCardInteractorInputProtocol { func setup() { - let resourceWrapper = payCardResourceProvider.loadResourceWrapper() - - let hooks = payCardHookFactory.createHooks(for: self) + let fetchParamsOperation = paramsProvider.fetchParamsOperation() execute( - wrapper: resourceWrapper, + operation: fetchParamsOperation, inOperationQueue: operationQueue, runningCallbackIn: .main ) { [weak self] result in - switch result { - case let .success(resource): - self?.messageHandlers = hooks.flatMap(\.handlers) - self?.provideModel(for: resource, hooks: hooks) - case let .failure(error): - self?.logger.error("Unexpected hooks \(error)") + guard let self else { return } + + do { + switch result { + case let .success(params): + let resource = try payCardResourceProvider.loadResource(using: params) + let hooks = payCardHookFactory.createHooks(using: params, for: self) + messageHandlers = hooks.flatMap(\.handlers) + provideModel(for: resource, hooks: hooks) + case let .failure(error): + logger.error("Unexpected hooks \(error)") + } + } catch { + logger.error("Resource unavailable \(error)") } } } diff --git a/novawallet/Modules/PayCard/PayCardViewController.swift b/novawallet/Modules/PayCard/PayCardViewController.swift index 5a20d8df0..1c71502dd 100644 --- a/novawallet/Modules/PayCard/PayCardViewController.swift +++ b/novawallet/Modules/PayCard/PayCardViewController.swift @@ -66,7 +66,7 @@ final class PayCardViewController: UIViewController, ViewHolder { rootView.webView.uiDelegate = self } - func load(resource: PayCardHtmlResource) { + func load(resource: PayCardResource) { let request = URLRequest(url: resource.url) rootView.webView.load(request) diff --git a/novawallet/Modules/PayCard/PayCardViewFactory.swift b/novawallet/Modules/PayCard/PayCardViewFactory.swift index 1abf8a7c1..59b41360e 100644 --- a/novawallet/Modules/PayCard/PayCardViewFactory.swift +++ b/novawallet/Modules/PayCard/PayCardViewFactory.swift @@ -26,14 +26,16 @@ struct PayCardViewFactory { let logger = Logger.shared - let hooksFactory = MercuryoCardHookFactory(logger: logger) - let resourceProvider = MercuryoCardResourceProvider( + let paramsProvider = MercuryoCardParamsProvider( chainRegistry: chainRegistry, wallet: SelectedWalletSettings.shared.value, chainId: KnowChainId.polkadot ) + let hooksFactory = MercuryoCardHookFactory(logger: logger) + let resourceProvider = MercuryoCardResourceProvider() return PayCardInteractor( + paramsProvider: paramsProvider, payCardHookFactory: hooksFactory, payCardResourceProvider: resourceProvider, settingsManager: SettingsManager.shared, From 7f4a513caeea829c3e2e0e1c892dd43a622a87c1 Mon Sep 17 00:00:00 2001 From: svojsu Date: Wed, 18 Dec 2024 11:31:08 +0200 Subject: [PATCH 3/8] introduce MercuryoCallbackBody to handle status callbacks --- .../Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift index 7b1d20e38..1d7fc3c4f 100644 --- a/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift +++ b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift @@ -1,5 +1,8 @@ import Foundation +struct MercuryoCallbackBody: Decodable { + let data: MercuryoStatusChange +} struct MercuryoStatusChange: Decodable { let id: String let amount: AmountDecimal From a8381e9c93355135d096aeb938b6f4c62d3ea919 Mon Sep 17 00:00:00 2001 From: svojsu Date: Wed, 18 Dec 2024 11:32:44 +0200 Subject: [PATCH 4/8] use new callback body struct --- .../Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift index d787d4134..71736fd35 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift @@ -22,7 +22,7 @@ extension MercuryoCardStatusHandler: PayCardMessageHandling { return } - let statusChange = try JSONDecoder().decode(MercuryoStatusChange.self, from: message) + let statusChange = try JSONDecoder().decode(MercuryoCallbackBody.self, from: message) logger.debug("New status: \(statusChange)") From 8c5f67a91ffabbf93d74821ef321aad87d3e0bbb Mon Sep 17 00:00:00 2001 From: svojsu Date: Wed, 18 Dec 2024 15:26:08 +0200 Subject: [PATCH 5/8] process pending intercepted status --- .../MessageHandlers/MercuryoCardStatusHandler.swift | 5 +++-- .../MercuryoSellRequestResponseHandler.swift | 7 ++++++- .../PayCard/Mercuryo/Model/MercuryoStatusChange.swift | 2 ++ novawallet/Modules/PayCard/Model/PayCardHook.swift | 1 + novawallet/Modules/PayCard/PayCardInteractor.swift | 8 ++++++++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift index 71736fd35..ac9fc013b 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift @@ -23,14 +23,15 @@ extension MercuryoCardStatusHandler: PayCardMessageHandling { } let statusChange = try JSONDecoder().decode(MercuryoCallbackBody.self, from: message) + let statusData = statusChange.data logger.debug("New status: \(statusChange)") - guard statusChange.type == MercuryoStatusType.fiatCardSell.rawValue else { + guard statusData.type == MercuryoStatusType.fiatCardSell.rawValue else { return } - switch MercuryoStatus(rawValue: statusChange.status) { + switch MercuryoStatus(rawValue: statusData.status) { case .succeeded: delegate?.didOpenCard() case .failed: diff --git a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift index ec92f6f5d..75b090c46 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift @@ -47,7 +47,8 @@ extension MercuryoSellRequestResponseHandler: PayCardMessageHandling { lastTransactionStatus = data.status - if data.status == .new { + switch data.status { + case .new: let model = PayCardTopupModel( chainAsset: chainAsset, amount: data.amounts.request.amount.decimalValue, @@ -55,6 +56,10 @@ extension MercuryoSellRequestResponseHandler: PayCardMessageHandling { ) delegate?.didRequestTopup(from: model) + case .pending: + delegate?.didReceivePendingCardOpen() + case .completed: + delegate?.didOpenCard() } } catch { logger.error("Unexpected error: \(error)") diff --git a/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift index 1d7fc3c4f..227760308 100644 --- a/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift +++ b/novawallet/Modules/PayCard/Mercuryo/Model/MercuryoStatusChange.swift @@ -3,6 +3,7 @@ import Foundation struct MercuryoCallbackBody: Decodable { let data: MercuryoStatusChange } + struct MercuryoStatusChange: Decodable { let id: String let amount: AmountDecimal @@ -16,6 +17,7 @@ enum MercuryoStatus: String, Decodable { case new case pending case succeeded + case completed case failed } diff --git a/novawallet/Modules/PayCard/Model/PayCardHook.swift b/novawallet/Modules/PayCard/Model/PayCardHook.swift index 59728e344..4c227c320 100644 --- a/novawallet/Modules/PayCard/Model/PayCardHook.swift +++ b/novawallet/Modules/PayCard/Model/PayCardHook.swift @@ -12,6 +12,7 @@ protocol PayCardHookDelegate: AnyObject { func didReceiveNoCard() func didOpenCard() func didFailToOpenCard() + func didReceivePendingCardOpen() } protocol PayCardHookFactoryProtocol { diff --git a/novawallet/Modules/PayCard/PayCardInteractor.swift b/novawallet/Modules/PayCard/PayCardInteractor.swift index 062156d28..6ffc3a404 100644 --- a/novawallet/Modules/PayCard/PayCardInteractor.swift +++ b/novawallet/Modules/PayCard/PayCardInteractor.swift @@ -143,4 +143,12 @@ extension PayCardInteractor: PayCardHookDelegate { presenter?.didReceiveCardStatus(failedStatus) } + + func didReceivePendingCardOpen() { + if let cardOpenTimestamp = settingsManager.novaCardOpenTimestamp { + checkPendingTimeout() + } else { + processIssueInit() + } + } } From 1f507388fa59a557af00ac9f63fe60e84749d05e Mon Sep 17 00:00:00 2001 From: svojsu Date: Wed, 18 Dec 2024 15:26:21 +0200 Subject: [PATCH 6/8] formatting --- novawallet/Modules/PayCard/PayCardInteractor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/novawallet/Modules/PayCard/PayCardInteractor.swift b/novawallet/Modules/PayCard/PayCardInteractor.swift index 6ffc3a404..6e8e40111 100644 --- a/novawallet/Modules/PayCard/PayCardInteractor.swift +++ b/novawallet/Modules/PayCard/PayCardInteractor.swift @@ -143,7 +143,7 @@ extension PayCardInteractor: PayCardHookDelegate { presenter?.didReceiveCardStatus(failedStatus) } - + func didReceivePendingCardOpen() { if let cardOpenTimestamp = settingsManager.novaCardOpenTimestamp { checkPendingTimeout() From b3c3526479c412b5e5e733c33313166c08f0b899 Mon Sep 17 00:00:00 2001 From: svojsu Date: Wed, 18 Dec 2024 15:46:10 +0200 Subject: [PATCH 7/8] fix switches for compilation --- .../Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift | 2 +- .../MessageHandlers/MercuryoSellRequestResponseHandler.swift | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift index ac9fc013b..b57bab21c 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoCardStatusHandler.swift @@ -32,7 +32,7 @@ extension MercuryoCardStatusHandler: PayCardMessageHandling { } switch MercuryoStatus(rawValue: statusData.status) { - case .succeeded: + case .completed, .succeeded: delegate?.didOpenCard() case .failed: delegate?.didFailToOpenCard() diff --git a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift index 75b090c46..1ba9437c7 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MessageHandlers/MercuryoSellRequestResponseHandler.swift @@ -58,8 +58,10 @@ extension MercuryoSellRequestResponseHandler: PayCardMessageHandling { delegate?.didRequestTopup(from: model) case .pending: delegate?.didReceivePendingCardOpen() - case .completed: + case .completed, .succeeded: delegate?.didOpenCard() + case .failed: + delegate?.didFailToOpenCard() } } catch { logger.error("Unexpected error: \(error)") From ac37467d39010762b572f6d88893993848eeb3fd Mon Sep 17 00:00:00 2001 From: svojsu Date: Thu, 19 Dec 2024 18:23:41 +0200 Subject: [PATCH 8/8] review fixes --- .../Mercuryo/MercuryoCardParamsProvider.swift | 12 ++++++++---- novawallet/Modules/PayCard/PayCardInteractor.swift | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift index bbf4a9838..9fa5d3371 100644 --- a/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift +++ b/novawallet/Modules/PayCard/Mercuryo/MercuryoCardParamsProvider.swift @@ -2,7 +2,7 @@ import Foundation import Operation_iOS protocol MercuryoCardParamsProviderProtocol { - func fetchParamsOperation() -> BaseOperation + func fetchParamsWrapper() -> CompoundOperationWrapper } final class MercuryoCardParamsProvider { @@ -24,16 +24,16 @@ final class MercuryoCardParamsProvider { // MARK: MercuryoCardParamsProviderProtocol extension MercuryoCardParamsProvider: MercuryoCardParamsProviderProtocol { - func fetchParamsOperation() -> BaseOperation { + func fetchParamsWrapper() -> CompoundOperationWrapper { let chainFetchWrapper = chainRegistry.asyncWaitChainWrapper(for: chainId) - return ClosureOperation { [weak self] in + let resultOperation = ClosureOperation { [weak self] in guard let self, let chain = try chainFetchWrapper.targetOperation.extractNoCancellableResultData(), let utilityAsset = chain.utilityChainAsset() else { - throw ChainModelFetchError.noAsset(assetId: 0) + throw ChainModelFetchError.noAsset(assetId: AssetModel.utilityAssetId) } guard @@ -50,5 +50,9 @@ extension MercuryoCardParamsProvider: MercuryoCardParamsProviderProtocol { refundAddress: refundAddress ) } + + resultOperation.addDependency(chainFetchWrapper.targetOperation) + + return chainFetchWrapper.insertingTail(operation: resultOperation) } } diff --git a/novawallet/Modules/PayCard/PayCardInteractor.swift b/novawallet/Modules/PayCard/PayCardInteractor.swift index 6e8e40111..c584b8cd2 100644 --- a/novawallet/Modules/PayCard/PayCardInteractor.swift +++ b/novawallet/Modules/PayCard/PayCardInteractor.swift @@ -53,10 +53,10 @@ final class PayCardInteractor { extension PayCardInteractor: PayCardInteractorInputProtocol { func setup() { - let fetchParamsOperation = paramsProvider.fetchParamsOperation() + let fetchParamsWrapper = paramsProvider.fetchParamsWrapper() execute( - operation: fetchParamsOperation, + wrapper: fetchParamsWrapper, inOperationQueue: operationQueue, runningCallbackIn: .main ) { [weak self] result in