Skip to content

Commit

Permalink
task: add support for guest checkout buyer (#14)
Browse files Browse the repository at this point in the history
* Pass buyer option

* Add missing properties and address / taxId structs

* Add buyer tests

* Bump pod version

* Separate billing and shipping details structs

* Add test for shippingDetails

* Update README
  • Loading branch information
luca-gr4vy authored Aug 26, 2024
1 parent e27aa88 commit 9c297ce
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ These are the parameteres available on the `launch` method:
| `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) |
| `connectionOptionsString`| `Optional` | A JSON String of connectionOptions |
| `buyer`| `Optional` | An optional buyer object to allow guest checkout (see https://docs.gr4vy.com/reference/transactions/new-transaction) |
| `debugMode`| `Optional` | `true`, `false`. Defaults to `false`, this prints to the console. |
| `onEvent` | `Optional` | **Please see below for more details.** |

Expand Down
8 changes: 6 additions & 2 deletions gr4vy-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
59FE69722722C131006C1C08 /* gr4vy_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5978B285271C7F1300F5CC00 /* gr4vy_ios.framework */; };
59FE69732722C131006C1C08 /* gr4vy_ios.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5978B285271C7F1300F5CC00 /* gr4vy_ios.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
59FE6978272C1188006C1C08 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FE6977272C1188006C1C08 /* SettingsViewController.swift */; };
81125F232C6C9C4500C26DDA /* Gr4vyBuyer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81125F222C6C9C4500C26DDA /* Gr4vyBuyer.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -137,6 +138,7 @@
59FE69692722BBD7006C1C08 /* Gr4vyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gr4vyViewController.swift; sourceTree = "<group>"; };
59FE696B2722BC38006C1C08 /* CheckoutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutViewController.swift; sourceTree = "<group>"; };
59FE6977272C1188006C1C08 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
81125F222C6C9C4500C26DDA /* Gr4vyBuyer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gr4vyBuyer.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -254,6 +256,7 @@
59C5D45F29E34D56008FFEBC /* Gr4vySetup.swift */,
59C5D46129F162C0008FFEBC /* Gr4vyStore.swift */,
59160F792AF6EDDD00143DA2 /* Gr4vyConnectionOptionsValue.swift */,
81125F222C6C9C4500C26DDA /* Gr4vyBuyer.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -404,7 +407,7 @@
};
};
};
buildConfigurationList = 5978B27F271C7F1300F5CC00 /* Build configuration list for PBXProject "gr4vy-ios" */;
buildConfigurationList = 5978B27F271C7F1300F5CC00 /* Build configuration list for PBXProject "gr4vy-iOS" */;
compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
Expand Down Expand Up @@ -493,6 +496,7 @@
59C5D45529E34C51008FFEBC /* Gr4vyTheme.swift in Sources */,
598D6A4A272F3AEF00EE5777 /* Gr4vyMessage.swift in Sources */,
59C5D46229F162C0008FFEBC /* Gr4vyStore.swift in Sources */,
81125F232C6C9C4500C26DDA /* Gr4vyBuyer.swift in Sources */,
59FE696A2722BBD7006C1C08 /* Gr4vyViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -916,7 +920,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5978B27F271C7F1300F5CC00 /* Build configuration list for PBXProject "gr4vy-ios" */ = {
5978B27F271C7F1300F5CC00 /* Build configuration list for PBXProject "gr4vy-iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5978B297271C7F1400F5CC00 /* Debug */,
Expand Down
4 changes: 3 additions & 1 deletion gr4vy-iOS/Gr4vy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class Gr4vy {
merchantAccountId: String? = nil,
connectionOptions: [String: [String: Gr4vyConnectionOptionsValue]]? = nil,
connectionOptionsString: String? = nil,
buyer: Gr4vyBuyer? = nil,
debugMode: Bool = false,
onEvent: Gr4vyCompletionHandler? = nil) {

Expand All @@ -85,7 +86,8 @@ public class Gr4vy {
requireSecurityCode: requireSecurityCode,
shippingDetailsId: shippingDetailsId,
merchantAccountId: merchantAccountId,
connectionOptions: Gr4vyUtility.getConnectionOptions(from: connectionOptions, connectionOptionsString: connectionOptionsString))
connectionOptions: Gr4vyUtility.getConnectionOptions(from: connectionOptions, connectionOptionsString: connectionOptionsString),
buyer: buyer)

self.debugMode = debugMode
self.onEvent = onEvent
Expand Down
119 changes: 119 additions & 0 deletions gr4vy-iOS/Models/Gr4vyBuyer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// Gr4vyBuyer.swift
// gr4vy-ios
//

import Foundation

public struct Gr4vyBuyer: Codable {
let displayName: String?
let externalIdentifier: String?
let billingDetails: Gr4vyBillingDetails?
let shippingDetails: Gr4vyShippingDetails?

public init(
displayName: String? = nil,
externalIdentifier: String? = nil,
billingDetails: Gr4vyBillingDetails? = nil,
shippingDetails: Gr4vyShippingDetails? = nil
) {
self.displayName = displayName
self.externalIdentifier = externalIdentifier
self.billingDetails = billingDetails
self.shippingDetails = shippingDetails
}
}

public struct Gr4vyBillingDetails: Codable {
let firstName: String?
let lastName: String?
let emailAddress: String?
let phoneNumber: String?
let address: Gr4vyAddress?
let taxId: Gr4vyTaxId?

public init(
firstName: String? = nil,
lastName: String? = nil,
emailAddress: String? = nil,
phoneNumber: String? = nil,
address: Gr4vyAddress? = nil,
taxId: Gr4vyTaxId? = nil
) {
self.firstName = firstName
self.lastName = lastName
self.emailAddress = emailAddress
self.phoneNumber = phoneNumber
self.address = address
self.taxId = taxId
}
}

public struct Gr4vyShippingDetails: Codable {
let firstName: String?
let lastName: String?
let emailAddress: String?
let phoneNumber: String?
let address: Gr4vyAddress?

public init(
firstName: String? = nil,
lastName: String? = nil,
emailAddress: String? = nil,
phoneNumber: String? = nil,
address: Gr4vyAddress? = nil
) {
self.firstName = firstName
self.lastName = lastName
self.emailAddress = emailAddress
self.phoneNumber = phoneNumber
self.address = address
}
}

public struct Gr4vyAddress: Codable {
let houseNumberOrName: String?
let line1: String?
let line2: String?
let organization: String?
let city: String?
let postalCode: String?
let country: String?
let state: String?
let stateCode: String?

public init(
houseNumberOrName: String? = nil,
line1: String? = nil,
line2: String? = nil,
organization: String? = nil,
city: String? = nil,
postalCode: String? = nil,
country: String? = nil,
state: String? = nil,
stateCode: String? = nil
) {
self.houseNumberOrName = houseNumberOrName
self.line1 = line1
self.line2 = line2
self.organization = organization
self.city = city
self.postalCode = postalCode
self.country = country
self.state = state
self.stateCode = stateCode
}
}

public struct Gr4vyTaxId: Codable {
let value: String?
let kind: String?

public init(
value: String? = nil,
kind: String? = nil
) {
self.value = value
self.kind = kind
}
}
5 changes: 4 additions & 1 deletion gr4vy-iOS/Models/Gr4vySetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct Gr4vySetup: Encodable {
var shippingDetailsId: String?
var merchantAccountId: String?
var connectionOptions: [String: [String: Gr4vyConnectionOptionsValue]]?
var buyer: Gr4vyBuyer?
var apiHost: String?
var apiUrl: String?
var supportedApplePayVersion: Int = 0
Expand Down Expand Up @@ -58,12 +59,13 @@ struct Gr4vySetup: Encodable {
case shippingDetailsId
case merchantAccountId
case connectionOptions
case buyer
case apiHost
case apiUrl
case supportedApplePayVersion
}

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, applePayMerchantName: String? = nil, theme: Gr4vyTheme? = nil, buyerExternalIdentifier: String? = nil, locale: String? = nil, statementDescriptor: Gr4vyStatementDescriptor? = nil, requireSecurityCode: Bool? = nil, shippingDetailsId: String? = nil, merchantAccountId: String? = nil, connectionOptions: [String: [String: Gr4vyConnectionOptionsValue]]? = 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, applePayMerchantName: String? = nil, theme: Gr4vyTheme? = nil, buyerExternalIdentifier: String? = nil, locale: String? = nil, statementDescriptor: Gr4vyStatementDescriptor? = nil, requireSecurityCode: Bool? = nil, shippingDetailsId: String? = nil, merchantAccountId: String? = nil, connectionOptions: [String: [String: Gr4vyConnectionOptionsValue]]? = nil, buyer: Gr4vyBuyer? = nil) {
self.gr4vyId = gr4vyId
self.token = token
self.amount = amount
Expand All @@ -88,5 +90,6 @@ struct Gr4vySetup: Encodable {
self.shippingDetailsId = shippingDetailsId
self.merchantAccountId = merchantAccountId
self.connectionOptions = connectionOptions
self.buyer = buyer
}
}
48 changes: 48 additions & 0 deletions gr4vy-iOSTests/gr4vy_iOSTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,54 @@ class gr4vy_iOSTests: XCTestCase {
XCTAssertEqual(Gr4vyUtility.getConnectionOptions(from: permutation, connectionOptionsString: "{\"keyA\":{\"subKeyA\":\"valueA\"}}"), permutation)
}
}

func testGenerateUpdateOptionsSucceedsWithBuyer() {
setup.buyerId = nil
setup.buyer = Gr4vyBuyer()

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

setup.buyer = Gr4vyBuyer(displayName: "displayName", externalIdentifier: "externalIdentifier")

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

setup.buyer = Gr4vyBuyer(billingDetails: Gr4vyBillingDetails(firstName: "firstName"))

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

setup.buyer = Gr4vyBuyer(billingDetails: Gr4vyBillingDetails(lastName: "lastName"))

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

setup.buyer = Gr4vyBuyer(billingDetails: Gr4vyBillingDetails(emailAddress: "emailAddress"))

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

setup.buyer = Gr4vyBuyer(billingDetails: Gr4vyBillingDetails(phoneNumber: "phoneNumber"))

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

setup.buyer = Gr4vyBuyer(billingDetails: Gr4vyBillingDetails(address: Gr4vyAddress(houseNumberOrName: "houseNumberOrName", line1: "line1", line2: "line2", organization: "organization", city: "city", postalCode: "postalCode", country: "country", state: "state", stateCode: "stateCode")))

sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ \"channel\": 123, \"type\": \"updateOptions\", \"data\": {\"amount\":100,\"apiHost\":\"api.ID123.gr4vy.app\",\"apiUrl\":\"https:\\/\\/api.ID123.gr4vy.app\",\"buyer\":{\"billingDetails\":{\"address\":{\"city\":\"city\",\"country\":\"country\",\"houseNumberOrName\":\"houseNumberOrName\",\"line1\":\"line1\",\"line2\":\"line2\",\"organization\":\"organization\",\"postalCode\":\"postalCode\",\"state\":\"state\",\"stateCode\":\"stateCode\"}}},\"country\":\"GB\",\"currency\":\"GBP\",\"supportedApplePayVersion\":0,\"token\":\"TOKEN123\"}})", sut)

setup.buyer = Gr4vyBuyer(billingDetails: Gr4vyBillingDetails(taxId: Gr4vyTaxId(value: "value", kind: "kind")))

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

setup.buyer = Gr4vyBuyer(shippingDetails: Gr4vyShippingDetails(firstName: "firstName", lastName: "lastName", address: Gr4vyAddress(houseNumberOrName: "houseNumberOrName", line1: "line1", line2: "line2", organization: "organization", city: "city", postalCode: "postalCode", country: "country", state: "state", stateCode: "stateCode")))

sut = Gr4vyUtility.generateUpdateOptions(from: setup)
XCTAssertEqual("window.postMessage({ \"channel\": 123, \"type\": \"updateOptions\", \"data\": {\"amount\":100,\"apiHost\":\"api.ID123.gr4vy.app\",\"apiUrl\":\"https:\\/\\/api.ID123.gr4vy.app\",\"buyer\":{\"shippingDetails\":{\"address\":{\"city\":\"city\",\"country\":\"country\",\"houseNumberOrName\":\"houseNumberOrName\",\"line1\":\"line1\",\"line2\":\"line2\",\"organization\":\"organization\",\"postalCode\":\"postalCode\",\"state\":\"state\",\"stateCode\":\"stateCode\"},\"firstName\":\"firstName\",\"lastName\":\"lastName\"}},\"country\":\"GB\",\"currency\":\"GBP\",\"supportedApplePayVersion\":0,\"token\":\"TOKEN123\"}})", 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 = '2.3.0'
s.version = '2.4.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 9c297ce

Please sign in to comment.