Skip to content

Commit

Permalink
Demo app - Make Buliding urlRequests Consistent (#257)
Browse files Browse the repository at this point in the history
* Modify setup token for card 3DS, UI for SCA

* added optional status field and more test cards in UI

* add more 3DS contingency, vaultDelegate 3DS functions

* unit tests, typo in vm

* pr feedback: remove deeplinkURL and add threeDSecureAttempted boolean in CardVaultResult

* pr feedback

* change threeDSecureAttempted to didAttemptThreeDSecureAuthentication

* CHANGELOG and CardVaultRequest visibility

* CHANGELOG for didAttemptThreeDSecureAuthentication name

* fix mistake on vault docString

* return notifyVaultFailure for invalid 3DS url, tests for 3DS url

* steven pr feedback

* extra space in docStrings

* CHANGELOG update for cancel delegate function name

* jax pr feedback, docstrings, sca omit default string

* Sammy pr feedback

* change cancel name to cardThreeDSecureDidCancel

* Demo app - make buliding urlRequests consistent

* remove PaymentTokenRequest

* Jax, Rich PR feedback

* combine if statements

* sammy and jax PR feedback: CHANGELOG

* Jax PR feedback
  • Loading branch information
KunJeongPark authored Feb 28, 2024
1 parent 2baf72b commit 670138c
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 170 deletions.
24 changes: 12 additions & 12 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
3B2027412A8A72050007907E /* VaultState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B2027402A8A72050007907E /* VaultState.swift */; };
3B2027432A8A95EF0007907E /* SetupTokenResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B2027422A8A95EF0007907E /* SetupTokenResultView.swift */; };
3B2027452A8AA78B0007907E /* UpdateSetupTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B2027442A8AA78B0007907E /* UpdateSetupTokenView.swift */; };
3B22E8BA2A842D8900962E34 /* PaymentTokenRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B22E8B92A842D8900962E34 /* PaymentTokenRequest.swift */; };
3B22E8BA2A842D8900962E34 /* PaymentTokenParam.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B22E8B92A842D8900962E34 /* PaymentTokenParam.swift */; };
3B22E8BC2A84397600962E34 /* PaymentTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B22E8BB2A84397600962E34 /* PaymentTokenResponse.swift */; };
3B3C51142B20F7CE009125FE /* PayPalVaultResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3C51132B20F7CE009125FE /* PayPalVaultResultView.swift */; };
3B4DD9A02A892A7000F4A716 /* CardVaultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4DD99F2A892A7000F4A716 /* CardVaultView.swift */; };
Expand Down Expand Up @@ -46,8 +46,8 @@
3BCCFE462A9D47AC00C5102F /* CardExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCCFE452A9D47AC00C5102F /* CardExtensions.swift */; };
3BCCFE492A9D96CA00C5102F /* DemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCCFE482A9D96CA00C5102F /* DemoApp.swift */; };
3BCCFE4B2A9D985F00C5102F /* FeatureSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCCFE4A2A9D985F00C5102F /* FeatureSelectionView.swift */; };
3BDB348E2A7CB02C008100D7 /* SetupTokenRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB348D2A7CB02C008100D7 /* SetupTokenRequest.swift */; };
3BDB34922A7CB5DE008100D7 /* SetupTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB34912A7CB5DE008100D7 /* SetupTokenResponse.swift */; };
3BDB348E2A7CB02C008100D7 /* CreateSetupTokenParam.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB348D2A7CB02C008100D7 /* CreateSetupTokenParam.swift */; };
3BDB34922A7CB5DE008100D7 /* CreateSetupTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB34912A7CB5DE008100D7 /* CreateSetupTokenResponse.swift */; };
3BF999762A8AC093009CBDF2 /* UpdateSetupTokenResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF999752A8AC093009CBDF2 /* UpdateSetupTokenResultView.swift */; };
3BF999782A8AD072009CBDF2 /* CreatePaymentTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF999772A8AD072009CBDF2 /* CreatePaymentTokenView.swift */; };
3BF9997A2A8AE12C009CBDF2 /* PaymentTokenResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF999792A8AE12C009CBDF2 /* PaymentTokenResultView.swift */; };
Expand Down Expand Up @@ -133,7 +133,7 @@
3B2027402A8A72050007907E /* VaultState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultState.swift; sourceTree = "<group>"; };
3B2027422A8A95EF0007907E /* SetupTokenResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupTokenResultView.swift; sourceTree = "<group>"; };
3B2027442A8AA78B0007907E /* UpdateSetupTokenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateSetupTokenView.swift; sourceTree = "<group>"; };
3B22E8B92A842D8900962E34 /* PaymentTokenRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTokenRequest.swift; sourceTree = "<group>"; };
3B22E8B92A842D8900962E34 /* PaymentTokenParam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTokenParam.swift; sourceTree = "<group>"; };
3B22E8BB2A84397600962E34 /* PaymentTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTokenResponse.swift; sourceTree = "<group>"; };
3B3C51132B20F7CE009125FE /* PayPalVaultResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalVaultResultView.swift; sourceTree = "<group>"; };
3B4DD99F2A892A7000F4A716 /* CardVaultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -167,8 +167,8 @@
3BCCFE452A9D47AC00C5102F /* CardExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardExtensions.swift; sourceTree = "<group>"; };
3BCCFE482A9D96CA00C5102F /* DemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoApp.swift; sourceTree = "<group>"; };
3BCCFE4A2A9D985F00C5102F /* FeatureSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureSelectionView.swift; sourceTree = "<group>"; };
3BDB348D2A7CB02C008100D7 /* SetupTokenRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupTokenRequest.swift; sourceTree = "<group>"; };
3BDB34912A7CB5DE008100D7 /* SetupTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupTokenResponse.swift; sourceTree = "<group>"; };
3BDB348D2A7CB02C008100D7 /* CreateSetupTokenParam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSetupTokenParam.swift; sourceTree = "<group>"; };
3BDB34912A7CB5DE008100D7 /* CreateSetupTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSetupTokenResponse.swift; sourceTree = "<group>"; };
3BF999752A8AC093009CBDF2 /* UpdateSetupTokenResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateSetupTokenResultView.swift; sourceTree = "<group>"; };
3BF999772A8AD072009CBDF2 /* CreatePaymentTokenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatePaymentTokenView.swift; sourceTree = "<group>"; };
3BF999792A8AE12C009CBDF2 /* PaymentTokenResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTokenResultView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -394,10 +394,10 @@
80F33CEC26F8E7A9006811B1 /* Order.swift */,
80F33CF026F8E7D9006811B1 /* ProcessOrderParams.swift */,
CBC16DD829ED90B600307117 /* UpdateOrderParams.swift */,
3BDB348D2A7CB02C008100D7 /* SetupTokenRequest.swift */,
3BDB34912A7CB5DE008100D7 /* SetupTokenResponse.swift */,
3BDB348D2A7CB02C008100D7 /* CreateSetupTokenParam.swift */,
3BDB34912A7CB5DE008100D7 /* CreateSetupTokenResponse.swift */,
80E4300B2AD82C8D003CA748 /* ShippingPreference.swift */,
3B22E8B92A842D8900962E34 /* PaymentTokenRequest.swift */,
3B22E8B92A842D8900962E34 /* PaymentTokenParam.swift */,
3B22E8BB2A84397600962E34 /* PaymentTokenResponse.swift */,
);
path = Models;
Expand Down Expand Up @@ -627,7 +627,7 @@
3B80D50E2A291C0800D2EAC4 /* ClientIDRequest.swift in Sources */,
3BA56FF02A9DCCFD0081D14F /* CardOrderApproveView.swift in Sources */,
BE8117682B080472009867B9 /* CurrentState.swift in Sources */,
3BDB348E2A7CB02C008100D7 /* SetupTokenRequest.swift in Sources */,
3BDB348E2A7CB02C008100D7 /* CreateSetupTokenParam.swift in Sources */,
3BA0A58B2B1E240300330681 /* VaultViewModel.swift in Sources */,
80F33CF326F8EA50006811B1 /* DemoSettings.swift in Sources */,
3BA56FE72A9DC9D70081D14F /* CardPaymentViewModel.swift in Sources */,
Expand All @@ -644,7 +644,7 @@
3B8EF4DB2A932DA300A70D0B /* ErrorView.swift in Sources */,
3BF9997A2A8AE12C009CBDF2 /* PaymentTokenResultView.swift in Sources */,
3BA56FFC2A9FEFE90081D14F /* PayPalWebViewModel.swift in Sources */,
3BDB34922A7CB5DE008100D7 /* SetupTokenResponse.swift in Sources */,
3BDB34922A7CB5DE008100D7 /* CreateSetupTokenResponse.swift in Sources */,
3BA56FF22A9DCD440081D14F /* CardApprovalResultView.swift in Sources */,
3B3C51142B20F7CE009125FE /* PayPalVaultResultView.swift in Sources */,
3BA570012AA052E80081D14F /* PayPalWebPaymentsView.swift in Sources */,
Expand All @@ -653,7 +653,7 @@
3BA56FF42A9DCD790081D14F /* CardPaymentView.swift in Sources */,
BE9F36D82745490400AFC7DA /* FloatingLabelTextField.swift in Sources */,
3B20273D2A89E3F00007907E /* CreateSetupTokenView.swift in Sources */,
3B22E8BA2A842D8900962E34 /* PaymentTokenRequest.swift in Sources */,
3B22E8BA2A842D8900962E34 /* PaymentTokenParam.swift in Sources */,
3BA570072AA0DF330081D14F /* PayPalWebButtonsView.swift in Sources */,
3BCCFE492A9D96CA00C5102F /* DemoApp.swift in Sources */,
3BA56FEC2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import Foundation

struct CreateSetupTokenParam: Encodable {

let customer: VaultCustomer?
let paymentSource: PaymentSourceType

enum CodingKeys: String, CodingKey {
case paymentSource = "payment_source"
case customer
}
}

struct VaultExperienceContext: Encodable {

let returnUrl = "sdk.ios.paypal://vault/success"
Expand Down Expand Up @@ -60,37 +71,3 @@ struct VaultCustomer: Encodable {
case id
}
}

struct SetupTokenRequestBody: Encodable {

var customer: VaultCustomer?
let paymentSource: PaymentSourceType

enum CodingKeys: String, CodingKey {
case paymentSource = "payment_source"
case customer
}
}

struct SetUpTokenRequest: Encodable {

let customerID: String?
let paymentSource: PaymentSourceType

var path: String {
"/setup_tokens/"
}

var method: String {
"POST"
}

var headers: [String: String] {
["Content-Type": "application/json"]
}

var body: Data? {
let requestBodyParam = SetupTokenRequestBody(customer: VaultCustomer(id: customerID), paymentSource: paymentSource)
return try? JSONEncoder().encode(requestBodyParam)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Foundation

struct SetUpTokenResponse: Decodable, Equatable {
struct CreateSetupTokenResponse: Decodable, Equatable {

static func == (lhs: SetUpTokenResponse, rhs: SetUpTokenResponse) -> Bool {
static func == (lhs: CreateSetupTokenResponse, rhs: CreateSetupTokenResponse) -> Bool {
lhs.id == rhs.id
}

Expand Down
21 changes: 21 additions & 0 deletions Demo/Demo/Models/PaymentTokenParam.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

struct PaymentTokenParam: Encodable {

let paymentSource: PaymentSource

struct PaymentSource: Encodable {

let token: Token

init(setupTokenID: String) {
token = Token(id: setupTokenID)
}
}

struct Token: Encodable {

let id: String
let type = "SETUP_TOKEN"
}
}
31 changes: 0 additions & 31 deletions Demo/Demo/Models/PaymentTokenRequest.swift

This file was deleted.

114 changes: 28 additions & 86 deletions Demo/Demo/Networking/DemoMerchantAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import CorePayments
/// API Client used to create and process orders on sample merchant server
final class DemoMerchantAPI {

// MARK: Public properties

static let sharedService = DemoMerchantAPI()

// To hardcode an order ID and client ID for this demo app, set the below values
Expand All @@ -16,41 +14,36 @@ final class DemoMerchantAPI {

private init() {}

// MARK: Public Methods

func getSetupToken(
func createSetupToken(
customerID: String? = nil,
selectedMerchantIntegration: MerchantIntegration,
paymentSourceType: PaymentSourceType
) async throws -> SetUpTokenResponse {
) async throws -> CreateSetupTokenResponse {
do {
// TODO: pass in headers depending on integration type
// Different request struct or integration type property
// in SetUpTokenRequest to conditionally add header
let request = SetUpTokenRequest(customerID: customerID, paymentSource: paymentSourceType)
let urlRequest = try createSetupTokenUrlRequest(
setupTokenRequest: request,
environment: DemoSettings.environment,
selectedMerchantIntegration: selectedMerchantIntegration
)

let data = try await data(for: urlRequest)
let requestBody = CreateSetupTokenParam(customer: VaultCustomer(id: customerID), paymentSource: paymentSourceType)

guard let url = buildBaseURL(with: "/setup_tokens") else {
throw URLResponseError.invalidURL
}
// make it mutable to add header fields for partner scenarios
let request = buildURLRequest(method: "POST", url: url, body: requestBody)
let data = try await data(for: request)
return try parse(from: data)
} catch {
print("error with the create setup token request: \(error.localizedDescription)")
throw error
}
}

func getPaymentToken(setupToken: String, selectedMerchantIntegration: MerchantIntegration) async throws -> PaymentTokenResponse {
func createPaymentToken(setupToken: String, selectedMerchantIntegration: MerchantIntegration) async throws -> PaymentTokenResponse {
do {
let request = PaymentTokenRequest(setupToken: setupToken)
let urlRequest = try createPaymentTokenUrlRequest(
paymentTokenRequest: request,
environment: DemoSettings.environment,
selectedMerchantIntegration: selectedMerchantIntegration
)
let data = try await data(for: urlRequest)
let requestBody = PaymentTokenParam(paymentSource: PaymentTokenParam.PaymentSource(setupTokenID: setupToken))
guard let url = buildBaseURL(with: "/payment_tokens") else {
throw URLResponseError.invalidURL
}
// make it mutable to add header value for partner scenarios
let request = buildURLRequest(method: "POST", url: url, body: requestBody)

let data = try await data(for: request)
return try parse(from: data)
} catch {
print("error with the create payment token request: \(error.localizedDescription)")
Expand All @@ -60,10 +53,7 @@ final class DemoMerchantAPI {

func completeOrder(intent: Intent, orderID: String) async throws -> Order {
let intent = intent == .authorize ? "authorize" : "capture"
guard let url = buildBaseURL(
with: "/orders/\(orderID)/\(intent)",
selectedMerchantIntegration: DemoSettings.merchantIntegration
) else {
guard let url = buildBaseURL(with: "/orders/\(orderID)/\(intent)") else {
throw URLResponseError.invalidURL
}

Expand All @@ -73,7 +63,7 @@ final class DemoMerchantAPI {
}

func captureOrder(orderID: String, selectedMerchantIntegration: MerchantIntegration) async throws -> Order {
guard let url = buildBaseURL(with: "/orders/\(orderID)/capture", selectedMerchantIntegration: selectedMerchantIntegration) else {
guard let url = buildBaseURL(with: "/orders/\(orderID)/capture") else {
throw URLResponseError.invalidURL
}

Expand All @@ -83,7 +73,7 @@ final class DemoMerchantAPI {
}

func authorizeOrder(orderID: String, selectedMerchantIntegration: MerchantIntegration) async throws -> Order {
guard let url = buildBaseURL(with: "/orders/\(orderID)/authorize", selectedMerchantIntegration: selectedMerchantIntegration) else {
guard let url = buildBaseURL(with: "/orders/\(orderID)/authorize") else {
throw URLResponseError.invalidURL
}

Expand All @@ -100,7 +90,7 @@ final class DemoMerchantAPI {
if let injectedOrderID = InjectedValues.orderID {
return Order(id: injectedOrderID, status: "CREATED")
}
guard let url = buildBaseURL(with: "/orders", selectedMerchantIntegration: selectedMerchantIntegration) else {
guard let url = buildBaseURL(with: "/orders") else {
throw URLResponseError.invalidURL
}

Expand All @@ -114,9 +104,7 @@ final class DemoMerchantAPI {
/// - updateOrderParams: the parameters to update the order with
/// - Throws: an error explaining why patching the order failed
func updateOrder(_ updateOrderParams: UpdateOrderParams, selectedMerchantIntegration: MerchantIntegration) async throws {
guard let url = buildBaseURL(
with: "/orders/" + updateOrderParams.orderID, selectedMerchantIntegration: selectedMerchantIntegration
) else {
guard let url = buildBaseURL(with: "/orders/" + updateOrderParams.orderID) else {
throw URLResponseError.invalidURL
}
let urlRequest = buildURLRequest(method: "PATCH", url: url, body: updateOrderParams.updateOperations)
Expand Down Expand Up @@ -147,9 +135,9 @@ final class DemoMerchantAPI {
urlRequest.httpMethod = method
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")

if let json = try? encoder.encode(body) {
if method != "GET", let json = try? encoder.encode(body) {
print(String(data: json, encoding: .utf8) ?? "")
urlRequest.httpBody = json
urlRequest.httpBody = json
}

return urlRequest
Expand All @@ -174,8 +162,8 @@ final class DemoMerchantAPI {
}
}

private func buildBaseURL(with endpoint: String, selectedMerchantIntegration: MerchantIntegration = .direct) -> URL? {
return URL(string: DemoSettings.environment.baseURL + selectedMerchantIntegration.path + endpoint)
private func buildBaseURL(with endpoint: String) -> URL? {
return URL(string: DemoSettings.environment.baseURL + DemoSettings.merchantIntegration.path + endpoint)
}

private func buildPayPalURL(with endpoint: String) -> URL? {
Expand Down Expand Up @@ -224,50 +212,4 @@ final class DemoMerchantAPI {
}
return request
}

private func createSetupTokenUrlRequest(
setupTokenRequest: SetUpTokenRequest,
environment: Demo.Environment,
selectedMerchantIntegration: MerchantIntegration
) throws -> URLRequest {
var completeUrl = environment.baseURL
completeUrl += selectedMerchantIntegration.path
completeUrl.append(contentsOf: setupTokenRequest.path)

guard let url = URL(string: completeUrl) else {
throw URLResponseError.invalidURL
}

var request = URLRequest(url: url)
request.httpMethod = setupTokenRequest.method
request.httpBody = setupTokenRequest.body
setupTokenRequest.headers.forEach { key, value in
request.addValue(value, forHTTPHeaderField: key)
}

return request
}

private func createPaymentTokenUrlRequest(
paymentTokenRequest: PaymentTokenRequest,
environment: Demo.Environment,
selectedMerchantIntegration: MerchantIntegration
) throws -> URLRequest {
var completeUrl = environment.baseURL
completeUrl += selectedMerchantIntegration.path
completeUrl.append(contentsOf: paymentTokenRequest.path)

guard let url = URL(string: completeUrl) else {
throw URLResponseError.invalidURL
}

var request = URLRequest(url: url)
request.httpMethod = paymentTokenRequest.method
request.httpBody = paymentTokenRequest.body
paymentTokenRequest.headers.forEach { key, value in
request.addValue(value, forHTTPHeaderField: key)
}

return request
}
}
Loading

0 comments on commit 670138c

Please sign in to comment.