Skip to content

Commit

Permalink
Added connectionOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
AJ9 committed Nov 7, 2023
1 parent 7dffb84 commit d3bb666
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 4 deletions.
5 changes: 4 additions & 1 deletion Gr4vy UIKit Sample App/CheckoutViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ class CheckoutViewController: UIViewController {
borderWidths: Gr4vyBorderWidths(container: "thin", input: "thin"),
radii: Gr4vyRadii(container: "subtle", input: "subtle"),
shadows: Gr4vyShadows(focusRing: "0 0 0 2px #ffffff, 0 0 0 4px #4844ff")),
debugMode: true
connectionOptions: Gr4vyConnectionOptions(data: [
"forter-anti-fraud": [
"is_guest_buyer": false]]),
debugMode: true
) else {
print("Unable to load Gr4vy")
return
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ These are the parameteres available on the `launch` method:
| `requireSecurityCode`| `Optional` | An optional boolean which forces security code to be prompted for stored card payments. |
| `shippingDetailsId`| `Optional` | An optional unique identifier of a set of shipping details stored for the buyer. |
| `merchantAccountId`| `Optional` | An optional merchant account ID. |
| `connectionOptions`| `Optional` | An optional set of options passed to a connection when processing a transaction (see https://docs.gr4vy.com/reference#operation/authorize-new-transaction) |
| `debugMode`| `Optional` | `true`, `false`. Defaults to `false`, this prints to the console. |
| `onEvent` | `Optional` | **Please see below for more details.** |

Expand Down
4 changes: 4 additions & 0 deletions gr4vy-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
59160F7A2AF6EDDD00143DA2 /* Gr4vyConnectionOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59160F792AF6EDDD00143DA2 /* Gr4vyConnectionOptions.swift */; };
592C4BBC28BF9AEE00063AC7 /* Gr4vy_SwiftUI_Sample_App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 592C4BBB28BF9AEE00063AC7 /* Gr4vy_SwiftUI_Sample_App.swift */; };
592C4BC028BF9AEF00063AC7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 592C4BBF28BF9AEF00063AC7 /* Assets.xcassets */; };
592C4BC328BF9AEF00063AC7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 592C4BC228BF9AEF00063AC7 /* Preview Assets.xcassets */; };
Expand Down Expand Up @@ -96,6 +97,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
59160F792AF6EDDD00143DA2 /* Gr4vyConnectionOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gr4vyConnectionOptions.swift; sourceTree = "<group>"; };
592C4BB928BF9AEE00063AC7 /* Gr4vy SwiftUI Sample App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Gr4vy SwiftUI Sample App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
592C4BBB28BF9AEE00063AC7 /* Gr4vy_SwiftUI_Sample_App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gr4vy_SwiftUI_Sample_App.swift; sourceTree = "<group>"; };
592C4BBF28BF9AEF00063AC7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -251,6 +253,7 @@
59C5D45D29E34D32008FFEBC /* Gr4vyStatementDescriptor.swift */,
59C5D45F29E34D56008FFEBC /* Gr4vySetup.swift */,
59C5D46129F162C0008FFEBC /* Gr4vyStore.swift */,
59160F792AF6EDDD00143DA2 /* Gr4vyConnectionOptions.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -478,6 +481,7 @@
files = (
59C5D45C29E34D19008FFEBC /* Gr4vyPaymentSource.swift in Sources */,
59C5D45A29E34D04008FFEBC /* Gr4vyCartItem.swift in Sources */,
59160F7A2AF6EDDD00143DA2 /* Gr4vyConnectionOptions.swift in Sources */,
59FE69502722BB7A006C1C08 /* Gr4vy.swift in Sources */,
59F72C012A17BE2B00DD2833 /* UIImage+Extensions.swift in Sources */,
5978B28A271C7F1300F5CC00 /* gr4vy_iOS.docc in Sources */,
Expand Down
4 changes: 3 additions & 1 deletion gr4vy-iOS/Gr4vy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class Gr4vy {
requireSecurityCode: Bool? = nil,
shippingDetailsId: String? = nil,
merchantAccountId: String? = nil,
connectionOptions: Gr4vyConnectionOptions? = nil,
debugMode: Bool = false,
onEvent: Gr4vyCompletionHandler? = nil) {

Expand All @@ -80,7 +81,8 @@ public class Gr4vy {
statementDescriptor: statementDescriptor,
requireSecurityCode: requireSecurityCode,
shippingDetailsId: shippingDetailsId,
merchantAccountId: merchantAccountId)
merchantAccountId: merchantAccountId,
connectionOptions: connectionOptions)

self.debugMode = debugMode
self.onEvent = onEvent
Expand Down
4 changes: 4 additions & 0 deletions gr4vy-iOS/Gr4vyUtility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ struct Gr4vyUtility {
optionalData = optionalData + ", merchantAccountId: '\(merchantAccountId)'"
}

if let connectionOptions = setup.connectionOptions, let connectionOptionsString = connectionOptions.convertedString {
optionalData = optionalData + ", connectionOptions: \(connectionOptionsString)"
}

let content =
"window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.\(setup.instance).gr4vy.app', apiUrl: 'https://api.\(setup.instance).gr4vy.app', token: '\(setup.token)', amount: \(setup.amount), country: '\(setup.country)', currency: '\(setup.currency)'"
+
Expand Down
35 changes: 35 additions & 0 deletions gr4vy-iOS/Models/Gr4vyConnectionOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Gr4vyConnectionOptions.swift
// gr4vy-ios
//
//

import Foundation

public struct Gr4vyConnectionOptions {
var data: [String: [String: Any]]

public init(data: [String : [String : Any]]) {
self.data = data
}

var convertedString: String? {

guard !data.isEmpty else {
return nil
}
let jsonData: Data
do {
jsonData = try JSONSerialization.data(withJSONObject: data)
} catch {
return nil
}


if !jsonData.isEmpty, let jsonString = String(data: jsonData, encoding: .utf8), !jsonString.isEmpty {
return jsonString.replacingOccurrences(of: "\"", with: "'")
} else {
return nil
}
}
}
4 changes: 3 additions & 1 deletion gr4vy-iOS/Models/Gr4vySetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ struct Gr4vySetup {
var requireSecurityCode: Bool?
var shippingDetailsId: String?
var merchantAccountId: String?
var connectionOptions: Gr4vyConnectionOptions?
var instance: String {
return environment == .production ? gr4vyId : "sandbox.\(gr4vyId)"
}

public init(gr4vyId: String, token: String, amount: Int, currency: String, country: String, buyerId: String? = nil, environment: Gr4vyEnvironment, externalIdentifier: String? = nil, store: Gr4vyStore? = nil, display: String? = nil, intent: String? = nil, metadata: [String : String]? = nil, paymentSource: Gr4vyPaymentSource? = nil, cartItems: [Gr4vyCartItem]? = nil, applePayMerchantId: String? = nil, theme: Gr4vyTheme? = nil, buyerExternalIdentifier: String? = nil, locale: String? = nil, statementDescriptor: Gr4vyStatementDescriptor? = nil, requireSecurityCode: Bool? = nil, shippingDetailsId: String? = nil, merchantAccountId: String? = nil) {
public init(gr4vyId: String, token: String, amount: Int, currency: String, country: String, buyerId: String? = nil, environment: Gr4vyEnvironment, externalIdentifier: String? = nil, store: Gr4vyStore? = nil, display: String? = nil, intent: String? = nil, metadata: [String : String]? = nil, paymentSource: Gr4vyPaymentSource? = nil, cartItems: [Gr4vyCartItem]? = nil, applePayMerchantId: String? = nil, theme: Gr4vyTheme? = nil, buyerExternalIdentifier: String? = nil, locale: String? = nil, statementDescriptor: Gr4vyStatementDescriptor? = nil, requireSecurityCode: Bool? = nil, shippingDetailsId: String? = nil, merchantAccountId: String? = nil, connectionOptions: Gr4vyConnectionOptions? = nil) {
self.gr4vyId = gr4vyId
self.token = token
self.amount = amount
Expand All @@ -55,5 +56,6 @@ struct Gr4vySetup {
self.requireSecurityCode = requireSecurityCode
self.shippingDetailsId = shippingDetailsId
self.merchantAccountId = merchantAccountId
self.connectionOptions = connectionOptions
}
}
106 changes: 106 additions & 0 deletions gr4vy-iOSTests/gr4vy_iOSTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,112 @@ class gr4vy_iOSTests: XCTestCase {
sut = Gr4vyUtility.handleOpenLink(from: payload)
XCTAssertNotNil(sut)
}

func testConnectionOptionsEncoding() {

let permutations = [
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1"]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": 1]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1", "subKey2": 1]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1", "subKey2": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": 1, "subKey2": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1", "subKey2": 1, "subKey3": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1"], "key2": ["subKey1": 1]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1"], "key2": ["subKey1": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": 1], "key2": ["subKey1": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1", "subKey2": 1], "key2": ["subKey1": 1, "subKey2": true]]),
Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1", "subKey2": true], "key2": ["subKey1": 1, "subKey2": "value2"]])
]

for obj in permutations {
XCTAssertNotNil(obj.convertedString)
}
}

func testGenerateUpdateOptionsSucceedsWithConnectionOptionsPermutations() {

struct ConnectionOptionsTestWrapper {
var input: Gr4vyConnectionOptions
var expectedOutput: String
}

let permutations: [ConnectionOptionsTestWrapper] = [
ConnectionOptionsTestWrapper(input: Gr4vyConnectionOptions(data: ["key1": ["subKey1": "value1"]]), expectedOutput: "connectionOptions: {'key1':{'subKey1':'value1'}"),
ConnectionOptionsTestWrapper(input: Gr4vyConnectionOptions(data: ["key1": ["subKey1": 1]]), expectedOutput: "connectionOptions: {'key1':{'subKey1':1}"),
ConnectionOptionsTestWrapper(input: Gr4vyConnectionOptions(data: ["key1": ["subKey1": true]]), expectedOutput: "connectionOptions: {'key1':{'subKey1':true}"),
]

var sut = Gr4vyUtility.generateUpdateOptions(from: setup)

for permutation in permutations {
setup.connectionOptions = permutation.input
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertTrue(sut.contains(permutation.expectedOutput))
}
}

func testA() {
setup.connectionOptions = Gr4vyConnectionOptions(data: [
"adyen-card": ["additionalData": "value"]
])
var sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'adyen-card':{'additionalData':'value'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"cybersource-anti-fraud": ["merchant_defined_data": "value"]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'cybersource-anti-fraud':{'merchant_defined_data':'value'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"forter-anti-fraud": ["delivery_method": "email"]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'forter-anti-fraud':{'delivery_method':'email'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"forter-anti-fraud": ["delivery_type": "PHYSICAL"]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'forter-anti-fraud':{'delivery_type':'PHYSICAL'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"paypal-paypal": ["additional_data": "value"],
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'paypal-paypal':{'additional_data':'value'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"paypal-paypalpaylater": ["additional_data": "value"]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'paypal-paypalpaylater':{'additional_data':'value'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"int": ["additional_data": 1]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'int':{'additional_data':1}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"bool": ["additional_data": true]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'bool':{'additional_data':true}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [
"string": ["additional_data": "gr4vy"]
])
sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0, connectionOptions: {'string':{'additional_data':'gr4vy'}}},})", sut)

setup.connectionOptions = Gr4vyConnectionOptions(data: [:])

sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ channel: 123, type: 'updateOptions', data: { apiHost: 'api.ID123.gr4vy.app', apiUrl: 'https://api.ID123.gr4vy.app', token: 'TOKEN123', amount: 100, country: 'GB', currency: 'GBP', buyerId: 'BUYER123', supportedApplePayVersion: 0},})", sut)

}
}

extension gr4vy_iOSTests {
Expand Down
2 changes: 1 addition & 1 deletion gr4vy-ios.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'gr4vy-ios'
s.version = '1.6.3'
s.version = '1.7.0'
s.license = 'MIT'
s.summary = 'Quickly embed Gr4vy in your iOS app to store card details, authorize payments, and capture a transaction.'
s.homepage = 'https://github.com/gr4vy/gr4vy-ios'
Expand Down

0 comments on commit d3bb666

Please sign in to comment.