Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Payment support #60

Merged
merged 90 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
62d16a4
SP-8 REST API klient
mbalsiger May 13, 2024
d4e8189
SP-19 Start native payment session SDK method
mbalsiger May 13, 2024
1ef22d1
SP-9 Get Session request
mbalsiger May 13, 2024
b0d6ffc
Fixed array for prefills
mbalsiger May 15, 2024
41e281a
SP-10 Post Integrations request
mbalsiger May 13, 2024
6e95f39
Updated body for preparePayment
mbalsiger May 15, 2024
9a7a7f9
SP-11 Post Swish Instrument Views and Attempt requests
mbalsiger May 14, 2024
3ea624f
Updated body for startPaymentAttempt
mbalsiger May 15, 2024
4804604
SP-17 Session operation handling
mbalsiger May 14, 2024
d5b5c01
SP-18 External app callback handling
mbalsiger May 15, 2024
8b1dc70
SP-27 Payment attempt problem handling
mbalsiger May 15, 2024
f08cd64
SP-29 Abort Payment request
mbalsiger May 15, 2024
1e21991
SP-31 State desync handling
mbalsiger May 17, 2024
a3b16c1
SP-36 Network instability handling
mbalsiger May 17, 2024
cebcae4
SP-35 Local problem handling
mbalsiger May 21, 2024
751c4dc
Added internalInconsistencyError
mbalsiger May 30, 2024
840202c
SP-33 Event logging
mbalsiger May 28, 2024
5978da7
Fixed AvailableInstrument object
mbalsiger May 28, 2024
614bfeb
Added "type" to Beacon body
mbalsiger May 29, 2024
e48031a
Logging for internalInconsistencyError
mbalsiger May 30, 2024
944913a
SP-39 Updated logic for availableInstruments callback
mbalsiger May 29, 2024
83535e8
Added extra check when doing start-payment-attempt
mbalsiger May 29, 2024
774ee9c
SP-41 Add public API documentation
mbalsiger May 30, 2024
068b1d7
Code Review Fixes
mbalsiger May 31, 2024
ee9af39
Merge branch 'feature/SP-41-Add-public-API-documentation' into 'featu…
mbalsiger May 31, 2024
6fde734
SP-43 Example app improvements
mbalsiger Jun 4, 2024
cdd19c3
Merge branch 'feature/SP-43-Example-app-improvements' into 'feature/n…
mbalsiger Jun 4, 2024
ee6c7b3
SP-45 CreditCard prefills and payment attempt
mbalsiger Jun 5, 2024
1bd4646
SP-47 CreditCard authentication request
mbalsiger Jun 5, 2024
4f9e719
SP-49 SCA Method Request
mbalsiger Jun 7, 2024
d0c4254
WKNavigationResponse
mbalsiger Jun 11, 2024
2f02b02
WIP: SwedbankPaySCAWebViewController
mbalsiger Jun 13, 2024
8effc8f
WIP: Fixed httpBody
mbalsiger Jun 20, 2024
bf224b7
Clean up and error handling
mbalsiger Jun 24, 2024
54f5a78
WIP: sdkProblemWithViewController
mbalsiger Jun 26, 2024
f8bbdb5
SP-55 Automatic configuration
mbalsiger Jun 17, 2024
6126005
WIP: Loading and alert in webview
mbalsiger Jun 26, 2024
5617c87
WIP: Renames
mbalsiger Jun 27, 2024
750afee
SP-59 Stöd för att visa betalmenyn
mbalsiger Jun 28, 2024
339871c
Code Review fixes
mbalsiger Jun 28, 2024
f4316cf
Merge branch 'feature/SP-59-Stöd-för-att-visa-betalmenyn' into 'featu…
mbalsiger Jun 28, 2024
2558b96
Clean up
mbalsiger Jul 1, 2024
c4e2221
WIP: Updating EndpointRouter to not take an OperationOutputModel
mbalsiger Jul 3, 2024
9d92103
SP-62 Stöd för null i parametrar för SCA Method Request
mbalsiger Sep 4, 2024
2bcf6a7
SP-64 Sluta använda hårdkodad NotificationURL till CreditCard authent…
mbalsiger Sep 6, 2024
66eb484
self.notificationUrl fix
mbalsiger Sep 6, 2024
dbc97a9
SP-70 Ändring av format för timeZoneOffset
mbalsiger Sep 6, 2024
8107429
Code Review Fixes
mbalsiger Sep 6, 2024
ea726e2
Merge branch 'feature/SP-70-Ändring-av-format-för-timeZoneOffset' int…
mbalsiger Sep 6, 2024
3fe9c0d
WIP: SP-72 Grundfunktionalitet för Apple Pay
mbalsiger Sep 13, 2024
001a3af
WIP: SP-72 Grundfunktionalitet för Apple Pay
mbalsiger Sep 25, 2024
f09e912
Added support for operation "attempt-payload"
mbalsiger Sep 26, 2024
3de0387
Clean up
mbalsiger Sep 26, 2024
2a09867
SP-77 Undvik application callback-hantering när merchant-appen är i w…
mbalsiger Sep 18, 2024
e4e20bb
WIP: SP-58 Stöd för nytt betalkort
mbalsiger Sep 17, 2024
12d8c52
"sdk" changed to string and fixed date format
mbalsiger Sep 27, 2024
d5aabb4
SP-79 Skicka vanliga session callbacks från SwedbankPaySDKController
mbalsiger Sep 23, 2024
7395b8d
Clean up
mbalsiger Sep 30, 2024
f5c6704
Code Review fixes
mbalsiger Oct 1, 2024
220fedb
Merge branch 'feature/SP-79-Skicka-vanliga-session-callbacks-från-Swe…
mbalsiger Oct 1, 2024
d4e969f
Move paymentSession3DSecureViewControllerLoadFailed into sdkProblemOc…
mbalsiger Oct 1, 2024
7dd5605
Bumped to iOS 11 and fixed all warnings
mbalsiger Oct 4, 2024
c792773
Merge branch 'feature/iOS-11-fixes' into 'feature/native-payments'
mbalsiger Oct 4, 2024
a262799
SP-80 Stöd för att återställa till Menu mode
mbalsiger Oct 14, 2024
ca6bc1f
Move logic from makeNativePaymentAttempt to sessionOperationHandling
mbalsiger Oct 15, 2024
35e233b
Code Review Fixes
mbalsiger Oct 15, 2024
e68e1c8
More Code Review Fixes
mbalsiger Oct 15, 2024
fcc7706
Merge branch 'feature/SP-80-Stöd-för-att-återställa-till-Menu-mode' i…
mbalsiger Oct 15, 2024
f16723e
Fixed Warnings in MerchantBackend
mbalsiger Oct 16, 2024
3172ef8
Removed Xcode 16 fix
alleus Oct 16, 2024
36ee745
Don't attempt loading empty SCA Method Request href strings
alleus Oct 21, 2024
8ff6663
Merge branch 'feature/sca-method-request-empty-href-string' into 'fea…
alleus Oct 21, 2024
632a69e
Adding Sequence extensions for more readable sesson logic
alleus Oct 23, 2024
8807c18
Merge branch 'feature/sequence-extensions' into 'feature/native-payme…
alleus Nov 6, 2024
55ed3c6
Fix for incorrect logic after adding sequence extensions
alleus Nov 6, 2024
c7ba110
Merge branch 'feature/sequence-extensions-fix' into 'feature/native-p…
alleus Nov 11, 2024
b4e062e
Adding cancel and fail for Apple Pay, removing temporary error handling
alleus Nov 6, 2024
4885687
Merge branch 'feature/SP-85-cancel-och-fail-for-apple-pay' into 'feat…
alleus Nov 11, 2024
6dd2fd5
Comments for main session logic
alleus Nov 6, 2024
a73b650
Merge branch 'feature/session-logic-comments' into 'feature/native-pa…
alleus Nov 11, 2024
f1d237e
Extended support for web based SDK controller configuration and instr…
alleus Nov 11, 2024
9ff6eb7
Improving beacon logging for new web based modes
alleus Nov 13, 2024
ce345ab
Tweaking customize-payment request parameters
alleus Nov 14, 2024
1b1de4f
Exposing paymentMethod on AvailableInstrument, for easier access
alleus Nov 14, 2024
ee72936
Adding ClickToPay as device accepted wallet
alleus Nov 14, 2024
86a85a0
Merge branch 'feature/SP-87-lagg-till-click-to-pay-i-device-accepted-…
alleus Nov 14, 2024
e710889
Merge branch 'feature/SP-60-konfiguration-av-betalmenyn' into 'featur…
alleus Nov 14, 2024
3e53193
Sending problem ACK only once
alleus Nov 15, 2024
2a6b312
Using server side problem node for ClientAppLaunchFailed error
alleus Nov 19, 2024
d25f198
Merge branch 'feature/client-app-launch-failed-serverside' into 'feat…
alleus Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SwedbankPaySDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The Swedbank Pay iOS SDK enables simple embedding of Swedbank Pay Checkout to an
s.author = 'Swedbank Pay'
s.source = { :git => 'https://github.com/SwedbankPay/swedbank-pay-sdk-ios.git', :tag => s.version.to_s }

s.ios.deployment_target = '10.0'
s.ios.deployment_target = '11.0'
s.swift_versions = '5.0', '5.1'

s.source_files = 'SwedbankPaySDK/Classes/**/*'
Expand Down
188 changes: 183 additions & 5 deletions SwedbankPaySDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions SwedbankPaySDK/Classes/Api/Helpers/CustomDateDecoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Copyright 2024 Swedbank AB
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

class CustomDateDecoder: JSONDecoder, @unchecked Sendable {
let dateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(identifier: "UTC")
return formatter
}()

override init() {
super.init()

dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)

if let date = self.dateFormatter.date(from: dateStr) {
return date
}

return Date.distantPast
})
}
}
57 changes: 57 additions & 0 deletions SwedbankPaySDK/Classes/Api/Helpers/NetworkStatusProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Copyright 2024 Swedbank AB
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

struct NetworkStatusProvider {
enum Network: String {
case wifi = "en0"
case cellular = "pdp_ip0"
}

static func getAddress(for network: NetworkStatusProvider.Network) -> String? {
var address: String?

// Get list of all interfaces on the local machine:
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }

// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee

// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

// Check interface name:
let name = String(cString: interface.ifa_name)
if name == network.rawValue {

// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)

return address
}
}
30 changes: 30 additions & 0 deletions SwedbankPaySDK/Classes/Api/Helpers/TimeZone+OffsetFromGMT.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Copyright 2024 Swedbank AB
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

extension TimeZone {
func offsetFromGMT() -> String {
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.timeZone = self
localTimeZoneFormatter.dateFormat = "Z"
return localTimeZoneFormatter.string(from: Date())
}

func minutesFromGMT() -> String {
let minutes = (secondsFromGMT() / 60)
return String(minutes)
}
}
176 changes: 176 additions & 0 deletions SwedbankPaySDK/Classes/Api/Models/IntegrationTask.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//
// Copyright 2024 Swedbank AB
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

struct IntegrationTask: Codable, Hashable {
let rel: IntegrationTaskRel?
let href: String?
let method: String?
let contentType: String?
let expects: [ExpectationModel]?
}

extension Sequence where Iterator.Element == ExpectationModel
{
func firstExpectation(withName name: String) -> ExpectationModel? {
return first(where: { $0.name == name })
}

func value(for name: String) -> String? {
return firstExpectation(withName: name)?.value
}

func contains(name: String) -> Bool {
return firstExpectation(withName: name) != nil
}
}

enum IntegrationTaskRel: Codable, Equatable, Hashable {
case scaMethodRequest
case scaRedirect
case launchClientApp
case walletSdk

case unknown(String)

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let type = try container.decode(String.self)

switch type {
case Self.scaMethodRequest.rawValue: self = .scaMethodRequest
case Self.scaRedirect.rawValue: self = .scaRedirect
case Self.launchClientApp.rawValue: self = .launchClientApp
case Self.walletSdk.rawValue: self = .walletSdk
default: self = .unknown(type)
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

var rawValue: String {
switch self {
case .scaMethodRequest: "sca-method-request"
case .scaRedirect: "sca-redirect"
case .launchClientApp: "launch-client-app"
case .walletSdk: "wallet-sdk"
case .unknown(let value): value
}
}
}

enum ExpectationModel: Codable, Equatable, Hashable {
case string(name: String?, value: String?)
case stringArray(name: String?, value: [String]?)

case unknown(String)

var name: String? {
switch self {
case .string(let name, _):
return name
case .stringArray(let name, _):
return name
case .unknown:
return "unknown"
}
}

var value: String? {
switch self {
case .string(_, let value):
return value
case .stringArray:
return nil
case .unknown:
return nil
}
}

var stringArray: [String]? {
switch self {
case .string:
return nil
case .stringArray(_, let value):
return value
case .unknown:
return nil
}
}


private enum CodingKeys: String, CodingKey {
case name, type, value
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let type = try container.decode(String.self, forKey: .type)
switch type {
case "string":
self = .string(
name: try? container.decode(String?.self, forKey: CodingKeys.name),
value: try? container.decode(String?.self, forKey: CodingKeys.value)
)
case "string[]":
self = .stringArray(
name: try? container.decode(String?.self, forKey: CodingKeys.name),
value: try? container.decode([String]?.self, forKey: CodingKeys.value)
)
default:
self = .unknown(type)
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let name, let value):
try container.encode(name)
try container.encode(value)
case .stringArray(let name, let value):
try container.encode(name)
try container.encode(value)
case .unknown(let type):
try container.encode(type)
}
}
}

extension Array where Element == ExpectationModel {
var httpBody: Data? {
return self.compactMap({
switch $0 {
case .string(let name, let value):
guard let name = name else {
return nil
}

let value = value?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""

return name + "=" + value
default:
return nil
}
})
.joined(separator: "&")
.data(using: .utf8)
}
}
Loading
Loading