diff --git a/Package.swift b/Package.swift index 8bfc77d..92775f3 100644 --- a/Package.swift +++ b/Package.swift @@ -10,13 +10,8 @@ let package = Package( products: [ .library(name: "Ramp", targets: ["Ramp"]) ], - dependencies: [ - .package(name: "Passbase", - url: "https://github.com/passbase/passbase-sp.git", - from: "2.8.0"), - ], targets: [ - .target(name: "Ramp", dependencies: ["Passbase"]), + .target(name: "Ramp"), .testTarget(name: "RampTests", dependencies: ["Ramp"]) ] ) diff --git a/Ramp.podspec b/Ramp.podspec index 53aa822..f06b77a 100644 --- a/Ramp.podspec +++ b/Ramp.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Ramp' - spec.version = '3.0.0' + spec.version = '4.0.0' spec.license = 'proprietary' spec.summary = 'Ramp SDK for iOS' spec.homepage = 'https://ramp.network/' @@ -9,5 +9,4 @@ Pod::Spec.new do |spec| spec.ios.deployment_target = '11.0' spec.source_files = 'Sources/Ramp/*.swift' spec.resource_bundles = { 'Ramp' => 'Sources/Ramp/Resources/*' } - spec.dependency 'Passbase', '~> 2.8' end diff --git a/Sources/Ramp/Configuration.swift b/Sources/Ramp/Configuration.swift index 83351d5..f218f35 100644 --- a/Sources/Ramp/Configuration.swift +++ b/Sources/Ramp/Configuration.swift @@ -21,6 +21,8 @@ public struct Configuration: Decodable { public var offrampAsset: String? public var offrampWebhookV3Url: String? public var selectedCountryCode: String? + public var sdkType: String { Constants.sdkType } + public var sdkVersion: String { Constants.sdkVersion } public var swapAmount: String? public var swapAsset: String? public var userAddress: String? @@ -56,6 +58,8 @@ extension Configuration { urlComponents.appendQueryItem(name: "offrampAsset", value: offrampAsset) urlComponents.appendQueryItem(name: "offrampWebhookV3Url", value: offrampWebhookV3Url) urlComponents.appendQueryItem(name: "selectedCountryCode", value: selectedCountryCode) + urlComponents.appendQueryItem(name: "sdkType", value: sdkType) + urlComponents.appendQueryItem(name: "sdkVersion", value: sdkVersion) urlComponents.appendQueryItem(name: "swapAmount", value: swapAmount) urlComponents.appendQueryItem(name: "swapAsset", value: swapAsset) urlComponents.appendQueryItem(name: "userAddress", value: userAddress) diff --git a/Sources/Ramp/Constants.swift b/Sources/Ramp/Constants.swift index 34d695a..3311721 100644 --- a/Sources/Ramp/Constants.swift +++ b/Sources/Ramp/Constants.swift @@ -5,6 +5,8 @@ struct Constants { static let defaultUrl: String = "https://buy.ramp.network" static let scriptMessageHandlerName: String = "RampInstantMobile" static let sendCryptoVersion: Int = 1 + static let sdkType: String = "IOS" + static let sdkVersion: String = "4.0.0" static func postMessageScript(_ message: String) -> String { return "window.postMessage(" + message + ");" diff --git a/Sources/Ramp/IncomingEvent.swift b/Sources/Ramp/IncomingEvent.swift index 1cb0726..895fb19 100644 --- a/Sources/Ramp/IncomingEvent.swift +++ b/Sources/Ramp/IncomingEvent.swift @@ -1,7 +1,6 @@ import Foundation enum IncomingEvent { - case kycInit(KycInitPayload) case onrampPurchaseCreated(OnrampPurchaseCreatedPayload) case widgetClose(WidgetClosePayload) case sendCrypto(SendCryptoPayload) @@ -19,11 +18,6 @@ extension IncomingEvent: DictionaryDecodable { switch type { - case EventTypes.kycInit: - guard let payload else { throw Error.missingPayload(type) } - let decoded: KycInitPayload = try decoder.decode(payload) - self = .kycInit(decoded) - case EventTypes.onrampPurchaseCreated: guard let payload else { throw Error.missingPayload(type) } let decoded: OnrampPurchaseCreatedPayload = try decoder.decode(payload) @@ -60,7 +54,6 @@ extension IncomingEvent: DictionaryDecodable { extension IncomingEvent { struct EventTypes { - static let kycInit = "KYC_INIT" static let onrampPurchaseCreated = "PURCHASE_CREATED" static let widgetClose = "WIDGET_CLOSE" static let sendCrypto = "SEND_CRYPTO" @@ -81,15 +74,6 @@ extension IncomingEvent { // MARK: - Payloads -struct KycInitPayload: Decodable { - let email: String - let countryCode: String - let verificationId: Int - let provider: String - let apiKey: String - let metaData: String? -} - struct OnrampPurchaseCreatedPayload: Decodable { let apiUrl: URL let purchase: OnrampPurchase diff --git a/Sources/Ramp/Localizable.swift b/Sources/Ramp/Localizable.swift index 64bfa59..c2be3c1 100644 --- a/Sources/Ramp/Localizable.swift +++ b/Sources/Ramp/Localizable.swift @@ -1,8 +1,18 @@ import Foundation +// use +// genstrings -o Sources/Ramp/Resources/en.lproj Sources/Ramp/*.swift +// to regenerate Localizable.strings + struct Localizable { - static var closeAlertTitle: String { NSLocalizedString("Do you really want to close Ramp?", comment: "Alert title for closing Ramp") } - static var closeAlertMessage: String { NSLocalizedString("You will loose all progress and will have to start over", comment: "Alert message for closing Ramp") } + static var closeAlertTitle: String { + NSLocalizedString("Do you really want to close Ramp?", + comment: "Alert title for closing Ramp") + } + static var closeAlertMessage: String { + NSLocalizedString("You will loose all progress and will have to start over", + comment: "Alert message for closing Ramp") + } static var yes: String { NSLocalizedString("Yes", comment: "Yes") } static var no: String { NSLocalizedString("No", comment: "No") } } diff --git a/Sources/Ramp/Logger.swift b/Sources/Ramp/Logger.swift new file mode 100644 index 0000000..9e2981b --- /dev/null +++ b/Sources/Ramp/Logger.swift @@ -0,0 +1,15 @@ +import Foundation + +struct Logger { + static func debug(_ message: String) { + #if DEBUG + print("[debug]", message) + #endif + } + + static func error(_ error: Error) { + #if DEBUG + print("[error]", String(describing: error)) + #endif + } +} diff --git a/Sources/Ramp/OnrampPurchase.swift b/Sources/Ramp/OnrampPurchase.swift index 5f6d737..657e755 100644 --- a/Sources/Ramp/OnrampPurchase.swift +++ b/Sources/Ramp/OnrampPurchase.swift @@ -1,24 +1,24 @@ import Foundation public struct OnrampPurchase: Codable { - public let id: String - public let endTime: String + public let appliedFee: Double public let asset: AssetInfo - public let receiverAddress: String + public let assetExchangeRate: Double + public let baseRampFee: Double + public let createdAt: String public let cryptoAmount: String + public let endTime: String? + public let escrowAddress: String? + public let escrowDetailsHash: String? public let fiatCurrency: String public let fiatValue: Double - public let assetExchangeRate: Double - public let baseRampFee: Double + public let finalTxHash: String? + public let id: String public let networkFee: Double - public let appliedFee: Double public let paymentMethodType: String - public let finalTxHash: String? - public let createdAt: String - public let updatedAt: String + public let receiverAddress: String public let status: String - public let escrowAddress: String? - public let escrowDetailsHash: String? + public let updatedAt: String public struct AssetInfo: Codable { public let address: String? diff --git a/Sources/Ramp/OutgoingEvent.swift b/Sources/Ramp/OutgoingEvent.swift index efc785a..5ee2e8f 100644 --- a/Sources/Ramp/OutgoingEvent.swift +++ b/Sources/Ramp/OutgoingEvent.swift @@ -1,11 +1,6 @@ import Foundation enum OutgoingEvent { - case kycStarted(KycStartedPayload) - case kycSubmitted(KycSubmittedPayload) - case kycSuccess(KycSuccessPayload) - case kycAborted(KycAbortedPayload) - case kycError(KycErrorPayload) case backButtonPressed case sendCryptoResult(SendCryptoResultPayload) } @@ -19,31 +14,6 @@ extension OutgoingEvent: MessageEventEncodable { switch self { - case .kycStarted(let payload): - type = EventType.kycStarted - version = nil - payloadData = try encoder.encode(payload) - - case .kycSubmitted(let payload): - type = EventType.kycSubmitted - version = nil - payloadData = try encoder.encode(payload) - - case .kycSuccess(let payload): - type = EventType.kycSuccess - version = nil - payloadData = try encoder.encode(payload) - - case .kycAborted(let payload): - type = EventType.kycAborted - version = nil - payloadData = try encoder.encode(payload) - - case .kycError(let payload): - type = EventType.kycError - version = nil - payloadData = try encoder.encode(payload) - case .backButtonPressed: type = EventType.backButtonPressed version = nil @@ -83,11 +53,6 @@ extension OutgoingEvent: MessageEventEncodable { extension OutgoingEvent { struct EventType { - static let kycStarted = "KYC_STARTED" - static let kycSubmitted = "KYC_SUBMITTED" - static let kycSuccess = "KYC_SUCCESS" - static let kycAborted = "KYC_ABORTED" - static let kycError = "KYC_ERROR" static let backButtonPressed = "BACK_BUTTON_PRESSED" static let sendCryptoResult = "SEND_CRYPTO_RESULT" } @@ -103,28 +68,6 @@ extension OutgoingEvent { // MARK: - Payloads -struct KycStartedPayload: Encodable { - let verificationId: Int -} - -struct KycSubmittedPayload: Encodable { - let verificationId: Int - let identityAccessKey: String -} - -struct KycSuccessPayload: Encodable { - let verificationId: Int - let identityAccessKey: String -} - -struct KycAbortedPayload: Encodable { - let verificationId: Int -} - -struct KycErrorPayload: Encodable { - let verificationId: Int -} - public struct SendCryptoResultPayload: Encodable { let txHash: String? diff --git a/Sources/Ramp/PassbaseError.swift b/Sources/Ramp/PassbaseError.swift deleted file mode 100644 index 53759e4..0000000 --- a/Sources/Ramp/PassbaseError.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -enum PassbaseError: String { - case cancelledByUser = "CANCELLED_BY_USER" - case biometricAuthenticationFailed = "BIOMETRIC_AUTHENTICATION_FAILED" -} diff --git a/Sources/Ramp/RampDelegate.swift b/Sources/Ramp/RampDelegate.swift index 6932fb1..f0247d8 100644 --- a/Sources/Ramp/RampDelegate.swift +++ b/Sources/Ramp/RampDelegate.swift @@ -1,8 +1,16 @@ import Foundation public protocol RampDelegate: AnyObject { - func ramp(_ rampViewController: RampViewController, didCreateOnrampPurchase purchase: OnrampPurchase, _ purchaseViewToken: String, _ apiUrl: URL) - func ramp(_ rampViewController: RampViewController, didRequestSendCrypto payload: SendCryptoPayload, responseHandler: @escaping (SendCryptoResultPayload) -> Void) - func ramp(_ rampViewController: RampViewController, didCreateOfframpSale sale: OfframpSale, _ saleViewToken: String, _ apiUrl: URL) + func ramp(_ rampViewController: RampViewController, + didCreateOnrampPurchase purchase: OnrampPurchase, + _ purchaseViewToken: String, + _ apiUrl: URL) + func ramp(_ rampViewController: RampViewController, + didRequestSendCrypto payload: SendCryptoPayload, + responseHandler: @escaping (SendCryptoResultPayload) -> Void) + func ramp(_ rampViewController: RampViewController, + didCreateOfframpSale sale: OfframpSale, + _ saleViewToken: String, + _ apiUrl: URL) func rampDidClose(_ rampViewController: RampViewController) } diff --git a/Sources/Ramp/RampViewController.swift b/Sources/Ramp/RampViewController.swift index 41ea729..2d6532a 100644 --- a/Sources/Ramp/RampViewController.swift +++ b/Sources/Ramp/RampViewController.swift @@ -1,6 +1,5 @@ import UIKit import WebKit -import Passbase public final class RampViewController: UIViewController { private let url: URL @@ -28,6 +27,7 @@ public final class RampViewController: UIViewController { self.stackView = stackView let configuration = WKWebViewConfiguration() + configuration.allowsInlineMediaPlayback = true let webView = WKWebView(frame: .zero, configuration: configuration) webView.scrollView.showsVerticalScrollIndicator = false webView.uiDelegate = self @@ -39,6 +39,7 @@ public final class RampViewController: UIViewController { super.viewDidLoad() subscribeMessageHandler() setupSwipeBackGesture() + Logger.debug("Loading URL: \(url.absoluteString)") let request = URLRequest(url: url) webView.load(request) } @@ -95,23 +96,31 @@ public final class RampViewController: UIViewController { } private func sendOutgoingEvent(_ event: OutgoingEvent) { - let message = try? event.messagePayload() - guard let message else { return } + let message: String + do { + message = try event.messagePayload() + } catch { + Logger.error(error) + return + } let script = Constants.postMessageScript(message) webView.evaluateJavaScript(script) { _, _ in } } private func handleIncomingEvent(_ event: IncomingEvent) { switch event { - case .kycInit(let payload): startPassbaseFlow(payload) - case .onrampPurchaseCreated(let payload): delegate?.ramp(self, didCreateOnrampPurchase: payload.purchase, payload.purchaseViewToken, payload.apiUrl) - case .widgetClose(let payload): handleCloseRampEvent(payload) + case .onrampPurchaseCreated(let payload): handleOnrampPurchaseCreatedEvent(payload) + case .widgetClose(let payload): handleWidgetCloseEvent(payload) case .sendCrypto(let payload): handleSendCryptoEvent(payload) - case .offrampSaleCreated(let payload): delegate?.ramp(self, didCreateOfframpSale: payload.sale, payload.saleViewToken, payload.apiUrl) + case .offrampSaleCreated(let payload): handleOfframpSaleCreatedEvent(payload) } } - private func handleCloseRampEvent(_ payload: WidgetClosePayload) { + private func handleOnrampPurchaseCreatedEvent(_ payload: OnrampPurchaseCreatedPayload) { + delegate?.ramp(self, didCreateOnrampPurchase: payload.purchase, payload.purchaseViewToken, payload.apiUrl) + } + + private func handleWidgetCloseEvent(_ payload: WidgetClosePayload) { if payload.showAlert { showCloseAlert() } else { closeRamp() } } @@ -123,59 +132,8 @@ public final class RampViewController: UIViewController { } } - // MARK: Passbase actions - - private var verificationId: Int? - - private func startPassbaseFlow(_ payload: KycInitPayload) { - verificationId = payload.verificationId - - PassbaseSDK.initialize(publishableApiKey: payload.apiKey) - PassbaseSDK.prefillUserEmail = payload.email - PassbaseSDK.prefillCountry = payload.countryCode - PassbaseSDK.metaData = payload.metaData - PassbaseSDK.delegate = self - - DispatchQueue.main.async { - try? PassbaseSDK.startVerification(from: self) - } - } - - // MARK: Passbase outgoing events - - private func handlePassbaseStarted() { - guard let verificationId else { return } - let payload = KycStartedPayload(verificationId: verificationId) - let event: OutgoingEvent = .kycStarted(payload) - sendOutgoingEvent(event) - } - - private func handlePassbaseSubmitted(identityAccessKey: String) { - guard let verificationId else { return } - let payload = KycSubmittedPayload(verificationId: verificationId, identityAccessKey: identityAccessKey) - let event: OutgoingEvent = .kycSubmitted(payload) - sendOutgoingEvent(event) - } - - private func handlePassbaseSuccess(identityAccessKey: String) { - guard let verificationId else { return } - let payload = KycSuccessPayload(verificationId: verificationId, identityAccessKey: identityAccessKey) - let event: OutgoingEvent = .kycSuccess(payload) - sendOutgoingEvent(event) - } - - private func handlePassbaseAborted() { - guard let verificationId else { return } - let payload = KycAbortedPayload(verificationId: verificationId) - let event: OutgoingEvent = .kycAborted(payload) - sendOutgoingEvent(event) - } - - private func handlePassbaseError() { - guard let verificationId else { return } - let payload = KycErrorPayload(verificationId: verificationId) - let event: OutgoingEvent = .kycError(payload) - sendOutgoingEvent(event) + private func handleOfframpSaleCreatedEvent(_ payload: OfframpSaleCreatedPayload) { + delegate?.ramp(self, didCreateOfframpSale: payload.sale, payload.saleViewToken, payload.apiUrl) } } @@ -193,49 +151,16 @@ extension RampViewController: WKUIDelegate { } } -extension RampViewController { - public enum Error: Swift.Error { - case closeRampFailed - case serializeOutgoingEventFailed - case missingKycVerificationId - case unableToOpenUrl - case deserializeIncomingEventFailed - case messaveEventReceiveFailed - case unknownPassbaseError - } -} - extension RampViewController: ScriptMessageDelegate { func handler(_ scriptMessageHandler: ScriptMessageHandler, didReceiveMessage body: [String : Any]) { - let event = try? IncomingEvent(dictionary: body) - guard let event else { return } - handleIncomingEvent(event) - } -} - -// MARK: - Passbase delegate - -/// Documentation: https://docs.passbase.com/ios#4-handling-verifications -extension RampViewController: PassbaseDelegate { - public func onStart() { - handlePassbaseStarted() - } - - public func onSubmitted(identityAccessKey: String) { - handlePassbaseSubmitted(identityAccessKey: identityAccessKey) - } - - public func onFinish(identityAccessKey: String) { - handlePassbaseSuccess(identityAccessKey: identityAccessKey) - } - - public func onError(errorCode: String) { - let error = PassbaseError(rawValue: errorCode) - guard let error else { return } - switch error { - case .cancelledByUser: handlePassbaseAborted() - case .biometricAuthenticationFailed: handlePassbaseError() + let event: IncomingEvent + do { + event = try IncomingEvent(dictionary: body) + } catch { + Logger.error(error) + return } + handleIncomingEvent(event) } } diff --git a/Sources/Ramp/Resources/en.lproj/Localizable.strings b/Sources/Ramp/Resources/en.lproj/Localizable.strings index ac25000..34ad5e3 100644 Binary files a/Sources/Ramp/Resources/en.lproj/Localizable.strings and b/Sources/Ramp/Resources/en.lproj/Localizable.strings differ diff --git a/Sources/Ramp/ScriptMessageHandler.swift b/Sources/Ramp/ScriptMessageHandler.swift index 427b952..a067936 100644 --- a/Sources/Ramp/ScriptMessageHandler.swift +++ b/Sources/Ramp/ScriptMessageHandler.swift @@ -10,8 +10,11 @@ class ScriptMessageHandler: NSObject { extension ScriptMessageHandler: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - let body = message.body as? [String: Any] - guard let body else { return } + guard let body = message.body as? [String: Any] + else { + Logger.debug("JS message has no body") + return + } delegate?.handler(self, didReceiveMessage: body) } } diff --git a/Tests/RampTests/ConfigurationTests.swift b/Tests/RampTests/ConfigurationTests.swift index 8b87ba7..9769cf5 100644 --- a/Tests/RampTests/ConfigurationTests.swift +++ b/Tests/RampTests/ConfigurationTests.swift @@ -4,7 +4,7 @@ import XCTest class ConfigurationTests: XCTestCase { struct Constants { - static let defaultUrl = "https://buy.ramp.network/?variant=sdk-mobile" + static let defaultUrl = "https://buy.ramp.network/?sdkType=IOS&sdkVersion=4.0.0&variant=sdk-mobile" } func testEmptyConfiguration() throws { diff --git a/Tests/RampTests/OutgoingEventTests.swift b/Tests/RampTests/OutgoingEventTests.swift index 202af2e..c39d40a 100644 --- a/Tests/RampTests/OutgoingEventTests.swift +++ b/Tests/RampTests/OutgoingEventTests.swift @@ -18,13 +18,4 @@ class OutgoingEventTests: XCTestCase { let template = #"{"type":"BACK_BUTTON_PRESSED"}"# XCTAssertEqual(message, template) } - - func testCorrectKycStarted() throws { - let verificationId = Int.random(in: 666...777) - let payload = KycStartedPayload(verificationId: verificationId) - let event = OutgoingEvent.kycStarted(payload) - let message = try event.messagePayload() - let template = #"{"payload":{"verificationId":"# + String(verificationId) + #"},"type":"KYC_STARTED"}"# - XCTAssertEqual(message, template) - } }